无论是黑客攻击技术还是防火墙技术,其实现均必须具备网络封包截获技术。黑客利用网络封包截获技术,侦听获取网络传输数据,并发起仿冒攻击、篡改攻击等;防火墙利用网络封包截获技术,截断式地或侦听式地获取通过本机的网络封包,进行安全策略捡择后,或放行、或拦截丢弃网络封包,以达到反攻击的目的。
一、概述
网络封包截获,涉及驱动编程技术、核心态编程技术、系统动态链接库编程技术、协议生成与解析编程技术等,集中体现了网络应用的核心技术,是防火墙等高级网络应用开发的基础。基于 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;
}
}