NDIS网络驱动程序学习(一)
关于网络编程,大家用的比较多的就是SOCKET。其中呢,SOCKET分TCP,UDP,原始套接字。
当然,TCP,UDP套接字是大家用的最多的,也是最常见的。也是学起来最简单的。
而微软向来不是很喜欢让我们付出很少的努力就能够实现很imba的功能,因此我们的编程受到了很大的限制,比如说我想构建自己的IP包。不过呢,微软还是提供了我们实现相关功能的接口,那就是原始套接字。很明显,原始套接字相比就稍微难一些,但是却能够实现很多令人赞赏的功能,比如DDOS攻击工具等等。
令人失望的是,原始套接字也只能让程序员封装到IP头就限制住了,因此你无论如何无法通过原始套接字构建ARP攻击包!无法编写出嗅探器。而这些应用往往是我们最喜欢的。因此就只能去学了。
(Iphlpapi.dll动态库里包含一些函数,能够实现一些比较实用的功能。具体自己去探索。)
既然用户层能干的事情太少,那么我们有必要进入内核层。这样我们就可以为所欲为了。不过呢,你还得时刻惦记着微软,也就是说,你在编程的时候必须符合微软的规范。就内核层而言,微软只提供了我们2个规范,分别是TDI和NDIS。
TDI(网络传输层接口)其实是一套规范。用户层调用SOCKET的相关函数,最终都会转变成afd.sys里的相关函数被调用。(和CreateFile----NtCreateFile道理一样)而我们知道,我们使用SOCKET的时候只会提供需要发送的数据,而系统会有一系列的封包操作,这些封包操作代码被封装在Tcpip.sys中。因此afd.sys中的内核函数都会和TCPIP.SYS打交道。另外,在系统中,NetBios.sys也会跟TCPIP.SYS打交道,那么就需要统一一下“打交道”的方式,用一个“白皮书”来规范这些行为。这样一来,TDI诞生了。
关于TDI,我们不打算去研究,现在把精力集中到NDIS上。
那么什么是NDIS呢?其实它也是一套规范,一套相当复杂的规范,另一方面,微软为了维持这样一个规范,就必须有一个模块来管理和监督此规范的实行情况,因此,你还可以把NDIS理解成管理器。它和I/O管理器一个德行。你完全可以用理解I/O管理器的方式理解NDIS管理器。
既然是规范,那么NDIS究竟想规范谁呢?答案是(从上层到下层):协议驱动,中间层过滤驱动,小端口驱动。
关于协议驱动,我们已经有所涉及,就是上面提及的TCPIP.SYS。从网络七层模型来看的话,此驱动程序做了很多事情,涉及到N多层的封包工作。当然,你也可以编写一个自己的协议驱动程序,只要有足够的想象力,你可以构建任意的包。这确实是一个很好的消息,因为此时此刻你再也没有限制了,你可以构建一个ARP攻击包!
中间层过滤驱动在协议驱动和小端口驱动之间,可以截获他们之间所有的数据。因此过滤驱动往往可以做防火墙,网络透明加密等工作。
小端口驱动主要和网卡硬件打交道,直接控制网卡(但很多时候这也不是必须的),以后会详解。
可以发现,这3类驱动之间总是有着千丝万缕的联系,比如协议驱动封包后就必须靠小端口驱动发出去,同时小端口从网络上接收到数据包后也必须发给协议驱动并一步步传递到应用层。那么他们之间的交流也必须符合一定的规范。OK,这就是NDIS规范。
下一节开始,我们就来分别介绍这3类驱动。
NDIS网络驱动程序学习(二)---NDIS协议驱动
什么是NDIS驱动呢?符合微软NDIS规范的,被NDIS管理器所管理的驱动程序,我们就叫它NDIS驱动。而NDIS协议驱动就是其中的一种类型。
所谓的符合NDIS规范的意思就是,你在编程的时候,要符合特定的编程规范,比如使用NDIS开头的API函数等等。那么如何才能接受NDIS管理器的管理呢?很简单,你只要注册就成了。注册的目的就是告诉操作系统,我这个驱动就是NDIS协议驱动了,以后呢,需要你NDIS管理器多多关照。和I/O管理器理解是一样的。
现在开始代码的分析部分。以寒江独钓里的代码为主。
在DriverEntry里的代码比较简单,一共做了2个比较重要的事情。
1,注册了一个设备,并指定了分发函数,这样为应用层留下了控制此驱动程序的机会。
2,注册自身为NDIS协议驱动。 现在分析具体细节
protocolChar.MajorNdisVersion = 5;
protocolChar.MinorNdisVersion = 0;
protocolChar.Name =protoName;
protocolChar.OpenAdapterCompleteHandler = NdisProtOpenAdapterComplete;
protocolChar.CloseAdapterCompleteHandler =NdisProtCloseAdapterComplete;
protocolChar.SendCompleteHandler = NdisProtSendComplete;
protocolChar.TransferDataCompleteHandler =NdisProtTransferDataComplete;
protocolChar.ResetCompleteHandler = NdisProtResetComplete;
protocolChar.RequestCompleteHandler =NdisProtRequestComplete;
protocolChar.ReceiveHandler = NdisProtReceive;
protocolChar.ReceiveCompleteHandler =NdisProtReceiveComplete;
protocolChar.StatusHandler = NdisProtStatus;
protocolChar.StatusCompleteHandler =NdisProtStatusComplete;
protocolChar.BindAdapterHandler = NdisProtBindAdapter;
protocolChar.UnbindAdapterHandler = NdisProtUnbindAdapter;
protocolChar.UnloadHandler = NULL;
protocolChar.ReceivePacketHandler = NdisProtReceivePacket;
protocolChar.PnPEventHandler =NdisProtPnPEventHandler;
上面这一堆,是我们需要关心的,说明如下:(讲解顺序是乱的,也必须乱)
1,告诉系统,本协议驱动使用的是哪个版本的NDIS,可以发现,我们现在使用的是5.0版本。需要说明的是,不同版本的回调函数并不相同,上面列举的回调函数是5.0版本所规定的。但是呢,高版本的NDIS管理器是支持低版本的NDIS驱动的,因此完全没有必要担心5.0的驱动不能用在6.0版本的NDIS系统上。
2,BindAdapterHandler:这个回调函数在什么时候调用呢?当一个真实网卡插入电脑后被小端口驱动识别并生成了一个“网卡设备”的时候,或者小端口驱动生成了一个“虚拟网卡设备”后,NDIS管理器调用这个回调函数来让NDIS协议驱动有机会来绑定被小端口驱动创建的“网卡设备”。以后呢,我们会称呼这些“网卡设备”为“实例”。
3,OpenAdapterCompleteHandler:当一个“实例”出现后,协议驱动都会去绑定它,那么很明显,绑定操作也是有个过程的,而系统内部很多时候都是异步模式。这个回调函数的作用就是:当协议驱动调用NdisOpenAdapter来进行绑定后,当绑定成功了,NDIS管理器会调用这个回调函数来通知此协议驱动已经绑定完成。
4,SendCompleteHandler:在协议驱动中可以调用NdisSendPackets来进行对数据包的发送。那么,发送数据包也是会有个过程的,和上面理解是一样的,此回调也是作为完成通知存在的。为什么没有发送回调呢?呵呵,发送操作本来就是“主动”的。没有必要和意义存在所谓的发送回调!
5,NdisProtCloseAdapterComplete:同理,当协议驱动调用NdisCloseAdapter来解除绑定的时候,作为解除成功的通知,此回调函数必须存在。
6,ReceiveHandler和ReceivePacketHandler:当NDIS管理器发现小端口“实例”有数据包到来了,那么这2个函数就会被调用。至于调用哪一个,下面详细分析。
7,UnbindAdapterHandler:当机器上一个物理网卡被拔出,或者小端口生成的“虚拟网卡”被消除,那么NDIS管理器会调用此回调函数来让协议驱动有机会来解除对这个网卡实例的绑定。通常,这个回调函数里会调用NdisCloseAdapter。并且呢,当解除绑定成功后,NDIS还会调用NdisProtCloseAdapterComplete来让协议驱动知道已经解除绑定成功。
8,其他的一些回调理解起来并不那么难,以后在分析具体的代码的时候再进行分析。
等把这些回调函数一个个的指定好了之后,我们就可以调用NdisRegisterProtocol了。此函数的作用非常重要,从效果上讲,他能够注册本驱动为NDIS协议驱动,从而可以接受NDIS管理器的管理和服务。从内部实现方面讲:此函数会在内核中创建一个非常重要的数据结构并记录下这些回调函数的地址,以后方便NDIS管理器调用。这和普通的Driver_Object理解上是相似的。
以后谈及NDIS过滤驱动的时候,你还会发现,一个驱动程序对应这3个重要的结构:普通的驱动结构,NDIS协议驱动结构,NDIS小端口驱动结构。而这些结构里都包含自己的一套回调函数,分别是:普通的分发函数,NDIS协议特征回调函数,NDIS小端口特征回调函数。而我们要做的就是好好理解透彻这些回调是如何协同工作的。一旦这些理解了,NDIS也就不难了。