
本文的文教网络目的是通过随机截取的一段网络数据包,然后根据协议类型来解析出这段内存。何用
学习本文需要掌握的码解基础知识:
网络协议 C语言 Linux操作 抓包工具的使用其中抓包工具的安装和使用见下文:
《一文包你学会网络数据抓包》
视频教学链接如下:
《教你如何抓取网络中的数据包!黑客必备技能》
通过抓包工具,析段随机抓取一个tcp数据包

科莱抓包工具解析出的数据数据包信息如下:

数据包的内存信息:

数据信息可以直接拷贝出来:

下面,一口君就手把手教大家如何解析出这些数据包的何用信息。
我们可以从Linux内核中找到协议头的码解定义
以太头:
drivers\staging\rtl8188eu\include\if_ether.h struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ };IP头
include\uapi\linux\ip.h struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) //小端模式 __u8 ihl:4, version:4; #elif defined(__BIG_ENDIAN_BITFIELD) //大端模式 __u8 version:4, ihl:4; #endif __u8 tos; __u16 tot_len; __u16 id; __u16 frag_off; __u8 ttl; __u8 protocol; __u16 check; __u32 saddr; __u32 daddr; /*The options start here. */ };tcp头
include\uapi\linux\tcp.h struct tcphdr { __be16 source; __be16 dest; __be32 seq; __be32 ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __be16 window; __sum16 check; __be16 urg_ptr; };因为协议头长度都是按照标准协议来定义的,
所以以太长度是析段14, IP头长度是数据20, tcp头长度是文教网络20,
各个协议头对应的何用内存空间如下:

当执行下面这条语句时,
ethh = (struct ethhdr *)p;结构体指针变量eth的成员对应关系如下:

最终打印结果如下:

解析ip头思路很简单,
就是从pkt头开始偏移过以太头长度(14字节)就可以找到IP头,
解析代码如下:
#define IP_ARG(p) p[0],p[1],p[2],p[3] /* 解析IP头 */ if(ntohs(ethh->h_proto) == 0x0800) { iph = (struct iphdr *)(p + sizeof(struct ethhdr)); q = (unsigned char *)&(iph->saddr); printf("src ip:%d.%d.%d.%dn",IP_ARG(q)); q = (unsigned char *)&(iph->daddr); printf("dest ip:%d.%d.%d.%dn",IP_ARG(q)); }
Iiph
最终解析结果如下:

可以看到我们正确解析出了IP地址, 结果与抓包工具分析出的数据保持了一致。
其中protocol字段表示了ip协议后面的额协议类型,常见的值如下:

查找tcp头思路很,
就是从pkt头开始偏移过以太头长度(14字节)、和IP头长度(20字节)就可以找到tcp头,
switch(iph->protocol) { case 0x1: //icmp break; case 0x6: //tcp tcph = (struct tcphdr *)(p + sizeof(struct ethhdr) + sizeof(struct iphdr)); printf("source:%d dest:%d n",ntohs(tcph->source),ntohs(tcph->dest); break; case 0x11: //udp break; }结构体与内存对应关系

打印结果如下:

在实际项目中,可能我们解析的并不是标准的TCP/IP协议数据包,
可能是我们自己的站群服务器定义的协议数据包,
只要掌握了上述方法,
所有的协议分析都能够手到擒来!
有时候我们还需要打印对方发送过来的数据帧内容,
往往我们会以16进制形式将所有数据打印出来,
这样是最有利于我们分析数据内容的。
代码如下:
for(i=0;i<400;i++) { printf("%02x ",pkt[i]); if(i%20 == 19) { printf("n"); } }
我们接收数据时,虽然使用一个unsigned char型数组,
但是有时候对方发送过来的数据可能是2个字节的数组,
那我们只需要用short类型的指针,指向内存的头,
然后就可以通过该指针访问到对方发送的数据,
这个时候一定要注意字节序问题,
不同场景可能不一样,所以一定要具体问题具体分析,
本例因为是网络字节序数据转换成主机字节序,
所以需要转换字节序。
//转变short型字节序 void indian_reverse(unsigned short arr[],int num) { int i; unsigned short temp; for(i=0;i<num;i++) { temp = 0; temp = (arr[i]&0xff00)>>8; temp |= (arr[i]&0xff)<<8; arr[i] = temp; } } main() { unsigned short spkt[200]; ……………… memcpy(spkt,pkt,sizeof(pkt)); indian_reverse(spkt,ARRAY_SIZE(spkt)); for(i=0;i<200;i++) { printf("%04x ",spkt[i]); if(i%10 == 9) { printf("n"); } } ……………… }结果如下:

转载本文请联系一口Linux公众号。
