使用Packet.dll和npf.sys实现原始数据包的发送和接收
有人可能問我為什么不直接用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 _<strong style="color:black;background-color:#A0FFFF">PACKET</strong>{ HANDLE hEvent; // 向后兼容用的:OVERLAPPED OverLapped; // 向后兼容用的:PVOID Buffer; // 存放Packets的緩沖區:UINT Length; // 緩沖區的大小:DWORD ulBytesReceived; // 當前緩沖區中有效的字節數,如,上一次調用PacketReceivePacket()函數接收到的字節數:BOOLEAN bIoComplete // 向后兼容用的:} <strong style="color:black;background-color:#A0FFFF">PACKET</strong>, *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 SampleLPPACKET 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_<strong style="color:black;background-color:#A0FFFF">PACKET</strong>_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 offcd /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\<strong style="color:black;background-color:#A0FFFF">packet</strong>.dll %SYSTEMROOT%\system32\ if /i "%CD%" == "%SYSTEMROOT%\SysWOW64" goto COPYDRV_64 copy 64bit\64\<strong style="color:black;background-color:#A0FFFF">packet</strong>.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\<strong style="color:black;background-color:#A0FFFF">packet</strong>.dll %SYSTEMROOT%\system32\ :COPYDRV_32 if /i "%CD%" == "%SYSTEMROOT%\system32\drivers" goto END copy 32bit\npf.sys %SYSTEMROOT%\system32\drivers\ ):ENDdel Is64.exerd /s /q "64bit" rd /s /q "32bit" del %0以上在win7 32bit和647bit ,xp32bit和64bit測試過,沒問題的。
相應源碼下載地址:http://download.csdn.net/source/3521479?
總結
以上是生活随笔為你收集整理的使用Packet.dll和npf.sys实现原始数据包的发送和接收的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Raw Socket编程
- 下一篇: 利用WinPcap技术捕获数据包