
认真学习了TCP/IP中包的结构以及封包解包,分包重组等步骤
利用实验之余写一个比较严格执行TCP/IP模型的解、封包模拟程序并以仿wireshark的GUI形式展现
本程序使用5层TCP/IP模型(多了个物理层)

比较严格地实现了 + 各个层的封包解包规则以及包结构 + 任意字符发包 + 动态构建菜单 + 数据包长度控制 + 网络层乱序包排序重组 + 每层的包数据含义解析 + 十六进制数据展示
每个层的结构,源码里面有展示 GUI库用的是Python自带的tkinter,不需要安装,有python2.7环境就能运行
源码
# -*- coding: UTF-8 -*-
import tkMessageBox
import threading
import time
import random
from Tkinter import *
from ScrolledText import ScrolledText
import tkFont
import sys
reload(sys)
sys.setdefaultencoding( 'UTF-8' )
MTU = 100
MSS = MTU-20-20
IP_MSS = MTU-20
# MAS = (65535-20-8-10-1)/2
MAS = 160
#应用层
'''
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Application Header 10字节 | Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|48位 时间戳 | 32位 Data长度 | Data 遵循UDP最大包,编码utf-16 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
def applicationLayer(_data):
stream = hex(int(time.time()))[2:].zfill(12)+hex(len(_data))[2:].zfill(8)
for i in range(len(_data)):
stream+=hex(ord(_data[i]))[2:].zfill(4)
return stream
def unPakingApplicationLayer(_data):
string = _data[20:]
Time = time.strftime( '%Y-%m-%d %X', time.localtime(int(_data[:12] ,16 )))
length = str(int(_data[12:20],16))
data=u""
chr=u""
for i in range(len(string)/4):
try:
chr=unichr(int(string[4*i:4*i+4],16))
except:
chr="?"
data+=chr
return data,{'data':data,'time':Time,'length':length}
#传输层 UDP模拟
'''
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP Lengt | CheckSum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
def UDP(_data):
return "0080"+"f27c"+str(hex(len(_data)/2+8))[2:].zfill(4)+"0000"+_data
def unPackingUDP(_data):
sPort = _data[:4]
dPort = _data[4:8]
length = _data[8:12]
checkSum = _data[12:16]
data = _data[16:]
return data,{'SourcePort':sPort,'DestinationPort':dPort,'Length':length,'CheckSum':checkSum,'Data':data}
#网络层 IP协议
'''
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL | Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address , |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
def IP(_data):
version = '4'
IHL = '5'
TOS = '00'
totalLength = ''#
identification = hex(random.randint(0,2**16-1))[2:].zfill(4)
evilFlag = 0
DF = 0#
MF = 0#
fragmentOffset = 0
TT = 'ff'
#https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
protocol = '11'#UDP
checkSum = '0000'
sIP = '0a0c0a9b'#10.12.10.155
dIP = '765923c0'#118.89.35.192
ip_stream =[]
if len(_data)/2/IP_MSS+1==1:
totalLength = str(len(_data)/2+20).zfill(4)
return [version+IHL+TOS+totalLength+identification+"0000"+TT+protocol+checkSum+sIP+dIP+_data[:IP_MSS*2]]
for i in range(len(_data)/2/IP_MSS+1):
totalLength = str(len(_data[2*i*IP_MSS:(i+1)*IP_MSS*2])/2+20).zfill(4)
ip_stream.append(version+IHL+TOS+totalLength+identification+hex(i*IP_MSS+((1 if i==(len(_data)/2/IP_MSS) else 0)<<13))[2:].zfill(4)\
+TT+protocol+checkSum+sIP+dIP+_data[2*i*IP_MSS:(i+1)*IP_MSS*2])
return ip_stream
def unPackingIP(_data):
version = _data[0]
IHL = _data[1]
TOS = _data[2:4]
totalLength = _data[4:8]#
identification = _data[8:12]
evilFlag = bin(int(_data[12],16))[2:].zfill(4)[3]
DF = bin(int(_data[12],16))[2:].zfill(4)[2]
MF = bin(int(_data[12],16))[2:].zfill(4)[1]
fragmentOffset =str(int(_data[13:16],16)+((int(bin(int(_data[12],16))[2:].zfill(4)[0]))<<12))
TT = _data[16:18]
#https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
protocol = _data[18:20]
checkSum = _data[20:24]
sIP = _data[24:32]#10.12.10.155
dIP = _data[32:40]#118.89.35.192
return _data[40:],{'ProtocolVersion':version,'IHL':IHL,'TOS':TOS,'TotalLength':totalLength,\
'Identification':identification,'EvilFlag':evilFlag,'Don\'tFragement':DF,'MoreFragment':MF,'FragmentOffset':fragmentOffset,\
'Time to Live':TT,'Protocol':protocol,'CheckSum':checkSum,'SourceIP':sIP,'DestinationIP':dIP,'Data':_data[40:]
}
#数据链路层 802.3帧格式
'''
btyes: 8 6 6 2 0-1500 0-46 4
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-++-+-+
| Preamble | Destination Address | Source Address | Length | Data | Padding | CheckSum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-++-+-+
'''
def linkLayer(_data):
preamble =hex(int('10101010',2))[2:]*7+hex(int('10101011',2))[2:]
dMAC = 'ffffffffffff'
sMAC = '2047472ebaf8'
data =_data if len(_data)/2>46 else (_data+(46-len(_data)/2)*'0')
length = hex(len(_data)/2)[2:].zfill(4)
checkSum ='00000000'
return preamble+dMAC+sMAC+length+data+checkSum
def unPackingLinkLayer(_data):
preamble =_data[:16]
dMAC = _data[16:28]
sMAC = _data[28:40]
length = _data[40:44]
data =_data[44:44+int(length,16)*2]
checkSum = str(_data[-8:])
return data,{'Preamble':preamble,'DestionationMAC':dMAC,'SourceMAC':sMAC,'Data':data,'Length':length,'CheckSum':checkSum}
#物理层
def physicalLayer(_data):
bitStream = ''
for i in _data:
bitStream += bin(int(i,16))[2:].zfill(4)
return bitStream
def unPackingPhysicalLayer(bitStream):
return hex(int(bitStream,2))[2:-1]
def packingData(event=""):
global reApp_StreamList,reUDP_StreamList,reIP_StreamList,reLink_StreamList,rePhysical_StreamList,seApp_StreamList,seUDP_StreamList\
,seIP_StreamList,seLink_StreamList,sePhysical_StreamList
#清除之前发包的菜单
senderMenu.delete(0,8)
senderMenu.ApplicationLayer.delete(0,len(seApp_StreamList))
senderMenu.TransportLayer.delete(0,len(seUDP_StreamList))
senderMenu.NetworkLayer.delete(0,len(seIP_StreamList))
senderMenu.LinkLayer.delete(0,len(seLink_StreamList))
senderMenu.PhysicalLayer.delete(0,len(sePhysical_StreamList))
receiverMenu.delete(0,8)
receiverMenu.ApplicationLayer.delete(0,len(reApp_StreamList))
receiverMenu.TransportLayer.delete(0,len(reUDP_StreamList))
receiverMenu.NetworkLayer.delete(0,len(reIP_StreamList))
receiverMenu.LinkLayer.delete(0,len(reLink_StreamList))
receiverMenu.PhysicalLayer.delete(0,len(rePhysical_StreamList))
analysisText.config(state=NORMAL)
dataText.config(state=NORMAL)
analysisText.delete(0.0, END)
dataText.delete(0.0, END)
reApp_StreamList=[]
reUDP_StreamList =[]
reIP_StreamList =[]
reLink_StreamList =[]
rePhysical_StreamList =[]
seApp_StreamList = []
seUDP_StreamList =[]
seIP_StreamList =[]
seLink_StreamList =[]
sePhysical_StreamList =[]
data =inputData.get("0.0", "end")
if type(data).__name__=='str':
data = data.decode('utf-8')
#应用层程序封装数据包
for i in range(len(data)/MAS+1):
seApp_StreamList.append(applicationLayer(data[i*MAS:(i+1)*MAS]))
#seApp_StreamList为我们需要发送的数据包
for i in seApp_StreamList:
l=[]
seUDP_StreamList.append(UDP(i))
for j in IP(UDP(i)):
l.append(j)
#链路层数据包列表
seLink_StreamList.append(linkLayer(j))
#物理层数据包列表
sePhysical_StreamList.append(physicalLayer(linkLayer(j)))
#IP层数据包分别装,以便演示
seIP_StreamList.append(l)
#-----------------------------接收方--------------------------------------
#物理层接收
rePhysical_StreamList = sePhysical_StreamList[:]
random.shuffle(rePhysical_StreamList)
#物理层解码至链路层
for i in rePhysical_StreamList:
reLink_StreamList.append(unPackingPhysicalLayer(i))
l=[]
#链路层解码至网络层
for i in reLink_StreamList:
l.append(unPackingLinkLayer(i)[0])
#网络层IP协议解码重组
l.sort(key=lambda x:x[8:12])#以标识排序
identification = l[0][8:12]
buffList=[]
for i in l:
if i[8:12]==identification:
buffList.append(i)
else:
identification=i[8:12]
reIP_StreamList.append(buffList)
buffList=[i]
reIP_StreamList.append(buffList)#得到分组完毕的乱序IP包
for i in reIP_StreamList:
#按照IP包Header的分段偏移量FragmentOffet排序
i.sort(key=lambda x:int(x[13:16],16)+((int(bin(int(x[12],16))[2:].zfill(4)[0]))<<12))
stream =''
#重组成UDP包
for j in i :
stream += unPackingIP(j)[0]
reUDP_StreamList.append(stream)
#UDP解包至应用层
for i in reUDP_StreamList:
reApp_StreamList.append(unPackingUDP(i)[0])
#创建菜单
createMenu()
def createMenu():
#Sender菜单更新
for i in range(len(seApp_StreamList)):
senderMenu.ApplicationLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seApp_StreamList[x],'Application'))
for i in range(len(seUDP_StreamList)):
senderMenu.TransportLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seUDP_StreamList[x],'Transport'))
for i in range(len(seLink_StreamList)):
senderMenu.LinkLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seLink_StreamList[x],'Link'))
for i in range(len(sePhysical_StreamList)):
senderMenu.PhysicalLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(sePhysical_StreamList[x],'Physical'))
#创建IP分组多级菜单
variable = locals()
for i in range(len(seIP_StreamList)):
variable['IP_PackageMenu%s'%str(i+1)]=Menu(senderMenu.NetworkLayer,tearoff=0)
for j in range(len(seIP_StreamList[i])):
variable['IP_PackageMenu%s'%str(i+1)].add_command(label='IP数据包%s'%str(j+1),command=lambda x=i,y=j:updateAnalysisFrame(seIP_StreamList[x][y],'Network'))
senderMenu.NetworkLayer.add_cascade(label='数据包%s'%str(i+1),menu = variable['IP_PackageMenu%s'%str(i+1)])
#Receiver菜单更新
for i in range(len(reApp_StreamList)):
receiverMenu.ApplicationLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reApp_StreamList[x],'Application'))
for i in range(len(reUDP_StreamList)):
receiverMenu.TransportLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reUDP_StreamList[x],'Transport'))
for i in range(len(reLink_StreamList)):
receiverMenu.LinkLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reLink_StreamList[x],'Link'))
for i in range(len(rePhysical_StreamList)):
receiverMenu.PhysicalLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(rePhysical_StreamList[x],'Physical'))
#创建IP分组多级菜单
variable = locals()
for i in range(len(reIP_StreamList)):
variable['IP_PackageMenu%s'%str(i+1)]=Menu(receiverMenu.NetworkLayer,tearoff=0)
for j in range(len(reIP_StreamList[i])):
variable['IP_PackageMenu%s'%str(i+1)].add_command(label='IP数据包%s'%str(j+1),command=lambda x=i,y=j:updateAnalysisFrame(reIP_StreamList[x][y],'Network'))
receiverMenu.NetworkLayer.add_cascade(label='数据包%s'%str(i+1),menu = variable['IP_PackageMenu%s'%str(i+1)])
#重新添加
senderMenu.add_cascade(label='应用层',menu=senderMenu.ApplicationLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='传输层',menu=senderMenu.TransportLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='网络层',menu=senderMenu.NetworkLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='链路层',menu=senderMenu.LinkLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='物理层',menu=senderMenu.PhysicalLayer )
receiverMenu.add_cascade(label='应用层',menu=receiverMenu.ApplicationLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='传输层',menu=receiverMenu.TransportLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='网络层',menu=receiverMenu.NetworkLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='链路层',menu=receiverMenu.LinkLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='物理层',menu=receiverMenu.PhysicalLayer )
def updateAnalysisFrame(stream='',type='Physical'):
analysisText.config(state=NORMAL)
analysisText.delete(0.0, END)
if type =="Application":
unpackingDic = unPakingApplicationLayer(stream)[1]
elif type =="Transport":
unpackingDic = unPackingUDP(stream)[1]
elif type =="Network":
unpackingDic = unPackingIP(stream)[1]
elif type =="Link":
unpackingDic = unPackingLinkLayer(stream)[1]
else :
# unpackingDic = stream
analysisText.insert(END,"BitStream:\n")
analysisText.insert(END,"\t"+unPackingPhysicalLayer(stream)+"\n")
updateDataFrame(stream)
analysisText.config(state=DISABLED)
return 0
updateDataFrame(stream)
for k in unpackingDic:
analysisText.insert(END,k+":\n")
analysisText.insert(END,"\t"+unpackingDic[k]+"\n")
analysisText.config(state=DISABLED)
def updateDataFrame(stream=''):
dataText.config(state=NORMAL)
n=0
string =' '
char =''
dataText.delete(0.0, END)
for i in range(0,len(stream),2):
if n==8 :
string+=' '
if n<16:
string += stream[i:i+2]+" "
if int(stream[i:i+2],16)!=0 and int(stream[i:i+2],16)!=0x0a and int(stream[i:i+2],16)!=0x0b and int(stream[i:i+2],16)!=0x09 and int(stream[i:i+2],16)!=0x08:
try:
char += chr(int(stream[i:i+2],16))
except:
char += '.'
else:
char += '.'
else:
string+=" "+char
dataText.insert(END,string+"\n")
n=-1
string =' '
char =''
n+=1
dataText.insert(END,"%-53s"%string+" "+char+"\n")
dataText.config(state=DISABLED)
#-------------------------------------------主程序开始--------------------------------------------------------
#数组初始化
reApp_StreamList=[]
reUDP_StreamList =[]
reIP_StreamList =[]
reLink_StreamList =[]
rePhysical_StreamList =[]
seApp_StreamList = []
seUDP_StreamList =[]
seIP_StreamList =[]
seLink_StreamList =[]
sePhysical_StreamList =[]
root = Tk()
root.title("网络传输模拟")
#主菜单
mainMenu = Menu(root)
senderMenu = Menu(mainMenu, tearoff=0)
#各层菜单
senderMenu.ApplicationLayer = Menu(senderMenu, tearoff=0)
senderMenu.TransportLayer = Menu(senderMenu, tearoff=0)
senderMenu.NetworkLayer = Menu(senderMenu, tearoff=0)
senderMenu.LinkLayer = Menu(senderMenu, tearoff=0)
senderMenu.PhysicalLayer = Menu(senderMenu, tearoff=0)
#Sender菜单添加进主菜单
mainMenu.add_cascade(label=" 发送方 ", menu=senderMenu)
#各层添加进Sender菜单
senderMenu.add_cascade(label='应用层',menu=senderMenu.ApplicationLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='传输层',menu=senderMenu.TransportLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='网络层',menu=senderMenu.NetworkLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='链路层',menu=senderMenu.LinkLayer )
senderMenu.add_separator()
senderMenu.add_cascade(label='物理层',menu=senderMenu.PhysicalLayer )
#Receiver菜单
receiverMenu = Menu(mainMenu, tearoff=0)
#Receiver各层菜单
receiverMenu.ApplicationLayer = Menu(receiverMenu, tearoff=0)
receiverMenu.TransportLayer = Menu(receiverMenu, tearoff=0)
receiverMenu.NetworkLayer = Menu(receiverMenu, tearoff=0)
receiverMenu.LinkLayer = Menu(receiverMenu, tearoff=0)
receiverMenu.PhysicalLayer = Menu(receiverMenu, tearoff=0)
#Receiver添加各层菜单
receiverMenu.add_cascade(label='应用层',menu=receiverMenu.ApplicationLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='传输层',menu=receiverMenu.TransportLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='网络层',menu=receiverMenu.NetworkLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='链路层',menu=receiverMenu.LinkLayer )
receiverMenu.add_separator()
receiverMenu.add_cascade(label='物理层',menu=receiverMenu.PhysicalLayer )
mainMenu.add_cascade(label=" 接收方 ", menu=receiverMenu)
#解包分析部分
Frame(root,height=root.winfo_screenheight()/2-15,width=root.winfo_screenwidth(),bg = 'gray').grid(row=0, column=0,sticky=W+N)
Frame(root,height=root.winfo_screenheight()/2-21,width=root.winfo_screenwidth(),bg = 'BurlyWood').grid(row=0, column=0,sticky=W+N)
analysisText = ScrolledText(root,width=149, height=18,font=('Consolas', 13),fg='black', bg='white')
analysisText.grid(row=0, column=0,sticky=W+N,padx=6)
#数据部分
Frame2 = Frame(root,height=root.winfo_screenheight()/4,width=root.winfo_screenwidth(),bg = 'gray')
Frame2.grid(row=1, column=0)
dataText = ScrolledText(Frame2,width=149, height=10,font=('Consolas', 13),fg='black', bg='white')
dataText.grid(row=1, column=0,sticky=W,padx=5,columnspan = 3)
#输入部分
Frame3 = Frame(root,height=root.winfo_screenheight()/8+20,width=root.winfo_screenwidth(),bg='gray')
Frame3.grid(row=2, column=0,sticky=W)
Button(root,text = ' Send ',command = packingData,height=2,width=13).grid(row=2, column=0,sticky=E,padx=40)
inputData = Text(root,width=160,height=4)
inputData.grid(row=2, column=0,sticky=W,padx=60)
def selectText(event):
event.widget.tag_add(SEL,"1.0",END)
return 'break'
inputData.bind("<Control-Key-a>", selectText)
inputData.bind("<Control-Key-A>", selectText)
root.geometry("%dx%d" %(root.winfo_screenwidth()-5, root.winfo_screenheight()))
root.config(menu=mainMenu)
root.mainloop()难点
开发过程中遇到最难的一个问题的就是如何动态构建菜单
通过查阅资料以及在stackoverflow与segementfault论坛上学习后发现可以通过python的内置函数locals()来获取程序的局部和全局变量当然也可以动态地创建变量
'locals()'函数返回的是一个字典,变量名与变量值一一对应,而我们
#程序273行
#Sender菜单更新
for i in range(len(seApp_StreamList)):
senderMenu.ApplicationLayer.add_command(label='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seApp_StreamList[x],'Application'))
展示
程序运行展示:
