当前位置: 首页 > 科技观察

本文教你如何用C代码解析网络数据包?

时间:2023-03-19 18:48:38 科技观察

本文的目的是随机截取一段网络数据包,然后根据协议类型解析出这段内存。学习本文需要掌握的基础知识:网络协议C语言Linux操作抓包工具抓包工具的安装和使用如下:《一文包你学会网络数据抓包》视频教学链接如下:《教你如何抓取网络中的数据包!黑客必备技能》1。抓包工具,随机抓取一个tcp数据包。抓包工具解析出的包信息如下:包的内存信息:可以直接复制数据信息:2.下面用到的结构,教大家怎么用。你如何解析出这些数据包的信息。我们可以从Linux内核中找到协议头的定义:drivers\staging\rtl8188eu\include\if_ether.hstructethhdr{unsignedcharh_dest[ETH_ALEN];/*destinationethaddr*/unsignedcharh_source[ETH_ALEN];/*sourceetheraddr*/unsignedshorth_proto;/*packettypeIDfield*/};IPheaderinclude\uapi\linux\ip.hstructiphdr{#ifdefined(__LITTLE_ENDIAN_BITFIELD)//小端模式__u8ihl:4,version:4;#elifdefined(__BIG_ENDIAN_BITFIELD)//大端模式__u8version:4,ihl:4;#endif__u8tos;__u16tot_len;__u16id;__u16frag_off;__u8ttl;__u8protocol;__u16check;__u32saddr;__u32daddr;/*Theoptionsstarthere.*/};__be32ack_seq;#ifdefined(__LITTLE_ENDIAN_BITFIELD)__u16res1:4,doff:4,fin:1,syn:1,rst:1,psh:1,ack:1,urg:1,ece:1,cwr:1;#elifdefined(__BIG_ENDIAN_BITFIELD)__u16doff:4,res1:4,cwr:1,ece:1,urg:1,ack:1,psh:1,rst:1,syn:1,fin:1;#else#error"Adjustyourdefines"#endif__be16window;__sum16check;__be16urg_ptr;};因为协议头的长度是按照标准协议定义的,以太网的长度是14,而长度IP标头的是20,tcp头长度为20,每个协议头对应的内存空间如下:3.解析以太网头#defineMAC_ARG(p)p[0],p[1],p[2],p[3],p[4],p[5]structethhdr*ethh;unsignedchar*p=pkt;ethh=(structethhdr*)p;printf("h_dest:%02x:%02x:%02x:%02x:%02x:%02x\n",MAC_ARG(ethh->h_dest));printf("h_source:%02x:%02x:%02x:%02x:%02x:%02x\n",MAC_ARG(ethh->h_source));printf("h_proto:%04x\n",ntohs(ethh->h_proto));注意数据包中的数据是网络字节序的。如果要提取数据,必须注意字节顺序问题。ethh->h_proto是short类型,占2个字符section,所以需要用ntohs函数在本地存储where:n:network网络字节序h:host主机字节序s:short2bytesl:long4bytesntohl():4bytesnetworkbytesConvertsequencedatatohostbytesequencehtons():Convert2-bytehostbytesequencedatatonetworkbytesequencentohs():将2字节网络字节序列数据转换为主机字节序列htonl():4wordsConverthostbyteorderdatatonetworkbyteorder当执行下面的语句时,ethh=(structethhdr*)p;结构体指针变量eth的成员对应关系如下:最终打印结果如下:4.ip头分析分析ip头的思路很简单,就是ip头可以通过pkt头偏移以太网头长度(14字节)找到,解析代码如下:#defineIP_ARG(p)p[0],p[1],p[2],p[3]/*解析IP头*/if(ntohs(ethh->h_proto)==0x0800){iph=(structiphdr*)(p+sizeof(structethhdr));q=(unsignedchar*)&(iph->saddr);printf("源代码:%d.%d.%d.%d\n",IP_ARG(q));q=(unsignedchar*)&(iph->daddr);printf("destip:%d.%d.%d.%d\n",IP_ARG(q));}Iiph最终解析结果如下:可以看到我们正确解析了IP地址,结果与抓包工具解析的数据一致,protocol字段表示协议ip协议后面的type,常见的值如下:5.分析tcpheader,找到tcpheader,思路是通过偏移Ethernetheader的长度(14字节)找到tcpheader和来自pkt标头的IP标头(20字节)的长度。Switch(iph->protocol){case0x1://icmpbreak;case0x6://tcptcph=(structtcphdr*)(p+sizeof(structethhdr)+sizeof(structiphdr));printf("source:%ddest:%d\n",ntohs(tcph->source),ntohs(tcph->dest);break;case0x11://udpbreak;}结构体和内存的对应关系为打印如下:6.学习用不同的格式打印这段内存,我们分析的不是standardTCP/IP协议数据包,也可以是我们自己定义的协议数据包。只要我们掌握了以上方法,所有的协议分析都可以轻松掌握!有时我们还需要打印对方发送的数据Frame内容,往往我们会把所有的数据都以十六进制的形式打印出来,这样最有利于我们分析数据内容。1、按字节打印的代码如下:for(i=0;i<400;i++){printf("%02x",pkt[i]);if(i%20==19){printf("\n");}}2.通过short类型分析一段内存我们在接收数据的时候,虽然我们使用的是unsignedchar类型的数组,但是有时候对方发送的数据可能是一个2字节的数组,那么我们只需要用一个short类型的指针指向内存的头部,就可以通过这个指针访问对方发送过来的数据了,这个一定要注意字节顺序问题。不同的场景可能不一样,具体问题具体分析。在这个例子中,需要转换字节顺序,因为网络字节顺序数据被转换为主机字节顺序。//改变短字节顺序voidindian_reverse(unsignedshortarr[],intnum){inti;unsignedshorttemp;for(i=0;i>8;temp|=(arr[i]&0xff)<<8;arr[i]=temp;}}main(){unsignedshortspkt[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公众号。