摘要 链路加密是保护内部网络数据安全的一种有效方法。为了提高内部网络数据的安全性,本文分析了windows网络驱动程序以及网络驱动程序接口规范(NDIS),提出一种基于NDIS的网络链路加密方案,并对该方案的原理、实现过程和实验效果进行了描述。
关键字 链路层 驱动程序 NDIS
目前的局域网基本上都采用以广播为技术基础的以太网技术,任何两个节点之间的通信数据包,不仅被这两个节点的网卡所接收,也同时可以被处在同一以太网上的任何一个节点的网卡所截取。因此,黑客只要接入以太网上任一节点进行侦听,就可以捕获在这个以太网上传输的所有数据包,对其进行解包分析,从而窃取关键信息,这是以太网所固有的安全隐患。为了对网络提供安全、可靠以及对上层应用完全透明的数据传输,可以考虑在链路层对网络数据进行加解密,将加密模块整合进网卡驱动程序当中。这样可以使其无缝地与其他网络设备一起工作,从而保护整个网络数据。
一、Windows环境下的网卡驱动程序
以太网为基础的局域网是以广播方式来发送数据的[1]。发送方主机将数据发送到网络上,这些数据会经过网络中每一个节点。但各节点的网卡只将与自身MAC地址相匹配的数据送到上层协议进行处理,与本机MAC地址不匹配的数据将被丢弃。但如果将该网卡的工作模式设置为混杂模式,则网卡将接收网络中所有流经该节点的数据包。以上这些都是由网卡以及其驱动程序来完成的,因此在windows环境下,要对网卡接收和发送的数据进行处理,首先要了解网络驱动程序接口规范。
1、网络驱动程序接口规范NDIS
Microsoft和3Com公司于1989年联合开发出了NDIS(网络驱动程序接口规范-Network Driver Interface Specification)。它使不同的协议和网络适配器分离,符合NDIS的网络驱动程序直接或稍加修改就可在几种Windows平台上运行。Windows软件开发者可以开发出能够和多种协议栈进行通信的网络驱动程序。NDIS规范了网络驱动程序间的标准接口,也维护着网络驱动程序的参数和状态信息,包括指向函数的指针、句柄和链接参数块的指针,以及其它系统参数。总的来说,NDIS 本质上是一种软件接口,它使不同的传输可以采用一种通用的方式来访问由不同厂商制造的网卡[2,3]。
2、NDIS库
网络驱动程序不是直接调用操作系统的例程,而是通过NDIS进行系统调用,而NDIS驱动程序所存在的环境都是由NDIS库(Ndis.sys)所创建的。这个库处理网络通信中涉及的许多细节,并且输出一组由所有NDIS驱动程序使用的标准接口,提供一个形如NdisXxx的系统函数集,使各NDIS驱动程序不需要直接与操作系统进行通信,为驱动开发者提供了方便[4]。
3、NDIS驱动程序
最初人们把NDIS构思为一个将局域网卡驱动程序在逻辑上同其协议实现分离的接口。在Windows2000环境下,NDIS已发展到5.0版本,成为集局域网、广域网等网络标准于一体完整家族。
根据其作用不同,NDIS支持下列几种网络驱动程序形式:
(1)微端口驱动程序(Miniport drivers)
(2)中间驱动程序(Intermediate drivers)
(3)协议驱动程序(Protocol drivers)
NDIS各层驱动程序以及它们之间的关系如图1所示:
图1 NDIS组件图
4、网卡驱动程序
网卡驱动程序向下管理物理硬件,向上提供接口。上层可以通过该驱动向网络发送数据、处理中断、查询驱动程序运行状态。在windows环境下,网卡驱动程序也就是对应于NDIS的微端口驱动程序。
微端口驱动程序工作在数据链路层,是与网络接口卡(Network Interface Card(NIC),基于网卡编程的主要控制对象)结合最紧密的一层驱动程序。它调用NDIS库提供的接口函数来完成NIC与上层驱动程序之间的相互通讯。NDIS库导出一组函数集合(NdisXxx函数)来封装所有微端口需要调用的操作系统函数,而微端口也输出一组MiniportXxx函数供NDIS和上层驱动程序调用。
二、链路加密方案的设计与实现
1、设计思想
数据链路层是OSI系统结构中的第二层,如果采用链路加密,则网络中每条通信链路上的加密是独立实现的。对每条链路可以使用不同的密钥,这样,当某条链路受到破坏时也不会导致其他链路上传递的加密信息被解出。
NDIS微端口驱动位于网络链路层,是网络驱动中与网卡结合最紧密的驱动程序。因此可以对微端口驱动程序进行改造,在驱动程序中实现对数据帧的截取,并调用加解密模块对数据进行加解密,如图2所示。对驱动程序的改造大体可以分为两步:截取数据帧和加解密模块的实现。
图2 链路层加密方案设计框图
(1)截取数据帧
NDIS库为微端口驱动程序提供了一系列接口函数。微端口驱动程序可以利用这些函数在主机与网络之间建立联系,也可以利用这些函数来截取数据。
在发送本机数据到网卡之前,可以在微端口驱动程序中加入截取子程序截获数据帧,并把数据帧送入加密模块进行加密,然后将加密后的密文送入网卡缓冲区中。接收数据时,从网卡缓冲区中接收的密文被送入主机内存。密文在送往上层网络驱动程序之前被接收截取子程序截获,并调用解密模块将其解密成明文,之后再将明文数据送交上层处理。
(2)加解密模块
考虑到帧长的限制,对于数据帧加密要求必须保证其明文和密文长度相等。因此在加解密模块当中采用分组密码算法。分组密码的工作方式是将明文分成固定长度的组(块),用同一密钥对每一块加密,输出也是固定长度的密文,本文以DES为例。
考虑到软件加解密过程速度较慢,为了提高速度,直接将加解密模块嵌入到微端口驱动程序中,这样所有对数据的处理将全部在系统底层进行,大大提高了数据处理的效率。最后将所有模块集成在一起,编译生成网卡驱动程序MYNE200.SYS文件,分别安装在两台主机中进行测试。
2、方案实现
(1)实现工具及环境
本方案提出在微端口驱动程序中实施加解密,因此要选用适当的工具对微端口驱动程序进行改造。
开发驱动程序的工具有很多种,本方案采用的是NuMega公司的驱动程序开发工具DriverStudio。这套软件开发工具包括SoftICE、DriverWorks、DriverNetWorks、VtoolsD和DriverAgent等多种软件工具。
作者使用的是针对Windows网络驱动程序的开发工具DriverNetworks。在安装了Windows2000DDK的环境下,该工具为NDIS驱动程序开发者提供了一个框架,开发者可以很方便地在已有的框架中添加程序并对程序进行编译。该工具可以生成VC++所需的源文件、DSP文件以及INF文件,方便了驱动程序的开发和改造。
由于本方案中对数据的截取和加密主要是在发送和接收过程中进行的,因此本文主要介绍在数据发送和接收过程中对数据的处理。
(2)数据的发送和截取
数据的发送由上层协议驱动发起,发送的数据信息用一个NDIS_PACKET包来描述。NDIS库提供了一些函数来提取包中的信息,并对其进行处理。
发送过程可分为两种情况:
1) 当协议驱动程序有数据要发送时,启动传输操作,通过NIDS库调用微端口驱动程序的MYNE2000Send函数。该函数调用的参数是一个指向NDIS_PACKET包(描述将要发送的信息)的指针。驱动调用NdisQueryPacket函数得到包的长度和存放待发送包缓冲区的逻辑地址。然后设置NIC上的寄存器将包发送出去,并返回一个发送成功的状态。
2) 如果驱动程序不能立即发送包,则将它送到“待传输”队列中,然后由中断处理函数MYNE2000HandleInterrupt来完成发送。完成发送以后,调用NdisMSendComplete函数通知上层发送已经完成。
由于从上层传下来的数据包到微端口层被放在预先分配的缓冲区中。用NDIS提供的相应函数,可以得到该缓冲区的首地址和数据长度。因此,可在驱动程序中加入Send_Intercept子程序,利用NDIS提供的函数得到存放数据缓冲区的相应参数。然后调用加密模块接口函数DES_ENCRYPT对数据包进行加密。最后调用CardWrite子程序将加密后的密文发送到网络上。
这里需要注意的是当微端口驱动程序准备发送数据包时,总是认为上层的协议驱动程序不会发送一个太大的数据包到微端口层。因为根据NDIS,协议驱动程序会在初始化时查询微端口并决定包的最大长度,协议驱动只能传送大小在微端口层所支持范围内的数据包。在本方案中,数据包的最大长度不会超过1514字节。
(3)数据的接收和截取
数据接收是将网络上的数据帧接收到网卡缓冲区中,然后由驱动程序将缓冲区中的数据读入内存中。
网卡接收数据时会产生一个中断,因此驱动程序接收数据首先要在中断处理过程MYNE2000HandleInterrup中进行。对于以太网卡,程序调用NdisMEthReceiveIndicate函数将一个称为lookahead的数据传递给上层协议驱动,由协议驱动检查收到的数据是否符合协议要求。lookahead是指网卡中准备接收的数据的一部分。因为协议驱动判断微端口接收的数据是否符合协议要求时,不需要对所有数据都进行判断,只需对一部分数据(lookahead)进行判断即可。
如果协议驱动判断数据符合要求,就会调用微端口的MYNE2000TransferData函数,将除lookahead之外剩余的数据传送到内存中,再交给上层驱动处理。当传送完毕时,调用NdisMEthReiveIndicatecomplete函数通知上层驱动数据已经接收完毕。如果协议驱动检查数据不符合要求,就会终止接收过程。而已经传送到内存中的lookahead也会被下次接收到的数据覆盖掉。
在接收过程中截取数据需要注意一个问题。按照NDIS的规定:在接收数据时,先要调用NdisMEthReceiveIndicate函数将网卡缓冲区中接收的数据的一部分(lookahead)送到上层驱动程序,余下的数据仍在网卡缓冲区中。但是这样会把密文数据分割开,从而无法完成对数据的解密。
因此,必须另外开辟出一块缓冲区Prebuffer,在接收截取子程序Receive_Intercept中先调用CardRead子程序,将网卡缓冲区中所有密文数据送入内存缓冲区Prebuffer,同时得到数据长度。然后调用解密模块函数DES_DECRYPT对密文进行解密,解密后的数据存放在Prebuffer中。接下来按照原先的接收顺序,先送一部分数据到lookahead,交协议驱动确认后,再将剩余数据送往上层,最后调用Indicatecomplete函数表示接收过程已经结束。
(4)加解密的实现
加解密实现的主要思想是将加解密算法集成到驱动程序中,本方案采用传统的DES加解密算法作为示例。
数据在被送入加密模块之前已经成帧,前14个字节存放的是源地址、目的地址和数据长度。发送截取函数Send_Intercept得到将要加密的数据长度和起始地址后,调用加密模块的接口函数DES_ENCRYPT,从数据的第15个字节开始进行加密,这样就将除地址和数据长度之外的所有数据都进行了加密。数据进行加密之后,驱动程序调用CardWrite函数将数据发送到网络中。
由于采用的是分组加密算法,密文长度和明文长度一致。这样就保证了传送的密文长度不会超过以太网协议所规定的帧最大长度,也就不存在因数据过长而需要对数据进行分割的问题。
数据帧被接收到网卡缓冲区后,读入到内存当中的前14个字节数据为源地址、目的地址和数据长度。这14个字节的信息是以明文形式存在的。解密时,接收截取子程序Receive_Intercept调用解密模块接口函数DES_DECRYPT,从密文数据的第15个字节开始进行解密运算。解密之后将数据存入预先分配的缓冲区Prebuffer中。此后,驱动程序无论调用NdisMEthReceiveIndicate函数,还是MYNE2000TransferData函数,都是从缓冲区Prebuffer中读取数据,而不是从网卡缓冲区中读取,直至解密过程结束。
作者利用DriverStudio提供的工具TrueCoverage对驱动程序进行了编译,生成MYNE2000.SYS文件。经安装测试,数据可以在两台安装有该文件的主机上进行正常传输。而在同一局域网内的其它主机无法得到正常的数据。实验证明本加密方案的设计思路是可行的。
用软件实现对数据的加解密有着一定的局限性,特别是处理速度较慢。本方案经过适当修改可以采用硬件实现。在实际使用当中,将部分功能用硬件实现,可以得到更快的执行速度,从而提高整个系统的性能。(作者单位 信息工程大学理学院)
参考文献
[1] Peter G Viscarola. Windows NT Device Driver Development[M]. 电子工业出版社,2000
[2] Numega. An Overview of Windows 2000 NDIS Driver Development[DB/OL]. http:// www.numega.com/library/docs/windows-2000-ndis.pdf. 2003
[3] Windows Device Driver Architectures[EB/OL]. http://www.bjlongway.com, 2003