认真学习了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)
'UTF-8' )
sys.setdefaultencoding(
= 100
MTU = MTU-20-20
MSS = MTU-20
IP_MSS # MAS = (65535-20-8-10-1)/2
= 160
MAS
#应用层
'''
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Application Header 10字节 | Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|48位 时间戳 | 32位 Data长度 | Data 遵循UDP最大包,编码utf-16 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
def applicationLayer(_data):
= hex(int(time.time()))[2:].zfill(12)+hex(len(_data))[2:].zfill(8)
stream
for i in range(len(_data)):
+=hex(ord(_data[i]))[2:].zfill(4)
streamreturn stream
def unPakingApplicationLayer(_data):
= _data[20:]
string = time.strftime( '%Y-%m-%d %X', time.localtime(int(_data[:12] ,16 )))
Time = str(int(_data[12:20],16))
length =u""
datachr=u""
for i in range(len(string)/4):
try:
chr=unichr(int(string[4*i:4*i+4],16))
except:
chr="?"
+=chr
data
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):
= _data[:4]
sPort = _data[4:8]
dPort = _data[8:12]
length = _data[12:16]
checkSum = _data[16:]
data 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):
= '4'
version = '5'
IHL = '00'
TOS = ''#
totalLength = hex(random.randint(0,2**16-1))[2:].zfill(4)
identification = 0
evilFlag = 0#
DF = 0#
MF = 0
fragmentOffset = 'ff'
TT #https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
= '11'#UDP
protocol = '0000'
checkSum = '0a0c0a9b'#10.12.10.155
sIP = '765923c0'#118.89.35.192
dIP =[]
ip_stream if len(_data)/2/IP_MSS+1==1:
= str(len(_data)/2+20).zfill(4)
totalLength 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):
= str(len(_data[2*i*IP_MSS:(i+1)*IP_MSS*2])/2+20).zfill(4)
totalLength +IHL+TOS+totalLength+identification+hex(i*IP_MSS+((1 if i==(len(_data)/2/IP_MSS) else 0)<<13))[2:].zfill(4)\
ip_stream.append(version+TT+protocol+checkSum+sIP+dIP+_data[2*i*IP_MSS:(i+1)*IP_MSS*2])
return ip_stream
def unPackingIP(_data):
= _data[0]
version = _data[1]
IHL = _data[2:4]
TOS = _data[4:8]#
totalLength = _data[8:12]
identification = bin(int(_data[12],16))[2:].zfill(4)[3]
evilFlag = bin(int(_data[12],16))[2:].zfill(4)[2]
DF = bin(int(_data[12],16))[2:].zfill(4)[1]
MF =str(int(_data[13:16],16)+((int(bin(int(_data[12],16))[2:].zfill(4)[0]))<<12))
fragmentOffset = _data[16:18]
TT #https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
= _data[18:20]
protocol = _data[20:24]
checkSum = _data[24:32]#10.12.10.155
sIP = _data[32:40]#118.89.35.192
dIP
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):
=hex(int('10101010',2))[2:]*7+hex(int('10101011',2))[2:]
preamble = 'ffffffffffff'
dMAC = '2047472ebaf8'
sMAC =_data if len(_data)/2>46 else (_data+(46-len(_data)/2)*'0')
data = hex(len(_data)/2)[2:].zfill(4)
length ='00000000'
checkSum return preamble+dMAC+sMAC+length+data+checkSum
def unPackingLinkLayer(_data):
=_data[:16]
preamble = _data[16:28]
dMAC = _data[28:40]
sMAC = _data[40:44]
length =_data[44:44+int(length,16)*2]
data
= str(_data[-8:])
checkSum return data,{'Preamble':preamble,'DestionationMAC':dMAC,'SourceMAC':sMAC,'Data':data,'Length':length,'CheckSum':checkSum}
#物理层
def physicalLayer(_data):
= ''
bitStream for i in _data:
+= bin(int(i,16))[2:].zfill(4)
bitStream 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#清除之前发包的菜单
0,8)
senderMenu.delete(0,len(seApp_StreamList))
senderMenu.ApplicationLayer.delete(0,len(seUDP_StreamList))
senderMenu.TransportLayer.delete(0,len(seIP_StreamList))
senderMenu.NetworkLayer.delete(0,len(seLink_StreamList))
senderMenu.LinkLayer.delete(0,len(sePhysical_StreamList))
senderMenu.PhysicalLayer.delete(
0,8)
receiverMenu.delete(0,len(reApp_StreamList))
receiverMenu.ApplicationLayer.delete(0,len(reUDP_StreamList))
receiverMenu.TransportLayer.delete(0,len(reIP_StreamList))
receiverMenu.NetworkLayer.delete(0,len(reLink_StreamList))
receiverMenu.LinkLayer.delete(0,len(rePhysical_StreamList))
receiverMenu.PhysicalLayer.delete(
=NORMAL)
analysisText.config(state=NORMAL)
dataText.config(state0.0, END)
analysisText.delete(0.0, END)
dataText.delete(
=[]
reApp_StreamList=[]
reUDP_StreamList =[]
reIP_StreamList =[]
reLink_StreamList =[]
rePhysical_StreamList
= []
seApp_StreamList =[]
seUDP_StreamList =[]
seIP_StreamList =[]
seLink_StreamList =[]
sePhysical_StreamList =inputData.get("0.0", "end")
data if type(data).__name__=='str':
= data.decode('utf-8')
data
#应用层程序封装数据包
for i in range(len(data)/MAS+1):
*MAS:(i+1)*MAS]))
seApp_StreamList.append(applicationLayer(data[i#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)
#-----------------------------接收方--------------------------------------
#物理层接收
= sePhysical_StreamList[:]
rePhysical_StreamList
random.shuffle(rePhysical_StreamList)
#物理层解码至链路层
for i in rePhysical_StreamList:
reLink_StreamList.append(unPackingPhysicalLayer(i))
=[]
l#链路层解码至网络层
for i in reLink_StreamList:
0])
l.append(unPackingLinkLayer(i)[#网络层IP协议解码重组
=lambda x:x[8:12])#以标识排序
l.sort(key= l[0][8:12]
identification =[]
buffListfor i in l:
if i[8:12]==identification:
buffList.append(i)else:
=i[8:12]
identification
reIP_StreamList.append(buffList)=[i]
buffList#得到分组完毕的乱序IP包
reIP_StreamList.append(buffList)
for i in reIP_StreamList:
#按照IP包Header的分段偏移量FragmentOffet排序
=lambda x:int(x[13:16],16)+((int(bin(int(x[12],16))[2:].zfill(4)[0]))<<12))
i.sort(key=''
stream #重组成UDP包
for j in i :
+= unPackingIP(j)[0]
stream
reUDP_StreamList.append(stream)#UDP解包至应用层
for i in reUDP_StreamList:
0])
reApp_StreamList.append(unPackingUDP(i)[#创建菜单
createMenu()
def createMenu():
#Sender菜单更新
for i in range(len(seApp_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seApp_StreamList[x],'Application'))
senderMenu.ApplicationLayer.add_command(labelfor i in range(len(seUDP_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seUDP_StreamList[x],'Transport'))
senderMenu.TransportLayer.add_command(labelfor i in range(len(seLink_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(seLink_StreamList[x],'Link'))
senderMenu.LinkLayer.add_command(labelfor i in range(len(sePhysical_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(sePhysical_StreamList[x],'Physical'))
senderMenu.PhysicalLayer.add_command(label#创建IP分组多级菜单
= locals()
variable for i in range(len(seIP_StreamList)):
'IP_PackageMenu%s'%str(i+1)]=Menu(senderMenu.NetworkLayer,tearoff=0)
variable[for j in range(len(seIP_StreamList[i])):
'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'))
variable[='数据包%s'%str(i+1),menu = variable['IP_PackageMenu%s'%str(i+1)])
senderMenu.NetworkLayer.add_cascade(label
#Receiver菜单更新
for i in range(len(reApp_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reApp_StreamList[x],'Application'))
receiverMenu.ApplicationLayer.add_command(labelfor i in range(len(reUDP_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reUDP_StreamList[x],'Transport'))
receiverMenu.TransportLayer.add_command(labelfor i in range(len(reLink_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(reLink_StreamList[x],'Link'))
receiverMenu.LinkLayer.add_command(labelfor i in range(len(rePhysical_StreamList)):
='数据包%s'%str(i+1),command=lambda x=i:updateAnalysisFrame(rePhysical_StreamList[x],'Physical'))
receiverMenu.PhysicalLayer.add_command(label#创建IP分组多级菜单
= locals()
variable for i in range(len(reIP_StreamList)):
'IP_PackageMenu%s'%str(i+1)]=Menu(receiverMenu.NetworkLayer,tearoff=0)
variable[for j in range(len(reIP_StreamList[i])):
'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'))
variable[='数据包%s'%str(i+1),menu = variable['IP_PackageMenu%s'%str(i+1)])
receiverMenu.NetworkLayer.add_cascade(label#重新添加
='应用层',menu=senderMenu.ApplicationLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='传输层',menu=senderMenu.TransportLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='网络层',menu=senderMenu.NetworkLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='链路层',menu=senderMenu.LinkLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='物理层',menu=senderMenu.PhysicalLayer )
senderMenu.add_cascade(label
='应用层',menu=receiverMenu.ApplicationLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='传输层',menu=receiverMenu.TransportLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='网络层',menu=receiverMenu.NetworkLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='链路层',menu=receiverMenu.LinkLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='物理层',menu=receiverMenu.PhysicalLayer )
receiverMenu.add_cascade(label
def updateAnalysisFrame(stream='',type='Physical'):
=NORMAL)
analysisText.config(state0.0, END)
analysisText.delete(if type =="Application":
= unPakingApplicationLayer(stream)[1]
unpackingDic
elif type =="Transport":
= unPackingUDP(stream)[1]
unpackingDic elif type =="Network":
= unPackingIP(stream)[1]
unpackingDic elif type =="Link":
= unPackingLinkLayer(stream)[1]
unpackingDic else :
# unpackingDic = stream
"BitStream:\n")
analysisText.insert(END,"\t"+unPackingPhysicalLayer(stream)+"\n")
analysisText.insert(END,
updateDataFrame(stream)=DISABLED)
analysisText.config(statereturn 0
updateDataFrame(stream)
for k in unpackingDic:
+":\n")
analysisText.insert(END,k"\t"+unpackingDic[k]+"\n")
analysisText.insert(END,=DISABLED)
analysisText.config(statedef updateDataFrame(stream=''):
=NORMAL)
dataText.config(state=0
n=' '
string =''
char 0.0, END)
dataText.delete(for i in range(0,len(stream),2):
if n==8 :
+=' '
stringif n<16:
+= stream[i:i+2]+" "
string 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:
+= chr(int(stream[i:i+2],16))
char except:
+= '.'
char else:
+= '.'
char else:
+=" "+char
string+"\n")
dataText.insert(END,string=-1
n=' '
string =''
char +=1
n"%-53s"%string+" "+char+"\n")
dataText.insert(END,=DISABLED)
dataText.config(state
#-------------------------------------------主程序开始--------------------------------------------------------
#数组初始化
=[]
reApp_StreamList=[]
reUDP_StreamList =[]
reIP_StreamList =[]
reLink_StreamList =[]
rePhysical_StreamList
= []
seApp_StreamList =[]
seUDP_StreamList =[]
seIP_StreamList =[]
seLink_StreamList =[]
sePhysical_StreamList
= Tk()
root "网络传输模拟")
root.title(
#主菜单
= Menu(root)
mainMenu
= Menu(mainMenu, tearoff=0)
senderMenu #各层菜单
= Menu(senderMenu, 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 #Sender菜单添加进主菜单
=" 发送方 ", menu=senderMenu)
mainMenu.add_cascade(label
#各层添加进Sender菜单
='应用层',menu=senderMenu.ApplicationLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='传输层',menu=senderMenu.TransportLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='网络层',menu=senderMenu.NetworkLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='链路层',menu=senderMenu.LinkLayer )
senderMenu.add_cascade(label
senderMenu.add_separator()='物理层',menu=senderMenu.PhysicalLayer )
senderMenu.add_cascade(label#Receiver菜单
= Menu(mainMenu, tearoff=0)
receiverMenu #Receiver各层菜单
= Menu(receiverMenu, tearoff=0)
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 #Receiver添加各层菜单
='应用层',menu=receiverMenu.ApplicationLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='传输层',menu=receiverMenu.TransportLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='网络层',menu=receiverMenu.NetworkLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='链路层',menu=receiverMenu.LinkLayer )
receiverMenu.add_cascade(label
receiverMenu.add_separator()='物理层',menu=receiverMenu.PhysicalLayer )
receiverMenu.add_cascade(label=" 接收方 ", menu=receiverMenu)
mainMenu.add_cascade(label
#解包分析部分
=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)
Frame(root,height
= ScrolledText(root,width=149, height=18,font=('Consolas', 13),fg='black', bg='white')
analysisText =0, column=0,sticky=W+N,padx=6)
analysisText.grid(row#数据部分
= Frame(root,height=root.winfo_screenheight()/4,width=root.winfo_screenwidth(),bg = 'gray')
Frame2 =1, column=0)
Frame2.grid(row
= ScrolledText(Frame2,width=149, height=10,font=('Consolas', 13),fg='black', bg='white')
dataText =1, column=0,sticky=W,padx=5,columnspan = 3)
dataText.grid(row#输入部分
= Frame(root,height=root.winfo_screenheight()/8+20,width=root.winfo_screenwidth(),bg='gray')
Frame3 =2, column=0,sticky=W)
Frame3.grid(row= ' Send ',command = packingData,height=2,width=13).grid(row=2, column=0,sticky=E,padx=40)
Button(root,text
= Text(root,width=160,height=4)
inputData =2, column=0,sticky=W,padx=60)
inputData.grid(rowdef selectText(event):
"1.0",END)
event.widget.tag_add(SEL,return 'break'
"<Control-Key-a>", selectText)
inputData.bind("<Control-Key-A>", selectText)
inputData.bind(
"%dx%d" %(root.winfo_screenwidth()-5, root.winfo_screenheight()))
root.geometry(=mainMenu)
root.config(menu 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'))
展示
程序运行展示: