国产久操视频-国产久草视频-国产久热精品-国产久热香蕉在线观看-青青青青娱乐-青青青青在线成人视99

  • 正文
    • 一、截取一個網絡數(shù)據包
    •  
    • 二、用到的結構體
    •  
    • 三、解析以太頭
    •  
    • 四、解析ip頭
    •  
    • 五、解析TCP頭
    •  
    • 六、學會用不同格式打印這塊內存
    •  
    • 2. 按short類型分析一段內存
  • 相關推薦
申請入駐 產業(yè)圖譜

一文教你如何用C代碼解析一段網絡數(shù)據包?(含代碼)

2021/08/11
239
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

本文的目的是通過隨機截取的一段網絡數(shù)據包,然后根據協(xié)議類型來解析出這段內存。

學習本文需要掌握的基礎知識:

  1. 網絡協(xié)議C語言Linux操作抓包工具的使用

視頻教學鏈接如下:

《教你如何抓取網絡中的數(shù)據包!黑客必備技能》

一、截取一個網絡數(shù)據包

通過抓包工具,隨機抓取一個tcp數(shù)據包

科萊抓包工具解析出的數(shù)據包信息如下:

數(shù)據包的內存信息:

數(shù)據信息可以直接拷貝出來:

 

二、用到的結構體

下面,一口君就手把手教大家如何解析出這些數(shù)據包的信息。

我們可以從Linux內核中找到協(xié)議頭的定義

  • 以太頭:
driversstagingrtl8188euincludeif_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頭
 includeuapilinuxip.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頭

includeuapilinuxtcp.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;
};

因為協(xié)議頭長度都是按照標準協(xié)議來定義的,所以以太長度是14, IP頭長度是20, tcp頭長度是20,各個協(xié)議頭對應的內存空間如下:

 

三、解析以太頭

#define MAC_ARG(p) p[0],p[1],p[2],p[3],p[4],p[5]
 struct ethhdr *ethh;
 unsigned char *p = pkt;
 
 ethh = (struct ethhdr *)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:%04xn",ntohs(ethh->h_proto));

注意,數(shù)據包中的數(shù)據是網絡字節(jié)序,如果要提取數(shù)據一定要注意字節(jié)序問題 ethh->h_proto 是short類型,占2個字節(jié),所以存儲到本地需要使用函數(shù)ntohs 其中:n:network 網絡字節(jié)序 h:host       主機字節(jié)序 s:short     2個字節(jié) l:long       4個字節(jié) ntohl()  :4字節(jié)網絡字節(jié)序數(shù)據轉換成主機字節(jié)序 htons() :2字節(jié)主機字節(jié)序數(shù)據轉換成網絡字節(jié)序 ntohs() :2字節(jié)網絡字節(jié)序數(shù)據轉換成主機字節(jié)序 htonl() :4字節(jié)主機字節(jié)序數(shù)據轉換成網絡字節(jié)序

當執(zhí)行下面這條語句時,

ethh = (struct ethhdr *)p;

結構體指針變量eth的成員對應關系如下:

最終打印結果如下:

 

四、解析ip頭

解析ip頭思路很簡單,就是從pkt頭開始偏移過以太頭長度(14字節(jié))就可以找到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地址, 結果與抓包工具分析出的數(shù)據保持了一致。

其中protocol字段表示了ip協(xié)議后面的額協(xié)議類型,常見的值如下:

數(shù)值 描述
0 保留字段,用于IPv6(跳躍點到跳躍點選項)
1 Internet控制消息 (ICMP)
2 Internet組管理 (IGMP)
3 網關到網關 (GGP)
4 1P中的IP(封裝)
6 傳輸控制 (TCP)
7 CBT
8 外部網關協(xié)議 (EGP)
9 任何私有內部網關(Cisco在它的IGRP實現(xiàn)中使用) (IGP)
10 BBNRCC監(jiān)視
11 網絡語音協(xié)議
12 PUP
13 ARGUS
14 EMCON
15 網絡診斷工具
16 混亂(Chaos)
17 用戶數(shù)據報文 (UDP)
41 1Pv6
58 1Pv6的ICMP
59 1Pv6的無下一個報頭
60 IPv6的信宿選項
89 OSPF IGP
92 多播傳輸協(xié)議
94 IP內部的IP封裝協(xié)議
95 移動網絡互連控制協(xié)議
96 旗語通訊安全協(xié)議
97 IP中的以太封裝
98 封裝報頭
100 GMTP
101 Ipsilon流量管理協(xié)議
133~254 未分配
255 保留

 

五、解析TCP頭

查找tcp頭思路很,就是從pkt頭開始偏移過以太頭長度(14字節(jié))、和IP頭長度(20字節(jié))就可以找到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協(xié)議數(shù)據包,可能是我們自己的定義的協(xié)議數(shù)據包,只要掌握了上述方法,所有的協(xié)議分析都能夠手到擒來!

有時候我們還需要打印對方發(fā)送過來的數(shù)據幀內容,往往我們會以16進制形式將所有數(shù)據打印出來,這樣是最有利于我們分析數(shù)據內容的。

 

1. 按字節(jié)打印

代碼如下:

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

 

 

2. 按short類型分析一段內存

我們接收數(shù)據時,雖然使用一個unsigned char型數(shù)組,但是有時候對方發(fā)送過來的數(shù)據可能是2個字節(jié)的數(shù)組,那我們只需要用short類型的指針,指向內存的頭,然后就可以通過該指針訪問到對方發(fā)送的數(shù)據,這個時候一定要注意字節(jié)序問題,不同場景可能不一樣,所以一定要具體問題具體分析,本例因為是網絡字節(jié)序數(shù)據轉換成主機字節(jié)序,所以需要轉換字節(jié)序。

//轉變short型字節(jié)序
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");
  }
 }
 ………………
}

結果如下:

 

好了,這個例子掌握了,那么網絡就算入門了,快操練起來吧!

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經驗和培訓經驗。曾任職ZTE,某研究所,華清遠見教學總監(jiān)。擁有多篇網絡協(xié)議相關專利和軟件著作。精通計算機網絡、Linux系統(tǒng)編程、ARM、Linux驅動、龍芯、物聯(lián)網。原創(chuàng)內容基本從實際項目出發(fā),保持原理+實踐風格,適合Linux驅動新手入門和技術進階。

郯城县| 万荣县| 黑水县| 安龙县| 河源市| 正蓝旗| 博爱县| 山东| 大冶市| 长宁县| 渭源县| 井冈山市| 化州市| 大宁县| 常德市| 彰化市| 南康市| 漳平市| 康乐县| 南召县| 焦作市| 宜兴市| 清水河县| 萍乡市| 湾仔区| 河北省| 历史| 故城县| 鄄城县| 石屏县| 阳山县| 奉节县| 阿荣旗| 旬邑县| 镇宁| 宜城市| 兴义市| 沾益县| 石城县| 南投市| 邓州市|