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