阅读:10973回复:18
网络数据包拦截通用技术看到很多仁兄提供的数据包的拦截技术,其中最多的是编写IM DRIVER在NDIS中间层 对MINIPORT(网卡驱动程序)和协议驱动程序之间的数据包进行拦截。这是微软提供的一种技术 但编写该过滤程序拦截程序非常的复杂,安装也很麻烦。 本人简单的介绍一种更有效的基于NDIS包拦截技术。 大家都知道,NDIS协议驱动程序是通过填写一张NDIS_PROTOCOL_CHARACTERISTICS的表,并调用NDIS API 函数NdisRegisterProtocol进行注册。现在我们来关注一下NDIS_PROTOCOL_CHARACTERISTICS这张表, 这张表中存有所有协议驱动程序与底层的派发函数的入口。如SendHandler,ReceiveHandler,BindAdapterHandler等, 当网卡有数据包进入时,会通过表中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。不要重复勾挂同一个函数。 有问题可以通过 QQ:3955727 mail:gjpland@netease.com 一起来讨论 |
|
|
沙发#
发布于:2001-09-14 14:31
如果能够写个简单的例子就好了,写上注释,那么你的方法会有更多的人接受,并且使用!
|
|
|
板凳#
发布于:2001-09-14 14:41
:)
已经有很多网络防火墙的产品都在使用这种方法的,这种方法其实已经太老了。比如说NORTON等。 |
|
|
地板#
发布于:2001-09-14 14:59
有想象力,有例子程序吗?能否让大家伙儿观摩一下,好长点见识!
|
|
|
地下室#
发布于:2001-09-14 15:13
不好意思,我做的是公司产品。只能说一些技术,不能给出代码了。:(
|
|
|
5楼#
发布于:2001-09-14 17:07
做firewall的确是不错,不过这里的人用imd几乎都用来做vpn。
做firewall,我觉得用tdifilter似乎更好,因为层次更高, 不需要组包,做连接跟踪之类的更容易。 |
|
|
6楼#
发布于:2001-09-14 17:15
如下两个疑问:
1如何支持pnp? 2何时是加载这个协议驱动的时机? |
|
|
7楼#
发布于:2001-09-14 17:33
对于FIREWALL来说, 最佳实现方式是NDIS层,于TDI双层过滤。
对于TDI拦截技术相对来说简单多了只要对 \Device\Tcp \device\udp \device\ip做一个FILTER DRIVER就能 对所有基于应用层的网络数据包进行拦截。 但是对于一此syn flood ,ICMP攻击来说,TDI是无能为力的。 1如何支持pnp? 当系统有一个新的网卡设备 加入时,NDIS会调用每一个协议驱动的BINDADAPTERHANDLER的。 2何时是加载这个协议驱动的时机 自启动。 那要看你目的了如果是针对TCPIP 把驱动程序的依懒关系设为TCPIP。SYS。 |
|
|
8楼#
发布于:2001-09-15 09:13
高,能否告知,你获得这些知识的途径?
我们不仅想获得一块金子,更想获得一个点石成金的手指。 |
|
|
9楼#
发布于:2001-09-15 22:54
知识的途径很多啊,INTERNET,开发经验,书,分析程序等。
只要有心这些纯技术性的知识是很容易获得的。 |
|
|
10楼#
发布于:2001-09-16 20:01
1、我说的pnp并非说的网卡,我说的是协议。
2、当动态增加卸载tcp/ip协议的时候,如果 不能获得这个信息,那么引用不存在的函数入口 意味着core dump。 |
|
|
11楼#
发布于:2001-09-16 21:28
1.
当协议即时的加入由于没有通知事件这是一件比较麻烦的事情。 现在的触决方法是在每次有网络数据进出时。开一个工作队列, 来进行检测是否有新的协议加入,一般来说,遍历一边协议的链表 是很快的。 2. 不会啊。 加载时是通过,ndis_protocol_block进行注册协议遍历也就是说要是没有TCP/IP是话,也就找不到TCP/IP的ndis_protocol_block。 由于所有的勾子函数都是通过Ndis_open_block 勾挂的。 也就是说当TCP/IP动态加载时是通过修改NDIS_OPEN_BLOCK里的派发函数表进行的,所有的勾子函数都是被NDIS被动的调用,所以当TCP/IP unload时,又怎么会引用不存在的函数入口呢? 如果你不放心的话,你也可以勾挂,ndis_protocol_characteristics里的UnloadHandler派发函数。 |
|
|
12楼#
发布于:2001-09-25 13:37
win2k下 struct _NDIS_PROTOCOL_BLOCK 没有定义,跟踪发现,第五个DWORD为 NextProtocol,然后是ProtocolCharacteristics,与NT4下的显然不一样,不知哪里有win2k下的定义?
|
|
13楼#
发布于:2001-09-25 13:52
win2k下有_NDIS_PROTOCOL_BLOCK的定义。在meddk下的NDIS。H里有定义。
每一个NDIS版本内的数据结构是不同的,所以是要注意的。 |
|
|
14楼#
发布于:2002-10-03 05:32
我发现一个有趣现象.将网卡先插入再拔出,然后再勾挂openqueue,这时候openqueue就不起做用了(我使用动态加载的driver). 发现顺序是着样的:
1当网卡加入时,有一个NDIS_OPEN_BLOCK 生成并被加入openqueue链中 2.当网卡被拔出时, 该网卡的NDIS_OPEN_BLOCK无效了.可是openqueue还指向这个NDIS_OPEN_BLOCK! 不知是我搞错了还是确实如此,有什么办法解决这个问题吗? |
|
|
15楼#
发布于:2002-10-03 10:48
我发现一个有趣现象.将网卡先插入再拔出,然后再勾挂openqueue,这时候openqueue就不起做用了(我使用动态加载的driver). 发现顺序是着样的: 在这个时候你应该准确勾挂ProtocolBindAdapter 和ProtocolUnbindAdapter两个接口,而不是去关注OPEN_BLOCK本身 具体操作: ProtocolBindAdapter中,首先调用原先的协议表中的ProtocolBindAdapter此时,原先协议会在ProtocolBindAdapter上 NdisOpenBlock。完成后你在遍历一边该协议的NDIS_OPEN_BLOCK队历 完成勾挂新打开的OPEN_BLOCK 在ProtocolUnbindAdapter中, 你应该恢复要被卸载的OPEN_BLOCK内的勾持内容。(但似乎这个动作不是必须的),完成后调用原先协议表中的ProtocolUnbindAdapter |
|
|
16楼#
发布于:2002-10-11 10:20
该篇文章有不妥处,请以范例代码为准。
|
|
|
17楼#
发布于:2003-01-10 11:40
非常非常感谢版主的这篇文章,让我拨开云雾见了青天,豁然开朗了!!!您救了我的一条小命啊:_)~~~~~我真不知该如何感谢您~~~~~~~~~~~~
[编辑 - 1/10/03 by ABUABU] |
|
|
18楼#
发布于:2003-12-31 11:35
除了用TDI和NDIS,以及两者结合,还有没有更好的方法?目前来说。
|
|
|