金庸群侠传x霹雳刀法:WinPcap基础知识(第三课:打开一个适配器捕捉数据包)

来源:百度文库 编辑:中财网 时间:2024/04/28 04:07:30
现在我们已经知道了怎样去获取一个适配器并使用它,让我们开始真正的工作-----开始抓取网络数据包吧。在这一课中我们将写一个程序,这个程序将在我们选择的适配器上监听,并抓取通过这个适配器上的每一个数据包,打印其中的一些信息。
 我们主要使用的函数是pcap_open(),这个函数的功能是打开一个抓取设备。在这里有必要对其中的几个参数snaplen, flags and to_ms作一下说明。
 snaplen指定了我们所要抓取的包的长度(译者:也就是我们想抓多长就设置多长)。在一些操作系统(如xBSD和Win32)中,底层驱动可以通过配置只抓取数据包的开始部分:这样就减少了拷贝给应用程序的数据量,因此可以提高抓取效率。在这个例子里我们使用65536这个比我们所能遇到的最大的MTU还大的数字。这样我们就能确保我们的程序可以抓到整个数据包。
 flags:最重要的标志是一个指示适配器是否工作在混杂模式下的。在正常状况下,一个适配器仅仅抓取网络中目的地是它自己的数据包;因此其他主机交换的数据包都被忽略。相反,当适配器处在混杂模式下的时候它就会抓取所有的数据包而不管是不是发给它的。这就意味着在共享媒体(如非交换的以太网)上,WinPcap将能够抓取其他主机的数据包。混杂模式是大部分抓取程序的默认模式,所以在下面的例子中我们就开启它。
 to_ms以豪秒为单位指定了读取操作的超时界限。在适配器上一个读取操作(比如,pcap_dispatch() 或者 pcap_next_ex())将总是在to_ms豪秒后返回,即使网络中没有数据包可供抓取。如果适配器工作在统计模式(如果对此不了解,请看课程9),to_ms还定义了统计报告之间的间隔。把tm_ms设置为0意味着没有时间限制,如果没有数据包到达适配器,读取操作将永远不会返回。反过来,把tm_ms设置为-1将使读取操作总是立即返回。
view plaincopy to clipboardprint?
#define HAVE_REMOTE  
#include   
 
#pragma comment(lib,"wpcap.lib")  
 
/* 数据包处理程序,回调函数 */ 
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);  
 
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("%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];  
      
    /* convert the timestamp to readable format */ 
    ltime=localtime(&header->ts.tv_sec);  
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);  
      
    printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);  
      

#define HAVE_REMOTE
#include #pragma comment(lib,"wpcap.lib")/* 数据包处理程序,回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);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("%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];
   
    /* convert the timestamp to readable format */
    ltime=localtime(&header->ts.tv_sec);
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
   
    printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
   
}
 程序运行说明:程序会打印出在上面时候接收到多大的数据包,注意到pcap_open()函数里面的3个重要参数:snaplen设置为65536保证每次都把整个包抓获;flags设置为混杂模式以便抓获所有的数据包。关于第三个参数to_ms,我运行的环境是WindowsXP,设置这个值对程序指向结果没有影响。疑问:会不会是to_ms参数不被Wins平台支持?一旦打开了适配器,就由pcap_dispatch() 或者pcap_loop()开始抓取。这两个函数都非常慢,所不同的是pcap_ dispatch()一旦超时就可以返回(尽管不能保证)而pcap_loop() 会一直等到cnt包被抓到才会返回,所以这个函数在没有数据包的网络中会阻塞任意的时间。在这个例子中pcap_loop()就足够了,而pcap_dispatch() 则可以在一个更复杂的程序中使用。
 这两个函数都有一个回调函数作为参数,packet_handler,这个参数指定的函数将收到数据包。这个函数在每一个新的数据包从网络中到达时都会被libpcap调用,并且会收到一个反映pcap_loop() 和 pcap_dispatch()函数的生成状态,和一个结构体header。这个header中带有一些数据包中的信息,比如时间戳和长度、包括所有协议头的实际数据包。注意结构体中是没有CRC的,因为在数据确认后已经被适配器去掉了。也要注意大部分适配器丢弃了CRC错误的数据包,因此Winpcap不能抓取这些包。
 上面的例子从pcap_pkthdr中提取每个数据包的时间戳和长度并显示它们。
 请注意使用pcap_loop()可能有一个缺点,就是使用这个函数时包处理函数要被包抓取驱动程序来调用;因此应用程序不能直接控制它。另一种方法(并且更容易理解)是使用函数pcap_next_ex(),这个函数我们将在下一个例子中使用。 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qsycn/archive/2009/08/18/4458204.aspx