Windows下网络数据报的监听和拦截技术1Windows下网络数据报的监听和拦截技术是一个比较古老的话题,应用也很广泛,例如防火墙等等。
这篇小文只是对该技术的一个总结,没有新技术,高手免看:)要监听和拦截Windows下的数据报,基本可以在两个层次进行,一个是用户态(user-mode),一个是核心态(kernel-mode)。
在用户态下,从高到低大概有四种方法。
1、原是套结字(Raw Socket)。
Winsock2以后提供了原始套结字功能,可以在用户态用Winsock函数接收所有流经Winsock的IP包。
这种方法在MSDN里面有叙述,是MS官方支持的方法,在网上也有很多资料。
但是这种方法只能监听但是不能拦截数据报,所以可以作为网络监视器的选择技术,但是不能实现防火墙等更高要求的功能。
另外最致命的缺点就是只能在Winsock层次上进行,而对于网络协议栈中底层协议的数据包例如TDI无法进行处理。
对于一些木马和病毒来说很容易避开这个层次的监听。
2、替换系统自带的WINSOCK动态连接库。
这种方法可以在很多文章里面找到详细的实现细节。
通过替换系统Winsock库的部分导出函数,实现数据报的监听和拦截。
缺点同1。
3、Winsock服务提供者(SPI)。
SPI是Winsock的另一面,是Winsock2的一个新特性。
起初的Winsock是围绕着TCP/IP协议运行的,但是在Winsock 2中却增加了对更多传输协议的支持。
Winsock2不仅提供了一个供应用程序访问网络服务的Windows socket 应用程序编程接口(API),还包含了由传输服务提供者和名字解析服务提供者实现的Winsock服务提供者接口(SPI)和ws2_32.dll。
Winsock 2的传输服务提供者是以动态链接库的形式(DLL)存在的。
以下是winsock 2在传输服务提供者上的WOSA(Windows 开放服务结构):----------------------------|Windows socket 2 应用程序|----------------------------Windows socket 2 API| WS2_32.DLL |----------------------------Windows socket 2 传输SPI| 传输服务提供者(DLL) |----------------------------Windows socket SPI提供三种协议:分层协议,基础协议和协议链。
分层协议是在基础协议的上层,依靠底层基础协议实现更高级的通信服务。
基础协议是能够独立,安全地和远程端点实现数据通信的协议,它是相对与分层协议而言的。
协议链是将一系列的基础协议和分层协议按特点的顺序连接在一起的链状结构,可以通过Platform SDK 附带的工具Sporder.exe察看系统已经安装的SPI,请参见下图:API------------------------| WS2_32.DLL |SPI------------------------| 分层协议 |SPI-------------| 分层协议 |SPI------------------------| 基础协议 |------------------------每个应用程序通过Ws2_32.dll和相应的服务提供者进行严格的数据交换。
Ws2_32.dll根据应用程序在创建套接字时所提供的参数来选择特定的服务提供者,然后把应用程序的实现过程转发由所选创建套接字的服务提供者来管理。
也就是说,Ws2_32.dll 只是一个中间过程,而应用程序只是一个接口,数据通信的实现却是由服务提供者来完成的。
所以我们通过适当的增加自己的分层协议服务提供者,使其位于SPI的顶端,那么就能将Ws2_32.dll传给服务提供者的数据报拦截下来。
由于是MS的官方方法,具体的使用方法在其Platform SDK里面有详细的例子(LSP),在MSDN里面也有详细的解释。
这种方法的优点是能够获得调用Winsock的进程的详细信息,并能实现Qos和数据加密。
所以SPI是用户态数据拦截的较好地点。
缺点同1。
4、Windows2000包过滤接口。
由于过滤规则限制太多不灵活而应用不多。
5、网络监视器SDK。
MS官方的实时监视分析网络数据的方法。
但是由于封装的太复?樱?使用起来不灵活。
在核心态下,数据报的监视和拦截方法比较复杂,由于大多个人防火墙都是在核心??实现的,所以在这里比较详细的叙述一下。
具体的请参见nt/2kDDK文档。
大概有下面几个方法。
1、 TDI过滤驱动程序(TDI Filter Driver)。
2、 NDIS中间层驱动程序(NDIS Intermediate Driver)。
编写IM DRIVER 在NDIS中间层对MINIPORT(网卡驱动程序)和协议驱动程序之间的数据包进行拦截。
这是微软提供的一种技术。
在DDK中MS提供了Passthru例子,很多中间层过滤驱动都可以由之改编。
但编写该过滤程序拦截程序非常的复杂,安装也很麻烦。
3、 Win2k Filter-Hook Driver。
4、 NDIS Hook Driver。
这种方法又有两种实现方式。
(1)向NDIS注册假协议(fake protocol)。
这是在协议层上的处理。
在Windows 内核中,所有已注册的协议是通过一个单向的协议链表来维护的。
这个单向链表保存了所有已注册协议的NDIS_PROTOCOL_BLOCK结构的地址,在这个结构中保存了协议驱动所指定的相应的派发函数的地址如RECEIVE_HANDLER等。
struct _NDIS_PROTOCOL_BLOCK{PNDIS_OPEN_BLOCK OpenQueue; // queue of opens for this protocolREFERENCE Ref; // contains spinlock for OpenQueueUINT Length; // of this NDIS_PROTOCOL_BLOCK structNDIS50_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics;// handler addressesstruct _NDIS_PROTOCOL_BLOCK * NextProtocol; // Link to nextULONG MaxPatternSize;#if defined(NDIS_WRAPPER)//// Protocol filters//struct _NDIS_PROTOCOL_FILTER * ProtocolFilter[NdisMediumMax+1];WORK_QUEUE_ITEM WorkItem; // Used during NdisRegisterProtocol to// notify protocols of existing drivers.KMUTEX Mutex; // For serialization of Bind/Unbind requestsPKEVENT DeregEvent; // Used by NdisDeregisterProtocol#endif};typedef struct _NDIS_PROTOCOL_BLOCK NDIS_PROTOCOL_BLOCK,*PNDIS_PROTOCOL_BLOCK;并且,每个协议驱动还对应一个NDIS_OPEN_BLOCK的单向链表来维护其所绑定的网卡信息。
当协议驱动调用NdisRegisterProtocol之后,EXPORTVOIDNdisRegisterProtocol(OUT PNDIS_STATUS Status,OUT PNDIS_PROTOCOL_BLOCK NdisProtocolHandle, /*注意NDIS_HANDLE所指向的就是PNDIS_PROTOCOL_BLOCK的结构,不要有什么怀疑。
*/IN PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,IN UINT CharacteristicsLength);NDIS总是会把新注册的协议放在协议链表的表头并返回这张表,所以只要我们注册一个新的协议通过新协议注册返回的链表头就可以轻而易举的遍历系统中所有协议表。
但是,如果要成功地挂接派发函数,还需要对协议所对应的NDIS_OPEN_BLOCK结构里的派发函数进行挂接,因为NDIS并不是直接调用协议驱动在NDIS_PROTOCOL_CHARACTERISTICS所注册的派发函数地址,而是调用NDIS_OPEN_BLOCK里的派发函数。
struct _NDIS_OPEN_BLOCK{PNDIS_MAC_BLOCK MacHandle; // pointer to our MACNDIS_HANDLE MacBindingHandle; // context when calling MacXX funcsPNDIS_ADAPTER_BLOCK AdapterHandle; // pointer to our adapterPNDIS_PROTOCOL_BLOCK ProtocolHandle; // pointer to our protocolNDIS_HANDLE ProtocolBindingContext;// context when calling ProtXX funcsPNDIS_OPEN_BLOCK AdapterNextOpen; // used by adapter's OpenQueuePNDIS_OPEN_BLOCK ProtocolNextOpen; // used by protocol's OpenQueuePFILE_OBJECT FileObject; // created by operating systemBOOLEAN Closing; // TRUE when removing this structBOOLEAN Unloading; // TRUE when processing unloadBOOLEAN NoProtRsvdOnRcvPkt; // Reflect the protocol_optionsNDIS_HANDLE CloseRequestHandle; // 0 indicates an internal close KSPIN_LOCK SpinLock; // guards ClosingPNDIS_OPEN_BLOCK NextGlobalOpen;//// These are optimizations for getting to MAC routines. They are not // necessary, but are here to save a dereference through the MAC block. //SEND_HANDLER SendHandler;TRANSFER_DATA_HANDLER TransferDataHandler;//// These are optimizations for getting to PROTOCOL routines. They are not// necessary, but are here to save a dereference through the PROTOCOL block.//SEND_COMPLETE_HANDLER SendCompleteHandler;TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler; RECEIVE_HANDLER ReceiveHandler;RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler;//// Extentions to the OPEN_BLOCK since Product 1.//RECEIVE_HANDLER PostNt31ReceiveHandler;RECEIVE_COMPLETE_HANDLER PostNt31ReceiveCompleteHandler;//// NDIS 4.0 extensions//RECEIVE_PACKET_HANDLER ReceivePacketHandler;SEND_PACKETS_HANDLER SendPacketsHandler;//// More NDIS 3.0 Cached Handlers//RESET_HANDLER ResetHandler;REQUEST_HANDLER RequestHandler;//// Needed for PnP//UNICODE_STRING AdapterName; // Upcased name of the adapter we are bound to};这张表是一个单向链接表,并且存放了和PNDIS_OPEN_BLOCK->ProtocolCharacteristics一样的数据收发派发函数,当第N块网卡发送数据包到第N个协议时,就会调用第N个协议与第N个网卡之间建立的NDIS_OPEN_BLOCK表里的SendHandler或SendPacketHandler。