相应源码下载地址:http://download.csdn.net/source/3521479
Packet.dll 是一个动态链接库,并提供了一些低层的函数,用来:
1> 安装,启动和停止NPF设备驱动
2> 从NPF驱动接收数据包
3> 通过NPF驱动发送数据包
4> 获取可用的网络适配器列表
5> 获取适配器的不同信息,比如设备描述,地址列表和掩码
6> 查询并设置一个低层的适配器参数
首先介绍一些相关的数据结构:
1> typedef struct _ADAPTER ADAPTER //描述一个网络适配器;
typedef struct _ADAPTER
{
HANDLE hFile; // 一个打开的NPF driver实例的句柄:
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; // 当前打开的网卡的名字:
int NumWrites; // 在这块Adapter上,一个数据包被写的次数:
HANDLE ReadEvent; /* 这块Adapter上的read操作的通知事件。它可以被传递给标准Win32函数(如WaitForSingleObject或者WaitForMultipleObjects),这样可以等待到driver的缓冲区内有数据到来。在同时等待几个事件的GUI程序中,它特别有用。在Windows2000/XP中,函数PacketSetMinToCopy()可以用来设置内核缓冲区中激发本事件的最小数据大小:*/
UINT ReadTimeOut; // 设置一个时间,到时候,即使没有捕获任何包,read操作也会被释放,ReadEvent也会被触发:
} ADAPTER, *LPADAPTER;
2> typedef struct _PACKET PACKET //描述一组网络数据报的结构;
typedef struct _PACKET
{
HANDLE hEvent; // 向后兼容用的:
OVERLAPPED OverLapped; // 向后兼容用的:
PVOID Buffer; // 存放Packets的缓冲区:
UINT Length; // 缓冲区的大小:
DWORD ulBytesReceived; // 当前缓冲区中有效的字节数,如,上一次调用PacketReceivePacket()函数接收到的字节数:
BOOLEAN bIoComplete // 向后兼容用的:
} PACKET, *LPPACKET;
下面,将介绍用到的各个函数,他们都是在packet.dll中定义的:
1> PacketGetAdapterNames(从注册表中读取网卡名)
得到现有的网络适配器的列表和它们的描述。 BOOLEAN PacketGetAdapterNames( PTSTR pStr, PULONG BufferSize );
参数:
pStr: [in , out] 一块用户负责分配的缓冲区,将把适配器的名字填充进去。 BufferSize: [in] pStr这块缓冲区的大小。
返回值:
如果查询成功,返回一个非零值。
Usage:
[C/C++] C/C++ Usage Sample
char AdapterNamea[8192];
ULONG AdapterLength;
PacketGetAdapterNames(AdapterName,&AdapterLength);
2> PacketOpenAdapter (打开网卡) 根据传入的设备名,打开它。
LPADAPTER PacketOpenAdapter(LPTSTR AdapterName);
参数:
AdapterName:
[in] 要打开的设备的名字。
返回值:
如果打开成功,返回一个指针,它指向一个正确初始化了的ADAPTER Object。否则,返回NULL。
Usage:
[C/C++]C/C++ Usage Sample
LPADAPTER adapter;
adapter = PacketOpenAdapter(pStr+rewind);
3> LPPACKET PacketAllocatePacket(void);
参数:无
返回值:如果执行成功,返回指向_PACKET结构的指针。否则,返回NULL。
Usage:
C/C++ Usage Sample
LPPACKET lpPacket; lpPacket = PacketAllocatePacket() ;
4> PacketInitPacket初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的buffer指针。
VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length);
参数:lpPacket[in] 指向一个_PACKET结构的指针。
Buffer[in] 一个指向一块用户分配的缓冲区的指针。捕获的数据将放置于此。
Length[in] 缓冲区的大小。这是一个读操作从driver传递到应用的最大数据量。
返回值:无。
Usage:
C/C++ Usage Sample
char buffer[256000];
LPPACKET lpPacket; PacketInitPacket(lpPacket,(char*)buffer,256000);
5> PacketSendPacket发送一个或多个数据报的副本。
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET lpPacket, BOOLEAN Sync)
6> PacketFreePacket释放参数提供的_PACKET结构。
VOID PacketFreePacket(LPPACKET lpPacket)
7> PacketCloseAdapter关闭网卡。
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
参数:
lpAdapter:[in] 指向一个_ADAPTER结构的指针。
8> PacketSetHwFilter 设置一个hardware filter。比如,Filter参数传递NDIS_PACKET_TYPE_PROMISCUOUS ,就可以设置网卡为混杂模式。
BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter);
参数:
AdapterObject:[in] 指向一个_ADAPTER结构的指针。Filter:[in] 过滤器的id。
返回值:如果执行成功,返回一个非零值。
Usage:
C/C++ Usage Sample
lpAdapter=PacketOpenAdapter(AdapterList[Open-1]); PacketSetHwFilter(lpAdapter,NDIS_PACKET_TYPE_PROMISCUOUS);
9> PacketSetBuff 设置捕获的内核级缓冲区的大小。
BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim);
参数:
AdapterObject:[in] 指向一个_ADAPTER结构的指针。dim: [in] 缓冲区的大小(单位:字节)。
返回值:
如果执行成功,返回一个TRUE。如果没有足够的内存分配,返回FALSE。
Usage:
C/C++ Usage Sample
lpAdapter=PacketOpenAdapter(AdapterList[Open-1]); PacketSetBuff(lpAdapter,512000) ; // 设置 driver 有 512KB 字节的缓冲区
10> PacketSetReadTimeout 设置一次读操作返回的超时时间。
BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout);
参数:
AdapterObject:[in] 指向一个_ADAPTER结构的指针。timeout:[in] 超时时间(单位:毫秒)。
返回值:
如果执行成功,返回非零值。
Usage:
C/C++ Usage Sample
lpAdapter=PacketOpenAdapter(AdapterList[Open-1]); PacketSetReadTimeout(lpAdapter,1000) ; // 设置读操作超时时间 1 秒
11> PacketReceivePacket 从NPF driver上读取数据(Packets或者统计信息)。
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
参数:
AdapterObject:[in] 指向一个_ADAPTER结构的指针。
lpPacket:[in , out] 放数据的_PACKET结构缓冲区。
Sync:[in] 一个可以忽略的参数,保留它是为了向后兼容。
返回值:如果执行成功,返回一个非零值。
Usage:
C/C++ Usage Sample
LPADAPTER lpAdapter = 0;
LPPACKET lpPacket;
lpAdapter = PacketOpenAdapter(AdapterList[Open-1]);
lpPacket = PacketAllocatePacket();
PacketInitPacket(lpPacket,(char*)buffer,256000);
PacketReceivePacket(lpAdapter,lpPacket,TRUE);
大概流程:
1> 打开指定的网络适配器,调用PacketOpenAdapter。
2> 调用PacketAllocatePacket分配一个packet。如果运行成功,返回一个_PACKET结构的指针,否则返回NULL。成功返回的结果将会传送到PacketSendPacket()函数,用来将数据发送出去。
3> 调用PacketInitPacket初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的buffer指针
4> 调用PacketSendPacket发送一个或多个数据报的副本。
大概流程:
1> 打开指定的网络适配器,调用PacketOpenAdapter。
2> 调用PacketSetHwFilter将网络适配器设置为混杂模式,这样才可以监听流过本地主机的数据报
3> 调用PacketSetBuff自定义网络适配器的内核缓存的大小。
4> 调用PacketAllocatePacket分配一个packet。如果运行成功,返回一个_PACKET结构的指针,否则返回NULL。成功返回的结果将会传送到PacketReceivePacket()函数,接收来自驱动的网络数据报。
5> 调用PacketInitPacket初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的buffer指针
6> 在设置网络适配器为混杂模式后,调用PacketReceivePacket接收数据包。
在一篇文章中看到(http://douvip.blog.51cto.com/75074/41228)说:
安装程序需要做两件事:
1> 拷贝packet.dll到Windows\system32目录下;
2> 拷贝驱动文件npf.sys到windows/systems/drivers/目录下,并向系统注册一下抓包驱动的服务。
经过我实践,仅仅需要第一步就可以了;也在“www.winpcap.org/”找到这样一句话:“It's installed by directly interacting with the service control manager。”
详情请进http://www.winpcap.org/pipermail/winpcap-users/2008-March/002326.html
并且,那篇文章仅仅能够在32bit系统下成功,64位系统还要在Windows\SysWOW64拷入相应packet.dll,64位的packet.dll和32bit的是不一样的。
不管多少位系统得到packet.dll和npf.sys的方法都很简单,只要安装winpcap包然后在Windows搜索即可。
以下我的批处理:
@echo off
cd /d %~dp0 ;转到当前目录
Is64.exe ;判断系统是不是64位系统,自己写的一个win32程序
if %errorlevel% == 64 (goto 64bit) else (goto 32bit)
:64bit
(
if /i "%CD%" == "%SYSTEMROOT%\system32" goto COPYDRV_64
copy 64bit\32\packet.dll %SYSTEMROOT%\system32\
if /i "%CD%" == "%SYSTEMROOT%\SysWOW64" goto COPYDRV_64
copy 64bit\64\packet.dll %SYSTEMROOT%\SysWOW64\
:COPYDRV_64
if /i "%CD%" == "%SYSTEMROOT%\system32\drivers" goto END
copy 64bit\npf.sys %SYSTEMROOT%\system32\drivers\
goto END
)
:32bit
(
if /i "%CD%" == "%SYSTEMROOT%\system32" goto COPYDRV_32
copy 32bit\packet.dll %SYSTEMROOT%\system32\
:COPYDRV_32
if /i "%CD%" == "%SYSTEMROOT%\system32\drivers" goto END
copy 32bit\npf.sys %SYSTEMROOT%\system32\drivers\
)
:END
del Is64.exe
rd /s /q "64bit"
rd /s /q "32bit"
del %0
以上在win7 32bit和647bit ,xp32bit和64bit测试过,没问题的。
相应源码下载地址:http://download.csdn.net/source/3521479