轉(zhuǎn)載的一篇對(duì)于libpcap很有用的帖子
初探數(shù)據(jù)包分析程序設(shè)計(jì) Author :maigan From : 第八軍團(tuán)-信息安全小組(www. www.) Mail : [email protected] Warnong: 轉(zhuǎn)載本文請(qǐng)注明作者及出處
整天在網(wǎng)上轉(zhuǎn),,也看到許多不錯(cuò)的文章,,但我發(fā)現(xiàn)大多文章要么只停留在理論上,,要么就
是太高深,。對(duì)問(wèn)題詳細(xì)分析介紹的很少,。今天,,我就想以數(shù)據(jù)包分析程序?yàn)橹黝}和大家討論一
下網(wǎng)絡(luò)編程的的相關(guān)問(wèn)題,,我也是新手,有不到之處,,還望大家不吝指正,。 通過(guò)對(duì)數(shù)據(jù)包的分析,,我們可以判斷通信雙方的操作系統(tǒng)、網(wǎng)絡(luò)信息流量,、經(jīng)過(guò)的路由,、
數(shù)據(jù)包的大小,,以及數(shù)據(jù)包的內(nèi)容等等,。對(duì)于喜歡網(wǎng)絡(luò)安全的人來(lái)說(shuō),掌握這方面的知識(shí)是相
當(dāng)重要的?,F(xiàn)在的網(wǎng)絡(luò)通信中,,大部分?jǐn)?shù)據(jù)都沒(méi)有加密,我們可以輕易地從數(shù)據(jù)包中提取賬號(hào)
,、密碼之類(lèi)我們關(guān)心的數(shù)據(jù).大家在看本文時(shí)如有困難,,可先讀一讀計(jì)算機(jī)網(wǎng)絡(luò)及C程序設(shè)計(jì)還
有協(xié)議分析方面的書(shū)。下面我將分TCP/IP族協(xié)議結(jié)構(gòu),、程序部分函數(shù)及數(shù)據(jù)結(jié)構(gòu)說(shuō)明,、案例程
序剖析三個(gè)部分與大家共同學(xué)習(xí)數(shù)據(jù)包分析程序的設(shè)計(jì)方法
一、TCP/IP族協(xié)議結(jié)構(gòu) 在說(shuō)TCP/IP之前,,先讓我們來(lái)認(rèn)識(shí)一下以太網(wǎng),,因?yàn)槲覀儸F(xiàn)在接觸最多的就是以太網(wǎng),
并且研究數(shù)據(jù)包又是離不開(kāi)以太網(wǎng)的幀的,。在以太網(wǎng)中,,數(shù)據(jù)是以被稱(chēng)為幀的數(shù)據(jù)結(jié)構(gòu)本為單
位進(jìn)行交換的。以太網(wǎng)中常用的協(xié)議是CSMA/CD(carrier sense multiple access with
collision detection)即載波監(jiān)聽(tīng)多點(diǎn)接入/碰撞檢測(cè),,在這里,,我們關(guān)注的是幀的格式。常
用的以太網(wǎng)幀的格式有兩種標(biāo)準(zhǔn),,一種是DIX Ethernet V2標(biāo)準(zhǔn),,另一種是IEEE的802.3標(biāo)準(zhǔn)。
現(xiàn)在最常用的MAC幀是V2格式,,這也是我們所要研究的格式,,至于802.3幀我們不再討論。以太
網(wǎng)V2幀的格式如下: (插入8字節(jié))目的地址(6字節(jié))->源地址(6字節(jié))->類(lèi)型(2字節(jié))->數(shù)據(jù)(46-1500)->FCS(4字
節(jié)) 以太網(wǎng)的地址由48位的二進(jìn)制來(lái)表示,也就是我們常說(shuō)的MAC地址及硬件地址,。在MAC幀前還有8
字節(jié)的前同步碼和幀的開(kāi)始定界符,,之后才是地址等報(bào)頭信息。接收端和發(fā)送端的地址之后是
2字節(jié)的類(lèi)型字段,,存放幀中傳送數(shù)據(jù)的上層協(xié)議類(lèi)型,,RFC1700號(hào)文檔規(guī)定了這些,如下: ETHER TYPES(十六進(jìn)制) PROTOCOlS 800 IP 806 ARP 8035 Revese ARP 809B Apple Talk 8137/8138 Novel 814c SNMP 幀的數(shù)據(jù)部分長(zhǎng)度為46-1500字節(jié),,當(dāng)小于46時(shí),,會(huì)在后面加入一個(gè)整數(shù)字節(jié)的填充字段,。
FCS(Frame Check Sequence)在以太網(wǎng)常用循環(huán)冗佘校檢(CRC:cyclic redandancy check)。 IP協(xié)議為網(wǎng)絡(luò)層協(xié)議,,網(wǎng)絡(luò)層的數(shù)據(jù)結(jié)構(gòu)體被稱(chēng)為IP數(shù)據(jù)報(bào),。IP地址及域名這兩個(gè)概念
我們就不說(shuō)了,下面我們來(lái)看一看IP數(shù)據(jù)報(bào)的結(jié)構(gòu): 成員名 字節(jié)數(shù) 說(shuō)明 version 1/2 IP的版本,,現(xiàn)在為IPV4 IHL(報(bào)送長(zhǎng)度) 1/2 最常用為20,,取5-15之前的值,最
大60字節(jié) Type Of Service 1 優(yōu)先和可靠性服務(wù)要求的數(shù)值 Total Lenth 2 IP數(shù)據(jù)報(bào)的全長(zhǎng) Identification 2 識(shí)別IP數(shù)據(jù)報(bào)的編號(hào) Flags 3/8 1位為0表示有碎塊,,2位為0表示是
最后的碎塊,,為1表示接收中。 Fragment Offset 13/8 分片在原分組中的位置 TTL 1 數(shù)據(jù)報(bào)壽命,,建議值為32秒 Protocol 1 上層協(xié)議 Headerchecksum 2 報(bào)頭檢驗(yàn)碼 Source Address 4 發(fā)送端IP地址 Destination Address 4 接收端IP地址 Options And Padding 4 選項(xiàng)及填充位 其中協(xié)議字段的值對(duì)我們分析數(shù)據(jù)包是很重要的,,下面列出來(lái)給大家看看: 值 協(xié)議 意義 1 ICMP Internet Control Message
Protocol 6 TCP Tranfer Control Protocol 8 EGP Exterior Gateway Protocol 9 IGP Interior Gateway Protocol 17 UDP User Datagram Protocol 下面這些協(xié)議的值在后面的程序中我們可以見(jiàn)到,請(qǐng)大家留心記一下,。接著我們介紹地址解析
協(xié)議(ARP/RARP): 成員名 字節(jié)數(shù) 說(shuō)明 Hardware address 2 硬件類(lèi)型,,以太網(wǎng)為1 Protocol address 2 上層協(xié)議類(lèi)型,IP為800 Byte length of each hardware 1 查詢(xún)物理地址的字節(jié)長(zhǎng)度,,
以太網(wǎng)為6 Byte length of each protocol address 1 查詢(xún)上層協(xié)議的字節(jié)長(zhǎng)度,,
IPv4時(shí)為4 Opcode 2 1為ARP請(qǐng)求,2為響應(yīng),;3為
RARP請(qǐng)求,,4為響應(yīng) Hardware address of sender of this packet 6 發(fā)送端硬件地址 protocol address of sender of this packet 4 發(fā)送端IP地址 Hardware address of target of this packet 6 查詢(xún)對(duì)象硬件地址 Protocol address of target of this packet 4 查詢(xún)對(duì)象IP地址 ARP/RARP協(xié)議用來(lái)查詢(xún)IP對(duì)應(yīng)的硬件地址或反過(guò)來(lái)查詢(xún)IP地址,這在我們分析數(shù)據(jù)包時(shí)也會(huì)見(jiàn)
到,。下面介紹ICMP協(xié)議,。我們常用的PING命令就是用的這個(gè)協(xié)議,這個(gè)協(xié)議比較簡(jiǎn)單,,由類(lèi)型
(1字節(jié)),、代碼(1字節(jié))、檢驗(yàn)和(2字節(jié)),、還有四個(gè)字節(jié)的與類(lèi)型相關(guān)的可變部分及數(shù)據(jù)構(gòu)成
,。 數(shù)據(jù)包在運(yùn)輸層還有兩個(gè)重要的協(xié)議,即TCP/UDP,,TCP/UDP中使用端口的概念,,以區(qū)別
計(jì)算機(jī)上不同的程序。下面我們先來(lái)看看TCP數(shù)據(jù)報(bào)的首部構(gòu)成: 成員名 字節(jié)數(shù) 說(shuō)明 Source Port 2 發(fā)送端端口號(hào) Destination Port 2 接收端端口號(hào) Sequence NO 4 本報(bào)文段所發(fā)送的
第一個(gè)字節(jié)的序號(hào) ACk Number 4 期望收到的下一個(gè)
報(bào)文段的序號(hào) DAta Offset 1/2 首部的長(zhǎng)度 Reserved 3/4 保留今后用 Contol Bits 3/4 控制位 Window 2 滑動(dòng)窗口的大小 Checksum 2 檢驗(yàn)和 Urgent Pointer 2 緊急指針 Options And Padding 4 可選,,真充項(xiàng) Tcp被使用在跨越路由器進(jìn)行網(wǎng)絡(luò)服務(wù)的網(wǎng)絡(luò)應(yīng)用程序中,,如WWW、電子郵件、新聞,、FTP等,。
UDP則是在IP的基礎(chǔ)上加入了端口的概念,其結(jié)構(gòu)很簡(jiǎn)單,,只有八個(gè)字節(jié)首部如下: 源端口(2字節(jié))->目的端口(2字節(jié))->長(zhǎng)度(2字節(jié))->檢驗(yàn)和(2字節(jié))
二,、程序部分函數(shù)及數(shù)據(jù)結(jié)構(gòu)說(shuō)明 在此部分我們將介紹后面程序中用到的部分函數(shù)及數(shù)據(jù)結(jié)構(gòu)。在程序中我們使用了PCAP
程序庫(kù),,大家可以從 ftp://ftp.ee.lbl.gov/libpcap.tar.z下載,。?..頤侵饕赗edhat Linux下測(cè)試程序,這里簡(jiǎn)單
介紹一下程序庫(kù)的安裝方法,,其它環(huán)境請(qǐng)大家自行解決,。我的目的是給大家編寫(xiě)數(shù)據(jù)包分析程
序提供思路,,至于實(shí)用程序的實(shí)現(xiàn)這里不做介紹,,第三部分給出的程序也不具實(shí)用性,為了演
示,,程序中實(shí)現(xiàn)的功能較多而有些地方又不夠詳細(xì),,編寫(xiě)實(shí)用程序時(shí)請(qǐng)適當(dāng)取舍并加入你所需
要的功能實(shí)現(xiàn)部分。PCAP程序庫(kù)的安裝方法如下: 1,、解壓文件 2,、進(jìn)入文件目錄執(zhí)行./configure 及make 3、使用Make命令,,設(shè)定手冊(cè)和Include文件(要有Root權(quán)限),,執(zhí)行以下命令: make install -man make install -incl 4、如出現(xiàn)不存在Include及Include/net目錄,,則建立此目錄并重新執(zhí)行 make
install -incl 5,、檢查/usr/include/netinet/目錄是否存在Protocols.h文件,不存在則拷貝過(guò)去,。
至此程序庫(kù)安裝完畢,。 下面介紹程序中出現(xiàn)的部分函數(shù)及數(shù)據(jù)結(jié)構(gòu): 1、PCAP_t *pd; 此型數(shù)據(jù)結(jié)構(gòu)稱(chēng)為數(shù)據(jù)包捕捉描述符,。 2,、Pcap_Open_Live(argv[1],DEFAUT_SNALEN,1,1000,ebuf) 此函數(shù)對(duì)Pcap程序庫(kù)進(jìn)行初始化并返回指向Pcap_t型數(shù)據(jù)的指針,其參數(shù)列表如下
: char * 指定網(wǎng)絡(luò)接口 int 取得數(shù)據(jù)的最大字節(jié)數(shù) int 指定網(wǎng)絡(luò)接口卡,,一般用1 int 讀出暫停時(shí)間 char * 錯(cuò)誤消息用緩沖區(qū) 3,、Pcap_loop(pd,-1,packet_proce,NUll) 此函數(shù)程序的核心,反復(fù)執(zhí)行,,利用Pcap取得數(shù)據(jù)包,,返回的是讀入數(shù)據(jù)包的個(gè)數(shù)
,錯(cuò)誤時(shí)返回-1,其參數(shù)列表如下: Pcap_t * 指定取得數(shù)據(jù)包的數(shù)據(jù)包捕捉描述符 int 取得數(shù)據(jù)包的個(gè)數(shù),,-1為無(wú)限 返回指向函數(shù)的指針 指定數(shù)據(jù)包處理的函數(shù) U_char * 指向賦給數(shù)據(jù)包處理函數(shù)字符串的指針 4,、struct ether_header * eth 此結(jié)構(gòu)體存儲(chǔ)以太網(wǎng)報(bào)頭信息,其成員如下: ether_dhost[6] 接收端的MAC地址 ether_shost[6] 發(fā)送端的MAC地址 ether_type 上層協(xié)議的種類(lèi) 5,、fflush(stdout) 此函數(shù)完成的是強(qiáng)制輸出,,參數(shù)Stdout,強(qiáng)制進(jìn)行標(biāo)準(zhǔn)輸出,。 6,、noths(((struct ether_header *P)->ether_type)) 此函數(shù)將短整型網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序。此類(lèi)函數(shù)還有: ntohl 長(zhǎng)整型 功能同上 htons 短整型 將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序 htons 長(zhǎng)整型 同上 7,、struct IP *iph ip型結(jié)構(gòu)體在IPh文件中定義,,其成員和第一部分講到的IP數(shù)據(jù)報(bào)結(jié)構(gòu)對(duì)應(yīng),如下
: 成員名 類(lèi)型 說(shuō)明 ip_hl 4位無(wú)符號(hào)整數(shù) 報(bào)頭長(zhǎng)度 ip_v 同上 版本,,現(xiàn)為4 ip_tos 8位無(wú)符號(hào)整數(shù) Type of service ip_len 16位無(wú)符號(hào)整數(shù) 數(shù)據(jù)報(bào)長(zhǎng)度 ip_id 同上 標(biāo)識(shí) ip_off 同上 數(shù)據(jù)塊偏移和標(biāo)志 ip_ttl 8位無(wú)符號(hào)整數(shù) TTL值 ip_p 同上 上層協(xié)議 ip_sum 16位無(wú)符號(hào)整數(shù) 檢驗(yàn)和 ip_src in_addr結(jié)構(gòu)體 發(fā)送端IP ip_dst 同上 接收端IP 8,、struct ether_arp *arph ether_arp型結(jié)構(gòu)體成員如下: 成員名 類(lèi)型 說(shuō)明 ea_hdr arphdr型結(jié)構(gòu)體 報(bào)頭中地址以外的部分 arp_sha 8位無(wú)符號(hào)整數(shù)數(shù)組 發(fā)送端MAC地址 arp_spa 同上 發(fā)送端IP地址 arp_tha 同上 目標(biāo)MAC地址 arp_tpa 同上 目標(biāo)IP地址 9、struct icmphdr * icmp icmphdr型結(jié)構(gòu)體中包含共用體根據(jù)數(shù)據(jù)報(bào)類(lèi)型的不同而表現(xiàn)不同性質(zhì),,這里不再列
出,,只列能通用的三個(gè)成員 成員名 說(shuō)明 type 類(lèi)型字段 code 代碼 checksum 檢驗(yàn)和
三、案例程序剖析
QUOTE //example.c //使用方法:example〈網(wǎng)絡(luò)接口名〉 > 〈輸出文件名〉 //例如:example etho > temp.txe //結(jié)束方法:ctrl+c //程序開(kāi)始,,讀入頭文件 #include #include #include #include #include #include #include #include
//pcap程序庫(kù) #include //DNS檢索使用 #define MAXSTRINGSIZE 256 //字符串長(zhǎng)度 #define MAXSIZE 1024 //主機(jī)高速緩存中的最大記錄條數(shù) #fefine DEFAULT_SNAPLEN 68 /數(shù)據(jù)包數(shù)據(jù)的長(zhǎng)度 typedef struct { unsigned long int ipaddr; //IP地址 char hostname[MAXSTRINGSIZE]; //主機(jī)名 }dnstable; //高速緩存數(shù)據(jù)結(jié)構(gòu) typedef struct { dnstable table[MAXSIZE]; int front; int rear; }sequeue; sequeue *sq; //定義緩存隊(duì)列 sq->rear=sq->front=0; //初始化隊(duì)列 //輸出MAC地址函數(shù) void print_hwadd(u_char * hwadd) { for(int i=0,i<5;++i) printf("%2x:",hwadd); printf("%2x",hwadd); } //輸出IP地址的函數(shù) void print_ipadd(u_char *ipadd) { for(int i=0;i<3;++i) printf("%d.",ipadd); printf("%d",ipadd); } //查詢(xún)端口函數(shù) void getportname(int portno,char portna[],char* proto) { if(getservbyport(htons(portno),proto)!=NULL) { strcpy(portna,getservbyport(htons(portno),proto)->s_name); } else sprintf(portna,"%d",portno); } //將IP轉(zhuǎn)化為DNS名 void iptohost(unsigned long int ipad,char* hostn) { struct hostent * shostname; int m,n,i; m=sq->rear; n=sq->front; for(i=n%MAXSIZE;i=m%MAXSIZE;i=(++n)%MAXSIZE) { //檢查IP是否第一次出現(xiàn) if(sq->table.ipaddr==ipad) { strcpy(hostn,sq->table.hostname); break; } } if(i=m%MAXSIZE) {//不存在則從域名服務(wù)器查詢(xún)并把結(jié)果放入高速緩存 if((sq->rear+1)%MAXSIZE=sq->front) //判隊(duì)滿 sq->front=(sq->front+1)%MAXSIZE; //出隊(duì)列 sq->table.ipaddr=ipad; shostname=gethostbyaddr((char*)&ipad,sizeof(ipad),AF_INET); if(shostname!=NULL) strcpy(sq->table.hostname,shostname->h_name); else strcpy(sq->table.hostname,""); sq->rear=(sq->rear+1)%MAXSIZE; } } void print_hostname(u_char* ipadd) { unsigned long int ipad; char hostn[MAXSTRINTSIZE]; ipad=*((unsigned long int *)ipadd); iptohost(ipad,hostn) if(strlen(hostn)>0) printf("%s",hostn); else print_ipadd(ipadd); } //處理數(shù)據(jù)包的函數(shù) void packet_proce(u_char* packets,const struct pcap_pkthdr * header,const u_char
*pp) { struct ether_header * eth; //以太網(wǎng)幀報(bào)頭指針 struct ether_arp * arth; //ARP報(bào)頭 struct ip * iph; //IP報(bào)頭 struct tcphdr * tcph; struct udphdr * udph; u_short srcport,dstport; //端口號(hào) char protocol[MAXSTRINGSIZE]; //協(xié)議類(lèi)型名 char srcp[MAXSTRINGSIZE],dstp[MAXSTRINGSIZE]; //端口名 unsigned int ptype; //協(xié)議類(lèi)型變量 u_char * data; //數(shù)據(jù)包數(shù)據(jù)指針 u_char tcpudpdata[MAXSTRINGSIZE]; //數(shù)據(jù)包數(shù)據(jù) int i; eth=(struct ether_header *)pp; ptype=ntohs(((struct ether_header *)pp)->ether_type); if((ptype==ETHERTYPE_ARP)||(ptype==ETHERTYPE_RARP)) { arph=(struct ether_arp *)(pp+sizeof(struct ether_header)); if(ptype==ETHERTYPE_ARP) printf("arp "); else printf("rarp "); //輸出協(xié)議類(lèi)型 print_hwadd((u_char *)&(arph->arp_sha)); printf("("); print_hostname((u_char *)&(arph->arp_spa)); printf(")->"); print_hwadd((u_char *)&(arph->arp_tha)); printf("("); print_hostname((u_char *)&(arph->arp_tpa)); printf(")tpacketlen:%d",header->len); } else if(ptype==ETHERTYPE_IP) //IP數(shù)據(jù)報(bào) { iph=(struct ip *)(pp+sizeof(struct ether_header)); if(iph->ip_p==1) //ICMP報(bào)文 { strcpy(protocol,"icmp"); srcport=dstport=0; } else if(iph->ip_p==6) //TCP報(bào)文 { strcpy(protocol,"tcp"); tcph=(struct tcphdr *)(pp+sizeof(struct ether_header)
+4*iph->ip_hl); srcport=ntohs(tcph->source); dstport=ntohs(tcph->dest); data=(u_char *)(pp+sizeof(struct ether_header)+4*iph-
>ip_hl+4*tcph->doff); for(i=0;i { if(i>=header->len-sizeof(struct ether_header)-
4*iph->ip_hl-4*tcph->doff); break; else tcpudpdata=data; } } //TCP數(shù)據(jù)處理完畢 else if(iph->ip_p=17) //UDP報(bào)文 { strcpy(protocol,"udp"); udph=(struct udphdr *)(pp+sizeof(struct ether_header)
+4*iph->ip_hl); srcport=ntohs(udph->source); dstport=ntohs(udph->dest); data=(u_char *)(pp+sizeof(struct ether_header)+4*iph-
>ip_hl+8); for(i=0;i { if(i>=header->len-sizeof(struct ether_header)-
4*iph->ip_hl-8); break; else tcpudpdata=data; } } tcpudpdata='\0'; getportname(srcport,srcp,protocol); getportname(dstport,dstp,protocol); printf("ip "); print_hwadd(eth->ether_shost); printf("("); print_hostname((u_char *)&(iph->ip_src)); printf(")[%s:%s]->",protocol,srcp); print_hwadd(eth->ether_dhost); printf("("); print_hostname((u_char *)&(iph->ip_dst)); printf(")[%s:%s]",protocol,dstp); printf("tttl:%d packetlen:%d,iph->ttl,header->len); printf("n"); printf("%s",tcpudpdata); printf("==endpacket=="); } printf("n"); } //Main函數(shù)取數(shù)據(jù)包并初始化程序環(huán)境 int main(int argc,char ** argv) { char ebuf[pcap_ERRBUF_SIZE]; pcap * pd; if(argc<=1) //參數(shù)檢查 { printf("usage:%sn",argv[0]); exit(0); } //設(shè)置PCAP程序庫(kù) if((pd=pcap_open_live(argv[1],DEFAULT_SNAPLEN,1,1000,ebuf))=NULL) { (void)fprintf(stderr,"%s",ebuf); exit(1); } //循環(huán)取數(shù)據(jù)包 //改變參數(shù)-1為其它值,,可確定取數(shù)據(jù)包的個(gè)數(shù),這里為無(wú)限個(gè) if(pcap_loop(pd,-1,packet_proce,NULL)<0) { (void)fprintf(stderr,"pcap_loop:%sn",pcap_geterr(pd)); exit(1); } pcap_colse(pd); exit(0); } //程序結(jié)束
本文來(lái)自CSDN博客,,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/flybabydog/archive/2005/04/19/354089.aspx |
|