费尔个人防火墙采用两种封包过滤技术
費爾個人防火墻采用兩種封包過濾技術 [轉原作者:FLASHSKY?
EMAIL:flashsky@xfocus.org
?
????? 由于互聯網發展的歷史原因, TCP/IP 協議及 HTTP 、 FTP 等基于 TCP/IP 協議的各種應用層協議,在協議設計之初均未考慮安全傳輸問題。隨著互聯網的發展,國際標準組織雖陸續推出了 SSL 、 HTTP1.1 等具有安全傳輸能力的應用層協議,但作為應用層承載協議的 TCP/IP 協議仍存在著固有的安全缺陷,造成至今未能有徹底的、低成本的、不需硬件支持的互聯網安全傳輸解決方案。正是由于網絡傳輸安全問題的現實存在,推動著黑客攻擊技術、防火墻技術的不斷發展。
????無論是黑客攻擊技術還是防火墻技術,其實現均必須具備網絡封包截獲技術。黑客利用網絡封包截獲技術,偵聽獲取網絡傳輸數據,并發起仿冒攻擊、篡改攻擊等;防火墻利用網絡封包截獲技術,截斷式地或偵聽式地獲取通過本機的網絡封包,進行安全策略撿擇后,或放行、或攔截丟棄網絡封包,以達到反攻擊的目的。
一、概述
????網絡封包截獲,涉及驅動編程技術、核心態編程技術、系統動態鏈接庫編程技術、協議生成與解析編程技術等,集中體現了網絡應用的核心技術,是防火墻等高級網絡應用開發的基礎。基于 Windows 2000 和 Windows XP 的網絡封包截獲技術主要分為三種: WinSock2 動態鏈接庫重載、傳輸層過濾驅動、中間層驅動。
??????WinSock2 動態鏈接庫重載 :?系統的 WinScok2 動態鏈接庫( ..\system32\winsock.dll ),隨系統啟動而載入內存,提供 29 個用于網絡傳輸的功能函數。 IE 等普通上層應用程序,調用 WinScok2 動態鏈接庫中的 WSPSend 、 WSPRecv 等函數,實現網絡收、發功能。修改注冊表中 HKEY_LOCAL_MACHINE\SYSTM\CURRENTCONTROLSET\SERVICES\WinSock2 項,建立新的自定義 WSPStartup 入口函數,可以重載 winsock.dll 。通過重載 winsock.dll 中的有關網絡收、發函數,增加網絡封包收、發前、后的自定義處理功能,實現網絡封包截獲。公開源代碼的費爾防火墻,使用了 WinSock2 動態鏈接庫重載,實現網絡封包的截獲與安全解析。
??????傳輸層過濾驅動:?使用 NDIS ( Network Driver Interface Specification 網絡驅動接口規范)技術,又稱為 TDI 編程( Transport Driver Interface 傳輸層驅動接口編程 )。 Windows 2000 和 Windows XP 操作系統中, TCP/IP 協議作為系統驅動程序( ..\system32\TcpIp.sys ),在系統啟動時加載入系統內存,以 TCP/IP 設備對象( DeviceObject )的形式供應用程序或其它系統程序調用。傳輸層過濾驅動程序創建一個或多個設備對象,直接掛接到 TCP/IP 設備對象之上。掛接成功后,當其它程序使用網絡傳輸功能,調用 TCP/IP 設備對象時,操作系統首先將該調用映射到 TCP/IP 設備對象之上所掛接的傳輸層過濾驅動程序。通過傳輸層過濾驅動程序,再調用下層的 TCP/IP 設備對象,從而完成網絡訪問功能。同樣,從 TCP/IP 層上傳至應用程序的網絡封包,也要經傳輸層過濾驅動程序后,再轉發至目標應用程序端口。基于此工作原理,可以在傳輸層過濾驅動程序中實現網絡封包截獲,完成網絡封包的過濾及加解密處理。公開原代碼的 TcpIpDog.sys 及微軟官方 2000 DDK 的 Packet.c 例程,使用傳輸層過濾驅動技術,實現了網絡封包截獲。
??????中間層驅動:?與傳輸層過濾驅動實現基本原理一致,也是使用 NDIS 技術。主要差別在于,中間層驅動程序,掛接在協議設備對象(包括 TCP/IP 設備對象)和網卡設備對象之間。任何進出網卡的網絡封包,均必須首先經過中間層驅動程序的處理。從某種意義上分析,中間層驅動程序更象一個虛擬網卡。該虛擬卡封裝了物理網卡,對物理網卡的一切網絡訪問操作,均必須先經虛擬卡處理。公開原代碼的微軟官方 2000 DDK 的 Passthru.c 例程,使用中間層驅動技術,實現了網絡封包截獲。
????以上三種網絡封包截獲技術中, WinSock2 動態鏈接庫重載、傳輸層過濾驅動截獲網絡封包不夠徹底,均存在被繞開的技術可能,而 中間層驅動作用于協議層之下、物理網卡驅動層之上,與協議無關,對網絡封包的截獲最為徹底,基本不存在被繞開的技術可能,但也最具有技術難度。中間層驅動程序的技術難點主要體現為:
 ??????缺少公開的技術資料,基本無中文講解資料、教材書籍。即使是 微軟官方 2000 DDK 的 Passthru.c 例程,也僅公開了偵聽式數據報頭的截獲方法與數據結構,而對 IRP ( I/O Request Packet 系統 I/O 請求包)結構未作進一步的公開。
 ???? ??與硬件相關性大。某種意義上,中間層驅動程序更象虛擬網卡,而網卡硬件性能的不一致,在中間層驅動程序中也必須有所體現。如,總線型網卡與非總線型網卡接收數據包的原理并不相同,就要求中間層驅動程序也必須同時考慮兩種網卡的不同要求。
 ????? 作用于協議層之下,必須手動解析各種傳輸協議,造成在中間層解析協議的技術難度較大。
 ????? 編程與調試困難。驅動程序的編制與調試本身就極為困難,加之中間層驅動程序作用于網卡驅動程序之上,而基本不可能擁有網卡驅動的原程序,無法使用 So ftICE.exe 等調試工具加載網卡驅動的調試符號,加之網卡工作原理復雜,使得中間層驅動程序的調試較 USB 接口等一般硬件驅動程序調試更為困難。
由于以上原因,基于中間層驅動技術的網絡通信產品,基本止于試驗室內,止于項目定制式的產品,少有成熟的通用產品問世。而也正是由于中間層驅動程序的協議無關性、不可繞開性及存在的技術挑戰性,吸引著包括黑客在內的大量精英程序員。
二、 NDIS 簡介
????NDIS 是 Network Driver Interface Specification (網絡驅動程序接口規范)的縮寫,為傳輸層提供了標準的網絡接口。在 Windows 操作系統中,所有的應用程序都最終通過調用 NDIS 接口,實現網絡訪問。 NDIS 在操作系統中的結構如圖一所示。
圖一: NDIS 結構圖
如圖一所示, NDIS 支持 3 種類型的驅動程序:
 ??????傳輸層驅動程序 ( Protocol Drivers ): 對上層應用程序開放 TDI 接口,對下層驅動程序開放 Protocol 接口,與下層 Miniport 接口對接,實現協議驅動功能。嚴格意義上講,傳輸層驅動與協議驅動尚有區別,但本文僅涉及 TCP/IP 協議,并不涉及本地介質認可的其它協議,為便于理解,而將傳輸層驅動、協議驅動簡化為傳輸層驅動。
 ????? 中間層驅動程序( Intermediate Drivers ): 位于物理網卡驅動程序(即微軟官方所稱的微型端口驅動程序)之上, 同時具備 Miniport 接口和 Protocol 接口,可與上、下層驅動程序對接,提供轉發驅動功能。利用其位于物理網卡之上的轉發功能,可以實現不可繞開的網絡封包截獲。
 ????? 微型端口驅動程序( Miniport Drivers ): 對上層的中間層驅動程序開放 Miniport 接口,再通過 NDIS 接口完成對物理網卡的操作,實現對物理網卡的驅動功能。
三、開發工具簡介
????由于缺少教材性的中文資料,客觀上造成理解、建立驅動程序開發環境有一定困難。許多對驅動開發感興趣的程序員,往往是因為不能夠正確理解和建立驅動程序開發環境,要么放棄了驅動程序研究,要么人為復雜化了驅動程序開發過程。筆者結合實際工作經驗,在有關幫助文檔之外,主要強調各種開發包的本質功能及相互關系。具體開發環境的建立步驟,特別是各種開發包的安裝順序,請參見有關開發包的幫助文檔。
????與 USB 接口驅動等其它驅動程序開發一樣,中間層驅動程序開發也主要是利用微軟提供的 Windows 2000 DDk 開發包,其中 DDK 是 Drivers Develop Kit 的簡稱。 DDK 提供了大量符合 Windows 操作系統 NDIS 接口規范的 C 語言函數,為驅動開發程序員提供了良好的開發接口。
????鑒于 DDK 開發包理解和使用較為困難,許多第三方公司對 DDK 進行了不嚴格的面向對象的封裝,為程序員在 VC++6.0 環境中使用面向對象的語言開發驅動程序,提供了更為友好的平臺接口。其中影響較大,應用較為成功的是 Compware 公司的 DriverStudio 3.1 。特別是 DriverStudio 3.1 提供了封裝類庫的原代碼(含注示),和常見的驅動例程,程序員不但可以參考其提供的驅動例程,而且可以通過修改現有的封裝類庫簡化開發功能的實現,甚至能夠生成自定義驅動類。同時, DriverStudio 3.1 還提供了開發向導功能,可以象使用 MFC 開發向導一樣,通過向導功能自動生成大量難以編寫的驅動公共模塊,極大地減少了開發工作量,降低了開發難度。
VC++6.0 、 DriverStudio 3.1 、 Windows 2000 DDk 三種主要開發工具的關系可簡述如下:
 ??????Windows 2000 DDk 是微軟官方提供的 Windows 2000/XP NDIS 接口的具體 C 語言編程實現,是包括中間層驅動在內的各種驅動程序的核心開發包。
 ????? DriverStudio 3.1 是 Compware 公司提供的基于 Windows 2000 DDk 的開發包,其主要是對 Windows 2000 DDk 進行了不嚴格的面向對象的封裝,并能夠在安裝過程中自動設置 VC++6.0 開發環境、自動設置系統環境變量,使程序員可以象調用 MFC 類庫一樣調用 DriverStudio 3.1 類庫,可以使用面向對象的語言進行驅動程序開發,以降低開發難度。同時, DriverStudio 3.1 還提供了 Compware 公司的 SoftICE.exe 調試工具,以提高驅動程序調試效率。
 ???? ? 通過 DriverStudio 3.1 的 DDK Build Setting 菜單功能調用 VC++6.0 ,能夠自動設置環境參數,使 VC++6.0 能夠編譯鏈接至 DriverStudio 3.1 的類庫文件,從而編譯鏈接至 DDK 庫文件,使 VC++6.0 成為驅動程序的最終開發平臺。
四、中間層驅動程序模型分析
????上文已簡單地提到過中間層驅動程序位于 Miniport 和 Protocol 驅動程序之間,同時具有 Miniport 和 Protocol 兩種驅動程序接口。具體來講,中間層驅動程序在自己的上下兩端分別開放出一個 Miniport 接口(對上)和 Protocol 接口(對下)。其中,位于中間驅動程序上面的 Miniport 接口,與上層驅動程序的 Protocol 接口對接;位于下面的 Protocol 接口,與下層驅動程序的 Miniport 接口對接。通過兩種接口的使用,使得中間層驅動程序能夠插入 Miniport 和 Protocol 驅動程序之間。
?
圖二: NDIS 中間層驅動程序安裝前后的對比結構示意
如圖二所示,中間層驅動程序安裝前,物理網卡驅動程序與傳輸層驅動程序進行直接通信,兩者間通過 NDIS 接口完成網絡封包傳遞;中間驅動程序安裝后,網絡封包經中間層驅動程序在物理網卡驅動程序、傳輸層驅動程序之間進行轉發,從而使得中間層驅動程序具備了網絡封包截獲功能。
????利用 NDIS 中間驅動程序,可以在網卡驅動程序和傳輸層驅動程序之間插入一層自定義處理,從而可以用來截獲網絡封包,并重新進行封包、加密、網絡地址轉換及過濾等操作。且由于中間層驅動程序位于網卡驅動程序和傳輸層驅動程序之間,可以截獲較為底層的與協議無關的網絡封包,可以完成更為底層的操作,編寫網絡安全軟件的安全強度更有保證。
????考慮到篇幅和水平所限,且考慮到 DriverStudio 3.1 開發向導能夠自動生成大量中間層驅動的基礎代碼,本文不再對中間層驅動程序內部的 UML 模型展開深入分析,有興趣的讀者可參見 DriverStudio 3.1 幫助文檔中的 DriverNetworks Help/Programing Guide/NDIS Drivers Framework/NDIS Intermediate Drivers Framework 的詳細分析說明,且可參見 DriverStudio 3.1 目錄 SOURCE 下的 DNW.dsw 中的 KndisLib Classes 進行原代碼級的理解分析。在此,筆者特別強調,對 KndisLib Classes 進行原代碼級的理解分析是打牢驅動程序開發技術基礎、提高驅動程序開發能力的有效方法。
五、網絡封包截獲核心原代碼分析
????網絡封包無外乎分為收、發兩種封包,因此截獲網絡封包的理想位置應是發送封包之前,和接收封包之后。具體來講,接收封包的截獲位置,應位于物理網卡接收封包之后;發送封包的截獲位置,應位于通過物理網卡發送封包之前。
????DriverStudio 3.1 開發包提供了 OnReceive 和 OnSend 兩個十分方便的虛函數。其中,物理網卡接收封包之后, NDIS 將調用 OnReceive 虛函數;通過物理網卡發送封包之前, NDIS 將調用 OnSend 虛函數。因此,只需在自開發的中間層驅動程序中實現這兩個虛函數,加入自定義的封包處理方法,就能夠很方便地實現網絡封包截獲處理功能。 DriverStudio 3.1 開發向導能夠自動生成大量基礎代碼,甚至自動生成了空處理的 OnReceive 和 OnSend 兩虛函數,程序員只要加以改寫,就能夠實現自定義的封包處理功能,極大地簡化了開發過程。
????但我們應注意到,中間層驅動程序位于傳輸層驅動程序或協議層驅動程序之下,因此就要求我們必須自編程實現協議的解析功能。之所以在此再次強調協議的解析,是因為 DriverStudio 3.1 開發包所提供的例程中,對 TCP/IP 協議的數據結構定義有誤( TCP 包頭長度的計算方法不正確),雖不影響該例程的正常編譯和運行,但若在該例程基礎上修改設計更完善的 TCP/IP 封包的截獲程序,將會造成難以測試發現的漏包現象。
(一) TCP/IP 協議數據結構的定義
????定義正確的協議數據結構,是實現協議解析,進而實現封包截獲的基礎。所謂協議數據結構,其實質是協議的包頭數據結構,包體內是 HTTP 等應用層協議數據,無關于本文涉及的網絡封包截獲。任何有效的 TCP/IP 數據包均由包頭數據和包體數據組成,其中包頭包括以太幀數據頭、 IP 數據頭、 TCP 數據頭;包體內包含應用層協議數據。
// Ethernet Packet Header
typedef struct _ETHERNET_HEADER {
UCHAR eth_dest[6]; // Destination address
UCHAR eth_src[6]; // Source address
union {
USHORT eth_len; // 802.3 length field.
USHORT eth_type; // Ethernet type field.
};
} ETHERNET_HEADER, *PETHERNET_HEADER;
// Internet Protocol (IP) Packet Header
typedef struct IP_HEADER
{
UCHAR iph_verlen; // Version and length
UCHAR iph_tos; // Type of service
USHORT iph_length; // Total datagram length
USHORT iph_id; // Identification
USHORT iph_offset; // Flags, fragment offset
UCHAR iph_ttl; // Time to live
UCHAR iph_protocol; // Protocol
USHORT iph_xsum; // Header checksum
ULONG iph_src; // Source address
ULONG iph_dest; // Destination address
} IP_HEADER, *PIP_HEADER;
// TCP Packet Header
typedef struct TCP_HEADER
{
USHORT tcph_src; // Source Port Number
USHORT tcph_dest; // Destination Port Number
ULONG tcph_seq; // Sequence Number
ULONG tcph_ack_seq; // Acknowlegdement Number
USHORT tcph_flags; // Flags
USHORT tcph_window; // Window
USHORT tcph_check; // Checksum
USHORT tcph_urgent; // Urgent pointer
UCHAR GetDataOffset() {
USHORT nLen=(tcph_flags & 0x 00f 0);// 例程中無此處理,造成標志字段高 4 位為全零(如 //tcph_flags=0x0270 )時, TCP 包頭長度計算出錯。 // 雖標志字段高 4 位為全零在正常收發封包過程中不 // 會出現,但在通過“三次握手”建立 TCP 連接和結 // 束 TCP 連接過程中,必然出現高 4 位為全零情況, // 進而造成在此過程中 TCP 包頭長度計算錯。一但 //TCP 包頭長度計算錯,可能引發進一步的異常。
return (nLen >> 4)*4;
}
} TCP_HEADER, *PTCP_HEADER;
(二) TCP/IP 協議解析函數的實現
????在 TCP/IP 協議數據結構的定義的基礎上,可根據項目開發需要實現協議解析功能函數。下文原代碼中,主要實現了源 IP 地址、目的 IP 地址、源 TCP 端口號、目的 TCP 端口號、包頭總長度的解析功能。現有的 DriverStudio 3.1 開發包例程和 Windows 2000 DDk 例程中,未能提供協議解析代碼。因此,初學者應特別注意下文原代碼中的移位計算。部分初學者就是因為對 VC 指針與協議中數據類型的對應關系理解不正確,或未進行移位處理,或移位處理不正確,均造成協議解析不正確,進而造成封包無法正確截獲。
//Parse address in KNdistPacket
bool SecurityVirtualCardAdapter::GetAddr_OnRec(const KNdisPacket& Original, //Data packet
//Out parameters
ULONG *pIpScrAddr,ULONG *pIpDesctAddr,
USHORT *pTcpScrAddr,USHORT *pTcpDesctAddr,
ULONG *pPckHdLen)
{
//Return buffers count in the packet
UINT nBufCnt=Original.QueryBufferCount();
//Return the first buffer in the packet
KNdisBuffer Buf=Original.QueryFirstBuffer();
//Tests if the buffer is initialed correctly
if(!Buf.IsValid())
return false;
//Return the first buffer length
//KNdisBuffer::Length
// UINT Length( ) ;
// Gets the length.
//Length of the underlying buffer in bytes.
ULONG nBufLen = Buf.Length();
//Return TCP packet header length
ULONG nHdrLen=sizeof(ETHERNET_HEADER)
+sizeof(TCP_HEADER)
+sizeof(IP_HEADER);
if(nHdrLen>nBufLen)
return false;
//KNdisBuffer::Address
// PVOID Address( ) ;
// Gets the virtual address.
//Virtual address of the first byte of the buffer, or NULL if an error has occurred.
PETHERNET_HEADER pEthHdr = (PETHERNET_HEADER) Buf.Address();
//Return false if ethernet packet is not IP protocol
if(pEthHdr->eth_type!=ETH_TYPE_IP)
return false;
//Return false if not TCP protocol
PIP_HEADER pIpHdr=(PIP_HEADER)((PCHAR)pEthHdr+sizeof(ETHERNET_HEADER));
if(pIpHdr->iph_protocol!=IP_PROTOCOL_TCP)
return false;
//Get IP address
ULONG srcIp=pIpHdr->iph_src;
*pIpScrAddr=((srcIp&0xff)<<24)+
((srcIp&0xff00)<<8)+
((srcIp&0xff0000)>>8)+
((srcIp&0xff000000)>>24);
ULONG destIp=pIpHdr->iph_dest;
*pIpDesctAddr=((destIp&0xff)<<24)+
((destIp&0xff00)<<8)+
((destIp&0xff0000)>>8)+
((destIp&0xff000000)>>24);
//Get TCP port number and TCP packet header length
PTCP_HEADER pTcpHdr=(PTCP_HEADER)((PCHAR)pIpHdr+sizeof(IP_HEADER));
UCHAR nTcpPckHdLen=pTcpHdr->GetDataOffset();
USHORT scrPort=pTcpHdr->tcph_src;
*pTcpScrAddr=((scrPort&0xff00)>>8)+((scrPort&0x00ff)<<8);
//TRACE("GW Rec Source TCP port=%d\n",*pTcpScrAddr);
USHORT desctPort=pTcpHdr->tcph_dest;
*pTcpDesctAddr=((desctPort&0xff00)>>8)+((desctPort&0x00ff)<<8);
//TRACE("GW Rec Desct TCP port=%d\n",*pTcpDesctAddr);
//Return packet header length
*pPckHdLen=sizeof(ETHERNET_HEADER)+sizeof(IP_HEADER)+nTcpPckHdLen;
return true;
}
(三) OnReceive 虛函數的實現
????根據物理網卡工作方式的不同,接收封包方式主要有全包接收方式和部分接收方式。其中,全包接收方式對應于總線型網卡,網卡收到封包后, NDIS 將一次性把完整的封包提交至 OnReceive 虛函數;部分接收方式對應于非總線型網卡,網卡收到封包后, NDIS 將先把封包的一部分提交至 OnReceive 虛函數,上層程序根據封包的一部分判斷是否同意接收,若同意接收網卡將再次提交封包的其它部分,若不同意接收網卡則丟棄該封包。相對應于以上兩種接收方式, DriverStudio 3.1 開發包也提供了 OnReceive 虛函數的兩種形式:
??????NDIS_STATUS SecurityVirtualCardAdapter::OnReceive
(const KNdisPacket& Original, KNdisPacket& Repackaged)// 對應于全包接收方式
??????NDIS_STATUS SecurityVirtualCardAdapter::OnReceive
(IN OUT KNdisPartialPacket& PacketToAccept,
IN PVOID HeaderBuffer, IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, IN UINT LookaheadBufferSize,
IN UINT PacketSize)// 對應于部分接收方式
????全包接收方式下的 OnReceive 虛函數的實現較為簡單,且與 OnSend 虛函數的實現原理完全一致,開發包例程中有詳細參考,本文“ OnSend 虛函數的實現”也將有原代碼展示,因此下文原代碼將僅涉及部分接收方式下的 OnReceive 虛函數的實現。
????部分接收方式下,若在中間層程序程序中對全包進行處理,按照 DriverStudio 3.1 的幫助說明,將需要進行私有包池的建立和管理,編程復雜、調試難度大。筆者研究測試發現,在 Windows 2000/XP/2003 下,由于操作系統對網卡緩存管理有相當好的連續性,完全可以經計算后,通過部分接收包的指針獲得全包指針。簡而言之,在 Windows 2000/XP/2003 下,可以通過指針位置計算后,采用全包接收處理方式對部分接收進行全包處理,從而極大地降低了部分接收方式的中間層驅動程序開發難度。指針位置計算的關鍵,是在部分包緩存區中忽略以太幀頭。
????具體原代碼如下(為補充上文的協議解析功能,其中涉及了包頭中其它字段的解析):
NDIS_STATUS SecurityVirtualCardAdapter::OnReceive
(IN OUT KNdisPartialPacket& PacketToAccept,
IN PVOID HeaderBuffer, IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, IN UINT LookaheadBufferSize,
IN UINT PacketSize)
{
PETHERNET_HEADER pEthHd=(PETHERNET_HEADER)HeaderBuffer;
PIP_HEADER pIpHd=(PIP_HEADER)((PUCHAR)HeaderBuffer+sizeof(ETHERNET_HEADER));
PTCP_HEADER pTcpHd=(PTCP_HEADER)((PUCHAR)HeaderBuffer+
sizeof(ETHERNET_HEADER)+
sizeof(IP_HEADER));
USHORT nEthType=pEthHd->eth_type;
UCHAR nProtocol=pIpHd->iph_protocol;
if(nEthType!=ETH_TYPE_IP||nProtocol!=IP_PROTOCOL_TCP)
{
//TRACE("Not TCP/IP");
UNREFERENCED_PARAMETER(PacketToAccept);
return NDIS_STATUS_SUCCESS;
}
ULONG nScrIpAddr=pIpHd->iph_src;
ULONG nDestIpAddr=pIpHd->iph_dest;
USHORT nScrPort=pTcpHd->tcph_src;
USHORT nDestPort=pTcpHd->tcph_dest;
USHORT nTcpHdLen=pTcpHd->GetDataOffset();
nScrIpAddr=((nScrIpAddr&0xff000000)>>24)+
((nScrIpAddr&0x00ff0000)>>8)+
((nScrIpAddr&0x0000ff00)<<8)+
((nScrIpAddr&0x000000ff)<<24);
nScrPort=((nScrPort&0xff00)>>8)+((nScrPort&0x00ff)<<8);
nDestIpAddr=((nDestIpAddr&0xff000000)>>24)+
((nDestIpAddr&0x00ff0000)>>8)+
((nDestIpAddr&0x0000ff00)<<8)+
((nDestIpAddr&0x000000ff)<<24);
nDestPort=((nDestPort&0xff00)>>8)+((nDestPort&0x00ff)<<8);
if(!IsEncrypt(nScrIpAddr,nDestIpAddr,nScrPort,nDestPort))
{
//TRACE("Submit not encrypt packet");
UNREFERENCED_PARAMETER(PacketToAccept);
return NDIS_STATUS_SUCCESS;
}
TRACE("******** Receive partial *************");
TRACE("HeaderBufferSize=%d",HeaderBufferSize);
TRACE("PacketSize=%d",PacketSize);
TRACE("LookaheadBufferSize=%d",LookaheadBufferSize);
TRACE("pHeaderBuffer=0x%0x",HeaderBuffer);
TRACE("pLookAheadBuffer=0x%0x",LookAheadBuffer);
TRACE("Source=%d.%d.%d.%d:%d",(nScrIpAddr&0xff000000)>>24,
(nScrIpAddr&0x00ff0000)>>16,
(nScrIpAddr&0x0000ff00)>>8,
(nScrIpAddr&0x000000ff),
nScrPort);
TRACE("Dest=%d.%d.%d.%d:%d",(nDestIpAddr&0xff000000)>>24,
(nDestIpAddr&0x00ff0000)>>16,
(nDestIpAddr&0x0000ff00)>>8,
(nDestIpAddr&0x000000ff),
nDestPort);
TRACE("// TCP Pakcet Header ///");
ULONG nSeq=pTcpHd->tcph_seq;
nSeq=((nSeq&0xff000000)>>24)+
((nSeq&0x00ff0000)>>8)+
((nSeq&0x0000ff00)<<8)+
((nSeq&0x000000ff)<<24);
ULONG nAck=pTcpHd->tcph_ack_seq;
nAck=((nAck&0xff000000)>>24)+
((nAck&0x00ff0000)>>8)+
((nAck&0x0000ff00)<<8)+
((nAck&0x000000ff)<<24);
USHORT nFlags=pTcpHd->tcph_flags;
USHORT nWin=pTcpHd->tcph_window;
TRACE("Seq=0x%0x",nSeq);
TRACE("Ack=0x%0x",nAck);
TRACE("Flags=0x%0x",nFlags);
TRACE("Win=0x%0x",nWin);
TRACE("///");
?
PUCHAR pLook=(PUCHAR)LookAheadBuffer;
USHORT nIpandTcpHdLen=sizeof(IP_HEADER)+nTcpHdLen;
if(PacketSize<(UINT)(nIpandTcpHdLen+7))
{
TRACE("Submit no Encrypt packet");
UNREFERENCED_PARAMETER(PacketToAccept);
return NDIS_STATUS_SUCCESS;
}
//LookaheadBufferSize
for(USHORT i=0;i<PacketSize;i++)
{
if(i>nIpandTcpHdLen-1)
{
UCHAR test=pLook[i];
TRACE("Look[%d]=(0x%0x,%c)->(0x%0x,%c",i,test,test,pLook[i],pLook[i]);
}
}
UNREFERENCED_PARAMETER(PacketToAccept);
return NDIS_STATUS_SUCCESS;
}
(四) OnSend 虛函數的實現
????OnReceive 虛函數的實現相對較為簡單, DriverStudio 3.1 提供了詳細例程及說明,本文不再詳細分析,僅列出自編寫的原代碼。
NDIS_STATUS SecurityVirtualCardAdapter::OnSend
(const KNdisPacket& Original, KNdisPacket& Repackaged)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG IpScrAddr=0,IpDesctAddr=0;
USHORT TcpScrAddr=0,TcpDesctAddr=0;
ULONG PckHdLen=0;
ULONG *pIpScrAddr,*pIpDesctAddr;
USHORT *pTcpScrAddr, *pTcpDesctAddr;
ULONG *pPckHdLen;
pIpScrAddr=&IpScrAddr;
pIpDesctAddr=&IpDesctAddr;
pPckHdLen=&PckHdLen;
pTcpScrAddr=&TcpScrAddr;
pTcpDesctAddr=&TcpDesctAddr;
if(GetAddr_OnSend(Original,
pIpScrAddr,pIpDesctAddr,
pTcpScrAddr,pTcpDesctAddr,
pPckHdLen))// 判斷是否是須加密處理的封包
{
TRACE("----- Send Encrypt -----");
Repackaged.CloneDown(Original);// 開發包提供的向下復制封包方法
EncryptPacket_OnSend(Repackaged,*pPckHdLen);// 自定義的加密處理函數
TRACE("----- Send Encrypt End -----");
return Status;
}
else
{
Repackaged.CloneDown(Original);
return Status;
}
 }
 【參考文獻】
 ?????《 Windows 防火墻與網絡封包截獲技術》 朱雁輝 電子工業出版社 2002
 ????? Windows 2000 DDk 幫助文檔 Microsoft 公司 2003
 ????? DriverStudio 3.1 幫助文檔 Compware 公司 2004
費爾個人防火墻采用兩種封包過濾技術:
1. 應用層封包過濾,采用 Winsock 2 SPI。
2. 核心層封包過濾,采用 NDIS-HOOK。
Winsock 2 SPI 的技術特點:
Winsock 2 SPI 工作在 API 之下 Driver 之上,屬于應用層的范疇。利用這項技術可以截獲所有的基于 Socket 的網絡通信。比如:IE、OUTLOOK 等常見的應用程序都是使用 Socket 進行通信。它的技術特點主要有:
優點:
1. 工作在應用層,以 DLL 的形式存在,編程、調試方便。
2. 跨 Windows 平臺,可以直接在 Windows 98/ME/NT/2000/XP上通用,Windows 95 只需安裝上 Winsock 2 for 95,也可以正常運行。
3. 效率高,由于工作在應用層,CPU 占用率低。
4. 封包還沒有按照低層協議進行切片,所以比較完整,很容易做內容過濾。
5. 做防色情之類的軟件,不用根據具體的瀏覽器進行分別編程,既簡單又安全。
缺點:
1. 不用 Socket 的網絡通信無法攔截,比如:使用NetBios的網上鄰居,和使用ICMP協議的Ping。
2. 微軟對SPI設計的問題,導致如果安裝順序出錯很容易造成網絡癱瘓。這意味著如果同時安裝幾個使用SPI技術的軟件,而且有使用非標準安裝方式的軟件,很容易有的被繞過或者不能正常網絡通信。所以建議編寫SPI程序一定要用標準的安裝方式。
SPI 在 操作系統種的結構如下圖:我們需要處理的是傳輸服務提供者。
層次結構圖:費爾個人防火墻的XFILTER.DLL處的就是基礎服務提供者的位置。
NDIS-HOOK 的技術特點:
NDIS 是網絡接口規范,Windows 使用 NDIS 函數庫實現 NDIS 接口。所有的網絡通信最終必須通過 NDIS 完成。NDIS 橫跨 傳輸層、網絡層和數據鏈路層,NDIS 的結構如下圖:
微軟提供了以下幾種標準接口編程方式:
1. TDI 傳輸層過濾驅動程序(TDI Filter,比如常見的 Tcp Filter Driver)
2. 協議驅動程序 (Protocol Driver)
3. 中間驅動程序 (IM Driver)
4. 小端口驅動程序 (Miniport Driver)
其中 TDI Filter Driver 和 IM Driver 通常用做封包過濾。也是防火墻和VPN軟件常用的技術。但是它們都有一些缺陷:
TDI Filter Driver 屬于 Upper Driver,位于 TcpIp.sys 之上,這就意味著由 TcpIp.sys 接收并直接處理的數據包就不會傳送到上面,從而無法過濾某些接收的數據包,典型的就是ICMP,ICMP的應答包直接由TcpIp.sys生成并回應,上面的過濾驅動程序全然不知。
IM Driver 功能比較強大,但編程接口復雜。最麻煩的是安裝,自動化安裝太困難。
NDIS-HOOK 克服了上面的缺陷。NDIS-HOOK 的工作原理是直接替換 NDIS 的函數庫中的函數地址,這樣只要向 NDIS 的請求就會先經過我們自己函數的處理,這樣就非常簡單,處理完轉發給系統函數就完成了。NDIS-HOOK技術有以下特點:
1. 編程方便、接口簡單、思路明確、性能穩定。
2. 更靈活,可以僅僅截獲自己需求的,不需要冗余的代碼。
3. 功能強大,可以截獲所有 NDIS 和 TDI 函數完成的功能。當然比標準方式功能強大許多。還可以用這項技術延伸到 HOOK 所有系統函數。
4. 安全性高,這樣截獲封包較為底層,不容易被穿透。
5. 安裝簡單。
NDIS-HOOK 安裝前的結構示意圖:
NDIS-HOOK 安裝后的結構示意圖:
總結
以上是生活随笔為你收集整理的费尔个人防火墙采用两种封包过滤技术的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 综述:用于自动驾驶的全景鱼眼相机的理论模
- 下一篇: Jenkins-Pipline
