Linux下使用libpcap進行網絡抓包並保存到文件(函數介紹)

libpcap是一個抓取網絡數據報文的C語言函數庫,使用這個庫可以非常方便的抓取網絡上的報文,方便我們分析經過我們設備上的各種報文;
使用libcap庫編譯時都要在後面加上-lpcap選項

使用pcap探測獲取網絡接口

char * pcap_lookupdev(char * errbuf)

這個函數就是用來探測網絡接口的,它會返回第一個合適的網絡接口字符串指針,如果出錯則在errbuf中返回,長度至少是PCAP_ERRBUF_SIZE。

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char errBuf[PCAP_ERRBUF_SIZE], * devStr;

    devStr = pcap_lookupdev(errBuf);
    if (devStr)
        printf("success: device: %s\n", devStr);
    else
    {
        printf("error: %s\n", errBuf);
        exit(1);
    }

    return 0;
}

注意這個函數是返回第一個合適的網絡接口字符串,我的主機第一個合適的網絡接口爲 vibir0端口,在這個虛擬端口下沒有辦法抓到包,因而要找到指定的端口要要循環遍歷端口後,進行選擇特定網卡端口

char * get(){
 pcap_if_t *alldevs;
   pcap_if_t *d;
   int i=0;
   char errbuf[PCAP_ERRBUF_SIZE];                                               
   /* 獲取本地機器設備列表 */
   if (pcap_findalldevs( &alldevs, errbuf   ) == -1)
   {
       fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf);
       exit(1);
   }
   for(d= alldevs; d != NULL; d= d->next)
   {
       printf("%d. %s\n", ++i, d->name);
   }
        //獲取指定的設備
   char getPort[32];
   gets(getPort);
   for(d= alldevs; d != NULL; d= d->next)
   {if(strcmp(d->name,getPort)==0)
          {
              printf("%d. %s", ++i, d->name);
              break;
          }
    }
    return d->name;
}

捕獲第一個報文

pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)

要捕獲報文,在探測到接口之後我們還要打開它,該函數會返回指定接口的pcap_t類型指針,後面的所有操作都要使用這個指針;

參數一:device是第一步探測到的接口的字符串;

參數二:snaplen是對於每個數據包,從開頭要抓多少個字節,我們可以設置這個值來只抓每個數據包的頭部,而不關心具體的內容。典型的以太網幀長度是1518字節,但其他的某些協議的數據包會更長一點,但任何一個協議的一個數據包長度都必然小於65535個字節;

參數三:promisc指定是否打開混雜模式(Promiscuous Mode),0表示非混雜模式,任何其他值表示混合模式。如果要打開混雜模式,那麼網卡必須也要打開混雜模式,可以使用如下的命令打開例如eth0混雜模式:
ifconfig eth0 promisc;

參數四:to_ms指定需要等待的毫秒數,超過這個數值後,第3步獲取數據包的這幾個函數就會立即返回。0表示一直等待直到有數據包到來;

參數五:用來返回錯誤信息;

void pcap_close(pcap_t * p)

釋放網絡接口,用於關閉pcap_open_live()獲取的pcap_t的網絡接口對象並釋放相關資源;

u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)

該函數收到一個包就立刻返回,返回值爲NULL表示沒有收到包;

第一個參數是第2)個函數pcap_open_live返回的指針,第二個參數記錄了報文長度等,其結構也可以在pcap.h裏查看到;

循環捕獲報文

int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)

第一個參數是第2)個函數pcap_open_live返回的指針,第二個參數是需要抓的數據包的個數,一旦抓到了cnt個數據包,pcap_loop立即返回。負數的cnt表示pcap_loop永遠循環抓包,直到出現錯誤;第三個參數是一個回調函數指針,形式如下:void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet) 第一個參數是pcap_loop的最後一個參數,第二個參數是收到的數據包的pcap_pkthdr結構,第三個參數是數據包數據;

int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)

和pcap_loop()非常類似,它同時還受pcap_open_live()的第4個參數to_ms控制超時返回時間;

#include <pcap.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

void processPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
    int *count = (int *)arg;

    printf("Packet Count: %d\n", ++(*count));
    printf("Received Packet Size: %d\n", pkthdr->len);
    printf("Payload:\n");

    for(int i=0; i < pkthdr->len; ++i)      //print
    {
        printf("%02x ", packet[i]);
        if ((i + 1) % 16 == 0)
        {
            printf("\n");
        }
    }
    printf("\n\n");
    return;
}

int main()
{
    char errBuf[PCAP_ERRBUF_SIZE], * devStr;

    devStr = pcap_lookupdev(errBuf);
    if (devStr)
        printf("success: device: %s\n", devStr);
    else
    {
        printf("error: %s\n", errBuf);
        exit(1);
    }

    /* open a device, wait until a packet arrives */
    pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
    if (!device)
    {
        printf("error: pcap_open_live(): %s\n", errBuf);
        exit(1);
    }

    int count = 0;
    /*Loop forever & call processPacket() for every received packet.*/
    pcap_loop(device, -1, processPacket, (u_char *)&count);

    pcap_close(device);
    return 0;
}

過濾數據包函數

libpcap捕獲報文是把經過的報文複製一份,當接口上報文數量很大時,抓取報文非常的佔用系統資源,通過過濾數據包來捕獲數據包,既可以過濾掉我們不想要的報文,又可以提高效率;

設置過濾條件,就是過濾表達式;

舉例:

src host 192.168.1.177,dst port 80,not tcp等;

int pcap_compile(pcap_t * p, struct bpf_program fp, char \ str, int optimize, bpf_u_int32 netmask)

fp是一個傳出參數,存放編譯後的bpf,str是過濾表達式,optimize是否需要優化過濾表達式,metmask簡單設置爲0即可;

int pcap_setfilter(pcap_t p, struct bpf_program fp)

fp就是前一步pcap_compile()的第二個參數;

    struct bpf_program filter;
    pcap_compile(device, &filter, "tcp", 1, 0);
    pcap_setfilter(device, &filter);

把數據包保存到文件

捕獲到數據包之後,通常就是對數據包的分析,具體的報文分析方法要依據網絡協議詳細分類並展開處理,這裏不做討論。我們可以暫時把捕獲到的數據包保存到文件中,稍後再由分析報文的程序或者工具來具體分析;libpcap庫提供了保存爲pcap類型的函數,非常方便,保存之後就可以用Wireshark直接打開了,如果想保存爲純粹的數據包,我們也可以用C語言的文件操作,直接把數據包以二進制的形式保存到文件中;

pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *file)

函數返回pcap_dumper_t類型的指針,file是文件名,可以是絕對路徑,例如:/home/iona/packet.pcap;

void   pcap_dump_close(pcap_dumper_t *p);

用來關閉pcap_dump_open打開的文件,入參是pcap_dump_open返回的指針;

int  pcap_dump_flush(pcap_dumper_t *p)

刷新緩衝區,把捕獲的數據包從緩衝區真正拷貝到文件;

void   pcap_dump(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)

輸出數據到文件,與pcap_loop的第二個參數回調函數void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet) 形式完全相同,可以直接當pcap_loop的第二個參數;

#include <pcap.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

void processPacket(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
    pcap_dump(arg, pkthdr, packet);
    printf("Received Packet Size: %d\n", pkthdr->len);
    return;
}

int main()
{
    char errBuf[PCAP_ERRBUF_SIZE], * devStr;

    devStr = pcap_lookupdev(errBuf);
    if (devStr)
        printf("success: device: %s\n", devStr);
    else
    {
        printf("error: %s\n", errBuf);
        exit(1);
    }

    /* open a device, wait until a packet arrives */
    pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
    if (!device)
    {
        printf("error: pcap_open_live(): %s\n", errBuf);
        exit(1);
    }

    /*open pcap write output file*/
    pcap_dumper_t* out_pcap;
    out_pcap  = pcap_dump_open(device,"pack.pcap");

    /*Loop forever & call processPacket() for every received packet.*/
    pcap_loop(device, 20, processPacket, (u_char *)out_pcap);

    /*flush buff*/
    pcap_dump_flush(out_pcap);

    pcap_dump_close(out_pcap);
    pcap_close(device);
    return 0;
}

轉載自:https://blog.csdn.net/lvjian_/article/details/88575458

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章