现在的位置: 首页 > 综合 > 正文

使用Packet.dll和npf.sys实现原始数据包的发送和接收

2013年09月05日 ⁄ 综合 ⁄ 共 6018字 ⁄ 字号 评论关闭

 相应源码下载地址:http://download.csdn.net/source/3521479 

有人可能问我为什么不直接用wpcap.dll,那个不但功能更强大还稳定。那是因为我这个功能很简单,使用packet.dll有点“杀鸡用牛刀”的味道了,而packet.dll足够我使用。

Packet.dll简介

Packet.dll 是一个动态链接库,并提供了一些低层的函数,用来:

1> 安装,启动和停止NPF设备驱动

2> 从NPF驱动接收数据包

3> 通过NPF驱动发送数据包

4> 获取可用的网络适配器列表

5> 获取适配器的不同信息,比如设备描述,地址列表和掩码

6> 查询并设置一个低层的适配器参数

用到的Packet.dll相关数据结构及函数

首先介绍一些相关的数据结构:

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); 



发送原始数据包 SendData

大概流程:

1>       打开指定的网络适配器,调用PacketOpenAdapter。

2>       调用PacketAllocatePacket分配一个packet。如果运行成功,返回一个_PACKET结构的指针,否则返回NULL。成功返回的结果将会传送到PacketSendPacket()函数,用来将数据发送出去。

3>       调用PacketInitPacket初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的buffer指针

4>       调用PacketSendPacket发送一个或多个数据报的副本。

接受原始数据包 RevData

大概流程:

1>     打开指定的网络适配器,调用PacketOpenAdapter。

2>     调用PacketSetHwFilter将网络适配器设置为混杂模式,这样才可以监听流过本地主机的数据报

3>     调用PacketSetBuff自定义网络适配器的内核缓存的大小。

4>     调用PacketAllocatePacket分配一个packet。如果运行成功,返回一个_PACKET结构的指针,否则返回NULL。成功返回的结果将会传送到PacketReceivePacket()函数,接收来自驱动的网络数据报。

5>     调用PacketInitPacket初始化一个_PACKET结构,即将packet结构中的buffer设置为传递的buffer指针

6>     在设置网络适配器为混杂模式后,调用PacketReceivePacket接收数据包。

安装packet.dll和npf.sys

在一篇文章中看到(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 

抱歉!评论已关闭.