WinPcap笔记(7):分析数据包(1)
現(xiàn)在,我們可以捕獲并過濾網(wǎng)絡(luò)流量了,那就簡(jiǎn)單協(xié)議個(gè)程序分析網(wǎng)絡(luò)數(shù)據(jù)包。
這里我們只是解析所捕獲數(shù)據(jù)包的首部,打印一些數(shù)據(jù)包首部的信息。我們以UDP為例,因?yàn)閁DP比較簡(jiǎn)單。
首先,應(yīng)該介紹下網(wǎng)絡(luò)數(shù)據(jù)包的格式。網(wǎng)絡(luò)中的數(shù)據(jù)包每經(jīng)過一個(gè)層次都會(huì)加上那個(gè)層的報(bào)頭來標(biāo)注一些重要的信息。捕獲到的數(shù)據(jù)包首先有個(gè)mac報(bào)頭,14字節(jié),包含6字節(jié)目的mac地址、6字節(jié)源mac地址,和2字節(jié)上一層協(xié)議。這里我們不關(guān)注mac報(bào)頭,所以省略。
接下來是IP數(shù)據(jù)包,包的格式如下圖:
IP數(shù)據(jù)包有20字節(jié)的報(bào)頭,報(bào)頭格式如下圖:
WinPcap沒有給出一個(gè)保存IP報(bào)頭信息的結(jié)構(gòu)體,所以需要我們自己定義。下面是IP報(bào)頭的定義:
/* IPv4 首部 */ typedef struct ip_header {u_char ver_ihl; // 版本 (4 bits) + 首部長(zhǎng)度 (4 bits)u_char tos; // 服務(wù)類型(Type of service) u_short tlen; // 總長(zhǎng)(Total length) u_short identification; // 標(biāo)識(shí)(Identification)u_short flags_fo; // 標(biāo)志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)u_char ttl; // 存活時(shí)間(Time to live)u_char proto; // 協(xié)議(Protocol)u_short crc; // 首部校驗(yàn)和(Header checksum)ip_address saddr; // 源地址(Source address)ip_address daddr; // 目的地址(Destination address)u_int op_pad; // 選項(xiàng)與填充(Option + Padding) }ip_header;我們可以通過首部長(zhǎng)度得到UDP數(shù)據(jù)包的位置。下面的代碼計(jì)算首部長(zhǎng)度:
ip_len = (ih->ver_ihl & 0xf) * 4;得到首部長(zhǎng)度后就可以得到UDP的位置了。下圖是UDP報(bào)頭的格式:
同樣,UDP結(jié)構(gòu)也需要我們自己定義: /* UDP 首部*/ typedef struct udp_header {u_short sport; // 源端口(Source port)u_short dport; // 目的端口(Destination port)u_short len; // UDP數(shù)據(jù)包長(zhǎng)度(Datagram length)u_short crc; // 校驗(yàn)和(Checksum) }udp_header;
這里涉及到小端法和大端法。下面簡(jiǎn)單介紹一下。 《UNXI網(wǎng)絡(luò)編程》定義:術(shù)語“小端”和“大端”表示多字節(jié)值的哪一端(小端或大端)存儲(chǔ)在該值的起始地址。小端存在起始地址,即是小端字節(jié)序;大端存在起始地址,即是大端字節(jié)序。 也就是說,小端法(Little-Endian)將低位字節(jié)存儲(chǔ)在內(nèi)存的低地址即起始地址處,高字節(jié)存儲(chǔ)在高地址;而大端法(Big-Endian)將低位字節(jié)存儲(chǔ)在高地址,將高位字節(jié)存儲(chǔ)在低地址處。 舉個(gè)例子,對(duì)于整形0x12345678。它在大端法和小端法的系統(tǒng)內(nèi)中,分別如下圖所示的方式存放:
還有一個(gè)概念叫網(wǎng)絡(luò)字節(jié)序,我們知道網(wǎng)絡(luò)上的數(shù)據(jù)流是字節(jié)流,對(duì)于一個(gè)多字節(jié)數(shù)值,在進(jìn)行網(wǎng)絡(luò)傳輸?shù)臅r(shí)候,先傳遞哪個(gè)字節(jié)?也就是說,當(dāng)接收端收到第一個(gè)字節(jié)的時(shí)候,它是將這個(gè)字節(jié)作為高位還是低位來處理呢?
網(wǎng)絡(luò)字節(jié)序定義:收到的第一個(gè)字節(jié)被當(dāng)作高位看待,這就要求發(fā)送端發(fā)送的第一個(gè)字節(jié)應(yīng)當(dāng)是高位。而在發(fā)送端發(fā)送數(shù)據(jù)時(shí),發(fā)送的第一個(gè)字節(jié)是該數(shù)字在內(nèi)存中起始地址對(duì)應(yīng)的字節(jié)??梢姸嘧止?jié)數(shù)值在發(fā)送前,在內(nèi)存中數(shù)值應(yīng)該以大端法存放。
網(wǎng)絡(luò)字節(jié)序說是大端字節(jié)序。
比如我們經(jīng)過網(wǎng)絡(luò)發(fā)送0x12345678這個(gè)整形,在80X86平臺(tái)中,它是以小端法存放的,在發(fā)送前需要使用系統(tǒng)提供的htonl將其轉(zhuǎn)換成大端法存放,如下圖:
因此,對(duì)于多字節(jié)屬性,需要使用函數(shù)在網(wǎng)絡(luò)字節(jié)序和主機(jī)字節(jié)序中轉(zhuǎn)換。 下面是程序的主要代碼,將UDP的源IP地址、源端口、目的IP地址、目的端口打印出來: #include "pcap.h"/* 4字節(jié)的IP地址 */ typedef struct ip_address{u_char byte1;u_char byte2;u_char byte3;u_char byte4; }ip_address;/* IPv4 首部 */ typedef struct ip_header{u_char ver_ihl; // 版本 (4 bits) + 首部長(zhǎng)度 (4 bits)u_char tos; // 服務(wù)類型(Type of service) u_short tlen; // 總長(zhǎng)(Total length) u_short identification; // 標(biāo)識(shí)(Identification)u_short flags_fo; // 標(biāo)志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)u_char ttl; // 存活時(shí)間(Time to live)u_char proto; // 協(xié)議(Protocol)u_short crc; // 首部校驗(yàn)和(Header checksum)ip_address saddr; // 源地址(Source address)ip_address daddr; // 目的地址(Destination address)u_int op_pad; // 選項(xiàng)與填充(Option + Padding) }ip_header;/* UDP 首部*/ typedef struct udp_header{u_short sport; // 源端口(Source port)u_short dport; // 目的端口(Destination port)u_short len; // UDP數(shù)據(jù)包長(zhǎng)度(Datagram length)u_short crc; // 校驗(yàn)和(Checksum) }udp_header;/* 回調(diào)函數(shù)原型 */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);main() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; u_int netmask; char packet_filter[] = "ip and udp"; struct bpf_program fcode;/* 獲得設(shè)備列表 */if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1){fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);exit(1);}/* 打印列表 */for(d=alldevs; d; d=d->next){printf("%d. %s", ++i, d->name);if (d->description)printf(" (%s)\n", d->description);elseprintf(" (No description available)\n");}if(i==0){printf("\nNo interfaces found! Make sure WinPcap is installed.\n");return -1;}printf("Enter the interface number (1-%d):",i);scanf("%d", &inum);if(inum < 1 || inum > i){printf("\nInterface number out of range.\n");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}/* 跳轉(zhuǎn)到已選設(shè)備 */for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);/* 打開適配器 */if ( (adhandle= pcap_open(d->name, // 設(shè)備名65536, // 要捕捉的數(shù)據(jù)包的部分 // 65535保證能捕獲到不同數(shù)據(jù)鏈路層上的每個(gè)數(shù)據(jù)包的全部?jī)?nèi)容PCAP_OPENFLAG_PROMISCUOUS, // 混雜模式1000, // 讀取超時(shí)時(shí)間NULL, // 遠(yuǎn)程機(jī)器驗(yàn)證errbuf // 錯(cuò)誤緩沖池) ) == NULL){fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}/* 檢查數(shù)據(jù)鏈路層,為了簡(jiǎn)單,我們只考慮以太網(wǎng) */if(pcap_datalink(adhandle) != DLT_EN10MB){fprintf(stderr,"\nThis program works only on Ethernet networks.\n");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}if(d->addresses != NULL)/* 獲得接口第一個(gè)地址的掩碼 */netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;else/* 如果接口沒有地址,那么我們假設(shè)一個(gè)C類的掩碼 */netmask=0xffffff; //編譯過濾器if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 ){fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}//設(shè)置過濾器if (pcap_setfilter(adhandle, &fcode)<0){fprintf(stderr,"\nError setting the filter.\n");/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);return -1;}printf("\nlistening on %s...\n", d->description);/* 釋放設(shè)備列表 */pcap_freealldevs(alldevs);/* 開始捕捉 */pcap_loop(adhandle, 0, packet_handler, NULL);return 0; }/* 回調(diào)函數(shù),當(dāng)收到每一個(gè)數(shù)據(jù)包時(shí)會(huì)被libpcap所調(diào)用 */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) {struct tm *ltime;char timestr[16];ip_header *ih;udp_header *uh;u_int ip_len;u_short sport,dport;time_t local_tv_sec;/* 將時(shí)間戳轉(zhuǎn)換成可識(shí)別的格式 */local_tv_sec = header->ts.tv_sec;ltime=localtime(&local_tv_sec);strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);/* 打印數(shù)據(jù)包的時(shí)間戳和長(zhǎng)度 */printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);/* 獲得IP數(shù)據(jù)包頭部的位置 */ih = (ip_header *) (pkt_data +14); //以太網(wǎng)頭部長(zhǎng)度/* 獲得UDP首部的位置 */ip_len = (ih->ver_ihl & 0xf) * 4;uh = (udp_header *) ((u_char*)ih + ip_len);/* 將網(wǎng)絡(luò)字節(jié)序列轉(zhuǎn)換成主機(jī)字節(jié)序列 */sport = ntohs( uh->sport );dport = ntohs( uh->dport );/* 打印IP地址和UDP端口 */printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,sport,ih->daddr.byte1,ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4,dport); }
運(yùn)行結(jié)果如下:
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的WinPcap笔记(7):分析数据包(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 长虹智控电视,只有画面没声音是指什么问题
- 下一篇: WinPcap笔记(8):分析数据包(2