WinPcap 网络库简介






4.33/5 (22投票s)
WinPcap 网络库简介。
动机
在你开始写一篇文章之前,你应该问自己的第一个问题是:为什么需要写这篇文章?就我而言,我决定写这篇入门文章是因为我看到很多人对网络感兴趣,并且想使用 WinPcap 库进行编程。
WinPcap 是一组强大的库,可用于各种重要的网络编程任务:获取所有可用的网络适配器、获取适配器信息(例如适配器的名称和描述)、使用计算机的网络接口卡捕获网络数据包、通过网络发送网络数据包,或者过滤捕获的数据包以只获取所需的数据包。
引言
在使用此库之前最重要的事情是正确安装它。这就是为什么我决定帮助所有想安装和使用 WinPcap 库的人。请记住,这不是 WinPcap 教程。这介绍了安装和开始使用 WinPcap 的最快方法。因此,让我们开始学习如何安装 WinPcap。
安装和配置 WinPcap
首先,我们必须安装 WinPcap 驱动程序和 DLL 组件。最新版本是 4.0.2。你可以从以下位置获取:http://www.winpcap.org/install/default.htm。
下载并运行可执行文件后,按照向导中的说明操作,WinPcap 组件将安装在你的计算机上。
下一步是下载 WinPcap 开发者包。可以从以下位置获取:http://www.winpcap.org/devel.htm。
这是一个 Zip 文件,其中包含创建 WinPcap 网络应用程序所需的资源:库、头文件、文档和示例应用程序。你必须下载 Zip 文件并将其解压缩到所需的文件夹。例如,我将开发者包放在了“C:\WpdPack_4_0_2\WpdPack”文件夹中。
现在,我们终于准备好开始使用 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。
在“Configuration Properties -> Linker -> General”选项卡下,将 WinPcap 的库路径(“C:\WpdPack_4_0_2\WpdPack\Lib”)添加到 Additional Library Directories。
在“Configuration Properties -> Linker -> Input”选项卡下,将两个主要的 WinPcap 库(wpcap.lib 和 Packet.lib)添加到 Additional Dependencies。
就是这样。现在,我们可以开始测试 WinPcap 网络库了。
接下来,我们将介绍一些使用 WinPcap 库的简单应用程序示例。有关更详细的教程,请参阅 WinPcap 文档资料。
使用 WinPcap
要使用这些库,我们只需在每个使用这些库的源文件的开头包含 WinPcap 头文件(#include <pcap.h>
)。此外,我们通常会打算使用 WinPcap 的远程功能。在这种情况下,我们必须将 HAVE_REMOTE
添加到预处理器定义中。要做到这一点,请在“Project -> winpcapTest Properties… -> Configuration Properties -> C/C++ -> Preprocessor”选项卡下,将 HAVE_REMOTE
定义添加到 Preprocessor Definitions。
获取适配器列表
通常,当我们使用基于 WinPcap 的应用程序时,第一件事就是获取已连接的网络适配器列表。我们通过 pcap_findalldevs_ex()
函数来实现这一点,该函数返回一个 pcap_if
结构的链表。每个 pcap_if
结构都包含有关网络适配器的详细信息。特别是,name
和 description
包含相应设备的名称和详细描述。
在下面的代码中,我们获取网络适配器并在屏幕上打印出来。如果没有找到适配器,我们会打印一条错误消息。
#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 计算机上编译上述程序,我们将获得以下适配器:
- rpcap://\Device\NPF_GenericDialupAdapter -- 本地主机上的网络适配器“用于通用拨号和 VPN 捕获的适配器”
- rpcap://\Device\NPF_{E5C91E92-0E7F-4286-BDC3-4A6547E099C1} -- 本地主机上的网络适配器“VIA Rhine II Fast Ethernet Adapter (Microsoft's Packet Scheduler)”
打开适配器并捕获数据包
一旦我们学会了如何获取已连接适配器的列表,就该看看如何打开指定的适配器并捕获一些数据包了。我们使用 pcap_open()
函数打开适配器。传递给 pcap_open()
的参数中有三个名为 snaplen
、flags
和 to_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 日:初次发布。