美国情浴威尼斯在线看:利用 NDIS 中间层驱动程序截获网络封包

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 08:02:35
由于互联网发展的历史原因, 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

{

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;

}

}