by fanxiushu 2019-01-30 转载或引用请注明原始作者。

提到NDIS协议驱动,可能比较陌生,因为毕竟用得挺少的。
但是一提到WireShark或ethereal等抓包软件,大家就不再陌生了。
这些抓包软件在windows平台大都使用的是winpcap接口库,winpcap应该也不陌生。
至少在我大学期间就开始接触到了这个接口库。
只是当时对它的实现原理并不熟悉,也就只会简单调用它提供的接口来抓包玩,即便如此,当时也觉得挺神奇的。
这么多年过去,随着对操作系统底层的了解,对网络底层的认识,WINPCAP变得不再那么神秘。
winpcap在驱动层其实就是NDIS协议驱动来实现网络数据包截获的,只是它很早就开发出来了,使用的是NDIS5的框架。
同时支持 WIN2000,winxp,win7,win8,win10等平台,甚至还能支持很早前的win9X平台。
目前NDIS5 框架的协议驱动,NDIS5 框架的网卡驱动,支持WIN8, WIN10这些最新的平台,
也就是WINPCAP在win10同样工作的很好。
NDIS5框架的中间层驱动,在WIN10上会有些问题。

大概在好几年前,就已经开发了NDIS的三大驱动,不过采用的全是 NDIS5.1框架。
开发NDIS虚拟网卡驱动来实现虚拟局域网,开发NDIS协议驱动来实现虚拟局域网的桥接,
开发NDIS中间驱动和TDI驱动来实现NAT功能和防火墙。
这在CSDN上,我的很早前的文章和资源下载中,都能看到一些这方面的影子。

现在讨论的是基于NDIS6 框架的协议驱动。
随着win7平台,微软对内核网络通讯的全新架构,已经与以前的系统发生了翻天覆地的变化。
整个网络内核变得更加复杂,同时功能也更加强大。
WFP替代了WINXP中的TDI,关于WFP的介绍,可以参看我下面链接中的文章:
https://blog.csdn.net/fanxiushu/article/details/78221340   ( Windows7以上使用WFP驱动框架实现IP数据包截取(一))
https://blog.csdn.net/fanxiushu/article/details/78347137  (  Windows7以上使用WFP驱动框架实现IP数据包截取(二))
 
 NDIS中用于承载数据包的数据结构不再是 NDIS_BUFFER,NDIS_PACKET,
而是 NET_BUFFER, NET_BUFFER_LIST。
NET_BUFFER_LIST,NET_BUFFER都是单链表,多个NET_BUFFER_LIST可以使用数据结构里边的Next连接成一个串,
同样的,NET_BUFFER也可以连成一串,每个NET_BUFFER_LIST可以包含一个或多个NET_BUFFER。
多个NET_BUFFER连成串之后,最后挂载到NET_BUFFER_LIST中的FirstNetBuffer上。
NET_BUFFER里边又可以包含一个MDL或多个MDL,MDL其实也是单链表,也可以形成MDL链。
MDL形成串之后,最后挂载到NET_BUFFER中的MdlChain上。
具体的描述请仔细查阅MSDN文档或WDK驱动开发包相关的头文件声明。

MDL里边存储的就是具体的数据包。
具体来说,NET_BUFFER存储就是一个完整的网卡数据包,用于在物理网上传输用的,比如以太网卡,基本都是不超过1514的数据包。
而每个NET_BUFFER也可能包含多个MDL,这种情况基本是在通讯协议栈比如TCP/IP协议栈对数据包进行分析处理造成的,
比如可能把一个完整数据包拆成 MAC网卡头,IP头,TCP头,用户数据内容;这些数据分别存储到不同MDL中,
从而一个NET_BUFFER中出现多个MDL。可以使用NdisGetDataBuffer一次性获取所有这些数据,
或者对每个MDL调用 MmGetSystemAddressForMdlSafe 获取数据之后,再把这些数据拼接起来。

再来看看NDIS6协议驱动的开发流程。
这些流程以及NET_BUFFER_LIST等的阐述都在介绍NdisFilter驱动的时候简单介绍过。详见下面的连接:
https://blog.csdn.net/fanxiushu/article/details/86516610 (Windows7以上平台 NdisFilter网卡过滤驱动开发)

在DriverEntry入口函数中,初始化 NDIS_PROTOCOL_DRIVER_CHARACTERISTICS   数据结构。
然后调用 NdisRegisterProtocolDriver注册我们的协议。
数据结构里边有个Name参数,系统会根据Name来识别我们的协议还是别的协议驱动。
重点是里边的回调函数接口,其实回调函数并不多,也就八,九个回调函数。
比起同样是NDIS的网卡驱动来说回调函数挺少的,也挺好理解。至少网卡驱动还会牵涉到硬件相关的比如中断之类的操作。
比起NdisFilter驱动和NDIS中间驱动来说,就更显得简单了。

我们先把回调函数分成以下几大类。
一,初始化回调函数,就是里边的 Bind相关函数。具体是
   ProtocolBindAdapterEx, 绑定到某块网卡
   ProtocolUnbindAdapterEx,解绑某块网卡
   ProtocolOpenAdapterCompleteEx,打开某块网卡,已经完成
   ProtocolCloseAdapterCompleteEx。
关闭某块网卡,已经完成
 

二,Oid请求完成函数和PNP事件和状态报告函数。
       ProtocolRequestComplete ,调用NdisOidRequest函数发送OID请求完成,
       ProtocolStatusEx, 指示网卡状态变化,比如连接状态的改变等
       ProtocolPnPEventHandler,网卡的PnP事件,比如网卡是否重启,暂停,电源是否关闭等

三,数据包接收或发送完成回调函数。
      ProtocolReceiveNetBufferLists, 接收到网卡发来的数据包
      ProtocolSendComplete, 调用NdisSendBufferLists函数完成之后被调用。

当使用inf配置文件安装我们的NDIS协议驱动的时候,系统就会把相关信息写到注册表数据库中,也会在
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\XXXX\Linkage
的UpperBind字段中添加我们的NDIS协议名,比如取名 FanxiushuNdisProto,
(名字不能有下划线,因为平时习惯了取名带下划线。测试了半天才发现的问题)
这个名字必须在inf配置文件中对应名字和NDIS_PROTOCOL_DRIVER_CHARACTERISTICS 里边的Name名字保持一致。
 
 这里还有一个奇特的地方,我们一般使用inf安装的驱动,操作系统会根据情况,自动把它运行起来,
而NDIS协议驱动就比较奇特,必须手动把驱动运行起来。等于是一个介于传统NT方式驱动和PnP驱动的中间产物。
具体启动方式可以使用命令 “net start 服务名”,或者调用CreateService等API函数。

当驱动运行之后,进入DriverEntry入口函数中,NdisRegisterProtocolDriver 被调用,
系统会从注册表数据库中查找绑定到这个协议驱动的所有网卡,
并且对每块网卡,都调用 ProtocolBindAdapterEx回调函数来指示绑定要求。在这个回调函数中,我们分配自己的资源,
调用 NdisOpenAdapterEx来完成真正的对这块网卡的绑定,
当NdisOpenAdapterEx返回NDIS_STATUS_PENDING,表示绑定操作不能立即完成,
等待真正完成的时候
ProtocolOpenAdapterCompleteEx 回调函数就会被调用。
当某块已经调用了NdisOpenAdapterEx实现真正绑定的网卡被卸载或者禁用或者停止等,

ProtocolUnbindAdapterEx 回调函数就会被调用,在此回调函数中我们调用NdisCloseAdapterEx来真正关闭到这块网卡的绑定。
当NdisCloseAdapterEx返回 NDIS_STATUS_PENDING,表示解绑不能立即完成,
等到真正完成的时候,
ProtocolCloseAdapterCompleteEx回调函数就会被调用。
调用NdisDeregisterProtocolDriver注销我们的协议驱动的时候,所有真正被绑定的网卡对应的
ProtocolUnbindAdapterEx  都会被调用。
同时当有新网卡被添加的时候,ProtocolBindAdapterEx也会同样被调用。

以上就是绑定解绑的大致过程,在NdisRegisterProtocolDriver注册的时候,系统会遍历安装的所有符合这个协议驱动条件的网卡,
并且调用绑定回调函数。之后就根据每块网卡的重启,删除,停止等状态自动调用绑定和解绑函数。
也可以在驱动里调用 NdisReEnumerateProtocolBindings 函数,让系统再次扫描一遍所有安装的网卡,
对没有成功绑定的网卡,会再次调用ProtocolBindAdapterEx绑定回调函数。

这里需要注意的一个问题,也是 NDIS6 和 NDIS5 的一个很大区别,
在NDIS5框架中,使用NdisOpenAdapter函数来打开网卡的绑定,这个函数可以在任何其他地方调用,
不一定非要在 ProtocolBindAdapter回调函数中调用,也一样能成功绑定。
而NDIS6框架中,对NdisOpenAdapterEx函数做了严格限制,它必须在 ProtocolBindAdapterEx回调函数中才能成功调用。
至少NdisOpenAdapterEx第四个参数
BindContext 就必须是 ProtocolBindAdapterEx回调函数传递的参数,
我曾尝试调用NdisOpenAdapterEx时候给BindContext传递NULL,结果系统以蓝屏来回应。
光就这一个参数就已经杜绝了NdisOpenAdapterEx在其他地方调用的可能。
这给以前开发的协议驱动软件带来了不少麻烦,比如winpcap接口库。
同样我以前开发的基于NDIS5的桥接虚拟网络的也是在需要的地方调用NdisOpenAdapter来绑定具体网卡的,
而不是在ProtocolBindAdapter回调函数中,不过我的很好改,无非就是在ProtocolBindAdapterEx中执行真正的绑定,
应用层调用IOCTL查找这个已经绑定的网卡,然后其他地方都跟原来一样的通讯即可,
因为一个协议驱动就只服务一个应用层客户端 。

而winpcap接口库,(WINPCAP源代码并没有仔细去研读,只是大致看了下驱动部分代码)
它是在应用层CreateFile一个接口,就会在驱动层调用NdisOpenAdapter一次。
比如应用层创建两个接口,在驱动就相当于对某块网卡NdisOpenAdapter调用两次,等于绑定了两个协议驱动。
应用层的每个接口都会在驱动层对应的每个协议驱动上截获数据包,并进行过滤等处理。
而且许多抓包软件也是根据winpcap的接口的这种设计来工作的。
解决这个问题也不是没办法,其实也不难处理。
既然NDIS6对每块网卡只能调用一次NdisOpenAdapterEx,并且只能在ProtocolBindAdapterEx回调函数中调用。
那就一切按照NDIS6的规则来做。
我们把这个绑定的唯一的真实的实例称为 NicProto_Parent, 然后模拟出许多虚拟 NicProto_Child 来跟应用层的每个接口对应,
每个NicProto_Child都挂载到 NicProto_Parent中,当在ProtocolReceiveNetBufferLists 回调函数中接收底层网卡的数据包的时候,
对每个 NicProto_Child都执行接收操作,当然每个NicProto_Child都在接收的时候执行不同的过滤条件。
于是问题就这么简单解决了。
WINPCAP一直是NDIS5框架,并且多年前停止更新,有个替代接口叫NPCAP的,不过主要使用的是NDISFilter框架。
只是对此有些疑惑,如果只是作为纯粹的抓包接口,没必要采用阻断方式的Ndisfilter中间层驱动,
毕竟在处理数据包的时候,会延缓数据包的传输,当然现在的电脑硬件基本看不出效率问题。但是心理上总是感觉怪怪的。

当调用NdisOpenAdapterEx打开某块网卡的数据包的时候,我们的协议驱动就可以接收这块网卡发上来的数据包了,
现在我们来关注
ProtocolReceiveNetBufferLists 回调函数,
一般来说,当底层网卡传输上来数据的时候,
ProtocolReceiveNetBufferLists回调函数就会被调用,这个很好理解。
 可是再看这些抓包软件,不单能抓取到从网卡发到系统的数据包,还能抓取到从系统发送出去的数据包。
也就是这个回调函数还能接收到其他地方发来的数据包。

ProtocolReceiveNetBufferLists究竟能接收哪些数据包,是根据对NDIS协议驱动过滤条件设置的,
具体来说就是调用NdisOidRequest来设置 OID_GEN_CURRENT_PACKET_FILTER ,具体介绍请查阅MSDN文档。
要接收所有数据包(包括从系统发给网卡,或者从网卡发到系统的数据包。)
可以设置
NDIS_PACKET_TYPE_PROMISCUOUS 表示把网卡设置为混杂模式,这样所有发给网卡的数据包都能接收到了
(包括从系统发给网卡的,网卡接收到的属于本网卡的,网卡接收到的不属于本网卡的,很显然,这种情况下,网卡非常繁忙)。
也可以设置
NDIS_PACKET_TYPE_ALL_LOCAL 表示所有安装的NDIS协议驱动发给网卡的
(当然也包括tcpip.sys协议驱动),或者从网卡接收的。
不过更偏向设置 ALL_LOCAL,能减轻网卡负担,除非你想抓取局域网中的不属于本网卡的数据包。
ProtocolReceiveNetBufferLists 回调函数不单能接受到从底层传输来的数据包,还能接收从其他协议驱动传输的数据包,
也就是能接收系统通过这个网卡传输的数据包(包括发送和接收两个方向)。
这是跟NDIS中间层驱动和底层网卡驱动有区别的地方。也正是这样的特性,
使得比如windows平台的vmware网络里边的桥接模式,(vmware的网络桥接驱动也是一个NDIS协议驱动)
能让vmware里边的虚拟机设置跟宿主机一样的网段,并且能顺利跟宿主机通讯。

当我们的协议驱动,需要发送数据包到网卡,可以调用 NdisSendNetBufferLists函数,然后底层网卡接收到数据包,
调用NdisMSendNetBufferListsComplete完成数据包的发送的时候,协议驱动中对应的 ProtocolSendComplete 回调函数就会被调用。
我们在这个回调函数中释放在调用NdisSendBufferLists时候分配的NET_BUFFER_LISTS等资源。数据包的发送就完成了。

NdisSendNetBufferLists函数调用的时候,还会判断是否应该朝绑定到同一块网卡的其他协议驱动发送这个数据包,
如果是的话,还会调用其他协议驱动的
ProtocolReceiveNetBufferLists 接收回调函数。

最后我们再来看看回环数据包的问题,
什么是回环数据包?
也就是发给 127.0.0.1 的数据包,这些数据包不会朝具体的网卡发送,最终都会被传递回来。
还有一种是朝本机地址发送的数据包,假设本机地址是192.168.1.100, 朝192.168.1.100发送数据包最终也会被传递回来。

比如A进程创建侦听地址 127.0.0.1,端口1234的socket,B进程朝127.0.0.1 的1234端口发送数据包,
数据包进入操作系统内核,按照一般想法,判断出地址是朝本机发送的,在传输层或者更早之前就应该被弹回来。
但是大部分操作系统还是按照正规流程,进入到网际层也就是IP层,然后再反弹回去。包括windows和linux系统都是这样。
windows的tcpip.sys驱动在网际层判断是回环数据包,自然也就不再朝底层网卡发送,再说127.0.0.1 也没有对应的网卡。
也就是说 tcpip.sys不再调用NdisSendBufferLists函数发送数据包,而是直接把这个回环数据包再朝上层传递回去。
这个时候,很显然,NDIS协议驱动是接收不到回环数据包的。自然也包括NDIS中间层驱动,NDIS网卡驱动也接收不到回环数据包。

要处理这个回环数据包,在WINXP这样的系统,似乎只有TDI可以使用了。
而在WIN7以上的平台使用WFP来抓取回环数据包。
在WFP的IP层挂钩

FWPM_LAYER_INBOUND_IPPACKET_V4/FWPM_LAYER_OUTBOUND_IPPACKET_V4, 然后抓取回环数据包。
(WFP的开发,具体可参阅我的WFP文章
Windows7以上使用WFP驱动框架实现IP数据包截取(一)和
Windows7以上使用WFP驱动框架实现IP数据包截取(二)。)

windows7以上平台NDIS6框架的NDIS协议驱动开发相关推荐

  1. ndis协议驱动开发

    协议驱动的开发流程: 首先,一个协议驱动调用函数ndisRegisterProtocol()先把自己注册为协议驱动,此举的意义告诉windows,我是一个ndis协议驱动,并将约定好的回调函数的列表告 ...

  2. 全志平台boot框架中增加设备驱动过程分析

    全志平台boot框架中增加设备驱动过程分析 在boot启动阶段,大家都知道他的主要目的就是引导uboot,uboot在引导内核,从而让整个系统运作起来.全志的boot阶段,对应平板这一块,它会驱动LC ...

  3. 驱动框架入门之LED-linux驱动开发第4部分-朱有鹏-专题视频课程

    驱动框架入门之LED-linux驱动开发第4部分-5199人已学习 课程介绍         本课程是linux驱动开发的第4个课程,主要内容是驱动框架的引入.通过led驱动框架和gpiolib的这两 ...

  4. 微软驱动模块框架旨在简化Windows驱动开发

    使用微软最近新开源的微软驱动模块框架(DMF),Windows驱动开发者现在有一种更简单的方式创建简单的结构化驱动以及在驱动之间共享代码了. \\ 该框架诞生于Surface团队,微软DMF是传统Wi ...

  5. agilebpm脑图_设计开发平台前端框架介绍 | AgileBPM 敏捷工作流开发平台—开源免费-基于 Activiti 工作流引擎、Flowable...

    目前设计平台前端是独立部署的,引入了 Angular 和 Vue,您可以根据自己技术强项 选择使用 Angular 或者 Vue,他们引入的成本都很小. 比如我们团队使用 Angular 很多年了,依 ...

  6. windows7以上平台 NDISFilter 网卡过滤驱动开发

    by fanxiushu 2019-01-16 转载或引用请注明原始作者 这里讨论的都是基于WIN7以上平台,NDIS 6.0以上版本的网络驱动. 做个驱动的目的,是因为很早之前,我使用 TDI 和 ...

  7. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platfrom))

    catalog 0.引言 1.Windows 2000网络结构和OSI模型 2.NDIS驱动 3.NDIS微端口驱动编程实例 4.NDIS中间层驱动编程实例 5.TDI驱动 6.TDI驱动 7.TDI ...

  8. Linux平台设备框架驱动

    Linux平台设备框架驱动   平台设备框架(platform)是将一个驱动分为设备层和驱动层两个部分,通过总线模型将设备和驱动进行绑定.在系统中每注册一个设备,都会与之匹配一个驱动,同样的,每注册一 ...

  9. NDIS驱动(一)协议驱动

    NDIS网络驱动分类 协议驱动:上层直接提供应用层socket使用的数据传输接口,下层绑定小端口驱动用于发送和接收以太网包. 小端口驱动:直接针对网卡,给协议驱动提供接收和发生数据的能力 中间层驱动: ...

最新文章

  1. unexpected symbol、unexpected end of input
  2. autocad三维汇报,bim汇报,视图汇报方法
  3. “不一样”的真实渗透测试案例分析
  4. 负载均衡实现,一个域名对应多个IP地址
  5. 牛客 - Final Exam(贪心)
  6. javascript实例_网页空降与抖动
  7. Linux中qt编写登录
  8. Java过滤器详细文档,简介,实例,应用
  9. 监督学习和无监督学习_一篇文章区分监督学习、无监督学习和强化学习
  10. 15.try...except...finally
  11. 2. SVM线性分类器
  12. Atitit websocket 使用大概总结 使用场景 websocket 实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应 用 实时数据可以用来更新缓存
  13. CentOS 安装 Nexus 3
  14. 硬盘测试软件w10,Win10硬盘检测工具
  15. 用R语言生成均匀设计
  16. ligerUI的dialog
  17. 淘气的小丁-将图片切换成背景
  18. 虚拟串口工具MCGS开发调试的灵活应用教程
  19. oracle 一个表上的多个触发器的执行顺序
  20. 74HC165基础篇(一)

热门文章

  1. 整理chinaUnix上【你职业生涯中最难忘的误操作】
  2. 金蝶生成凭证模板_金蝶凭证导入模板
  3. linux大于3T硬盘多个分区,linux之Ubuntu挂载3T硬盘或大于2T磁盘
  4. 各种博客的代码高亮是如何实现的
  5. 通过BL102实现Modbus PLC接入Thingsboard
  6. Cinemachine 之简单的相机跟随
  7. scrapy爬取昵图网图片
  8. visual studio 2015下载地址
  9. [bzoj4372]烁烁的游戏
  10. 关于微信公众号文章抓取