WinPcap教程(3):打开网卡抓包

我们已经知道如何获取网卡信息了,现在让我们开始进行真正的开发工作,即打开一个网卡并抓取数据包。本文中,我们将写一个程序来打印出流经网卡的每个包的相关信息。

 

打开抓包设备的函数是pcap_open(),该函数的参数中,snaplen,flags和to_ms这三个值得解释一下:

snaplen

该参数设定了包需要抓取的部分。在一些操作系统上(如xBSD和Win32),包驱动程序可以配置成只抓取包的初始部分:这降低了应用拷贝数据的数量,从而提高扎包的效率。在我们使用高于我们可能碰到的最大MTU(Maximum Transmission Unit,最大传输单元,以字节为单位) 65536的情况下,这将确保应用总是接收到包的全部。

 

flags

最重要的标致应该是否将网卡设置成混杂模式(promiscuous mode)。在正常模式下,网卡只抓取来自网络的,以它为目的地的数据包,其他机器之间交换的包将被忽略。而在混杂模式下,将抓取所有的包,而不管该网卡是不是这些包的目的地。这意味着在共享介质(如未经交换的以太网,non-switched Ethernet)的情况下,WinPcap能够抓取到其他机器的包。多数抓包应用,混杂模式都是缺省的模式,下面的示例程序中,我们也采用混杂模式。

 

to_ms

该参数指定了读超时,以毫秒为单位。从网卡读数据时(比如,用pcap_dispatch()或者pcap_next_ex()),如果网络上无包可读,超时后(即过了to_ms毫秒后)总会返回。to_ms也定义了统计报告的时间间隔。将to_ms设为0,表示无超时,即在读网卡时,如果没有数据包到来,将永不返回;另一方面,如果设为-1,会使得对网卡的读操作立即返回。

 

#include "pcap.h"

 

/* prototype of the packet handler */

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

 

int main()

{

pcap_if_t *alldevs;

pcap_if_t *d;

int inum;

int i=0;

pcap_t *adhandle;

char errbuf[PCAP_ERRBUF_SIZE];

 

    /* Retrieve the device list on the local machine */

    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)

    {

        fprintf(stderr,"Error in pcap_findalldevs: %s/n", errbuf);

        exit(1);

    }

 

    /* Print the list */

    for(d=alldevs; d; d=d->next)

    {

        printf("%d. %s", ++i, d->name);

        if (d->description)

            printf(" (%s)/n", d->description);

        else

            printf(" (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_s("%d", &inum);

 

    if(inum < 1 || inum > i)

    {

        printf("/nInterface number out of range./n");

        /* Free the device list */

        pcap_freealldevs(alldevs);

        return -1;

    }

 

    /* Jump to the selected adapter */

    for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

 

    /* Open the device */

    if ( (adhandle= pcap_open(d->name,      // name of the device

                              65536,      // portion of the packet to capture

                                          // 65536 guarantees that the whole packet will be captured on all the link layers

                              PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode

                              1000,             // read timeout

                              NULL,             // authentication on the remote machine

                              errbuf            // error buffer

                              ) ) == NULL)

    {

        fprintf(stderr,"/nUnable to open the adapter. %s is not supported by WinPcap/n", d->name);

        /* Free the device list */

        pcap_freealldevs(alldevs);

        return -1;

    }

 

    printf("/nlistening on %s.../n", d->description);

 

    /* At this point, we don't need any more the device list. Free it */

    pcap_freealldevs(alldevs);

 

    /* start the capture */

    pcap_loop(adhandle, 0, packet_handler, NULL);

 

    return 0;

}

 

 

/* Callback function invoked by libpcap for every incoming packet */

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)

{

    struct tm ltime;

    char timestr[16];

    time_t local_tv_sec;

 

    /*

     * unused variables

     */

    (VOID)(param);

    (VOID)(pkt_data);

 

    /* convert the timestamp to readable format */

    local_tv_sec = header->ts.tv_sec;

    localtime_s(&ltime, &local_tv_sec);

    strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

 

    printf("%s,%.6d len:%d/n", timestr, header->ts.tv_usec, header->len);

 

}

 

打开网卡后,就可以用pcap_dispatch()或者pcap_loop()开始抓包了。这两个函数非常相似,不同的是当超时到了后,pcap_dispatch()会返回(尽管不能保证),而pcap_loop()则不会返回,直到抓取了cnt个包(译注:cnt是pcap_loop()函数的一个参数,其含义见pcap_loop()的原型),因此在比较空闲网络情况下,会被阻塞很长时间。在本例中,pcap_loop()就够用了,pcap_dispatch()通常用于更复杂的程序。

 

这两个函数都各有一个callback参数,即packet_handler,它指向用于处理包数据的函数。每当有新的包从网络上到达时,该函数就会自动被libpcap调用,以接收一个通用状态(generic status,和pcap_loop()或pcap_dispatch()中的user参数对应);一个含有信息包头,诸如时间戳和长度;以及包括协议头在内的真正包数据。注意CRC帧一般不提供,因为网卡在帧完整性验证后会将其删除。另外,大多数网卡在收到错误的CRC后,会将相应的包丢弃。因此,这些被丢弃的包,WinPcap是抓取不到的。

 

上面的例子从pcap_pkthdr头提取了时间戳和每个包的长度,并打印在屏幕上。

 

请注意到,上面的pcap_loop()或许存在一个不足之处,主要是基于这样一个事实:处理函数(handler)是由抓包驱动程序调用的,因此用户的应用程序不能直接控制它。另外一个方法就是使用pcap_next_ex()函数,将在下一个示例程序说明。

 

后篇:待续…

 

此文章来自于【http://blog.csdn.net/pathuang68/article/details/4292603

你可能感兴趣的:(WinPcap教程(3):打开网卡抓包)