使用Packet.dll和npf.sys实现原始数据包的发送和接收
有人可能問我為什么不直接用wpcap.dll,那個不但功能更強大還穩(wěn)定。那是因為我這個功能很簡單,使用packet.dll有點“殺雞用牛刀”的味道了,而packet.dll足夠我使用。
Packet.dll簡介
Packet.dll 是一個動態(tài)鏈接庫,并提供了一些低層的函數(shù),用來:
1> 安裝,啟動和停止NPF設備驅動
2> 從NPF驅動接收數(shù)據(jù)包
3> 通過NPF驅動發(fā)送數(shù)據(jù)包
4> 獲取可用的網(wǎng)絡適配器列表
5> 獲取適配器的不同信息,比如設備描述,地址列表和掩碼
6> 查詢并設置一個低層的適配器參數(shù)
用到的Packet.dll相關數(shù)據(jù)結構及函數(shù)
首先介紹一些相關的數(shù)據(jù)結構:
1>???? ?typedef struct _ADAPTER ADAPTER //描述一個網(wǎng)絡適配器;
typedef struct _ADAPTER {HANDLE hFile; // 一個打開的NPF driver實例的句柄:CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; // 當前打開的網(wǎng)卡的名字:int NumWrites; // 在這塊Adapter上,一個數(shù)據(jù)包被寫的次數(shù):HANDLE ReadEvent; /* 這塊Adapter上的read操作的通知事件。它可以被傳遞給標準Win32函數(shù)(如WaitForSingleObject或者WaitForMultipleObjects),這樣可以等待到driver的緩沖區(qū)內(nèi)有數(shù)據(jù)到來。在同時等待幾個事件的GUI程序中,它特別有用。在Windows2000/XP中,函數(shù)PacketSetMinToCopy()可以用來設置內(nèi)核緩沖區(qū)中激發(fā)本事件的最小數(shù)據(jù)大小:*/UINT ReadTimeOut; // 設置一個時間,到時候,即使沒有捕獲任何包,read操作也會被釋放,ReadEvent也會被觸發(fā):} ADAPTER, *LPADAPTER;2>???? ?typedef struct _PACKET PACKET //描述一組網(wǎng)絡數(shù)據(jù)報的結構;
typedef struct _<strong style="color:black;background-color:#A0FFFF">PACKET</strong>{ HANDLE hEvent; // 向后兼容用的:OVERLAPPED OverLapped; // 向后兼容用的:PVOID Buffer; // 存放Packets的緩沖區(qū):UINT Length; // 緩沖區(qū)的大小:DWORD ulBytesReceived; // 當前緩沖區(qū)中有效的字節(jié)數(shù),如,上一次調(diào)用PacketReceivePacket()函數(shù)接收到的字節(jié)數(shù):BOOLEAN bIoComplete // 向后兼容用的:} <strong style="color:black;background-color:#A0FFFF">PACKET</strong>, *LPPACKET;?
下面,將介紹用到的各個函數(shù),他們都是在packet.dll中定義的:
?????? 1>?? PacketGetAdapterNames(從注冊表中讀取網(wǎng)卡名)
得到現(xiàn)有的網(wǎng)絡適配器的列表和它們的描述。 BOOLEAN PacketGetAdapterNames( PTSTR pStr, PULONG BufferSize );
參數(shù):
pStr: [in , out] 一塊用戶負責分配的緩沖區(qū),將把適配器的名字填充進去。 BufferSize: [in] pStr這塊緩沖區(qū)的大小。
返回值:
如果查詢成功,返回一個非零值。
Usage:
[C/C++] C/C++ Usage Sample
?
char AdapterNamea[8192];ULONG AdapterLength;PacketGetAdapterNames(AdapterName,&AdapterLength);
?????? 2>?? PacketOpenAdapter (打開網(wǎng)卡) 根據(jù)傳入的設備名,打開它。
LPADAPTER PacketOpenAdapter(LPTSTR AdapterName);
參數(shù):
AdapterName:
[in] 要打開的設備的名字。
返回值:
如果打開成功,返回一個指針,它指向一個正確初始化了的ADAPTER Object。否則,返回NULL。
Usage:
[C/C++]C/C++ Usage Sample
LPADAPTER adapter;adapter = PacketOpenAdapter(pStr+rewind);
?????? 3>?? LPPACKET PacketAllocatePacket(void);
參數(shù):無
返回值:如果執(zhí)行成功,返回指向_PACKET結構的指針。否則,返回NULL。
Usage:
C/C++ Usage SampleLPPACKET lpPacket; lpPacket = PacketAllocatePacket() ;
4>?? PacketInitPacket初始化一個_PACKET結構,即將packet結構中的buffer設置為傳遞的buffer指針。
VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length);
參數(shù):lpPacket[in] 指向一個_PACKET結構的指針。
Buffer[in] 一個指向一塊用戶分配的緩沖區(qū)的指針。捕獲的數(shù)據(jù)將放置于此。
Length[in] 緩沖區(qū)的大小。這是一個讀操作從driver傳遞到應用的最大數(shù)據(jù)量。
返回值:無。
Usage:
C/C++ Usage Sample
?
char buffer[256000]; LPPACKET lpPacket; PacketInitPacket(lpPacket,(char*)buffer,256000);
5>?? PacketSendPacket發(fā)送一個或多個數(shù)據(jù)報的副本。
BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET lpPacket, BOOLEAN Sync)
?????? 6>?? PacketFreePacket釋放參數(shù)提供的_PACKET結構。
VOID PacketFreePacket(LPPACKET lpPacket)
?????? 7>?? PacketCloseAdapter關閉網(wǎng)卡。
VOID PacketCloseAdapter(LPADAPTER lpAdapter);
參數(shù):
lpAdapter:[in] 指向一個_ADAPTER結構的指針。
8>?? PacketSetHwFilter ???? 設置一個hardware filter。比如,Filter參數(shù)傳遞NDIS_PACKET_TYPE_PROMISCUOUS ,就可以設置網(wǎng)卡為混雜模式。
BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter);
參數(shù):
AdapterObject:[in] 指向一個_ADAPTER結構的指針。Filter:[in] 過濾器的id。
返回值:如果執(zhí)行成功,返回一個非零值。
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 ??? 設置捕獲的內(nèi)核級緩沖區(qū)的大小。
BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim);
參數(shù):
AdapterObject:[in] 指向一個_ADAPTER結構的指針。dim: [in] 緩沖區(qū)的大小(單位:字節(jié))。
返回值:
如果執(zhí)行成功,返回一個TRUE。如果沒有足夠的內(nèi)存分配,返回FALSE。
Usage:
C/C++ Usage Sample
lpAdapter=PacketOpenAdapter(AdapterList[Open-1]); PacketSetBuff(lpAdapter,512000) ; // 設置 driver 有 512KB 字節(jié)的緩沖區(qū)
?????? 10>? PacketSetReadTimeout ?設置一次讀操作返回的超時時間。
?
BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout);
參數(shù):
AdapterObject:[in] 指向一個_ADAPTER結構的指針。timeout:[in] 超時時間(單位:毫秒)。
返回值:
如果執(zhí)行成功,返回非零值。
Usage:
C/C++ Usage Sample
lpAdapter=PacketOpenAdapter(AdapterList[Open-1]); PacketSetReadTimeout(lpAdapter,1000) ; // 設置讀操作超時時間 1 秒
?????? 11>? PacketReceivePacket? 從NPF driver上讀取數(shù)據(jù)(Packets或者統(tǒng)計信息)。
?
BOOLEAN ??PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync);
參數(shù):
AdapterObject:[in] 指向一個_ADAPTER結構的指針。
lpPacket:[in , out] 放數(shù)據(jù)的_PACKET結構緩沖區(qū)。
Sync:[in] 一個可以忽略的參數(shù),保留它是為了向后兼容。
返回值:如果執(zhí)行成功,返回一個非零值。
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);
發(fā)送原始數(shù)據(jù)包 SendData
大概流程:
1>?????? 打開指定的網(wǎng)絡適配器,調(diào)用PacketOpenAdapter。
2>?????? 調(diào)用PacketAllocatePacket分配一個packet。如果運行成功,返回一個_PACKET結構的指針,否則返回NULL。成功返回的結果將會傳送到PacketSendPacket()函數(shù),用來將數(shù)據(jù)發(fā)送出去。
3>?????? 調(diào)用PacketInitPacket初始化一個_PACKET結構,即將packet結構中的buffer設置為傳遞的buffer指針
4>?????? 調(diào)用PacketSendPacket發(fā)送一個或多個數(shù)據(jù)報的副本。
接受原始數(shù)據(jù)包 RevData
大概流程:
1>???? 打開指定的網(wǎng)絡適配器,調(diào)用PacketOpenAdapter。
2>???? 調(diào)用PacketSetHwFilter將網(wǎng)絡適配器設置為混雜模式,這樣才可以監(jiān)聽流過本地主機的數(shù)據(jù)報
3>???? 調(diào)用PacketSetBuff自定義網(wǎng)絡適配器的內(nèi)核緩存的大小。
4>???? 調(diào)用PacketAllocatePacket分配一個packet。如果運行成功,返回一個_PACKET結構的指針,否則返回NULL。成功返回的結果將會傳送到PacketReceivePacket()函數(shù),接收來自驅動的網(wǎng)絡數(shù)據(jù)報。
5>???? 調(diào)用PacketInitPacket初始化一個_PACKET結構,即將packet結構中的buffer設置為傳遞的buffer指針
6>???? 在設置網(wǎng)絡適配器為混雜模式后,調(diào)用PacketReceivePacket接收數(shù)據(jù)包。
安裝packet.dll和npf.sys
在一篇文章中看到(http://douvip.blog.51cto.com/75074/41228)說:
安裝程序需要做兩件事:
1>?????? 拷貝packet.dll到Windows\system32目錄下;
2>?????? 拷貝驅動文件npf.sys到windows/systems/drivers/目錄下,并向系統(tǒng)注冊一下抓包驅動的服務。
經(jīng)過我實踐,僅僅需要第一步就可以了;也在“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系統(tǒng)下成功,64位系統(tǒng)還要在Windows\SysWOW64拷入相應packet.dll,64位的packet.dll和32bit的是不一樣的。
不管多少位系統(tǒng)得到packet.dll和npf.sys的方法都很簡單,只要安裝winpcap包然后在Windows搜索即可。
以下我的批處理:
@echo offcd /d %~dp0 ;轉到當前目錄Is64.exe ;判斷系統(tǒng)是不是64位系統(tǒng),自己寫的一個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实现原始数据包的发送和接收的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Raw Socket编程
- 下一篇: 利用WinPcap技术捕获数据包