socket数据包解析问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket数据包解析问题相关的知识,希望对你有一定的参考价值。
数据包格式如下:
1、包头标志:固定为0x7e。
2、控制码C:1个字节命令字(不得使用0x7E,0x7D)。
3、数据长度L:L 为数据域的字节数。2个字节。
4、数据域DATA:数据域包括数据标识和数据、密码等,其结构随控制码的功能而改变。
5、校验码CS:1个字节,控制码开始到校验码之前的所有各字节的模256 的和,即各字节二进制算术和,不计超过256 的溢出值。
6、包尾标志:固定为0x7e。
转译说明:
每个数据包的包头标志和包尾标志均使用0x7e标识。被包头标志和包尾标志包含的数据为正文
为了避免正文内也出现0x7e,收发送TCP包时需要对正文的0x7e进行转译。
如果正文出现0x7e,则发送方需将0x7e转为0x7d-0x5e,接收方需将0x7d-0x5e还原为0x7e。
如果正文数据出现0x7d,则发送方需将0x7d转为0x7d-0x5d,接收方需将0x7d-0x5d还原为0x7d。
特别说明:要求每个TCP包(转译后)的不得大于2048字节。在原文组包的时候就需要进行长度控制。
目前的问题就是怎么来组织这么个格式的数据?麻烦知道的提供思路或实现代码
struct header
char flag = 0x7e;
char cFlag;
unsigned short len;
;
//消息
struct msg
struct header msg_header;
char data[2048];
char cs_code;
char bottom = 0x7e;
追问
能不能详细些?
追答数据包格式就是让你定义一个协议体,我这里写的是C版的,char 是一个字节,short两个字节,
首先定义一个协议头,然后再进行定义整个协议,header就是协议头,包含 flag = 0x7e,控制码还有一个长度,然后msg定义整体协议,即一个协议头header,再加data[]协议体,最后加协议尾bottom=0x7e,如果是JAVA版,定义类即可
python使用原始套接字 解析原始ip头数据
使用底层套接字解码底层流量,是这次做的重点工作。
首先来捕获第一个包
1 # coding:utf-8import socket 2 3 # 监听的主机IP 4 host = "192.168.1.100" 5 6 socket_protocol = socket.IPPROTO_ICMP 7 8 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 9 sniffer.bind((host, 0)) 10 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 11 12 raw_buffer = sniffer.recvfrom(65535) 13 print raw_buffer
下面一行一行解释上面代码的意思。
1. 导入socket包
2. 需要监听的本机ip地址
3. 给socket_protocol变量赋值icmp变量
4. 为sniffer变量创建一个soket对象,该对象为ipv4 原始套接字并指定其协议为icmp
5. 绑定到指定地址和端口进行监听
6. 为sniffer套接字设置选项参数,使其携带ip头
7. 将监听端口的套接字收到的原始数据赋值给raw_buffer
8. 打印raw_buffer的值
这个时候,我们使用root权限运行这个脚本,并且开启另外一个terminal对任意一个地址发送icmp包,我们监听的接口的recvfrom 会收到回监听回包到指定地址。recvfrom与recv不同的是 recvfrom会同时接收回包地址。(string, address)的格式
这个时候我们可以看到打印出来的值,是一堆完全看不懂的东西,因为是没有解码的状态,下面我们将对ip头进行解码。
使用python的struct和ctypes两个库实现这一点。
1 # coding:utf-8import socket 2 import struct 3 from ctypes import * 4 5 # 监听的主机IPhost = "192.168.1.100" 6 7 # IP头定义 8 class IP(Structure): 9 _fields_ = [ 10 ("ihl", c_ubyte, 4), 11 ("version", c_ubyte, 4), 12 ("tos", c_ubyte), 13 ("len", c_ushort), 14 ("id", c_ushort), 15 ("offset", c_ushort), 16 ("ttl", c_ubyte), 17 ("protocol_num", c_ubyte), 18 ("sum", c_ushort), 19 ("src", c_uint), 20 ("dst", c_uint), 21 ] 22 23 def __new__(self, socket_buffer=None): 24 return self.from_buffer_copy(socket_buffer) 25 26 def __init__(self, socket_buffer=None): 27 self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 28 29 # readable ip address 30 self.src_address = socket.inet_ntoa(struct.pack("<I", self.src)) 31 self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst)) 32 33 # type of protocol 34 try: 35 self.protocol = self.protocol_map[self.protocol_num] 36 except: 37 self.protocol = str(self.protocol_num) 38 39 socket_protocol = socket.IPPROTO_ICMP 40 41 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 42 sniffer.bind((host, 0)) 43 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 44 45 try: 46 while True: 47 raw_buffer = sniffer.recvfrom(65535)[0] 48 49 ip_header = IP(raw_buffer[:20]) 50 51 print "Protocol: %s %s -> %s " % (ip_header.protocol, ip_header.src_address, ip_header.dst_address) 52 53 except KeyboardInterrupt: 54 pass
1. 导入各模块
2. 监听的本机ip地址
3. 使用ctypes 构造一个解析ip头的结构体(structure) IP
4. 使用from_buffer_copy方法在__new__方法将收到的数据生成一个IP class的实例
5. __init__方法初始化一部分数据保存到对应的实例属性值中。
6. 特别说明下面代码, 使用了python struct库的pack方法 用指定的格式化参数将src 和dst的long型数值转换为字符串,然后使用socket.inet_ntoa方法将字符串的一串数字转换为对应的ip格式。最后赋值给对应的src或者dst变量
# readable ip address self.src_address = socket.inet_ntoa(struct.pack("<I", self.src)) self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst))
7. 一个接收icmp包的服务器,没什么说的。
8. 无限循环监听指定端口,将recvfrom收到的数据的第一部分 也就是不要ip地址的部分传递给raw_buffer
9. ip头raw_buffer的前20个字节传递给结构体进行解码。
10. 然后打印。
可以看到大致思路就是,将原型socket数据拿过来,然后通过模拟c语言的结构体,使用python的库对这个格式的包进行一一对应的解码,将解码之后的数据打印出来。
到此为止可以看到,在ip层已经可以解析出数据包从哪儿去哪儿的信息。
以上是关于socket数据包解析问题的主要内容,如果未能解决你的问题,请参考以下文章