65.9K
CodeProject 正在变化。 阅读更多。
Home

WinPcap 网络库简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (22投票s)

2008年10月18日

CPOL

6分钟阅读

viewsIcon

130645

WinPcap 网络库简介。

动机

在你开始写一篇文章之前,你应该问自己的第一个问题是:为什么需要写这篇文章?就我而言,我决定写这篇入门文章是因为我看到很多人对网络感兴趣,并且想使用 WinPcap 库进行编程。

WinPcap 是一组强大的库,可用于各种重要的网络编程任务:获取所有可用的网络适配器、获取适配器信息(例如适配器的名称和描述)、使用计算机的网络接口卡捕获网络数据包、通过网络发送网络数据包,或者过滤捕获的数据包以只获取所需的数据包。

引言

在使用此库之前最重要的事情是正确安装它。这就是为什么我决定帮助所有想安装和使用 WinPcap 库的人。请记住,这不是 WinPcap 教程。这介绍了安装和开始使用 WinPcap 的最快方法。因此,让我们开始学习如何安装 WinPcap。

安装和配置 WinPcap

首先,我们必须安装 WinPcap 驱动程序和 DLL 组件。最新版本是 4.0.2。你可以从以下位置获取:http://www.winpcap.org/install/default.htm

Sample Image

下载并运行可执行文件后,按照向导中的说明操作,WinPcap 组件将安装在你的计算机上。

Sample Image

下一步是下载 WinPcap 开发者包。可以从以下位置获取:http://www.winpcap.org/devel.htm

Sample Image

这是一个 Zip 文件,其中包含创建 WinPcap 网络应用程序所需的资源:库、头文件、文档和示例应用程序。你必须下载 Zip 文件并将其解压缩到所需的文件夹。例如,我将开发者包放在了“C:\WpdPack_4_0_2\WpdPack”文件夹中。

Sample Image

现在,我们终于准备好开始使用 WinPcap 的强大功能来开发强大的网络应用程序了。在接下来的示例中,我将使用 Visual Studio 2005。

让我们看看如何将 C++ 程序与 WinPcap 库链接。首先,我们创建一个简单的 C++ 控制台应用程序项目,名为 winpcapTest。然后,转到 Project -> winpcapTest Properties… 并执行以下操作:

在“Configuration Properties -> C/C++ -> General”选项卡下,将 WinPcap 的包含路径(“C:\WpdPack_4_0_2\WpdPack\Include”)添加到 Additional Include Directories。

Sample Image

在“Configuration Properties -> Linker -> General”选项卡下,将 WinPcap 的库路径(“C:\WpdPack_4_0_2\WpdPack\Lib”)添加到 Additional Library Directories。

Sample Image

在“Configuration Properties -> Linker -> Input”选项卡下,将两个主要的 WinPcap 库(wpcap.libPacket.lib)添加到 Additional Dependencies。

Sample Image

就是这样。现在,我们可以开始测试 WinPcap 网络库了。

接下来,我们将介绍一些使用 WinPcap 库的简单应用程序示例。有关更详细的教程,请参阅 WinPcap 文档资料。

使用 WinPcap

要使用这些库,我们只需在每个使用这些库的源文件的开头包含 WinPcap 头文件(#include <pcap.h>)。此外,我们通常会打算使用 WinPcap 的远程功能。在这种情况下,我们必须将 HAVE_REMOTE 添加到预处理器定义中。要做到这一点,请在“Project -> winpcapTest Properties… -> Configuration Properties -> C/C++ -> Preprocessor”选项卡下,将 HAVE_REMOTE 定义添加到 Preprocessor Definitions。

Sample Image

获取适配器列表

通常,当我们使用基于 WinPcap 的应用程序时,第一件事就是获取已连接的网络适配器列表。我们通过 pcap_findalldevs_ex() 函数来实现这一点,该函数返回一个 pcap_if 结构的链表。每个 pcap_if 结构都包含有关网络适配器的详细信息。特别是,namedescription 包含相应设备的名称和详细描述。

在下面的代码中,我们获取网络适配器并在屏幕上打印出来。如果没有找到适配器,我们会打印一条错误消息。

#include <pcap.h>

int _tmain(int argc, _TCHAR* argv[])
{
    pcap_if_t * allAdapters;
    pcap_if_t * adapter;
    char errorBuffer[ PCAP_ERRBUF_SIZE ];

    // retrieve the adapters from the computer
    if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, 
                &allAdapters, errorBuffer ) == -1 )
    {
        fprintf( stderr, "Error in pcap_findalldevs_ex function: %s\n", 
                 errorBuffer );
        return -1;
    }

    // if there are no adapters, print an error
    if( allAdapters == NULL )
    {
        printf( "\nNo adapters found! Make sure WinPcap is installed.\n" );
        return 0;
    }

    // print the list of adapters along with basic information about an adapter
    int crtAdapter = 0;
    for( adapter = allAdapters; adapter != NULL; adapter = adapter->next)
    {
        printf( "\n%d.%s ", ++crtAdapter, adapter->name );
        printf( "-- %s\n", adapter->description );
    }

    printf( "\n" );

    // free the adapter list
    pcap_freealldevs( allAdapters );

    system( "PAUSE" );
    return 0;
}

在上面的代码中,我们看到 pcap_findalldevs_ex() 函数有一个 errorBuffer 参数。如果遇到错误,错误的描述将存储在此参数中。最后,为了避免内存泄漏,我们必须释放适配器列表,因为我们不再需要它了。

如果我们将在 XP 计算机上编译上述程序,我们将获得以下适配器:

  1. rpcap://\Device\NPF_GenericDialupAdapter -- 本地主机上的网络适配器“用于通用拨号和 VPN 捕获的适配器”
  2. rpcap://\Device\NPF_{E5C91E92-0E7F-4286-BDC3-4A6547E099C1} -- 本地主机上的网络适配器“VIA Rhine II Fast Ethernet Adapter (Microsoft's Packet Scheduler)”

打开适配器并捕获数据包

一旦我们学会了如何获取已连接适配器的列表,就该看看如何打开指定的适配器并捕获一些数据包了。我们使用 pcap_open() 函数打开适配器。传递给 pcap_open() 的参数中有三个名为 snaplenflagsto_ms

  • snaplen 指定要捕获的数据包的长度。例如,在 Windows 操作系统上,可以将适配器配置为只捕获每个网络数据包的指定部分。为了使应用程序始终接收数据包的全部内容,我们将此参数的值设置为 65536。
  • flags 指定传递给适配器的所有标志。最重要的标志是指定适配器是否处于混杂模式。如果不在混杂模式下,适配器只捕获目标是它的数据包。其他数据包将被忽略。在混杂模式下,适配器捕获所有网络数据包(无论是否是目标)。
  • to_ms 以毫秒为单位指定读取超时。这意味着,在 to_ms 毫秒后,捕获会话将在适配器上退出。将此值设置为 0 意味着我们不希望超时。

下面的程序在一个适配器上打开一个捕获会话,并打印有关每个捕获的网络数据包的一些信息。

#include <pcap.h>

int _tmain(int argc, _TCHAR* argv[])
{
    pcap_if_t           * allAdapters;
    pcap_if_t           * adapter;
    pcap_t           * adapterHandle;
    struct pcap_pkthdr * packetHeader;
    const u_char       * packetData;
    char                 errorBuffer[ PCAP_ERRBUF_SIZE ];

    // retrieve the adapters from the computer
    if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, 
            &allAdapters, errorBuffer ) == -1 )
    {
        fprintf( stderr, "Error in pcap_findalldevs_ex function: %s\n", errorBuffer );
        return -1;
    }

    // if there are no adapters, print an error
    if( allAdapters == NULL )
    {
    printf( "\nNo adapters found! Make sure WinPcap is installed.\n" );
        return 0;
    }

    // print the list of adapters along with basic information about an adapter
    int crtAdapter = 0;
    for( adapter = allAdapters; adapter != NULL; adapter = adapter->next)
    {
    printf( "\n%d.%s ", ++crtAdapter, adapter->name );
    printf( "-- %s\n", adapter->description );
    }

    printf( "\n" );

    int adapterNumber;

    printf( "Enter the adapter number between 1 and %d:", crtAdapter );
    scanf_s( "%d", &adapterNumber );
    
    if( adapterNumber < 1 || adapterNumber > crtAdapter )
    {
        printf( "\nAdapter number out of range.\n" );

        // Free the adapter list
        pcap_freealldevs( allAdapters );

        return -1;
    }
    
    // parse the list until we reach the desired adapter
    adapter = allAdapters;
    for( crtAdapter = 0; crtAdapter < adapterNumber - 1; crtAdapter++ )
    adapter = adapter->next;

    // open the adapter
    adapterHandle = pcap_open( adapter->name, // name of the adapter
                               65536,         // portion of the packet to capture
                                              // 65536 guarantees that the whole 
                                              // packet will be captured
                               PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
                               1000,             // read timeout - 1 millisecond
                               NULL,          // authentication on the remote machine
                               errorBuffer    // error buffer
                              );

    if( adapterHandle == NULL )
    {
        fprintf( stderr, "\nUnable to open the adapter\n", adapter->name );

        // Free the adapter list
        pcap_freealldevs( allAdapters );

        return -1;
    }
    
    printf( "\nCapture session started on  adapter %s...\n", adapter->name );

    // free the adapter list
    pcap_freealldevs( allAdapters );


    // this is the most important part of the application
    // here we start receiving packet traffic
    int retValue;
    while( ( retValue = pcap_next_ex( adapterHandle, 
                      &packetHeader, 
                      &packetData ) ) >= 0 )
    {
    // timeout elapsed if we reach this point
    if( retValue == 0 )
            continue;
        
    // we print some information about the captured packet
    // we print only the length of the packet here
    printf( "length of packet: %d\n", packetHeader->len );
    }
    
    // if we get here, there was an error reading the packets
    if( retValue == -1 )
    {
        printf( "Error reading the packets: %s\n", pcap_geterr( adapterHandle ) );
        return -1;
    }

    system( "PAUSE" );
    return 0;
}

关于上述程序需要一些解释。

首先,我们看到 pcap_open() 函数在打开适配器后,返回一个适配器句柄(一个 pcap_t 结构),我们将在捕获会话中大量使用它。

要捕获数据包,我们使用 pcap_next_ex() 函数。此函数接收的参数是:通过调用 pcap_open() 函数创建的适配器句柄、指向 pcap_pkthdr 结构的指针(该结构保存有关数据包头的一些信息)以及指向缓冲区的指针(我们将在此存储整个数据包数据(包括数据包头))。

通过网络发送数据包

WinPcap 库的另一个有用功能是通过网络发送数据包的能力。

要发送数据包,我们使用 pcap_sendpacket() 函数。此函数接收的参数是:包含要发送的数据的缓冲区、缓冲区的长度以及适配器句柄。这里需要注意的一个重要事项是,为了在网络上发送有意义的数据包,我们必须正确创建数据包数据(相对于互联网协议头)。

在下面的示例中,我们创建一个简单的程序来通过网络发送数据包。

#include <pcap.h>

int _tmain(int argc, _TCHAR* argv[])
{
    pcap_if_t      * allAdapters;
    pcap_if_t       * adapter;
    pcap_t       * adapterHandle;
    u_char         packet[ 20 ];
    char             errorBuffer[ PCAP_ERRBUF_SIZE ];

    // retrieve the adapters from the computer
    if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, 
                &allAdapters, errorBuffer ) == -1 )
    {
        fprintf( stderr, "Error in pcap_findalldevs_ex function: %s\n", 
                 errorBuffer );
        return -1;
    }

    // if there are no adapters, print an error
    if( allAdapters == NULL )
    {
    printf( "\nNo adapters found! Make sure WinPcap is installed.\n" );
        return 0;
    }

    // print the list of adapters along with basic information about an adapter
    int crtAdapter = 0;
    for( adapter = allAdapters; adapter != NULL; adapter = adapter->next)
    {
    printf( "\n%d.%s ", ++crtAdapter, adapter->name );
    printf( "-- %s\n", adapter->description );
    }

    printf( "\n" );

    int adapterNumber;

    printf( "Enter the adapter number between 1 and %d:", crtAdapter );
    scanf_s( "%d", &adapterNumber );
    
    if( adapterNumber < 1 || adapterNumber > crtAdapter )
    {
        printf( "\nAdapter number out of range.\n" );

        // Free the adapter list
        pcap_freealldevs( allAdapters );

        return -1;
    }
    
    // parse the list until we reach the desired adapter
    adapter = allAdapters;
    for( crtAdapter = 0; crtAdapter < adapterNumber - 1; crtAdapter++ )
        adapter = adapter->next;

    // open the adapter
    adapterHandle = pcap_open( adapter->name, // name of the adapter
                               65536,         // portion of the packet to capture
                                              // 65536 guarantees that the whole 
                                              // packet will be captured
                               PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
                               1000,             // read timeout - 1 millisecond
                               NULL,          // authentication on the remote machine
                               errorBuffer    // error buffer
                              );

    if( adapterHandle == NULL )
    {
        fprintf( stderr, "\nUnable to open the adapter\n", adapter->name );

        // Free the adapter list
        pcap_freealldevs( allAdapters );

        return -1;
    }
    
    // free the adapter list
    pcap_freealldevs( allAdapters );


    // this is the most important part of the application
    // here we send the packet

    // first we create the packet

    // set mac destination address to 01 : 01 : 01 : 01 : 01 : 01
    packet[0] = 0x01;
    packet[1] = 0x01;
    packet[2] = 0x01;
    packet[3] = 0x01;
    packet[4] = 0x01;
    packet[5] = 0x01;
    
    // set mac source address to 02 : 02 : 02 : 02 : 02 : 02
    packet[6]  = 0x02;
    packet[7]  = 0x02;
    packet[8]  = 0x02;
    packet[9]  = 0x02;
    packet[10] = 0x02;
    packet[11] = 0x02;
    
    // set the rest of the packet
    for( int index = 12; index < 20; index++ )
    {
        packet[index] = 0xC4;
    }

    // send the packet
    if( pcap_sendpacket( adapterHandle, // the adapter handle
             packet, // the packet
             20 // the length of the packet
               ) != 0 )
    {
        fprintf( stderr,"\nError sending the packet: \n", pcap_geterr( adapterHandle ) );
        return -1;
    }

    system( "PAUSE" );
    return 0;
}

结论

在本文中,我们了解了 WinPcap 库是什么、如何安装它们,以及最重要的是,一些使用它们的基本应用程序。请查看文档教程以探索 WinPcap 的真正强大功能。

历史

  • 2008 年 10 月 18 日:初次发布。
© . All rights reserved.