本文的目的是通過(guò)隨機(jī)截取的一段網(wǎng)絡(luò)數(shù)據(jù)包,然后根據(jù)協(xié)議類型來(lái)解析出這段內(nèi)存。
學(xué)習(xí)本文需要掌握的基礎(chǔ)知識(shí):
- 網(wǎng)絡(luò)協(xié)議
- C語(yǔ)言
- Linux操作
- 抓包工具的使用
其中抓包工具的安裝和使用見(jiàn)下文:
《一文包你學(xué)會(huì)網(wǎng)絡(luò)數(shù)據(jù)抓包》
視頻教學(xué)鏈接如下:
《教你如何抓取網(wǎng)絡(luò)中的數(shù)據(jù)包!黑客必備技能》
一、截取一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包
通過(guò)抓包工具,隨機(jī)抓取一個(gè)tcp數(shù)據(jù)包
科萊抓包工具解析出的數(shù)據(jù)包信息如下:
數(shù)據(jù)包的內(nèi)存信息:
數(shù)據(jù)信息可以直接拷貝出來(lái):
二、用到的結(jié)構(gòu)體
下面,一口君就手把手教大家如何解析出這些數(shù)據(jù)包的信息。
我們可以從Linux內(nèi)核中找到協(xié)議頭的定義
- 以太頭:
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;
};
因?yàn)閰f(xié)議頭長(zhǎng)度都是按照標(biāo)準(zhǔn)協(xié)議來(lái)定義的,
所以以太長(zhǎng)度是14, IP頭長(zhǎng)度是20, tcp頭長(zhǎng)度是20,
各個(gè)協(xié)議頭對(duì)應(yīng)的內(nèi)存空間如下:
三、解析以太頭
#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:%04x\n",ntohs(ethh->h_proto));
注意,數(shù)據(jù)包中的數(shù)據(jù)是網(wǎng)絡(luò)字節(jié)序,如果要提取數(shù)據(jù)一定要注意字節(jié)序問(wèn)題 ethh->h_proto 是short類型,占2個(gè)字節(jié),所以存儲(chǔ)到本地需要使用函數(shù)ntohs 其中: n:network 網(wǎng)絡(luò)字節(jié)序 h:host 主機(jī)字節(jié)序 s:short 2個(gè)字節(jié) l:long 4個(gè)字節(jié) ntohl() :4字節(jié)網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序 htons() :2字節(jié)主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序 ntohs() :2字節(jié)網(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序 htonl() :4字節(jié)主機(jī)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
當(dāng)執(zhí)行下面這條語(yǔ)句時(shí),
ethh = (struct ethhdr *)p;
結(jié)構(gòu)體指針變量eth的成員對(duì)應(yīng)關(guān)系如下:
最終打印結(jié)果如下:
四、解析ip頭
解析ip頭思路很簡(jiǎn)單,
就是從pkt頭開(kāi)始偏移過(guò)以太頭長(zhǎng)度(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.%d\n",IP_ARG(q));
q = (unsigned char *)&(iph->daddr);
printf("dest ip:%d.%d.%d.%d\n",IP_ARG(q));
}
Iiph
最終解析結(jié)果如下:
可以看到我們正確解析出了IP地址, 結(jié)果與抓包工具分析出的數(shù)據(jù)保持了一致。
其中protocol字段表示了ip協(xié)議后面的額協(xié)議類型,常見(jiàn)的值如下:
數(shù)值 描述 0 保留字段,用于IPv6(跳躍點(diǎn)到跳躍點(diǎn)選項(xiàng)) 1 Internet控制消息 (ICMP) 2 Internet組管理 (IGMP) 3 網(wǎng)關(guān)到網(wǎng)關(guān) (GGP) 4 1P中的IP(封裝) 6 傳輸控制 (TCP) 7 CBT 8 外部網(wǎng)關(guān)協(xié)議 (EGP) 9 任何私有內(nèi)部網(wǎng)關(guān)(Cisco在它的IGRP實(shí)現(xiàn)中使用) (IGP) 10 BBNRCC監(jiān)視 11 網(wǎng)絡(luò)語(yǔ)音協(xié)議 12 PUP 13 ARGUS 14 EMCON 15 網(wǎng)絡(luò)診斷工具 16 混亂(Chaos) 17 用戶數(shù)據(jù)報(bào)文 (UDP) 41 1Pv6 58 1Pv6的ICMP 59 1Pv6的無(wú)下一個(gè)報(bào)頭 60 IPv6的信宿選項(xiàng) 89 OSPF IGP 92 多播傳輸協(xié)議 94 IP內(nèi)部的IP封裝協(xié)議 95 可移動(dòng)網(wǎng)絡(luò)互連控制協(xié)議 96 旗語(yǔ)通訊安全協(xié)議 97 IP中的以太封裝 98 封裝報(bào)頭 100 GMTP 101 Ipsilon流量管理協(xié)議 133~254 未分配 255 保留
五、解析tcp頭
查找tcp頭思路很,
就是從pkt頭開(kāi)始偏移過(guò)以太頭長(zhǎng)度(14字節(jié))、和IP頭長(zhǎng)度(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;
}
結(jié)構(gòu)體與內(nèi)存對(duì)應(yīng)關(guān)系
打印結(jié)果如下:
六、學(xué)會(huì)用不同格式打印這塊內(nèi)存
在實(shí)際項(xiàng)目中,可能我們解析的并不是標(biāo)準(zhǔn)的TCP/IP協(xié)議數(shù)據(jù)包,
可能是我們自己的定義的協(xié)議數(shù)據(jù)包,
只要掌握了上述方法,
所有的協(xié)議分析都能夠手到擒來(lái)!
有時(shí)候我們還需要打印對(duì)方發(fā)送過(guò)來(lái)的數(shù)據(jù)幀內(nèi)容,
往往我們會(huì)以16進(jìn)制形式將所有數(shù)據(jù)打印出來(lái),
這樣是最有利于我們分析數(shù)據(jù)內(nèi)容的。
1. 按字節(jié)打印
代碼如下:
for(i=0;i<400;i++)
{
printf("%02x ",pkt[i]);
if(i%20 == 19)
{
printf("\n");
}
}
2. 按short類型分析一段內(nèi)存
我們接收數(shù)據(jù)時(shí),雖然使用一個(gè)unsigned char型數(shù)組,
但是有時(shí)候?qū)Ψ桨l(fā)送過(guò)來(lái)的數(shù)據(jù)可能是2個(gè)字節(jié)的數(shù)組,
那我們只需要用short類型的指針,指向內(nèi)存的頭,
然后就可以通過(guò)該指針訪問(wèn)到對(duì)方發(fā)送的數(shù)據(jù),
這個(gè)時(shí)候一定要注意字節(jié)序問(wèn)題,
不同場(chǎng)景可能不一樣,所以一定要具體問(wèn)題具體分析,
本例因?yàn)槭蔷W(wǎng)絡(luò)字節(jié)序數(shù)據(jù)轉(zhuǎn)換成主機(jī)字節(jié)序,
所以需要轉(zhuǎn)換字節(jié)序。
//轉(zhuǎn)變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");
}
}
………………
}
結(jié)果如下:
完整代碼請(qǐng)關(guān)注公眾號(hào):一口Linux,回復(fù):數(shù)據(jù)包解析