대학에서나 자격증 시험을 준비하면서나 너무나(?) 복잡한 TCP의 3 way handshake 는 반복하면 익숙해 지겠지로 넘어가곤 했다. 금번에는 직접 실습 가능한 도구가 있어 패킷을 생성해 전송해 가며 실습해 봤다.
아직 해결하지 못한 부분이 있지만, 그럭저럭 패킷을 만드는 데에는 성공했다.
실습하는데에 이용한 프로그램은 소켓 통신을 이용한 채팅 프로그램이다. 서버 포트는 고정으로 3490이고, 클라이언트는 변동되는데, 대충 5531로 했다.
패킷을 만들기 위해 scapy를 이용했다.
1. syn 보내기
syn = IP(dst='채팅서버') / TCP(sport=5531, dport=3490, flags='S')
syn_ack = sr1(syn)
sr1은 lsc()를 입력해 Send packets at layer 3 and return only the first answer 라는 답을 얻을 수 있다. 레이어 3(네트워크 계층, IP)에서 패킷을 보내고 처음 응답 값을 받는 것이다. sr 명령을 사용해도 되는데, 2개 이상 나오면 배열 형태로 잘라써야하고, 두 번째 응답은 없으니 편의상 sr1을 쓴다.(참고한 예제가 sr1을 써서 그래도 쓴 이유도 있다.)
2. SYN/ACK 에 대한 ACK 보내기
1에서 SYN을 보내면, SYN/ACK가 온다. 그럼 ACK를 주면 3way handshake 가 완성된다.
ack = IP(dst='채팅서버') / TCP(dport=syn_ack[TCP].sport, sport=syn_ack[TCP].dport, seq=syn_ack[TCP].ack, ack=syn_ack[TCP].seq + 1, flags='A')
req_ack = sr1(ack)
syn 보다 ack가 뭔가 더 복잡하다. 일단 TCP는 연결지향적인 프로토콜이므로 seq, ack 번호를 맞춰주기 위해 sr1로 syn을 보내면서 응답받은 sys_ack의 값을 참고해, seq, ack를 설정한다. 처음 연결이라면 거의 1이기 때문에 큰 문제가 안되지만 연속적으로 시도할 경우 번호를 맞춰줘야 한다. 안그러면 와이어샤크에서 검은색 라인을 보게 된다.
[SYN 보내고, SYN, ACK 받았다. 바로 ACK를 보내기엔 손이 느려서 패킷 설정하고, sr1 명령어로 보내면 ACK 된다.]
3. 채팅하기
여기서 끝이면 좋겠지만, 서버에서 클라이언트에게 psh, ack 를 보낸다. 이 패킷에 대해서도 ack를 보내줘야하는데, 캡처해서 seq나 ack를 받야하는지, 이미 비연결적인 프로그램으로 하는 것이라 안되는지 ack가 안된다.
이를 무시하고 data 영역에 별도 스택 없이 text를 실어 보낸다.
ack_psh = IP(dst='채팅서버') / TCP(dport=req_ack[TCP].sport, sport=req_ack[TCP].dport, seq=req_ack[TCP].ack, ack=req_ack[TCP].seq + 1, flags='AP')/"aaaan"
req_ack = sr1(ack_psh)
[서버에 보낼 메시지를 만들어 보내면 위와 같다.]
플래그에 P를 추가해주면 push 플래그가 set 된다.
[ACK, PSH가 set 되어 서버에서 접속되었다고 반향하는 메시지가 있는데, ack 하는 방법이 있는지 ...........]
[ACK가 안오면 서버에서 계속 ACK를 달라고 한다. 그래도 무시하고 메시지를 보내면 TCP ACKed unseen segment 라고 나오는 것 같은데, 메시지가 가긴 간다
4. 접속 종료
rs = IP(dst='채팅서버') / TCP(sport=5533, dport=3490, flags='R', seq=1, ack=257)
reset = sr1(rs)
TCP 플래그는 6개를 쓴다고 알려졌는데, 실제로 보면 더 많다. 아무튼 주로 쓰는 것은 S, A, R, P 로 생각된다. F도 종종 쓰지 않을까 한다. 종료할 때, 채팅 클라이언트는 R을 보내므로, 나도 R을 보내봤다.
[서버는 잘 응답한다. 안녕 난 client 프로그램이 아니고, scapy 라고 해]
댓글 달기