栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 网络技术 > 网络管理

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

网络管理 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力



本文的目的是通过随机截取的一段网络数据包,然后根据协议类型来解析出这段内存。

学习本文需要掌握的基础知识:

  1. 网络协议
  2. C语言
  3. Linux操作
  4. 抓包工具的使用

    其中抓包工具的安装和使用见下文:

    《一文包你学会网络数据抓包》

    视频教学链接如下:

    《教你如何抓取网络中的数据包!黑客必备技能》

    一、截取一个网络数据包

    通过抓包工具,随机抓取一个tcp数据包


    一文教你如何用C代码解析一段网络数据包?「含代码」

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


    一文教你如何用C代码解析一段网络数据包?「含代码」

    数据包的内存信息:


    一文教你如何用C代码解析一段网络数据包?「含代码」

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


    一文教你如何用C代码解析一段网络数据包?「含代码」

    二、用到的结构体

    下面,一口君就手把手教大家如何解析出这些数据包的信息。

    我们可以从Linux内核中找到协议头的定义

    以太头:

    driversstagingrtl8188euincludeif_ether.h  
    struct ethhdr { 
  5.  unsigned char h_dest[ETH_ALEN];   unsigned char h_source[ETH_ALEN];  
  6.  unsigned short h_proto;   }; 
  7. IP头

    includeuapilinuxip.h  
    struct iphdr { 
  8. #if defined(__LITTLE_ENDIAN_BITFIELD)  //小端模式  __u8 ihl:4, 
  9.   version:4; #elif defined(__BIG_ENDIAN_BITFIELD)    //大端模式 
  10.  __u8 version:4,   ihl:4; 
  11. #endif  __u8 tos; 
  12.  __u16 tot_len;  __u16 id; 
  13.  __u16 frag_off;  __u8 ttl; 
  14.  __u8 protocol;  __u16 check; 
  15.  __u32 saddr;  __u32 daddr; 
  16.   }; 
  17. tcp头

    includeuapilinuxtcp.h 
    struct tcphdr { 
  18.  __be16 source;  __be16 dest; 
  19.  __be32 seq;  __be32 ack_seq; 
  20. #if defined(__LITTLE_ENDIAN_BITFIELD)  __u16 res1:4, 
  21.   doff:4,   fin:1, 
  22.   syn:1,   rst:1, 
  23.   psh:1,   ack:1, 
  24.   urg:1,   ece:1, 
  25.   cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) 
  26.  __u16 doff:4,   res1:4, 
  27.   cwr:1,   ece:1, 
  28.   urg:1,   ack:1, 
  29.   psh:1,   rst:1, 
  30.   syn:1,   fin:1; 
  31. #else #error "Adjust your  defines" 
  32. #endif   __be16 window; 
  33.  __sum16 check;  __be16 urg_ptr; 
  34. }; 
  35. 因为协议头长度都是按照标准协议来定义的,

    所以以太长度是14, IP头长度是20, tcp头长度是20,

    各个协议头对应的内存空间如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    三、解析以太头
    #define MAC_ARG(p) p[0],p[1],p[2],p[3],p[4],p[5] 
    struct ethhdr *ethh; 
  36.  unsigned char *p = pkt;   
  37.  ethh = (struct ethhdr *)p;  
  38.  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)); 
  39.  printf("h_proto:%04xn",ntohs(ethh->h_proto)); 
    • 注意,数据包中的数据是网络字节序,如果要提取数据一定要注意字节序问题 ethh->h_proto 是short类型,占2个字节,所以存储到本地需要使用函数ntohs 其中:
    • n:network 网络字节序
    • h:host 主机字节序
    • s:short 2个字节 l:
    • long 4个字节
    • ntohl() :4字节网络字节序数据转换成主机字节序
    • htons() :2字节主机字节序数据转换成网络字节序
    • ntohs() :2字节网络字节序数据转换成主机字节序
    • htonl() :4字节主机字节序数据转换成网络字节序

    当执行下面这条语句时,

    ethh = (struct ethhdr *)p; 

    结构体指针变量eth的成员对应关系如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    最终打印结果如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    四、解析ip头

    解析ip头思路很简单,

    就是从pkt头开始偏移过以太头长度(14字节)就可以找到IP头,

    解析代码如下:

    #define IP_ARG(p)  p[0],p[1],p[2],p[3] 
     
  40. if(ntohs(ethh->h_proto) == 0x0800) { 
  41.   iph = (struct iphdr *)(p + sizeof(struct ethhdr)); 
  42.   q = (unsigned char *)&(iph->saddr); 
  43.  printf("src ip:%d.%d.%d.%dn",IP_ARG(q));  
  44.  q = (unsigned char *)&(iph->daddr);  printf("dest ip:%d.%d.%d.%dn",IP_ARG(q)); 

  45. 一文教你如何用C代码解析一段网络数据包?「含代码」

    Iiph

    最终解析结果如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    可以看到我们正确解析出了IP地址, 结果与抓包工具分析出的数据保持了一致。

    其中protocol字段表示了ip协议后面的额协议类型,常见的值如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    五、解析tcp头

    查找tcp头思路很,

    就是从pkt头开始偏移过以太头长度(14字节)、和IP头长度(20字节)就可以找到tcp头,

    switch(iph->protocol) 
  46.  {   case 0x1: 
  47.    //icmp    break; 
  48.   case 0x6:    //tcp     
  49.    tcph = (struct tcphdr *)(p + sizeof(struct ethhdr) + sizeof(struct iphdr));    printf("source:%d dest:%d n",ntohs(tcph->source),ntohs(tcph->dest);  
  50.     break; 
  51.   case 0x11:    //udp 
  52.        break; 
  53.  } 
  54. 结构体与内存对应关系


    一文教你如何用C代码解析一段网络数据包?「含代码」

    打印结果如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

    六、学会用不同格式打印这块内存

    在实际项目中,可能我们解析的并不是标准的TCP/IP协议数据包,

    可能是我们自己的定义的协议数据包,

    只要掌握了上述方法,

    所有的协议分析都能够手到擒来!

    有时候我们还需要打印对方发送过来的数据帧内容,

    往往我们会以16进制形式将所有数据打印出来,

    这样是最有利于我们分析数据内容的。

    1. 按字节打印

    代码如下:

    for(i=0;i<400;i++) 
  55. {  printf("%02x ",pkt[i]); 
  56.  if(i%20 == 19)  { 
  57.   printf("n");  } 

  58. 一文教你如何用C代码解析一段网络数据包?「含代码」

    2. 按short类型分析一段内存

    我们接收数据时,虽然使用一个unsigned char型数组,

    但是有时候对方发送过来的数据可能是2个字节的数组,

    那我们只需要用short类型的指针,指向内存的头,

    然后就可以通过该指针访问到对方发送的数据,

    这个时候一定要注意字节序问题,

    不同场景可能不一样,所以一定要具体问题具体分析,

    本例因为是网络字节序数据转换成主机字节序,

    所以需要转换字节序。

    //转变short型字节序 
  59. void indian_reverse(unsigned short arr[],int num) { 
  60.  int i;  unsigned short temp; 
  61.   for(i=0;i {   temp = 0; 
  62.    temp = (arr[i]&0xff00)>>8; 
  63.   temp |= (arr[i]&0xff)<<8;   arr[i] = temp; 
  64.  } } 
  65. main() { 
  66.  unsigned short spkt[200];   
  67.  ………………  memcpy(spkt,pkt,sizeof(pkt)); 
  68.   indian_reverse(spkt,ARRAY_SIZE(spkt)); 
  69.    for(i=0;i<200;i++) 
  70.  {   printf("%04x ",spkt[i]); 
  71.   if(i%10 == 9)   { 
  72.    printf("n");   } 
  73.  }  ……………… 
  74. 结果如下:


    一文教你如何用C代码解析一段网络数据包?「含代码」

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



     

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/823720.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号