中間層是生成並操作接收信息的業務規則和函式的集合。它們通過業務規則(可以頻繁更改)完成該任務,並由此被封裝到在物理上與套用程式程式邏輯本身相獨立的組件中。請參見客戶端層、數據源層。
三層網路結構指的是將數據處理過程分為三部分:第一層是客戶端(用戶界面層),提供用戶與系統的友好訪問;第二層是套用服務層(也叫中間層),專司業務邏輯的實現;第三層是數據源層(數據服務層,資料庫系統),負責數據信息的存儲、訪問及其最佳化。由於業務邏輯被提取到套用服務層,大大降低了客戶端負擔,因此也成為瘦客戶(Thin Client)結構,三層結構在傳統的二層結構的基礎上增加了套用服務層,將套用邏輯單獨進行處理,從而使得用戶界面與套用邏輯位於不同的平台上,兩者之間的通信協定由系統自行定義。通過這樣的結構設計,使得套用邏輯被所有用戶共享,這是兩層結構套用軟體與三層套用軟體之間最大的區別。三層結構將表示部分和業務邏輯部分按照客戶層和套用服務層相分離,客戶端和套用服務層、套用服務層和資料庫服務層之間的通訊、異構平台之間的數據交換等都可以通過中間件或者相關程式來實現。當資料庫或者套用服務層的業務邏輯改變時,客戶端並不需要改變,反之亦然,大大提高了系統模組的復用性,縮短開發周期,降低維護費用。以Java Applet為客戶端, 以Java Servlet為中間層的三層網路結構,在目前的實時網路信息平台得到了廣泛的套用,其結構和一般的三層結構如圖1所示:
中間層驅動工作原理
(1)註冊表常識:
1)、設備資料庫所在的註冊表健值為:
HKLM\SYSTEM\CurrentControlSet\Enum\
ENUM子項中是一個設備資料庫,在資料庫存放計算機中所有安裝的,並且被系統認識到的設備。
所有的用戶(包括管理員)都不能更改ENUM項的內容。這是為了保護作業系統和安裝的設備的完整性。為了更改設備的設定,應該使用"設備管理器"。
為了在設備管理器中現實隱藏的,非即插即用的,以及沒有連線到計算機上的所有設備,你應該首先在命令解釋器中敲入命令set DEVMGR_SHOW_NONPRESENT_DEVICES=1,然後啟動設備管理器,就可以在設備管理器中刪除和重新配置這些設備了。
2)、硬體設備類所在ntControlSet\Control\Class\
Class項下存放硬體設備類的配置信息。在Class項下的每個子項都代表一個設備類,子項的名稱使用"唯一全局標識符(GUIS)",這些標識符存放該設備類的配置信息。在每個類標識符下,還會有以4位數命名的子項,他們代表該設備類里的具體設備,其他的配置數據只套用於該具體設備。
如網卡的設備類是{4D36E972-E325-11CE-BFC1-08002BE10318},並假定我們網卡對應的4為數命名子項名為0005。
其中
HKLM\SYSTEM\CurrentControlSet\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0005\Linkage\ 中:
Export:代表該設備在設備名字空間輸出的設備名字。
RootDevice:代表當前設備的GUID。中間層驅動這裡有兩個GUID,第一個是自己的GUID,第二個是該中間層驅動綁定的下層MINIPORT的GUID。
UpperBind:代表上層綁定它的NDIS協定驅動或NDIS中間層驅動。當某個協定驅動綁定該MINIPORT設備時,則這個協定驅動的名字必須出現在UpperBind健值的字元串中,否則不能進行綁定。也就是說,UpperBind健值的字元串決定了那個協定驅動(當然也包括中間層驅動註冊的協定)和當前的MINIPORT設備綁定,即,它決定了NDIS的上下層綁定關係。
註:一般添加中間層驅動後,中間層驅動只插入到真是網卡和相應協定中間,不會插入到虛擬網卡(如安裝虛擬機後虛擬出來的網卡設備)和相應協定中間。
3)、驅動程式所在的註冊表健值為:
HKLM\SYSTEM\CurrentControlSet\Services
ENUM子項
通常如果某個服務下存在ENUM子項,表明該服務是用來控制某個設備或者設備互動的,它的下面存放該設備的實例。用戶不要去試圖修改該子項的內容,因為每次系統啟動時,都會重寫該子項的內容。
LINKAGE子項
值項Bind
存放該協定所在綁定棧的最低層小連線埠設備實例(即MINIPORT)。
值項Export
存放該服務必須訪問的對象,該對象必須已經安裝在系統中,並且該服務能夠使用。
值項Route
指定子項Linkage從那裡獲取綁定數據。
Parameters\ Adapters\子項
這裡,我們只解釋中間層驅動中該子項的意義。為此,我們假設當前我們討論的HKLM\SYSTEM\CurrentControlSet\Services\XXXX中的XXXX為中間層驅動。
對於中間層驅動,該子項下含有一個子項,是以我們當前中間層驅動綁定的下層MINIPORT設備的GUID命名的,在我的系統中,健值如下:
{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}
在{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子項有個健,名稱為UpperBindings,該健的健值是當前中間層驅動的MINIPORT設備名稱,在我的系統中為如下健值:
\Device\ {5BF5A311-13E4-4746-8865-339DDD6C73AF}
(2)中間層驅動
在我們的函式NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()中,會調用一系列函式(如NdisOpenProtocolConfiguration、NdisReadConfiguration)來訪問註冊表,其實都是訪問Parameters\Adapters\{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}子項。
注意,函式NDIS_PROTOCOL_CHARACTERISTICS-> BindAdapterHandler()的倒數第二個參數是SystemSpecific1,如果我們安裝的是XPASSTHRU,則其具體指的是如下字元串:
xfilter\Parameters\Adapters\{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}
其中xfilter 是XPASSTHRU的,而{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系統中不同。
函式NdisOpenProtocolConfiguration()其實只是構造一個查詢註冊表的RTL_QUERY
_REGISTRY_TABLE結構(該結構在利用函式RtlQueryRegistryValues()查詢註冊表是使用)。並將這個結構封裝到NDIS_WRAPPER_CONFIGURATION_HANDLE結構中,然後作為NdisOpenProtocolConfiguration()的第二個參數返回。
其實NdisOpenProtocolConfiguration()構造的RTL_QUERY_REGISTRY_TABLE結構的含義也就是查詢HKLM\SYSTEM\CurrentControlSet\Services\ xfilter\Parameters\Adapters\{
102454C2-9DB3-42A1-B4CF-6A8B67A516C0}\下的健值(xfilter 會隨著安裝不同的中間層驅動而不同,{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}在不同的系統中不同,下面均省略這些注釋)。
函式NdisReadConfiguration()有兩個作用,它首先修改在函式NdisOpenProtocolConf
iguration()構造的RTL_QUERY_REGISTRY_TABLE結構。也就是在查詢HKLM\SYSTEM
\CurrentControlSet\Services\ xfilter\Parameters\Adapters\{102454C2-9DB3-42A1-B4CF-6A8B6
7A516C0}\的基礎上加上了一個健,將其變成HKLM\SYSTEM\CurrentControlSet\Services\ xfilter\Parameters\Adapters\{102454C2-9DB3-42A1-B4CF-6A8B67A516C0}\UpperBindings。然後函式NdisReadConfiguration()會調用函式RtlQueryRegistryValues()查詢新構造的這個註冊表,並將結果存儲在調用函式NdisReadConfiguration()時的第二個參數中。在我的系統中,RtlQueryRegistryValues()讀出的這個新構造的這個註冊表的健值是:\Device\ {5BF5A311-13E4-4746-8865-339DDD6C73AF}(後面我們稱為RESULT1),它其實是我們註冊的中間層驅動(XPASSTHRU)的設備輸出(其構造是\Device+\GUID)。
其實我們讀出的這箇中間層驅動(XPASSTHRU)的設備(即我們剛才讀出的那個健值RESULT1)只在後面的函式NdisIMInitializeDeviceInstanceEx()中才用得著,並且RESULT1是作為函式NdisIMInitializeDeviceInstanceEx()的第二個參BindAdapterHandler()我們一旦調用了函式NdisOpenAdapter()綁定了一個下層的MINIPORT,為什麼還要調用函式NdisIMInitializeDeviceInstanceEx()初始化我們中間層驅動自己的MINIPORT(因為函式NdisIMInitializeDeviceInstanceEx()的參數是RESULT1,而RESULT1代表我們中間層驅動的MINIPORT)。為了解釋這個原因,我們先做如下假設。
我們假設我們系統安裝了一個中間層驅動(假設為XPASSTRHU),在上圖中,PROT-IM和MINIPORT-IM分別代表我們中間層驅動的協定驅動程式和小連線埠驅動程式,PROT-TCPIP代表真正的協定驅動程式,MINIPORT-NIC代表真是網卡的小連線埠驅動程式。
當PROT-TCPIP需要傳送數據時,會調用函式NdisSend(),其實它會調用MINIPORT-IM中的傳送數據函式,但是MINIPORT-IM和MINIPORT-NIC沒聯繫,按照常規是不能傳送數據到MINIPORT-NIC的。但是我們可以看到PROT-IM是可以和MINIPORT-NIC互相傳送數據的,所以我們必須將MINIPORT-IM和PROT-IM聯繫起來。
另外當MINIPORT-NIC需要將數據提交給PROT-TCPIP時,只能首先將數據提交給PROT-IM,PROT-IM也只能通過MINIPORT-IM才能和PROT-TCPIP聯繫起來。
所以必須將PROT-IM和MINIPORT-IM聯繫起來。有人說這兩個東西都是我們中間層驅動註冊得,難道還聯繫不起來嗎?不錯,但是不管怎樣你都得通過你得代碼才能將他們聯繫起來呀!下面我們就介紹聯繫得方法。
我們以XPASSTHRU為例,在其函式ProtocolBindAdapter()中,先分配了一個ADAPT結構(這個結構是自己定義的,可根據用戶的需要定義)。當調用函式ProtocolBindAdapter()調用函式NdisOpenAdapter()進行綁定時,是以&Adapt->BindingHandle作為函式NdisOpenAdapter()的第三個參數的,這樣Adapt->BindingHandle就指向了NDIS_OPEN_BLOCK1(注意,調用函式NdisOpenAdapter()時的第三個參數會返回指向綁定以後的NDIS_OPEN_BLOCK指針)。然後函式ProtocolBindAdapter()會調用函式NdisIMInitializeDeviceInstanceEx(),該函式會進一步調用XPASSTHRU的NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函式。上面我們討論過,NdisIMInitializeDeviceInstanceEx()中的第二個參數就是XPASSTHRU註冊的小連線埠驅動的設備實例(即上面的RESULT1)。在函式NdisIMInitializeDeviceInstanceEx()中會根據設備名稱(RESULT1),找到對應的MINIPORT結構,並將其指針作為參數傳遞給NDIS_MINIPORT_CHARACTERISTICS->InitializeHandler()函式(以後簡稱InitializeHandler()函式)。在InitializeHandler()函式中,會進一步將MINIPORT結構指針賦值給ADAPT->MiniportHandle(ADAPT就是上面在函式ProtocolBindAdapter()中分配的那個結構)。
這樣數據結構ADAPT中就含有了兩個指針,一個指向NDIS_OPEN_BLOCK1,另一個指向MINIPORT-IM,而NDIS_OPEN_BLOCK1和PROT-IM是密切聯繫的,所以ADAPT就將PROT-IM和MINIPORT-IM緊密的聯繫起來了。
另外注意,NDIS_MINIPORT_BLOCK->DeviceContext是指向我們的ADAPT結構的,這個賦值在函式NdisIMInitializeDeviceInstanceEx()中完成。上面談到了也是在函式NdisIMInitializeDeviceInstanceEx()中完成了從ADAPT-> MiniportHandle到MINIPORT的綁定,所以在函式NdisIMInitializeDeviceInstanceEx()中完成了ADAPT和MINIPORT的互相連線(即指針互相指向)。
函式InitializeHandler()中可以利用句柄MINIPORT的句柄得到我們的ADAPT結構,具體實現這個功能的函式是NdisIMGetDeviceContext(),這個函式的參數是個NDIS_HANDLE類型,但是在該函式內部,會將這個參數轉換成NDIS_MINIPORT_BLOCK結構,並返回NDIS_MINIPORT_BLOCK->DeviceContext。
在函式NdisOpenAdapter()中,除了上面提到的完成了由Adapt->BindingHandle到NDIS_OPEN_BLOCK1的指向外,還完成了我們沒有提到的由NDIS_OPEN_BLOCK1-> ProtocolBindingContext到ADAPT的指向,所以函式NdisOpenAdapter()完成了ADAPT和NDIS_OPEN_BLOCK1的互相連線(即指針互相指向)。
講到這裡,我們上圖完善為下圖:
下面大家也許就可以做一些HOOK了,例如對OPEN_BLOCK結構的HOOK,對ADAPT的HOOK等,這裡就不作介紹了(作者:noble 出處:http://www.nsfocus.com)