利用libpcap抓取數據包


  • 轉載自:http://blog.csdn.net/tennysonsky/article/details/44811899
  • 概述
    • libpcap是一個網絡數據包捕獲函數庫,tcpdump就是以libpcap為基礎的。
    • 主要作用:
      • 捕獲各種數據包,例如:網絡流量統計
      • 過濾網絡數據包,例如:過濾掉本地上的一些數據,類似防火牆
      • 分析網絡數據包,例如:分析網絡協議,數據的采集
      • 存儲網絡數據包,例如:保存捕獲的數據以為將來進行分析

 

  • libpcap的抓包框架
    • pcap_lookupdev():函數用來查找網絡設備,返回可被pcap_open_live()函數調用的網絡設備名指針。
    • pcap_lookupnet():函數獲得指定網絡設備的網絡號和掩碼。
    • pcap_open_live():函數用於打開設備,並且返回用於捕獲網絡數據包的數據包捕獲描述字。對於此網絡設備的操作都要基於此網絡設備描述字。
    • pcap_compile():函數用於將用戶制定的過濾策略編譯到過濾程序中
    • pcap_setfilter():函數用於設置過濾器
    • pcap_loop():與pcap_next()和pcap_next_ex()兩個函數一樣用來捕獲數據包
    • pcap_close():函數用於關閉網絡設備,釋放資源

 

  • 利用libpcap函數庫開發應用程序的步驟:
    • 打開網絡設備
    • 設置過濾規則
    • 捕獲數據
    • 關閉網絡設備

 

  • 詳細步驟:
    • 首先要使用libpcap,需要包含pcap.h頭文件。
    • 獲取網絡設備接口:
      • char *pcap_lookupdev(char * errbuf);
      • 功能:自動獲取可用的網絡設備名指針
      • 參數:errbuf,存放出錯信息字符串,有宏定義緩沖區大小,PCAP_ERRBUF_SIZE
      • 返回值:成功返回設備名指針(第一個合適的網絡接口的字符串指針),失敗則返回NULL,同時,errbuf存放出錯信息字符串
      • 1 //自動獲取網絡接口形式
        2 char errBuf[PCAP_ERRBUF_SIZE], *devStr;
        3 devStr = pcap_lookupdev(errBuf);
        4 
        5 //手動獲取網絡接口形式只需要被devStr賦值即可
        6 char errBuf[PCAP_ERRBUF_SIZE], *devStr = “eth0”;

         

    • 獲取網絡號(ip地址)和掩碼
      • int pcap_lookupnet(char* device,bpf_u_int32 *netp,bpf_u_int32 *maskp,char *errbuf);
      • 功能:獲取指定網卡的ip地址,子網掩碼
      • 參數:device:網絡設備名,為第一步獲取的網絡接口字符串,也可以人為指定,如“eth0”;
      •          netp:存放ip地址的指針,buf_u_int32為32位無符號整型
      •          maskp:存放子網掩碼的指針
      •          errbuf:存放出錯信息
      • 返回值:成功返回0,失敗返回1
      •  1 char error_content[PCAP_ERRBUF_SIZE] = {0}; // 出錯信息  
         2 char *dev = pcap_lookupdev(error_content);  
         3 if(NULL == dev)  
         4 {  
         5     printf(error_content);  
         6     exit(-1);  
         7 }  
         8   
         9   
        10 bpf_u_int32 netp = 0, maskp = 0;  
        11 pcap_t * pcap_handle = NULL;  
        12 int ret = 0;  
        13   
        14 //獲得網絡號和掩碼  
        15 ret = pcap_lookupnet(dev, &netp, &maskp, error_content);  
        16 if(ret == -1)  
        17 {  
        18     printf(error_content);  
        19     exit(-1);  
        20 }  

         

    • 打開網絡接口
      • pcap_t *pcap_open_live(const char * device,int snaplen,int promisc,int to_ms,char *errbuf);
      • 功能:打開一個用於捕獲數據的網絡端口
      • 參數:device:網絡接口的名字,為第一步獲取的網絡接口字符串,也可以人為指定,如:”eth0“
      •          snaplen:捕獲數據包的長度,不能大於65535個字節
      •          promise:”1“代表混雜模式,其他值代表非混雜模式
      •          to_ms:指定需要等地啊的毫秒數,超過這個時間后,獲得數據包的函數會立即返回,0表示一直等待直到有數據包到來
      •          errbuf:存儲錯誤信息
      • 返回值:返回pcap_t類型指針,后面的所有操作都要使用這個指針。
      •  1 char error_content[PCAP_ERRBUF_SIZE] = {0}; // 出錯信息  
         2 char *dev = pcap_lookupdev(error_content);  // 獲取網絡接口  
         3 if(NULL == dev)  
         4 {  
         5     printf(error_content);  
         6     exit(-1);  
         7 }  
         8   
         9 // 打開網絡接口  
        10 pcap_t * pcap_handle = pcap_open_live(dev, 65535, 1, 0, error_content);  
        11 if(NULL == pcap_handle)  
        12 {  
        13     printf(error_content);  
        14     exit(-1);  
        15 }  

         

    • 獲取數據包:
      • 方法一:const u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h);
      • 功能:捕獲一個網絡數據包,收到一個數據包立即返回
      • 參數:p:pcap_open_live()返回的pcap_t類型的指針
      •          h:數據包頭
      • pcap_pkthdr類型的定義如下:
      • 1 struct pcap_pkthdr  
        2 {  
        3     struct timeval ts; // 抓到包的時間  
        4     bpf_u_int32 caplen; // 表示抓到的數據長度  
        5     bpf_u_int32 len; // 表示數據包的實際長度  
        6 }  

         

      • len和caplen的區別:因為在某些情況下不能保證捕獲的包是完整的,例如一個包長1480,但是你捕獲到1000的時候,可能因為某些原因就終止捕獲了,所以caplen是記錄實際捕獲的包長,也就是1000,而len就是1480
      • 返回值:成功則返回捕獲數據包的地址,失敗返回NULL
      •  1 const unsigned char *p_packet_content = NULL; // 保存接收到的數據包的起始地址  
         2 pcap_t *pcap_handle = NULL;  
         3 struct pcap_pkthdr protocol_header;  
         4   
         5 pcap_handle = pcap_open_live("eth0", 1024, 1, 0,NULL);  
         6   
         7 p_packet_content = pcap_next(pcap_handle, &protocol_header);   
         8 //p_packet_content  所捕獲數據包的地址  
         9           
        10 printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec)); // 時間  
        11 printf("Packet Lenght is :%d\n",protocol_header.len);   // 數據包的實際長度

         

      • 方法二:int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
      • 功能:循環捕獲網絡數據包,直到遇到錯誤或者滿足退出條件,每次捕獲一個數據包就會調用callback指定的回調函數,所以,可以在回調函數中進行數據包的處理操作。
      • 參數:p:pcap_open_live()返回的pcap_t類型的指針
      •          cnt:指定捕獲數據包的個數,一旦抓到cnt個數據包,pcap_loop立即返回,如果是-1,就會一直捕獲直到出錯
      •          callback:回調函數,名字任意,根據需要自行取名
      •          user:向回調函數中傳遞的參數
      • callback回調函數的定義:
        • void callback(u_char *userarg,const struct pcap_pkthdr *pkthdr,const u_char *packet)
        • userarg:pcap_loop()的最后一個參數,當收到足夠數量的包后pcap_loop會調用callback回調函數,同時將pcap_loop()的user參數傳遞給它
        • pkthdr:是收到數據包的pcap_pkthdr類型的指針,和pcap_next()第二個參數是一樣的
        • packet:收到的數據包數據
        • 返回值:成功返回0,失敗返回負數
      • 方法三:int pcap_dispatch(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
        • 這個函數和pcap_loop()非常類似,只是在超過to_ms毫秒后就會返回(to_ms是pcap_open_live()的第四個參數)
    • 釋放網絡接口:
      • void pcap_close(pcap_t *p);
      • 功能:關閉pcap_open_live()打開的網絡接口,並釋放相關資源
      • 參數:p:需要關閉的網絡接口,pcap_open_live()的返回值
      •  1 // 打開網絡接口  
         2 pcap_t * pcap_handle = pcap_open_live("eth0", 1024, 1, 0, error_content);  
         3 if(NULL == pcap_handle)  
         4 {  
         5     printf(error_content);  
         6     exit(-1);  
         7 }  
         8   
         9 //// ……  
        10 //// ……  
        11   
        12 pcap_close(pcap_handle); //釋放網絡接口  

         

  • //接收一個數據包
    #include <stdio.h>  
    #include <pcap.h>  
    #include <arpa/inet.h>  
    #include <time.h>  
    #include <stdlib.h>  
    struct ether_header  
    {  
        unsigned char ether_dhost[6];   //目的mac  
        unsigned char ether_shost[6];   //源mac  
        unsigned short ether_type;      //以太網類型  
    };  
    #define BUFSIZE 1514  
      
    int main(int argc,char *argv[])  
    {  
        pcap_t * pcap_handle = NULL;  
        char error_content[100] = "";   // 出錯信息  
        const unsigned char *p_packet_content = NULL;       // 保存接收到的數據包的起始地址  
        unsigned char *p_mac_string = NULL;         // 保存mac的地址,臨時變量  
        unsigned short ethernet_type = 0;           // 以太網類型  
        char *p_net_interface_name = NULL;      // 接口名字  
        struct pcap_pkthdr protocol_header;  
        struct ether_header *ethernet_protocol;  
      
        //獲得接口名  
        p_net_interface_name = pcap_lookupdev(error_content);  
        if(NULL == p_net_interface_name)  
        {  
            perror("pcap_lookupdev");  
            exit(-1);  
        }  
          
        //打開網絡接口  
        pcap_handle = pcap_open_live(p_net_interface_name,BUFSIZE,1,0,error_content);  
        p_packet_content = pcap_next(pcap_handle,&protocol_header);  
          
        printf("------------------------------------------------------------------------\n");  
        printf("capture a Packet from p_net_interface_name :%s\n",p_net_interface_name);  
        printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec));  
        printf("Packet Lenght is :%d\n",protocol_header.len);  
          
        /* 
        *分析以太網中的 源mac、目的mac 
        */  
        ethernet_protocol = (struct ether_header *)p_packet_content;  
        p_mac_string = (unsigned char *)ethernet_protocol->ether_shost;//獲取源mac  
        printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+0),*(p_mac_string+1),*(p_mac_string+2),*(p_mac_string+3),*(p_mac_string+4),*(p_mac_string+5));  
        p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//獲取目的mac  
        printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+0),*(p_mac_string+1),*(p_mac_string+2),*(p_mac_string+3),*(p_mac_string+4),*(p_mac_string+5));  
      
        /* 
        *獲得以太網的數據包的地址,然后分析出上層網絡協議的類型 
        */  
        ethernet_type = ntohs(ethernet_protocol->ether_type);  
        printf("Ethernet type is :%04x\t",ethernet_type);  
        switch(ethernet_type)  
        {  
            case 0x0800:printf("The network layer is IP protocol\n");break;//ip  
            case 0x0806:printf("The network layer is ARP protocol\n");break;//arp  
            case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp  
            default:printf("The network layer unknow!\n");break;  
        }  
          
        pcap_close(pcap_handle);  
        return 0;  
    }  

     

  •  1//接收多個數據包 2 #include <stdio.h>  
     3 #include <pcap.h>  
     4 #include <arpa/inet.h>  
     5 #include <time.h>  
     6 #include <stdlib.h>  
     7   
     8 #define BUFSIZE 1514  
     9   
    10 struct ether_header  
    11 {  
    12     unsigned char ether_dhost[6];   //目的mac  
    13     unsigned char ether_shost[6];   //源mac  
    14     unsigned short ether_type;      //以太網類型  
    15 };  
    16   
    17 /*******************************回調函數************************************/  
    18 void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)  
    19 {  
    20     unsigned char *mac_string;              //  
    21     struct ether_header *ethernet_protocol;  
    22     unsigned short ethernet_type;           //以太網類型  
    23     printf("----------------------------------------------------\n");  
    24     printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //轉換時間  
    25     ethernet_protocol = (struct ether_header *)packet_content;  
    26       
    27     mac_string = (unsigned char *)ethernet_protocol->ether_shost;//獲取源mac地址  
    28     printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));  
    29     mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//獲取目的mac  
    30     printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));  
    31       
    32     ethernet_type = ntohs(ethernet_protocol->ether_type);//獲得以太網的類型  
    33     printf("Ethernet type is :%04x\n",ethernet_type);  
    34     switch(ethernet_type)  
    35     {  
    36         case 0x0800:printf("The network layer is IP protocol\n");break;//ip  
    37         case 0x0806:printf("The network layer is ARP protocol\n");break;//arp  
    38         case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp  
    39         default:break;  
    40     }  
    41     usleep(800*1000);  
    42 }  
    43   
    44 int main(int argc, char *argv[])  
    45 {  
    46     char error_content[100];    //出錯信息  
    47     pcap_t * pcap_handle;  
    48     unsigned char *mac_string;                
    49     unsigned short ethernet_type;           //以太網類型  
    50     char *net_interface = NULL;                 //接口名字  
    51     struct pcap_pkthdr protocol_header;  
    52     struct ether_header *ethernet_protocol;  
    53       
    54     //獲取網絡接口  
    55     net_interface = pcap_lookupdev(error_content);  
    56     if(NULL == net_interface)  
    57     {  
    58         perror("pcap_lookupdev");  
    59         exit(-1);  
    60     }  
    61   
    62     pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打開網絡接口  
    63           
    64     if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)  
    65     {  
    66         perror("pcap_loop");  
    67     }  
    68       
    69     pcap_close(pcap_handle);  
    70     return 0;  
    71 }  

     

  • 過濾數據包:
    • 設置過濾條件:舉一些例子:
      • src host 192.168.1.177:只接收源ip地址是192.168.1.177的數據包
      • dst port 80:只接收tcp、udp的目的端口是80的數據包
      • not tcp:只接收不使用tcp協議的數據包
      • tcp[13] == 0x02 and (dst port 22 or dst port 23) :只接收 SYN 標志位置位且目標端口是 22 或 23 的數據包( tcp 首部開始的第 13 個字節)
      • icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo:只接收 icmp 的 ping 請求和 ping 響應的數據包
      • ehter dst 00:e0:09:c1:0e:82:只接收以太網 mac 地址是 00:e0:09:c1:0e:82 的數據包
      • ip[8] == 5:只接收 ip 的 ttl=5 的數據包(ip首部開始的第8個字節)
    • 編譯BPF過濾規則:
      • int pcap_compile(pcap_t *p,struct bpf_program *fp,char *buf,int optimize,bpf_u_int32 mask);
      • 參數:
        • p:pcap_open_live()返回的pcap_t類型的指針
        • fp:存放編譯后的bpf,應用過來規則時需要使用這個指針
        • buf:過濾規則
        • optimize:是否需要優化過濾表達式
        • mask:指定本地網絡的網絡掩碼,不需要時可寫0
      • 返回值:成功返回0,失敗返回-1
    • 應用BPF過濾規則:
      • int pcap_setfilter(pcap_t *p,struct bpf_program *fp);
      • 功能:應用BPF過濾規則
      • 參數:p:pcap_open_live()返回的pcap_t類型的指針
      •         fp:pcap_compile()的第二個參數
      • 返回值:成功返回0,失敗返回-1
      •  1 #include <pcap.h>  
         2 #include <time.h>  
         3 #include <stdlib.h>  
         4 #include <stdio.h>  
         5   
         6 void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
         7 {  
         8   int * id = (int *)arg;  
         9     
        10   printf("id: %d\n", ++(*id));  
        11   printf("Packet length: %d\n", pkthdr->len);  
        12   printf("Number of bytes: %d\n", pkthdr->caplen);  
        13   printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
        14     
        15   int i;  
        16   for(i=0; i<pkthdr->len; ++i)  
        17   {  
        18     printf(" %02x", packet[i]);  
        19     if( (i + 1) % 16 == 0 )  
        20     {  
        21       printf("\n");  
        22     }  
        23   }  
        24     
        25   printf("\n\n");  
        26 }  
        27   
        28 int main()  
        29 {  
        30   char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
        31     
        32   /* get a device */  
        33   devStr = pcap_lookupdev(errBuf);  
        34     
        35   if(devStr)  
        36   {  
        37     printf("success: device: %s\n", devStr);  
        38   }  
        39   else  
        40   {  
        41     printf("error: %s\n", errBuf);  
        42     exit(1);  
        43   }  
        44     
        45   /* open a device, wait until a packet arrives */  
        46   pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
        47     
        48   if(!device)  
        49   {  
        50     printf("error: pcap_open_live(): %s\n", errBuf);  
        51     exit(1);  
        52   }  
        53     
        54   /* construct a filter */  
        55   struct bpf_program filter;  
        56   pcap_compile(device, &filter, "dst port 80", 1, 0);  
        57   pcap_setfilter(device, &filter);  
        58     
        59   /* wait loop forever */  
        60   int id = 0;  
        61   pcap_loop(device, -1, getPacket, (u_char*)&id);  
        62     
        63   pcap_close(device);  
        64   
        65   return 0;  
        66 } 

         


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM