当网卡有数据包进入时,会通过表中ReceiveHandle 或ReceivePacketHandler通知协议驱动程序有一个该协议的数据包进入,反之协议驱动程序是通过SendHandler或SendPacketsHandler函数向网卡驱动发送数据包到网络上去的,有人会奇怪程序中明明不是调用NdisSend或NdisSendPackets函数发送的吗?没错,是这样的,但是你可以看一下NDIS。H的头文件里对这两个函数的定义就知道了,他们都是一个宏定义实际并通过这表中SendHandler或SendPacketsHandler发送的。
现在我们所要做的事情应该很清楚了,只要我们能够将每一个协议程序所填写的NDIS_PROTOCOL_CHARACTERISTICS表里的派发函数指向自己的函数,我们就能成功的对数据包进行拦截。那么每个协议驱动程序的这张表到底存放在那里呢?看下面的对NdisRegisterProtocol重新给出的原型就很明白了。
struct _NDIS_PROTOCOL_BLOCK { PNDIS_OPEN_BLOCK OpenQueue; // queue of opens for this protocol REFERENCE Ref; // contains spinlock for OpenQueue UINT Length; // of this NDIS_PROTOCOL_BLOCK struct NDIS50_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics;// handler addresses struct _NDIS_PROTOCOL_BLOCK * NextProtocol; // Link to next ULONG 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 requests PKEVENT DeregEvent; // Used by NdisDeregisterProtocol #endif }; typedef struct _NDIS_PROTOCOL_BLOCK NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLOCK; EXPORT VOID NdisRegisterProtocol( OUT PNDIS_STATUS Status, OUT PNDIS_PROTOCOL_BLOCK NdisProtocolHandle, /*注意NDIS_HANDLE所指向的就是PNDIS_PROTOCOL_BLOCK的结构,不要有什么怀疑。*/ IN PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics, IN UINT CharacteristicsLength ); |
NDIS_PROTOCOL_BLOCK(协议表) 是NDIS维护所有系统中已注册协义的单向链接表。字段NextProtocol指向下一个协议表。
庆幸的是,当我们注册一新的协议时,NDIS总是会把新注册的协义放在链表的头并返回这张表,所以只要我们注册一个新的协议通过新协议注册返回的链表头就可以轻而易举的遍历系统中所有协议表.现在我们所希望得到的每个协议的NDIS_PROTOCOL_CHARACTERISTICS表就放在我们面前了,如何勾挂表中的派发函数,我想不必多说了吧。顺便说一句NDISREGISTERPROTOCOL为NDIS_PROTOCOL_BLOCK所分配的内存是NonPagedPool类型的。对于核心DRIVER来说,核心区内存是一个线性的内存区,所有核心DRIVER是可以随便访问核心内存区的任意地址。所要注意的是不同IRQL级别下对分页和非分页内存。
有人会问这样就行了吗?真的拦截下来了吗?如果有那位仁兄心急现在就写程序的话,准会失望的,因为他会发现结果什么东西都没拦截到或偶而会拦截到一些数据包。为什么?
因为NDIS网卡驱动和协议驱动在发送和接收到数居时并不是调用PNDIS_OPEN_BLOCK->ProtocolCharacteristics里的派发函数。怎么办?
有必要先介绍一下NDIS网卡驱动和协议驱动之间是如何BINDING 的吧,NdisRegisterProtocol在注册完一个协议后,不久NDIS会通过调用表中BindAdapterHandler派发函数,通知协议对每一个网卡进行BINDING。或者当系统通PNP找到一块新的网卡,也会调用BindAdapterHandler对协议进行BINDING。协议在BINDING 调用里,会根据自己的需要使用NdisOpenAdapter将自身绑定到适合的网卡。并返回NdisBindingHandle.NdisBindingHandle是什么?NdisBindingHandl其实是指向NDIS_OPEN_BLOCK表的一根指针,那么NDIS_OPEN_BLOCK表有什么用呢?当协议顺利的绑定后,每个绑定的网卡和每一个协议之间建立了数据传输的通道,而NDIS_OPEN_BLOCK就是用来维护这一数据通道的表。
struct _NDIS_OPEN_BLOCK { PNDIS_MAC_BLOCK MacHandle; // pointer to our MAC NDIS_HANDLE MacBindingHandle; // context when calling MacXX funcs PNDIS_ADAPTER_BLOCK AdapterHandle; // pointer to our adapter PNDIS_PROTOCOL_BLOCK ProtocolHandle; // pointer to our protocol NDIS_HANDLE ProtocolBindingContext;// context when calling ProtXX funcs PNDIS_OPEN_BLOCK AdapterNextOpen; // used by adapter\''''s OpenQueue PNDIS_OPEN_BLOCK ProtocolNextOpen; // used by protocol\''''s OpenQueue PFILE_OBJECT FileObject; // created by operating system BOOLEAN Closing; // TRUE when removing this struct BOOLEAN Unloading; // TRUE when processing unload BOOLEAN NoProtRsvdOnRcvPkt; // Reflect the protocol_options NDIS_HANDLE CloseRequestHandle; // 0 indicates an internal close KSPIN_LOCK SpinLock; // guards Closing PNDIS_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。所以我们还需要对这张表里的派发函数进行处理(勾挂)。
那么又如何勾挂协议与网卡之间的NDIS_OPEN_BLOCK表呢。我们再回到NDIS_PROTOCOL_BLOCK这张表中,在NDIS_PROTOCOL_BLOCK表中字段PNDIS_OPEN_BLOCK OpenQueue;就是所有该协议所有NDIS_OPEN_BLOCK的表头。
通过AdapterNextOpen遍历一下,再勾挂一把。就可以顺利拦截了。
值得注意的是。
1、NDIS_OPEN_BLOCK
NDIS_PROTOCOL_BLOCK这些结构不同NDIS版本是不同的,解决方法是在windows 98和windows95下(ndis 3.1)使用windows98ddk 带的NDIS.H 里的定义在windows me下(ndis 5.0或4。0)请使用WINDOWS 98ddk里NDIS.H里的定义nt(ndis4.0)用NTDDK里的定议,以此类推,2000(ndis5.0)
2、不要重复勾挂同一个函数。
http://www.gjpsoft.com/ndishook1.c此部分为NT部分的源代码。要使用在9X下需要修改,因为没有整体分开,一些函数和头文件未给出,但不影响框架请阅读者自己修改。
评论