一、TCP協議介紹
?? 見TCP流量分析篇
??TCP 流量分析 - 簡書 (jianshu.com)
二、實驗環(huán)境
??使用一臺windows主機作為TCP Server,使用一臺Linux作為TCP Client,發(fā)起TCP連接,發(fā)送數據,結束連接。
三、Python實現
3.1 TCP Server
??以下Python腳本通過Socket實現TCP Server端,接收TCP連接。
#!/usr/bin/python3.4
# -*- coding=utf-8 -*-
from socket import *
myHost = '192.168.10.113'
myPort = 6666
#創(chuàng)建TCP Socket,
sockobj = socket(AF_INET, SOCK_STREAM)
#綁定套接字到地址
sockobj.bind((myHost, myPort))
#在拒絕連接前,操作系統(tǒng)可以掛起的最大連接數量,一般配置為5
sockobj.listen(5)
while True:#
#接受TCP連接
print("等待客戶端連接。。。")
connection, address = sockobj.accept()
#打印連接客戶端的IP地址
print('Server Connected by', address)
while True:
data = connection.recv(1024)#接收數據
if not data: break#如果沒有數據就退出循環(huán)
connection.send(b'Echo==>' + data)#發(fā)送回顯數據給客戶
connection.close()#關閉連接
3.2 TCP Client
??以下Python腳本通過Scapy實現TCP Client端,向Server端發(fā)起TCP連接。
#!/usr/bin/python3.4
# -*- coding=utf-8 -*-
import re
from kamene.all import *
def tcp_connection(ip, port):
dstport = port
sportid = random.randint(1024, 65535)
seqid = random.randint(1, 65535*65535)
#產生SYN包(FLAG = 2 為SYN)并等待回應####源端口調用隨機端口,序列號調用隨機序列號
syn_pkt=IP(dst=ip)/TCP(dport=dstport,sport=sportid,flags=2,seq=seqid)#第一個包(SYN)
print("C:SYN:\n",syn_pkt.summary())
result_raw_synack = sr1(syn_pkt, timeout=1, verbose=False)#接收第二個包(SYN ACk)
print("S:SYN_ACK:\n",result_raw_synack.summary())
#由于SYN算一個字節(jié),所以客戶到服務器序列號(sc_sn)需要增加1
sc_sn = result_raw_synack.getlayer(TCP).fields['seq'] + 1#獲取SYN ACK包的序列號作為ACK包的ack號
cs_sn = result_raw_synack.getlayer(TCP).fields['ack']#獲取SYN ACK包的ack號作為ACK包的seq號
#發(fā)送ACK(flag = 16),完成三次握手!
ack_pkt=IP(dst=ip)/TCP(dport=dstport,sport=sportid,flags=16,seq=cs_sn,ack=sc_sn)
print("C:ACK:\n",ack_pkt.summary())
send(ack_pkt, verbose = False)#發(fā)送第三個包(ACK)
#發(fā)送數據
msg_pkt = IP(dst=ip) / TCP(dport=dstport, sport=sportid, flags=24, seq=cs_sn, ack=sc_sn) / b"Hello Word"
print("C:msg:\n",msg_pkt.summary())
#print("msg\n",msg_pkt.show())
result_raw_msg = sr1(msg_pkt, verbose=False, timeout=1)#接收服務端的響應
print("S:msg_ack:\n",result_raw_msg.summary())
ip_len=result_raw_msg.getlayer(IP).fields["len"]#IP數據包總長度
ip_ihl=result_raw_msg.getlayer(IP).fields["ihl"]#IP首部長度
tcp_hl=result_raw_msg.getlayer(TCP).fields["dataofs"]#IP首部長度
#如果回顯數據中有‘Echo’字段就打印回顯內容
if re.search(b'Echo', result_raw_msg.getlayer(Raw).fields['load']):
print("Server Echo",result_raw_msg.getlayer(Raw).fields['load'])
#計算數據長度,ip總長度 - ip頭部長度(['ihl']*4) - tcp頭部長度(['dataofs']*4)
data_len = ip_len - ip_ihl*4 -tcp_hl*4
#客戶到服務器端的序列號為,服務器回顯中的‘seq’加上傳輸的數據長度!
sc_sn = result_raw_msg.getlayer(TCP).fields['seq'] + data_len
cs_sn = result_raw_msg.getlayer(TCP).fields['ack']
#發(fā)送ACK對服務器的回顯進行確認,flag = 16(ACK)
raw_msg_ack_pkt = IP(dst=ip)/TCP(dport=dstport,sport=sportid,flags=16,seq=cs_sn,ack=sc_sn)
print("C:ack:\n",raw_msg_ack_pkt.summary())
send(raw_msg_ack_pkt, verbose = False)
#客戶端主動發(fā)送FIN(1) + ACK(16),進行連接終結。
fin_pkt = IP(dst=ip)/TCP(dport=dstport,sport=sportid,flags=17,seq=cs_sn,ack=sc_sn)
print("C:FIN:\n",fin_pkt.summary())
# #由于FIN算一個字節(jié),所以客戶到服務器序列號(sc_sn)需要增加1
result_raw_fin_list=sr(fin_pkt,multi=1, timeout=1,verbose = False)
result_raw_fin_ack=(result_raw_fin_list[0].res)[0]
result_raw_fin_fin = (result_raw_fin_list[0].res)[1]
print("S:ack\n",result_raw_fin_ack[1].summary())
print("S:Fin\n",result_raw_fin_fin[1].summary())
sc_sn = result_raw_fin_ack[1][1].fields['seq'] + 1
cs_sn = result_raw_fin_ack[1][1].fields['ack']
#發(fā)送最后一個ACK(16),結束整個TCP連接!??!
fin_ack_pkt=IP(dst=ip)/TCP(dport=dstport,sport=sportid,flags=16,seq=cs_sn,ack=sc_sn)
print("C:ACK:\n",fin_ack_pkt.summary())
send(fin_ack_pkt, verbose = False)
if __name__ == '__main__':
tcp_connection('192.168.10.113', 6666)
3.3 運行過程
3.3.1 運行Server端
??首先在Windows主機上運行TCP Server腳本。

TCP Server
3.3.2 運行Client端
??在linux主機上運行TCP Client腳本后,會將TCP交互過程打印出來。

TCP Cleint
3.3.3 抓包分析
??通過科來的csna抓包,并追蹤TCP流,如下為交互的數據包

TCP 數據包
??通過交易視圖,能夠直觀的看到TCP連接的交互過程:

TCP 交易
??進一步分析數據傳輸過程,通過對Client端發(fā)送的ACK進行解析,可以發(fā)現數據載荷長度=IP數據包總長度(50)-IP頭部長度(5)4-TCP首部長度(5)4,因為IP與TCP首部長度字段的單位為4字節(jié),因此需乘4;另外可以看到服務端給的ACK確認包中ack的序號=客戶端發(fā)送的ACK序列號+載荷長度。

TCP 傳輸
??另外可以看到Server端重復發(fā)送FIN包,是由于Client端發(fā)送ACK的間隔時間較長,導致服務端認為可客戶端未收到FIN包而重傳FIN包

TCP 重傳
??從圖中可以看到Client端發(fā)送ACK的間隔時間接近1秒鐘,與腳本中設置的sr超時時間為1秒相符。
result_raw_fin_list=sr(fin_pkt,multi=1, timeout=1,verbose = False)