防火墙的数据包拦截方式小结网络防火墙都是基于数据包的拦截技术之上的。
在Windows下,数据包的拦截方式有很多种,其原理和实现方式也千差万别。
总的来说,可分为“用户级”和“内核级”数据包拦截两大类。
用户级下的数据包拦截方式有:* Winsock Layered Service Provider (LSP)。
* Win2K 包过滤接口(Win2K Packet Filtering Interface)。
* 替换Winsock动态链接库 (Winsock Replacem ent DLL)。
内核级下的数据包拦截方式有:* TDI过滤驱动程序 (TDI-Filter Driver)。
* NDIS中间层驱动程序 (NDIS Intermediate Driver)。
* Win2K Filter-Hook Driver。
* Win2K Firewall-Hook Driver。
* NDIS-Hook Driver。
在这么多种方式面前,我们该如何决定采用哪一种作为自己项目的实现技术?这需要对每一种方式都有一个大致的了解,并清楚它们各自的优缺点。
技术方案的盲目选用往往会带来一些技术风险。
以自己为例,我需要在截包的同时得到当前进程文件名,也就是说,需向用户报告当前是哪个应用程序要访问网络。
在选用Win2K Filter-Hook Driver这一方案之后(很多小型开源项目都采用这一方案),便开始编码。
但之后发现Win2K Filter-Hook Driver的截包上下文处于内核进程中,即IRQL >= DISPATCH_LEVEL,根本无法知道当前应用程序的名字。
相比之下,TDI-Filter Driver和NDIS-Hook Driver则可以得知这些信息。
其中TDI-Filter Driver比NDIS-Hook Driver更能准确地获知当前应用程序文件名,后者的接收数据包和少数发送数据包的场景仍然处于内核进程中。
下面列出了各种截包方式的特点:* Winsock Layered Service Provider (LSP)该方式也称为SPI(Service Provider Interface) 截包技术。
SPI是由Winsock2提供的一个接口,它需要用户机上安装有Winsock 2.0。
Winsock2 SPI工作在API之下的Driver 之上,可以截获所有基于Socket的网络数据包。
优点:* 以DLL形式存在,编程方便,调试简单。
* 数据封包比较完整,未做切片,便于做内容过滤。
缺点:* 拦截不够严密,对于不用Socket的网络通讯则无法拦截 (如ICMP),木马病毒很容易绕过。
* Win2K Packet Filtering Interface这是Win2K中一组API提供的功能 (PfCreateInterface, PfAddFiltersToInterface, ...)。
优点:* 接口简单,实现起来没什么难度。
缺点:* 功能过于简单,只能提供IP和端口的过滤,可能无法满足防火墙的复杂需求。
* 处于API层,木马病毒容易绕过。
* 只能在Win2K以上(含)系统中使用。
* Winsock Replacem ent DLL这种方法通过替换系统Winsock库的部分导出函数,实现数据报的监听和拦截。
缺点:* 由于工作在Winsock层,所以木马病毒容易绕过。
* TDI-Filter DriverTDI的全称是Transport Driver Interface。
传输层过滤驱动程序通过创建一个或多个设备对象直接挂接到一个现有的驱动程序之上。
当有应用程序或其它驱动程序调用这个设备对象时,会首先映射到过滤驱动程序上,然后由过滤驱动程序再传递给原来的设备对象。
优点:* 能获取到当前进程的详细信息,这对开发防火墙尤其有用。
缺点:* 该驱动位于tcpip.sys之上,所以没有机会得到那些由tcpip.sys直接处理的包,比如ICMP。
* TDI驱动需要重启系统方能生效。
* NDIS Intermediate Driver也称之为IM Driver。
它位于协议层驱动和小端口驱动之间,它主要是在网络层和链路层之间对所有的数据包进行检查,因而具有强大的过滤功能。
它能截获所有的数据包。
可参考DDK中附带的例子Passthru。
优点:* 功能非常强大,应用面广泛,不仅仅是防火墙,还可以用来实现VPN,NAT和VLan等。
缺点:* 编程复杂,难度较大。
* 中间层驱动的概念是在WinNT SP4之后才有的,因此Win9X无法使用。
* 不容易安装,自动化安装太困难。
* Win2K Filter-Hook Driver这是从Win2K开始提供的一种机制,该机制主要利用ipfiltdrv.sys所提供的功能来拦截网络数据包。
Filter-Hook Driver的结构非常简单,易于实现。
但是正因为其结构过于简单,并且依赖于ipfiltdrv.sys,微软并不推荐使用。
可参考CodeProject上的例子:/KB/IP/drvfltip.aspx 优点:* 结构简单,易于实现。
* 能截获所有的IP包(包括ICMP包)。
缺点:* 工作于内核进程中,无法取得当前应用程序进程的信息。
* 虽能截获所有IP包,但无法取得数据包的以太帧(Ethernet Frame)。
* 只能在Win2K以上(含)系统中使用。
* Win2K Firewall-Hook Driver这是一种和Win2k Filter-Hook Driver差不多的机制,所不同的是,Firewall-Hook Driver 能在IP Driver上挂接多个回调函数,所以和前者相比,它引起冲突的可能性更小一些。
可参考CodeProject上的例子:/KB/IP/FwHookDrv.aspx 这种方式的优缺点和Win2K Filter-Hook Driver基本相同。
* NDIS-Hook Driver这是一种要重点讲述的截包方式。
它是目前大多数网络防火墙所使用的方法。
这种方式的做法是安装钩子到ndis.sys中,替换其中的某些关键函数,从而达到截包的目的。
在下一节中我们将详细地介绍它的实现方法。
优点:* 安装简单,可即时安装和卸载驱动,无需重启系统。
* 能截获所有的IP包,同时能取得数据包的以太帧(Ethernet Fram e)。
* 安全性高,木马病毒不容易穿透。
* 在大多数情况下,能获取到当前应用程序的进程信息。
* 能在Win98以上(含)系统中使用。
缺点:* 接收数据包、或偶尔发送数据包时,驱动工作在内核进程中,无法获得应用程序进程信息。
◎ NDIS-Hook技术微软和3COM公司在1989年制定了一套开发Windows下网络驱动程序的标准,称为NDIS。
NDIS的全称是Network Driver Interface Specification。
NDIS为网络驱动的开发提供了一套标准的接口,使得网络驱动程序的跨平台性更好。
NDIS提供以下几个层次的接口:1. NDIS小端口驱动 (NDIS Miniport Driver)。
这也就是我们常说的网卡驱动。
2. NDIS协议驱动 (NDIS Protocol Driver)。
用来实现某个具体的协议栈,如TCP/IP协议栈,并向上层导出TDI接口。
3. NDIS中间层驱动 (NDIS Intermediate Driver)。
这是位于小端口驱动和协议驱动之间的驱动。
NDIS为了给出上述三种接口,提供了一个系统的、完整的Wrapper。
这个Wrapper即ndis.sys。
上面提到的Miniport Driver、Protocol Driver、Intermediate Driver均属于插入到这个Wrapper中的“模块”,它们调用Wrapper提供的函数,同时也向Wrapper注册回调函数。
在简单了解了NDIS的机制之后,不难得知,网络防火墙只需要将自己的函数挂钩(Hook)到ndis.sys中即可截获网络数据包。
NDIS-Hook技术有两种实现方案:1. 修改ndis.sys的Export Table。
在Win32下,可执行文件(EXE/DLL/SYS)都遵从PE格式。
所有提供接口的驱动都有Export Table,因此只要修改ndis.sys的Export Table,就可实现对关键函数的挂接。
在实现步骤中,首先需要得到ndis.sys的内存基址,再根据PE格式得到DOS头部结构(IMAGE_DOS_HEADER),进一步得到NT头部结构(IMAGE_NT_HEADER),最后从头部结构中查得Export Table的地址。
由于协议驱动程序(NDIS Protocol Driver)在系统启动时会调用NdisRegisterProtocol() 来向系统注册协议,因此这种方法关键在于修改ndis.sys所提供的NdisRegisterProtocol、NdisDeRegisterProtocol、NdisOpenAdapter、NdisCloseAdapter、NdisSend这几个函数的地址。
对于处于系统核心的ndis.sys而言,要修改它的内存区域,只有驱动程序才能做到,所以我们必须编写驱动程序来达到这个目的。
该方案的缺点是加载或卸载驱动后无法立即生效,必须重启系统。
且挂钩方法较为复杂。
早期凡使用NDIS-Hook的防火墙都采用这一方法,包括著名的费尔防火墙的早期版本(v2.1)。
直到2004年,上一名黑客公布了一种全新的NDIS-Hook技术(即下文即将提到的第2种方法),诸多防火墙产品才都悄悄对自己的核心技术进行了升级。
由于新的挂钩技术更好,故本文不打算详述修改Export Table这一方法的具体细节。
2. 向系统注册假协议(Bogus Protocol)。
NDIS提供了一个API: NdisRegisterProtocol(),这个API的职责是向系统注册一个协议(如TCPIP),并将该协议作为一个链表节点插入到“协议链表”的头部,最后返回该链表头节点(即新节点)的地址。
正常情况下,只有NDIS协议驱动程序(NDIS Protocol Driver)才会调用这个API。
既然如此,如果我们也调用NdisRegisterProtocol() 向系统注册一个新的协议,我们也就能轻易地得到“协议链表”的首地址,通过走访这个链表,就能修改其中的某些关键信息,比如关键函数的地址。
修改完毕后,再调用NdisDeRegisterProtocol() 注销掉新协议。
这看似一切都没发生,但事实上目的已经达到了。
这个新协议我们称之为假协议(Bogus Protocol)。