Windows下網絡數據報的監聽和攔截技術1
Windows下網絡數據報的監聽和攔截技術是一個比較古老的話題,應用也很廣泛,例如
防火墻等等。這篇小文只是對該技術的一個總結,沒有新技術,高手免看:)
要監聽和攔截Windows下的數據報,基本可以在兩個層次進行,一個是用戶態(user-mo
de),一個是核心態(kernel-mode)。
在用戶態下,從高到低大概有四種方法。
1、原是套結字(Raw Socket)。Winsock2以后提供了原始套結字功能,可以在用戶態用
Winsock函數接收所有流經Winsock的IP包。這種方法在MSDN里面有敘述,是MS官方支持
的方法,在網上也有很多資料。但是這種方法只能監聽但是不能攔截數據報,所以可以
作為網絡監視器的選擇技術,但是不能實現防火墻等更高要求的功能。另外最致命的缺
點就是只能在Winsock層次上進行,而對于網絡協議棧中底層協議的數據包例如TDI無法
進行處理。對于一些木馬和病毒來說很容易避開這個層次的監聽。
2、替換系統自帶的WINSOCK動態連接庫。這種方法可以在很多文章里面找到詳細的實現
細節。 通過替換系統Winsock庫的部分導出函數,實現數據報的監聽和攔截。缺點同1。
3、Winsock服務提供者(SPI)。SPI是Winsock的另一面,是Winsock2的一個新特性。
起初的Winsock是圍繞著TCP/IP協議運行的,但是在Winsock 2中卻增加了對更多傳輸協
議的支持。Winsock2不僅提供了一個供應用程序訪問網絡服務的Windows socket應用程
序編程接口(API),還包含了由傳輸服務提供者和名字解析服務提供者實現的Winsock
服務提供者接口(SPI)和ws2_32.dll。 Winsock 2的傳輸服務提供者是以動態鏈接庫的
形式(DLL)存在的。以下是winsock 2在傳輸服務提供者上的WOSA(Windows開放服務結
構):
----------------------------
|Windows socket 2 應用程序|
----------------------------Windows socket 2 API
| WS2_32.DLL |
----------------------------Windows socket 2 傳輸SPI
| 傳輸服務提供者(DLL) |
----------------------------
Windows socket SPI提供三種協議:分層協議,基礎協議和協議鏈。分層協議是在基礎
協議的上層,依靠底層基礎協議實現更高級的通信服務。基礎協議是能夠獨立,安全地
和遠程端點實現數據通信的協議,它是相對與分層協議而言的。協議鏈是將一系列的基
礎協議和分層協議按特點的順序連接在一起的鏈狀結構,可以通過Platform SDK附帶的
工具Sporder.exe察看系統已經安裝的SPI,請參見下圖:
API------------------------
| WS2_32.DLL |
SPI------------------------
| 分層協議 |
SPI-------------
| 分層協議 |
SPI------------------------
| 基礎協議 |
------------------------
每個應用程序通過Ws2_32.dll和相應的服務提供者進行嚴格的數據交換。Ws2_32.dl
l根
據應用程序在創建套接字時所提供的參數來選擇特定的服務提供者,然后把應用程序的
實現過程轉發由所選創建套接字的服務提供者來管理。也就是說,Ws2_32.dll只是一個
中間過程,而應用程序只是一個接口,數據通信的實現卻是由服務提供者來完成的。所
以我們通過適當的增加自己的分層協議服務提供者,使其位于SPI的頂端,那么就能將W
s2_32.dll傳給服務提供者的數據報攔截下來。由于是MS的官方方法,具體的使用方法在
其Platform SDK里面有詳細的例子(LSP),在MSDN里面也有詳細的解釋。這種方法的優點
是能夠獲得調用Winsock的進程的詳細信息,并能實現Qos和數據加密。所以SPI是用戶態
數據攔截的較好地點。缺點同1。
4、Windows2000包過濾接口。由于過濾規則限制太多不靈活而應用不多。
5、網絡監視器SDK。MS官方的實時監視分析網絡數據的方法。但是由于封裝的太復?
櫻?
使用起來不靈活。
在核心態下,數據報的監視和攔截方法比較復雜,由于大多個人防火墻都是在核心?
?
實現的,所以在這里比較詳細的敘述一下。具體的請參見nt/2kDDK文檔。大概有下面幾
個方法。
1、 TDI過濾驅動程序(TDI Filter Driver)。
2、 NDIS中間層驅動程序(NDIS Intermediate Driver)。編寫IM DRIVER在NDIS中間層
對MINIPORT(網卡驅動程序)和協議驅動程序之間的數據包進行攔截。這是微軟提供的
一種技術。在DDK中MS提供了Passthru例子,很多中間層過濾驅動都可以由之改編。但編
寫該過濾程序攔截程序非常的復雜,安裝也很麻煩。
3、 Win2k Filter-Hook Driver。
4、 NDIS Hook Driver。這種方法又有兩種實現方式。
(1)向NDIS注冊假協議(fake protocol)。這是在協議層上的處理。在Windows內核中
,所有已注冊的協議是通過一個單向的協議鏈表來維護的。這個單向鏈表保存了所有已
注冊協議的NDIS_PROTOCOL_BLOCK結構的地址,在這個結構中保存了協議驅動所指定的相
應的派發函數的地址如RECEIVE_HANDLER等。
struct _NDIS_PROTOCOL_BLOCK
{
PNDIS_OPEN_BLOCK OpenQueue; // queue of opens for this protocol
REFERENCE Ref; // contains spinlock for OpenQueue
UINT Length; // of this NDIS_PROTOCOL_BLOCK struct
NDIS50_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics;// handler addresses
struct _NDIS_PROTOCOL_BLOCK * NextProtocol; // Link to next
ULONG MaxPatternSize;
#if defined(NDIS_WRAPPER)
//
// Protocol filters
//
struct _NDIS_PROTOCOL_FILTER * ProtocolFilter[NdisMediumMax+1];
WORK_QUEUE_ITEM WorkItem; // Used during NdisRegisterProtocol to
// notify protocols of existing drivers.
KMUTEX Mutex; // For serialization of Bind/Unbind requests
PKEVENT DeregEvent; // Used by NdisDeregisterProtocol
#endif
};
typedef struct _NDIS_PROTOCOL_BLOCK NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLO
CK;并且,每個協議驅動還對應一個NDIS_OPEN_BLOCK的單向鏈表來維護其所綁定的網卡
信息。當協議驅動調用NdisRegisterProtocol之后,
EXPORT
VOID
NdisRegisterProtocol(
OUT PNDIS_STATUS Status,
OUT PNDIS_PROTOCOL_BLOCK NdisProtocolHandle, /*注意NDIS_HANDLE所指向的就是PN
DIS_PROTOCOL_BLOCK的結構,不要有什么懷疑。*/
IN PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
IN UINT CharacteristicsLength
);
NDIS總是會把新注冊的協議放在協議鏈表的表頭并返回這張表,所以只要我們注冊一個
新的協議通過新協議注冊返回的鏈表頭就可以輕而易舉的遍歷系統中所有協議表。但是
,如果要成功地掛接派發函數,還需要對協議所對應的NDIS_OPEN_BLOCK結構里的派發函
數進行掛接,因為NDIS并不是直接調用協議驅動在NDIS_PROTOCOL_CHARACTERISTICS所注
冊的派發函數地址,而是調用NDIS_OPEN_BLOCK里的派發函數。
struct _NDIS_OPEN_BLOCK
{
PNDIS_MAC_BLOCK MacHandle; // pointer to our MAC
NDIS_HANDLE MacBindingHandle; // context when calling MacXX funcs
PNDIS_ADAPTER_BLOCK AdapterHandle; // pointer to our adapter
PNDIS_PROTOCOL_BLOCK ProtocolHandle; // pointer to our protocol
NDIS_HANDLE ProtocolBindingContext;// context when calling ProtXX funcs
PNDIS_OPEN_BLOCK AdapterNextOpen; // used by adapter's OpenQueue
PNDIS_OPEN_BLOCK ProtocolNextOpen; // used by protocol's OpenQueue
PFILE_OBJECT FileObject; // created by operating system
BOOLEAN Closing; // TRUE when removing this struct
BOOLEAN Unloading; // TRUE when processing unload
BOOLEAN NoProtRsvdOnRcvPkt; // Reflect the protocol_options
NDIS_HANDLE CloseRequestHandle; // 0 indicates an internal close
KSPIN_LOCK SpinLock; // guards Closing
PNDIS_OPEN_BLOCK NextGlobalOpen;
//
// These are optimizations for getting to MAC routines. They are not
// necessary, but are here to save a dereference through the MAC block.
//
SEND_HANDLER SendHandler;
TRANSFER_DATA_HANDLER TransferDataHandler;
//
// These are optimizations for getting to PROTOCOL routines. They are not
// necessary, but are here to save a dereference through the PROTOCOL block.
//
SEND_COMPLETE_HANDLER SendCompleteHandler;
TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
RECEIVE_HANDLER ReceiveHandler;
RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler;
//
// Extentions to the OPEN_BLOCK since Product 1.
//
RECEIVE_HANDLER PostNt31ReceiveHandler;
RECEIVE_COMPLETE_HANDLER PostNt31ReceiveCompleteHandler;
//
// NDIS 4.0 extensions
//
RECEIVE_PACKET_HANDLER ReceivePacketHandler;
SEND_PACKETS_HANDLER SendPacketsHandler;
//
// More NDIS 3.0 Cached Handlers
//
RESET_HANDLER ResetHandler;
REQUEST_HANDLER RequestHandler;
//
// Needed for PnP
//
UNICODE_STRING AdapterName; // Upcased name of the adapter we are bound to
};
這張表是一個單向鏈接表,并且存放了和PNDIS_OPEN_BLOCK->ProtocolCharacteristic
s
一樣的數據收發派發函數,當第N塊網卡發送數據包到第N個協議時,就會調用第N個協議
與第N個網卡之間建立的
NDIS_OPEN_BLOCK表里的SendHandler或SendPacketHandler。所以我們還需要對這張表里
的派發函數進行處理(勾掛)。
值得注意的是,在Windows9x/Me/NT的DDK中,NDIS_PROTOCOL_BLOCK的定義是很明確的,
而在Windows 2000/xp的DDK中,并沒有該結構的詳細定義,也就是說該結構在Windows2
000/xp下是非公開的,因此開發人員需要利用各種調試工具來發掘該結構的詳細定義。
也正是因為如此,這種方法對平臺的依賴性比較大,需要在程序中判斷不同的操作系統
版本而使用不同的結構定義??梢杂肗disOpenProtocolConfiguration打開協議配置,用
NdisReadConfiguration查詢NDIS版本。
下面的函數注冊fake protocol并將PNDIS_PROTOCOL_BLOCK結構存在ProtHandle中NDIS_
HANDLE GetProtocolBlock()
{
NDIS_PROTOCOL_CHARACTERISTICS PChars;
NDIS_STRING Name;
NDIS_HANDLE ProtHandle;
NDIS_STATUS Status;
NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
PChars.MajorNdisVersion = 5;
PChars.MinorNdisVersion = 0;
NdisInitUnicodeString(&Name, L"WssFW"); // Protocol name
PChars.Name = Name;
PChars.OpenAdapterCompleteHandler = NULL;
PChars.CloseAdapterCompleteHandler = NULL;
PChars.SendCompleteHandler = NULL;
PChars.TransferDataCompleteHandler = NULL;
PChars.ResetCompleteHandler = NULL;
PChars.RequestCompleteHandler = NULL;
PChars.ReceiveHandler = NULL;
PChars.ReceiveCompleteHandler = NULL;
PChars.StatusHandler = NULL;
PChars.StatusCompleteHandler = NULL;
PChars.BindAdapterHandler = NULL;
PChars.UnbindAdapterHandler = NULL;
PChars.UnloadHandler = NULL;
PChars.ReceivePacketHandler = NULL;
PChars.PnPEventHandler= NULL;
NdisRegisterProtocol(&Status,
&ProtHandle,
&PChars,
sizeof(NDIS_PROTOCOL_CHARACTERIS
TICS));
ASSERT(Status == NDIS_STATUS_SUCCESS);
if(Status == NDIS_STATUS_SUCCESS)
return ProtHandle;
else
return NULL;
}
下面的函數掛接PNDIS_PROTOCOL_BLOCK中PNDIS_PROTOCOL_CHARACTERISTICS結構的R
ec
eiveHandler和ReceivePacketHandler
PVOID
HookProtoFunc(
PNDIS_PROTOCOL_CHARACTERISTICS pCharacteristics,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
PVOID pOldFunc = NULL;
//Check parameters
if( (!pCharacteristics ) || (!pfuncNew) )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//Just hook once!
if(pCharacteristics->ReceiveHandler != pfuncNew )
{
pOldFunc = pCharacteristics->ReceiveHandler;
if( pOldFunc )
pCharacteristics->ReceiveHandler = pfuncNew;
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
if(pCharacteristics->ReceivePacketHandler != pfuncNew)
{
//if pOpenBlock is NULL or pOpenBlock->ReceivePacketHandl
er is NULL,
//just hook Characteristics;
pOldFunc = pCharacteristics->ReceivePacketHandler;
if(pOldFunc)
pCharacteristics->ReceivePacketHandler = pfuncNew
;
}
break;
default:
break;
}
return pOldFunc;
}
下面的函數掛接PNDIS_OPEN_BLOCK結構里的ReceiveHandler和ReceivePacketHandler
PVOID
HookBlockFunc(
PNDIS_OPEN_BLOCK pFirstOpenBlock,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
RECEIVE_HANDLER * pReceiveHandler = NULL;
RECEIVE_PACKET_HANDLER * pReceivePacketHandler = NULL;
// PVOID pFuncHandler = NULL;
PVOID pOldFunc = NULL;
PNDIS_OPEN_BLOCK pOpenBlock = pFirstOpenBlock;
if(!pFirstOpenBlock)
return NULL;
if(!pfuncNew )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
{
pReceiveHandler = GetReceiveHandler(pOpenBlock,dwNdisVers
ion);
//Just hook once!
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
if(dwNdisVersion == 0x00040001)//win2k ????
{
pReceiveHandler = GetPostNt31ReceiveHandler(pOpen
Block,dwNdisVersion);
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
}
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
pReceivePacketHandler = GetReceivePacketHandler( pOpenBlo
ck,dwNdisVersion
);
//Just hook once !
if(*pReceivePacketHandler != pfuncNew)
{
pOldFunc = *pReceivePacketHandler;
*pReceivePacketHandler = pfuncNew;
}
}
break;
default:
break;
}
return pOldFunc;
}
bsp; &PChars,
sizeof(NDIS_PROTOCOL_CHARACTERIS
TICS));
ASSERT(Status == NDIS_STATUS_SUCCESS);
if(Status == NDIS_STATUS_SUCCESS)
return ProtHandle;
else
return NULL;
}
下面的函數掛接PNDIS_PROTOCOL_BLOCK中PNDIS_PROTOCOL_CHARACTERISTICS結構的R
ec
eiveHandler和ReceivePacketHandler
PVOID
HookProtoFunc(
PNDIS_PROTOCOL_CHARACTERISTICS pCharacteristics,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
PVOID pOldFunc = NULL;
//Check parameters
if( (!pCharacteristics ) || (!pfuncNew) )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//Just hook once!
if(pCharacteristics->ReceiveHandler != pfuncNew )
{
pOldFunc = pCharacteristics->ReceiveHandler;
if( pOldFunc )
pCharacteristics->ReceiveHandler = pfuncNew;
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
if(pCharacteristics->ReceivePacketHandler != pfuncNew)
{
//if pOpenBlock is NULL or pOpenBlock->ReceivePacketHandl
er is NULL,
//just hook Characteristics;
pOldFunc = pCharacteristics->ReceivePacketHandler;
if(pOldFunc)
pCharacteristics->ReceivePacketHandler = pfuncNew
;
}
break;
default:
break;
}
return pOldFunc;
}
下面的函數掛接PNDIS_OPEN_BLOCK結構里的ReceiveHandler和ReceivePacketHandler
PVOID
HookBlockFunc(
PNDIS_OPEN_BLOCK pFirstOpenBlock,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
RECEIVE_HANDLER * pReceiveHandler = NULL;
RECEIVE_PACKET_HANDLER * pReceivePacketHandler = NULL;
// PVOID pFuncHandler = NULL;
PVOID pOldFunc = NULL;
PNDIS_OPEN_BLOCK pOpenBlock = pFirstOpenBlock;
if(!pFirstOpenBlock)
return NULL;
if(!pfuncNew )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
{
pReceiveHandler = GetReceiveHandler(pOpenBlock,dwNdisVers
ion);
//Just hook once!
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
if(dwNdisVersion == 0x00040001)//win2k ????
{
pReceiveHandler = GetPostNt31ReceiveHandler(pOpen
Block,dwNdisVersion);
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
}
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
pReceivePacketHandler = GetReceivePacketHandler( pOpenBlo
ck,dwNdisVersion
);
//Just hook once !
if(*pReceivePacketHandler != pfuncNew)
{
pOldFunc = *pReceivePacketHandler;
*pReceivePacketHandler = pfuncNew;
}
}
break;
default:
break;
}
return pOldFunc;
}
bsp; &PChars,
sizeof(NDIS_PROTOCOL_CHARACTERIS
TICS));
ASSERT(Status == NDIS_STATUS_SUCCESS);
if(Status == NDIS_STATUS_SUCCESS)
return ProtHandle;
else
return NULL;
}
下面的函數掛接PNDIS_PROTOCOL_BLOCK中PNDIS_PROTOCOL_CHARACTERISTICS結構的R
ec
eiveHandler和ReceivePacketHandler
PVOID
HookProtoFunc(
PNDIS_PROTOCOL_CHARACTERISTICS pCharacteristics,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
PVOID pOldFunc = NULL;
//Check parameters
if( (!pCharacteristics ) || (!pfuncNew) )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//Just hook once!
if(pCharacteristics->ReceiveHandler != pfuncNew )
{
pOldFunc = pCharacteristics->ReceiveHandler;
if( pOldFunc )
pCharacteristics->ReceiveHandler = pfuncNew;
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
if(pCharacteristics->ReceivePacketHandler != pfuncNew)
{
//if pOpenBlock is NULL or pOpenBlock->ReceivePacketHandl
er is NULL,
//just hook Characteristics;
pOldFunc = pCharacteristics->ReceivePacketHandler;
if(pOldFunc)
pCharacteristics->ReceivePacketHandler = pfuncNew
;
}
break;
default:
break;
}
return pOldFunc;
}
下面的函數掛接PNDIS_OPEN_BLOCK結構里的ReceiveHandler和ReceivePacketHandler
PVOID
HookBlockFunc(
PNDIS_OPEN_BLOCK pFirstOpenBlock,
DWORD dwFunctionCode,
PVOID pfuncNew,
DWORD dwNdisVersion)
{
RECEIVE_HANDLER * pReceiveHandler = NULL;
RECEIVE_PACKET_HANDLER * pReceivePacketHandler = NULL;
// PVOID pFuncHandler = NULL;
PVOID pOldFunc = NULL;
PNDIS_OPEN_BLOCK pOpenBlock = pFirstOpenBlock;
if(!pFirstOpenBlock)
return NULL;
if(!pfuncNew )
return NULL;
switch(dwFunctionCode)
{
case PROTO_RECEIVE_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
{
pReceiveHandler = GetReceiveHandler(pOpenBlock,dwNdisVers
ion);
//Just hook once!
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
if(dwNdisVersion == 0x00040001)//win2k ????
{
pReceiveHandler = GetPostNt31ReceiveHandler(pOpen
Block,dwNdisVersion);
if( *pReceiveHandler != pfuncNew )
{
pOldFunc = *pReceiveHandler;
*pReceiveHandler = pfuncNew;
}
}
}
break;
case PROTO_RECEIVE_PACKET_HANDLER:
//travel all NDIS_OPEN_BLOCK
for(;pOpenBlock;pOpenBlock = GetNextBlock( pOpenBlock,dwNdisVersi
on ))
pReceivePacketHandler = GetReceivePacketHandler( pOpenBlo
ck,dwNdisVersion
);
//Just hook once !
if(*pReceivePacketHandler != pfuncNew)
{
pOldFunc = *pReceivePacketHandler;
*pReceivePacketHandler = pfuncNew;
}
}
break;
default:
break;
}
return pOldFunc;
}