转载自:http://blog.sina.com.cn/s/blog_4de78d5901000bfd.html
本人简单的介绍一种更有效的基于NDIS包拦截技术。
大家都知道,NDIS协议驱动程序是通过填写一张NDIS_PROTOCOL_CHARACTERISTICS的表,并调用NDIS API
函数NdisRegisterProtocol进行注册。现在我们来关注一下NDIS_PROTOCOL_CHARACTERISTICS这张表,
这张表中存有所有协议驱动程序与底层的派发函数的入口。
如SendHandler,ReceiveHandler,BindAdapterHandler等,当网卡有数据包进入时,会通过表中ReceiveHandle 或ReceivePacketHandler通知协议驱动程序有一个该协议
的数据包进入,反之协议驱动程序是通过SendHandler或SendPacketsHandler函数向网卡驱动发送数据包到网络上去的,有人会奇怪程序中明明不是调用NdisSend或NdisSendPackets函数发送的吗?没错,是这样的,但是你可以看一下NDIS.H的头文件里对这两个函数的定义就知道了,他们都是一个宏定义实际还是通过这表中SendHandler或SendPacketsHandler发送的。
#define NdisSend(Status, NdisBindingHandle, Packet) \ { \ *(Status) = \ (((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.SendHandler)( \ ((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.BindingHandle, \ (Packet)); \ } #define NdisSendPackets(NdisBindingHandle, PacketArray, NumberOfPackets) \ { \ (((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.SendPacketsHandler)( \ (PNDIS_OPEN_BLOCK)(NdisBindingHandle), \ (PacketArray), \ (NumberOfPackets)); \ }
现在我们所要做的事情应该很清楚了,只要我们能够将每一个协议程序所填写的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 };
以上struct _NDIS_PROTOCOL_BLOCK的定义为网友提供,在DDK的安装包里并没有搜索到,只是搜索到了以下:(它存在于nids.h中)
typedef struct _NDIS_PROTOCOL_BLOCK NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLOCK;
EXPORT VOID NdisRegisterProtocol( __out PNDIS_STATUS Status, __out PNDIS_HANDLE NdisProtocolHandle, __in PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics, __in UINT CharacteristicsLength );
/*注意NDIS_HANDLE所指向的就是PNDIS_PROTOCOL_BLOCK的结构,不要有什么怀疑。*/
NDIS_PROTOCOL_BLOCK(协议表) 是NDIS维护所有系统中已注册协义的单向链接表。字段NextProtocol指向下一个协议表。
庆幸的是,当我们注册一新的协议时,NDIS总是会把新注册的协义放在链表的头并返回这张表,所以只要我们注册一个新的协议,通过新协议注册返回的链表头就可以轻而易举的遍历系统中所有协议表。
eg: 以下是一个注册自己的协议的函数
NDIS把新注册的协义放在链表的头并返回这张表,新表指针为phProtocolHandle
NTSTATUS RegisterFakeProtocol( OUT PNDIS_HANDLE phProtocolHandle, IN PUCHAR pszProtocolName ) { NDIS40_PROTOCOL_CHARACTERISTICS ndis40pcFakeProtCharacts; NDIS_STATUS nsRegStatus; // Prepare the Protocol Characteristics structure. memset( & ndis40pcFakeProtCharacts, 0, sizeof( ndis40pcFakeProtCharacts ) ); ndis40pcFakeProtCharacts.MajorNdisVersion = 0x4; ndis40pcFakeProtCharacts.MinorNdisVersion = 0; ndis40pcFakeProtCharacts.Reserved = 0; ndis40pcFakeProtCharacts.OpenAdapterCompleteHandler = & FakeProtocol_OpenAdapterComplete; ndis40pcFakeProtCharacts.CloseAdapterCompleteHandler = & FakeProtocol_CloseAdapterComplete; ndis40pcFakeProtCharacts.SendCompleteHandler = & FakeProtocol_SendComplete; ndis40pcFakeProtCharacts.TransferDataCompleteHandler = & FakeProtocol_TransferDataComplete; ndis40pcFakeProtCharacts.ResetCompleteHandler = & FakeProtocol_ResetComplete; ndis40pcFakeProtCharacts.RequestCompleteHandler = & FakeProtocol_RequestComplete; ndis40pcFakeProtCharacts.ReceiveHandler = & FakeProtocol_Receive; ndis40pcFakeProtCharacts.ReceiveCompleteHandler = & FakeProtocol_ReceiveComplete; ndis40pcFakeProtCharacts.StatusHandler = & FakeProtocol_Status; ndis40pcFakeProtCharacts.StatusCompleteHandler = & FakeProtocol_StatusComplete; NdisInitializeString( & ndis40pcFakeProtCharacts.Name, pszProtocolName ); ndis40pcFakeProtCharacts.ReceivePacketHandler = & FakeProtocol_ReceivePacket; ndis40pcFakeProtCharacts.BindAdapterHandler = & FakeProtocol_BindAdapter; ndis40pcFakeProtCharacts.UnbindAdapterHandler = & FakeProtocol_UnbindAdapter; ndis40pcFakeProtCharacts.PnPEventHandler = & FakeProtocol_PnpEvent; ndis40pcFakeProtCharacts.UnloadHandler = & FakeProtocol_UnloadProtocol; // Register the Fake Protocol with the Fake Handlers. NdisRegisterProtocol( & nsRegStatus, phProtocolHandle, & ndis40pcFakeProtCharacts, sizeof( ndis40pcFakeProtCharacts ) ); if ( nsRegStatus != NDIS_STATUS_SUCCESS ) { * phProtocolHandle = NULL; return STATUS_UNSUCCESSFUL; } // Return to the caller. return STATUS_SUCCESS; }
以上注册的是NDIO 4.0的协议,从D:\WINDDK\7600.16385.1\inc\ddk\ndis.h(6164)有如下定义:
typedef struct _NDIS40_PROTOCOL_CHARACTERISTICS { UCHAR MajorNdisVersion; UCHAR MinorNdisVersion; USHORT Filler; union { UINT Reserved; UINT Flags; }; OPEN_ADAPTER_COMPLETE_HANDLER OpenAdapterCompleteHandler; CLOSE_ADAPTER_COMPLETE_HANDLER CloseAdapterCompleteHandler; union { SEND_COMPLETE_HANDLER SendCompleteHandler; WAN_SEND_COMPLETE_HANDLER WanSendCompleteHandler; }; union { TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler; WAN_TRANSFER_DATA_COMPLETE_HANDLER WanTransferDataCompleteHandler; }; RESET_COMPLETE_HANDLER ResetCompleteHandler; REQUEST_COMPLETE_HANDLER RequestCompleteHandler; union { RECEIVE_HANDLER ReceiveHandler; WAN_RECEIVE_HANDLER WanReceiveHandler; }; RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler; STATUS_HANDLER StatusHandler; STATUS_COMPLETE_HANDLER StatusCompleteHandler; NDIS_STRING Name; // // Start of NDIS 4.0 extensions. // RECEIVE_PACKET_HANDLER ReceivePacketHandler; // // PnP protocol entry-points // BIND_HANDLER BindAdapterHandler; UNBIND_HANDLER UnbindAdapterHandler; PNP_EVENT_HANDLER PnPEventHandler; UNLOAD_PROTOCOL_HANDLER UnloadHandler; } NDIS40_PROTOCOL_CHARACTERISTICS;
需要说明的在其实nids.h中都只是定义NDIS 3.0 协议:
如D:\WINDDK\3790.1830\inc\ddk\wxp\ndis.h(9370):
typedef struct _NDIS30_PROTOCOL_CHARACTERISTICS { UCHAR MajorNdisVersion; UCHAR MinorNdisVersion; USHORT Filler; union { UINT Reserved; UINT Flags; }; OPEN_ADAPTER_COMPLETE_HANDLER OpenAdapterCompleteHandler; CLOSE_ADAPTER_COMPLETE_HANDLER CloseAdapterCompleteHandler; union { SEND_COMPLETE_HANDLER SendCompleteHandler; WAN_SEND_COMPLETE_HANDLER WanSendCompleteHandler; }; union { TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler; WAN_TRANSFER_DATA_COMPLETE_HANDLER WanTransferDataCompleteHandler; }; RESET_COMPLETE_HANDLER ResetCompleteHandler; REQUEST_COMPLETE_HANDLER RequestCompleteHandler; union { RECEIVE_HANDLER ReceiveHandler; WAN_RECEIVE_HANDLER WanReceiveHandler; }; RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler; STATUS_HANDLER StatusHandler; STATUS_COMPLETE_HANDLER StatusCompleteHandler; NDIS_STRING Name; } NDIS30_PROTOCOL_CHARACTERISTICS;
顺便说一句NDISREGISTERPROTOCOL为NDIS_PROTOCOL_BLOCK所分配的内存是NonPagedPool类型的。对于核心DRIVER来说,核心区内存是一个线性的内存区,所有核心DRIVER是可以随便访问核心内存区的任意地址。所要注意的是不同IRQL级别下对分页和非分页内存。
有人会问这样就行了吗?真的拦截下来了吗?如果有那位仁兄心急现在就写程序的话,准会失望的,因为他会发现结果什么东西都没拦截到或偶而会拦截到一些数据包。为什么?
因为NDIS网卡驱动和协议驱动在发送和接收到数居时并不是调用PNDIS_OPEN_BLOCK->ProtocolCharacteristics里的派发函数。怎么办?
有必要先介绍一下NDIS网卡驱动和协议驱动之间是如何BINDING 的吧:
(1)NdisRegisterProtocol在注册完一个协议后,不久NDIS会通过调用表中BindAdapterHandler派发函数,通知协议对每一个网卡进行BINDING。或者当系统通PNP找到一块新的网卡时也会调用BindAdapterHandler对协议进行BINDING;
(2)协议在BINDING 调用里,会根据自己的需要使用NdisOpenAdapter将自身绑定到适合的网卡。并返回NdisBindingHandle.NdisBindingHandle。
(2、1)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 PNDIS_OPEN_BLOCK NextGlobalOpen; BOOLEAN Closing; // TRUE when removing this struct BOOLEAN Unbinding; // TRUE when starting to unbind the adapter BOOLEAN NoProtRsvdOnRcvPkt; // Reflect the protocol_options BOOLEAN ProcessingOpens; PNDIS_STRING BindDeviceName; KSPIN_LOCK SpinLock; // guards Closing PNDIS_STRING RootDeviceName; // // These are optimizations for getting to MAC routines. They are not // necessary, but are here to save a dereference through the MAC block. // union { SEND_HANDLER SendHandler; WAN_SEND_HANDLER WanSendHandler; }; 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. // union { RECEIVE_HANDLER PostNt31ReceiveHandler; WAN_RECEIVE_HANDLER WanReceiveHandler; }; 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; RESET_COMPLETE_HANDLER ResetCompleteHandler; STATUS_HANDLER StatusHandler; STATUS_COMPLETE_HANDLER StatusCompleteHandler; REQUEST_COMPLETE_HANDLER RequestCompleteHandler; };
上面的表结构可以很清楚的看到这张表是一个单向链接表,并且存放了和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、不要重复勾挂同一个函数。