基于NDIS中間層的驅(qū)動包截獲技術(shù)
09-03-25 作者: 編輯:校方人員 NDIS(Network Driver Interface Specification)是網(wǎng)絡(luò)驅(qū)動程序接口規(guī)范的簡稱。它橫跨傳輸層、網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層,定義了網(wǎng)卡或網(wǎng)卡驅(qū)動程序與上層協(xié)議驅(qū)動程序之間的通信接口規(guī)范,屏蔽了底層物理硬件的不同,使上層的協(xié)議驅(qū)動程序可以和底層任何型號的網(wǎng)卡通信。NDIS為網(wǎng)絡(luò)驅(qū)動程序創(chuàng)建了一個完整的開發(fā)環(huán)境,只需調(diào)用NDIS函數(shù),而不用考慮操作系統(tǒng)的內(nèi)核以及與其他驅(qū)動程序的接口問題,從而使得網(wǎng)絡(luò)驅(qū)動程序可以從與操作系統(tǒng)的復(fù)雜通訊中分離,極大地方便了網(wǎng)絡(luò)驅(qū)動程序的編寫。另外,利用NDIS的封裝特性,可以專注于一層驅(qū)動的設(shè)計,減少了設(shè)計的復(fù)雜性,同時易于擴展驅(qū)動程序棧。 1 NDIS驅(qū)動模型簡介
NDIS支持的網(wǎng)絡(luò)驅(qū)動程序類型: 網(wǎng)卡驅(qū)動程序(NIC Drivers):網(wǎng)卡驅(qū)動程序是網(wǎng)卡與上層驅(qū)動程序通信的接口,它負責(zé)接收來自上層的數(shù)據(jù)包,或?qū)?shù)據(jù)包發(fā)送到上層相應(yīng)的驅(qū)動程序,同時它還完成處理中斷等工作。 中間驅(qū)動程序(Intermediate Protocol
Drivers):中間驅(qū)動程序位于網(wǎng)卡驅(qū)動程序和協(xié)議驅(qū)動程序之間,它向上提供小端口(Minport)函數(shù)集,向下提供協(xié)議(protocol)函數(shù)集,因此對于上層驅(qū)動程序而言,它是小端口驅(qū)動程序。對于底層的驅(qū)動程序,它是協(xié)議驅(qū)動程序。 協(xié)議驅(qū)動程序(Upper Level Protocol
Drivers):協(xié)議驅(qū)動程序執(zhí)行具體的網(wǎng)絡(luò)協(xié)議,如IPX/SPX、TCP/IP等。協(xié)議驅(qū)動程序為應(yīng)用層客戶程序提供服務(wù),接收來自網(wǎng)卡或中間驅(qū)動程序的信息。 防火墻的開發(fā)一般采用的是中間驅(qū)動程序。通過NDIS中間層驅(qū)動,我們可以截獲來自網(wǎng)卡的所有原始數(shù)據(jù)包。圖1則是NDIS中間層驅(qū)動的工作過程圖: NDIS中間層驅(qū)動程序是工作在MINIPROT和PROTOCOL接口之間的,驅(qū)動程序必須向下導(dǎo)出一個PROTOCOL接口,向上導(dǎo)出一個MINIPORT接口。將自己創(chuàng)建的驅(qū)動程序插入到網(wǎng)卡驅(qū)動程序與傳輸驅(qū)動程序之間。如此一來,當下層的網(wǎng)卡驅(qū)動程序接收到數(shù)據(jù)后會通過MINIPORT接口發(fā)送到導(dǎo)出的PROTOCOL接口上,NDIS中間層驅(qū)動程序便接收到了來自網(wǎng)卡的數(shù)據(jù)并調(diào)用準備好的回調(diào)函數(shù)處理數(shù)據(jù)包信息。接著NDIS中間層驅(qū)動在處理數(shù)據(jù)包完畢后再繼續(xù)把數(shù)據(jù)通過導(dǎo)出的MINIPROT接口向PROTOCOL接口發(fā)送,這樣就完成了一個截獲數(shù)據(jù)包的過程[1]。 2 NDIS中間層驅(qū)動的工作流程在開始學(xué)習(xí)NDIS中間層驅(qū)動之前,我們有必要了解下NDIS是怎樣工作的。當然這就包括了它的接收數(shù)據(jù)包的流程了。那么我們來看看NDIS接收數(shù)據(jù)包流程到底是怎樣的: 1.低層的網(wǎng)卡驅(qū)動調(diào)用NdisMIndicateReceive或者NdisMEthIndicateReceive函數(shù)通知上一層已經(jīng)它們已經(jīng)收到數(shù)據(jù)。 2.接著系統(tǒng)調(diào)用自定義的PtReceive或者PtReceivePacket函數(shù),到底系統(tǒng)會調(diào)用哪個函數(shù)跟機器的網(wǎng)卡有關(guān)。接著在函數(shù)中調(diào)用NdisGetReceivedPacket函數(shù)接受低層傳上來的數(shù)據(jù),如果我們得到了一個完整的packet包,我們就申請一個緩沖區(qū)存放下層傳上來的數(shù)據(jù),接著調(diào)用NdisMIndicateReceivePacket通知上層設(shè)備。如果此時MyPacket的status是NDIS_STATUS_RESOURCES,我們就在本函數(shù)中釋放我們分配的緩沖區(qū);否則我們在上層發(fā)送4的時候,在MPReturnPacket中釋放該緩沖區(qū)。 3.如果在PtReceive或者PtReceivePacket函數(shù)中無法得到一個完整的packet,那么就調(diào)用NdisMEthIndicateReceive等函數(shù)通知系統(tǒng)。 4.當上層設(shè)備得到了一個完整的數(shù)據(jù)并且處理完畢以后,它會調(diào)用NdisReturnPacket,然后NDIS會調(diào)用我們的MPReturnPacket。如果申請的緩沖區(qū)沒釋放,則在MPReturnPacket函數(shù)中釋放該緩沖區(qū)。然后同樣的向下層調(diào)用NdisReturnPacket。下層會釋放他們自己申請的緩沖區(qū)。 5.如果3發(fā)生,那么系統(tǒng)會調(diào)用PtReceiveComplete函數(shù)。在PtReceiveComplete函數(shù)中我們應(yīng)該調(diào)用NdisMEthIndicateReceiveComplete,通知系統(tǒng)收到了完整的數(shù)據(jù)。 6.當上層協(xié)議驅(qū)動得知底層已經(jīng)收到了完整的數(shù)據(jù)報文以后,可能會調(diào)用NdisTransferData,要求下層把剩余的數(shù)據(jù)傳上來。然后系統(tǒng)調(diào)用MPTransferData例程。在MPTransferData中,調(diào)用NdisTransferData。必須注意的是該函數(shù)的返回值:如果返回success,說明剩余的數(shù)據(jù)立刻就傳上來了。此時會立即返回。7步驟就不會調(diào)用;如果返回pending,表明底層在此阻塞,底層會在稍后的時候調(diào)用7。 7.當?shù)讓?/span>miniport驅(qū)動做好了一個完整的packet,它會調(diào)用NdisTransferDataComplete。同樣的,系統(tǒng)會調(diào)用我們的PtTransferDataComplete函數(shù)。這樣,整個接收數(shù)據(jù)的流程就結(jié)束了[2]。 通過流程圖可以知道在PtReceive或者PtReceivePacket中可以得到我們所希望的數(shù)據(jù),然后在以上2個函數(shù)中加入自己的處理代碼,就可以達到截獲數(shù)據(jù)并進行相應(yīng)處理的目的了。 3 在驅(qū)動程序中導(dǎo)出接口我們首先必須在驅(qū)動程序中向系統(tǒng)注冊導(dǎo)出虛擬接口。這些工作將在DriverEntry函數(shù)中完成,代碼如下: DriverEntry( IN PDRIVER_OBJECT
DriverObject, IN PUNICODE_STRING
RegistryPath
) {
NDIS_STATUS
Status; NDIS_PROTOCOL_CHARACTERISTICS PChars;
//保存有關(guān)導(dǎo)出PROTOCOL接口的回調(diào)函數(shù)地址的結(jié)構(gòu) NDIS_MINIPORT_CHARACTERISTICS MChars;
//保存有關(guān)導(dǎo)出MINIPORT接口的回調(diào)函數(shù)地址的結(jié)構(gòu) PNDIS_CONFIGURATION_PARAMETER Param; NDIS_STRING
Name; NdisMInitializeWrapper(&NdisWrapperHandle,
DriverObject, RegistryPath, NULL);
//初始化NdisWrapperHandle
//設(shè)置其他的回調(diào)函數(shù) MChars.SendPacketsHandler
= MPSendPackets; //設(shè)置發(fā)送數(shù)據(jù)包的回調(diào)函數(shù) //向NDIS注冊我們的MINIPORT接口 Status
= NdisIMRegisterLayeredMiniport(NdisWrapperHandle,
&MChars,
sizeof(MChars),
&DriverHandle); PChars.ReceivePacketHandler
= PtReceivePacket; //設(shè)置接收數(shù)據(jù)包的回調(diào)函數(shù)
//向NDIS注冊MINIPORT接口 NdisRegisterProtocol(&Status,
&ProtHandle,
&PChars,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
//通知NDIS生成所注冊的2個接口 NdisIMAssociateMiniport(DriverHandle, ProtHandle); }
如此一來,驅(qū)動程序可以看成是工作在網(wǎng)卡層與協(xié)議層之間了,當?shù)讓泳W(wǎng)卡有數(shù)據(jù)到來時會先經(jīng)過驅(qū)動程序處理后再往上層設(shè)備發(fā)送的。那么我們就可以在自己的回調(diào)函數(shù)中處理來自網(wǎng)絡(luò)的數(shù)據(jù)了。 4 回調(diào)函數(shù)的工作在向系統(tǒng)注冊的回調(diào)函數(shù)中,比較重要的就是PtReceive和PtReceivePacket函數(shù)了。為了程序的通用性,2個回調(diào)函數(shù)的大致處理流程是一樣的。我們僅拿PtReceive函數(shù)來做例子。PtReceive函數(shù)的原型如下: NDIS_STATUS PtReceive( IN
NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE
MacReceiveContext,
IN PVOID HeaderBuffer,
IN
UINT HeaderBufferSize,
IN
PVOID LookAheadBuffer, IN
UINT LookAheadBufferSize, IN
UINT PacketSize
) 在該函數(shù)中,第三個參數(shù)的指向幀頭的起始緩沖區(qū),第五個參數(shù)指向數(shù)據(jù)體的起始緩沖區(qū),第七個參數(shù)的值為緩沖區(qū)大小。如果PacketSize大于LookAheadBufferSize,表明數(shù)據(jù)還未全部拷貝上來。如果這2個參數(shù)相等,那么說明數(shù)據(jù)全部在LookAheadBuffer變量指向的緩沖區(qū)內(nèi)。來看看下面的代碼: NDIS_STATUS PtReceive( IN
NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE
MacReceiveContext, IN PVOID HeaderBuffer, //以太頭數(shù)據(jù) IN
UINT HeaderBufferSize, //以太頭數(shù)據(jù)大小 IN
PVOID LookAheadBuffer, //數(shù)據(jù)體部分 IN
UINT LookAheadBufferSize, //LookAheadBuffer數(shù)據(jù)大小 IN
UINT PacketSize
//數(shù)據(jù)包大小 ) { PADAPT pAdapt
=(PADAPT)ProtocolBindingContext; PNDIS_PACKET MyPacket,
Packet; NDIS_STATUS Status = NDIS_STATUS_SUCCESS
, DataStatus
; if(!pAdapt->MiniportHandle)
{ Status = NDIS_STATUS_FAILURE;
} else
do
{ if(pAdapt->isSecondary) ASSERT(0); //從下層驅(qū)動獲取數(shù)據(jù)包 Packet
= NdisGetReceivedPacket(pAdapt->BindingHandle,
MacReceiveContext); if(Packet
!= NULL)
{
//如果數(shù)據(jù)包不為空那么就為下層即將
//發(fā)送上來的數(shù)據(jù)包分配空間 NdisDprAllocatePacket(&Status , &MyPacket, pAdapt->RecvPacketPoolHandle); if(Status
== NDIS_STATUS_SUCCESS) { //拷貝原下層數(shù)據(jù)包到我們分配的緩沖中 MyPacket->Private.Head
= Packet->Private.Head; MyPacket->Private.Tail = Packet->Private.Tail; NDIS_SET_ORIGINAL_PACKET( MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet));
NDIS_SET_PACKET_HEADER_SIZE(MyPacket,
HeaderBufferSize);
NdisGetPacketFlags(MyPacket) =
NdisGetPacketFlags(Packet);
NDIS_SET_PACKET_STATUS(MyPacket,
DIS_STATUS_RESOURCES);
ASSERT(NDIS_GET_PACKET_STATUS(MyPacket) ==
NDIS_STATUS_RESOURCES);
//拷貝數(shù)據(jù)包完成
//數(shù)據(jù)包分析處理函數(shù)
PacketAnalysis(MyPacket);
//處理代碼
//通知NDIS已復(fù)制數(shù)據(jù)包到緩沖區(qū)中 NdisMIndicateReceivePacket(pAdapt->MiniportHandle,
&MyPacket, 1); //釋放數(shù)據(jù)包
NdisDprFreePacket(MyPacket);
break; 在PtReceive函數(shù)中我們要做的就是為從下層傳上來的數(shù)據(jù)分配緩沖區(qū),然后將收到的數(shù)據(jù)拷貝到分配的緩沖區(qū)中,接著調(diào)用NdisMIndicateReceivePacket函數(shù)將數(shù)據(jù)傳給上一層。PacketAnalysis函數(shù)就是包分析函數(shù),在該函數(shù)中我們就可以對傳來的數(shù)據(jù)進行處理,過濾和攔截了。 5 數(shù)據(jù)包的分析與處理
在以上代碼中,其實在MyPacket這個結(jié)構(gòu)中就儲存了所希望得到的數(shù)據(jù)包地址,但是如何得到數(shù)據(jù)呢?我們在得到數(shù)據(jù)的過程中需要了解NDIS_PACKET和NDIS_BUFFER這兩個結(jié)構(gòu)。下面給出這兩個結(jié)構(gòu)的定義:
我們要的數(shù)據(jù)就儲存在NDIS_BUFFER這個結(jié)構(gòu)中的VirtualAddress成員里面,這個指針指向數(shù)據(jù)包的首地址。關(guān)系圖如圖3所示: NDIS_PACKET是一個描述NDIS_BUFFER鏈表的結(jié)構(gòu),在NDIS_PACKET中的成員Private中有指向第一個NDIS_BUFFER的指針和指向最后一個NDIS_BUFFER的指針分別是Private.Head和Private.Tail[3][4]。而NDIS_BUFFER中就記錄了我們數(shù)據(jù)包的地址和下一個NDIS_BUFFER的地址。操作有很多種方法,但是由于這些結(jié)構(gòu)體本來對我們是不透明的,所以最安全的方法是用微軟提供的一系列函數(shù)來操作NDIS_PACKET和NDIS_BUFFER。這些函數(shù)都可以在DDK中查得到。 獲取數(shù)據(jù)包內(nèi)容的代碼如下: NDIS_STATUS status ; 我們想要的截獲數(shù)據(jù)包的功能就達到了,如果想要過濾數(shù)據(jù)包,那么就只需要對數(shù)據(jù)包的內(nèi)容進行判斷就可以了。但是需要注意的是mybuffer里面的數(shù)據(jù)為原始數(shù)據(jù)包的數(shù)據(jù),也就是包括了包頭等一系列信息,需要自己分析包頭信息來獲取希望的數(shù)據(jù)。 6 結(jié)束語本文只通過簡單的一些示例代碼闡述了如何利用驅(qū)動來截獲數(shù)據(jù)包的方法。大部分防火墻就是通過該技術(shù)截獲網(wǎng)絡(luò)數(shù)據(jù)并判斷數(shù)據(jù)的合法性實現(xiàn)保護的。但是要寫出很具有通用性的代碼還需要更廣泛的知識作為基礎(chǔ)。在這里僅給大家拋磚引玉,至于關(guān)于NDIS中間驅(qū)動更詳細的信息讀者們可以去參考微軟提供的WDK文檔。 參
考
文
獻 [1]
朱耀輝《Windows防火墻與數(shù)據(jù)封包截獲技術(shù)》 北京.電子工業(yè)出版社 [2]
《關(guān)于passthru的Send/Receive的流程圖》(http://bbs./htm_data/10/0305/40727.html) [3]
《NDIS_PACKET結(jié)構(gòu)討論[一]》(http://feikoo./viewdiary.10774705.html) [4]
《NDIS_PACKET結(jié)構(gòu)討論[二]》(http://feikoo./viewdiary.10774711.html) |
|
來自: tuohuang0303 > 《我的圖書館》