NDIS协议驱动开发指南

文章目录

  • NDIS协议驱动开发指南
    • 1. 技术概览
    • 2. NDIS协议驱动
      • 2.1 BindAdapterHandlerEx
      • 2.2 SendNetBufferListsCompleteHandler
      • 2.3 ReceiveNetBufferListsHandler
      • 2.4 ProtocolNetPnpEvent
    • 3. NET_BUFFER_LIST
    • 4. ndisprot实例
    • 5. 总结

NDIS协议驱动开发指南

我们知道,在以太网中所有的数据包都是通过以太网帧来发送的;但是在网络上面的应用程序如果需要通过网络数据包交互,就需要依赖网络协议来保障通信。平时我们用的最多的协议就是TCPIP协议。

其实在Windows中,我们可以注册自己的协议,开发自己的协议解析和封装驱动,实现以太网帧的通信,这就是本文的NDIS协议驱动。

以太网的以太包格式都是固定的,格式如下:

6字节6字节2字节其他长度
源MAC目的MAC类型数据部分

NDIS协议驱动就是针对以太包的协议封装和解析过程,对上层提供一个稳定的数据包通信的协议,对下层提供一个可以供以太网发送的以太数据包。本文我们来看一下NDIS协议驱动的开发原理。

1. 技术概览

在Windows下面,网络栈的基本架构如下:
在这里插入图片描述

对于NDSI提供了三种功能(能力)的驱动:

  1. 上层的协议驱动。
  2. 中层的过滤驱动。
  3. 下层的小端口驱动。

由于NDIS早期并没有直接提供过滤层的基本框架,因此对于早期(XP系统下面)版本如果需要对NDIS层的数据包进行过滤,需要在中间层实现协议驱动和小端口驱动:

  • 对上层,创建小端口驱动来和上层协议层通信(主要过滤数据包的发送)。
  • 对下层,创建协议驱动来和小端口驱动通信(主要过滤数据包的接收)。

对于协议层驱动,实现比较简单,只需要设置和处理好NDIS相关的协议层回调函数即可,下面我们看一下协议层驱动的具体实现。

2. NDIS协议驱动

上面我们知道NDIS协议驱动主要处理NDIS的相关协议回调例程,向NDIS注册回调例程的函数为NdisRegisterProtocolDriver,该例程声明如下:

NDIS_STATUS NdisRegisterProtocolDriver(NDIS_HANDLE                           ProtocolDriverContext,PNDIS_PROTOCOL_DRIVER_CHARACTERISTICS ProtocolCharacteristics,PNDIS_HANDLE                          NdisProtocolHandle
);

其中NDIS_PROTOCOL_DRIVER_CHARACTERISTICS就是协议驱动的回调函数结构体,该结构体如下:

typedef struct _NDIS_PROTOCOL_DRIVER_CHARACTERISTICS {NDIS_OBJECT_HEADER                     Header;UCHAR                                  MajorNdisVersion;UCHAR                                  MinorNdisVersion;UCHAR                                  MajorDriverVersion;UCHAR                                  MinorDriverVersion;ULONG                                  Flags;NDIS_STRING                            Name;SET_OPTIONS_HANDLER                    SetOptionsHandler;BIND_HANDLER_EX                        BindAdapterHandlerEx;UNBIND_HANDLER_EX                      UnbindAdapterHandlerEx;OPEN_ADAPTER_COMPLETE_HANDLER_EX       OpenAdapterCompleteHandlerEx;CLOSE_ADAPTER_COMPLETE_HANDLER_EX      CloseAdapterCompleteHandlerEx;NET_PNP_EVENT_HANDLER                  NetPnPEventHandler;UNINSTALL_PROTOCOL_HANDLER             UninstallHandler;OID_REQUEST_COMPLETE_HANDLER           OidRequestCompleteHandler;STATUS_HANDLER_EX                      StatusHandlerEx;RECEIVE_NET_BUFFER_LISTS_HANDLER       ReceiveNetBufferListsHandler;SEND_NET_BUFFER_LISTS_COMPLETE_HANDLER SendNetBufferListsCompleteHandler;DIRECT_OID_REQUEST_COMPLETE_HANDLER    DirectOidRequestCompleteHandler;
} NDIS_PROTOCOL_DRIVER_CHARACTERISTICS, *PNDIS_PROTOCOL_DRIVER_CHARACTERISTICS;

在上述结构体中间,对于一个简要的协议驱动,只需要实现部分主要的回调函数即可,包括:

  • BindAdapterHandlerEx:绑定回调函数,当小端口驱动和协议驱动进行绑定的时候调用该函数通知协议驱动。
  • UnbindAdapterHandlerEx:解除绑定的回调函数,和BindAdapterHandlerEx相反。
  • OpenAdapterCompleteHandlerEx:当使用NdisOpenAdapterEx绑定小端口驱动完成的时候被调用(相当IRP的完成例程)。
  • CloseAdapterCompleteHandlerEx:当使用NdisCloseAdapterEx解除协议驱动和小端口驱动完成的时候被调用。
  • OidRequestCompleteHandlerNdisOidRequest请求完成的时候被调用的函数。
  • SendNetBufferListsCompleteHandler:表示使用NdisSendNetBufferLists发送完成数据包之后被调用的回调函数。
  • ReceiveNetBufferListsHandler:当小端口驱动接收到数据的时候就会通过该回调函数通知协议驱动数据包的到来。

2.1 BindAdapterHandlerEx

协议驱动是对网络数据包的封装,当将网络数据包按照协议封装为以太网数据包之后,就需要通过网卡发送出去,那么协议驱动就需要和网卡驱动进行关联(协议驱动的数据包知道如何发送给网卡驱动)。

有两种情况需要进行协议的绑定:

  1. 当协议驱动使用NdisRegisterProtocolDriver注册驱动的时候,NDIS框架就会遍历当前系统所有的小端口驱动,对每个小端口驱动调用BindAdapterHandlerEx回调函数。
  2. 当有新的网卡设备插入并启动的时候(IRP_MN_START),就会对该小端口驱动遍历所有的协议驱动,然后调用其BindAdapterHandlerEx回调函数进行绑定。

BindAdapterHandlerEx只是绑定的回调函数,该函数声明如下:

PROTOCOL_BIND_ADAPTER_EX ProtocolBindAdapterEx;NDIS_STATUS ProtocolBindAdapterEx(NDIS_HANDLE ProtocolDriverContext,NDIS_HANDLE BindContext,PNDIS_BIND_PARAMETERS BindParameters
)
{...}

该函数只是将小端口驱动和协议驱动的信息当作回调函数的参数传递过来,小端口驱动的信息通过PNDIS_BIND_PARAMETERS进行描述。真实的绑定是通过NdisOpenAdapterEx函数来完成的,该函数如下:

NDIS_STATUS NdisOpenAdapterEx(NDIS_HANDLE           NdisProtocolHandle,NDIS_HANDLE           ProtocolBindingContext,PNDIS_OPEN_PARAMETERS OpenParameters,NDIS_HANDLE           BindContext,PNDIS_HANDLE          NdisBindingHandle
);

NdisOpenAdapterEx其实是建立小端口和协议驱动的桥梁,大致如下:

在这里插入图片描述

这样小端口的数据可以在NDIS框架中通过NDIS_OPEN_BLOCK回调给协议驱动,协议驱动也可以通过NDIS_OPEN_BLOCK调用小端口驱动。

2.2 SendNetBufferListsCompleteHandler

在协议驱动中,我们通过NdisSendNetBufferLists将以太包发送数据,该函数声明如下:

void NdisSendNetBufferLists(NDIS_HANDLE                       NdisBindingHandle,__drv_aliasesMem PNET_BUFFER_LIST NetBufferLists,NDIS_PORT_NUMBER                  PortNumber,ULONG                             SendFlags
);

NET_BUFFER_LIST描述着我们需要发送的数据包集合,当小端口驱动将数据包发送成功之后,就会调用NdisMSendNetBufferListsComplete来通知协议驱动数据包被发送完成,该函数如下:

void NdisMSendNetBufferListsComplete(NDIS_HANDLE      MiniportAdapterHandle,PNET_BUFFER_LIST NetBufferList,ULONG            SendCompleteFlags
);

NdisMSendNetBufferListsComplete完成回调的函数就是SendNetBufferListsCompleteHandler,该函数如下:

PROTOCOL_SEND_NET_BUFFER_LISTS_COMPLETE ProtocolSendNetBufferListsComplete;void ProtocolSendNetBufferListsComplete(NDIS_HANDLE ProtocolBindingContext,PNET_BUFFER_LIST NetBufferList,ULONG SendCompleteFlags
)
{...}

SendNetBufferListsCompleteHandler这个函数将会重新获取NdisSendNetBufferLists发送的数据包,在该函数中可以释放发送分配的NET_BUFFER_LIST内存。

不过这里需要注意的是NET_BUFFER_LIST是一个链表结构,在底层可能会被断链发送;因此SendNetBufferListsCompleteHandler这个函数中的NET_BUFFER_LIST可能是NdisSendNetBufferLists中的子链。

2.3 ReceiveNetBufferListsHandler

当我们的网卡接收到数据的时候,就会通过硬件方式(例如中断等)通知数据包到来,然后小端口驱动通过NdisMIndicateReceiveNetBufferLists将数据包传送给协议层驱动进行协议解析和数据包的传递,该函数如下:

void NdisMIndicateReceiveNetBufferLists(NDIS_HANDLE      MiniportAdapterHandle,PNET_BUFFER_LIST NetBufferList,NDIS_PORT_NUMBER PortNumber,ULONG            NumberOfNetBufferLists,ULONG            ReceiveFlags
);

NdisMIndicateReceiveNetBufferLists内部就会调用ReceiveNetBufferListsHandler将接收到的网络数据包通过NET_BUFFER_LIST进行传递,该函数声明如下:

PROTOCOL_RECEIVE_NET_BUFFER_LISTS ProtocolReceiveNetBufferLists;void ProtocolReceiveNetBufferLists(NDIS_HANDLE ProtocolBindingContext,PNET_BUFFER_LIST NetBufferLists,NDIS_PORT_NUMBER PortNumber,ULONG NumberOfNetBufferLists,ULONG ReceiveFlags
)
{...}

在该函数中NetBufferLists表示数据包,通过解析该数据包我们就可以进行TCPIP网络栈的协议解析了。

2.4 ProtocolNetPnpEvent

改例程是NDIS框架对于网络PNP事件响应的回调函数,该函数声明如下:

PROTOCOL_NET_PNP_EVENT ProtocolNetPnpEvent;NDIS_STATUS ProtocolNetPnpEvent(NDIS_HANDLE ProtocolBindingContext,PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification
)
{...}

NET_PNP_EVENT_NOTIFICATION描述了一个PNP事件的信息,该结构如下:

typedef struct _NET_PNP_EVENT_NOTIFICATION {NDIS_OBJECT_HEADER       Header;NDIS_PORT_NUMBER         PortNumber;NET_PNP_EVENT            NetPnPEvent;ULONG                    Flags;NDIS_NIC_SWITCH_ID       SwitchId;NDIS_NIC_SWITCH_VPORT_ID VPortId;
} NET_PNP_EVENT_NOTIFICATION, *PNET_PNP_EVENT_NOTIFICATION;typedef struct _NET_PNP_EVENT {NET_PNP_EVENT_CODE NetEvent;PVOID              Buffer;ULONG              BufferLength;ULONG_PTR          NdisReserved[4];ULONG_PTR          TransportReserved[4];ULONG_PTR          TdiReserved[4];ULONG_PTR          TdiClientReserved[4];
} NET_PNP_EVENT, *PNET_PNP_EVENT;

NET_PNP_EVENT_CODE描述网络PNP事件的类型,有如下:

typedef enum _NET_PNP_EVENT_CODE
{NetEventSetPower,NetEventQueryPower,NetEventQueryRemoveDevice,NetEventCancelRemoveDevice,NetEventReconfigure,NetEventBindList,NetEventBindsComplete,NetEventPnPCapabilities,NetEventPause,NetEventRestart,NetEventPortActivation,NetEventPortDeactivation,NetEventIMReEnableDevice,NetEventNDKEnable,NetEventNDKDisable,NetEventFilterPreDetach,NetEventBindFailed,NetEventSwitchActivate,NetEventAllowBindsAbove,NetEventInhibitBindsAbove,NetEventAllowStart,NetEventRequirePause,NetEventUploadGftFlowEntries,NetEventMaximum
} NET_PNP_EVENT_CODE, *PNET_PNP_EVENT_CODE;

NET_PNP_EVENT_CODE的具体值,参见MSDN(例如NetEventSetPower类型Buffer表示了NDIS_DEVICE_POWER_STATE结构,描述电源状态)。

3. NET_BUFFER_LIST

在NDIS中,网络数据包通过NET_BUFFER_LIST来进行抽象,这个结构表示着网络数据包的集合;该数据结构如下:
在这里插入图片描述

NET_BUFFER_LIST是一个NET_BUFFER_LIST的链表集合;单个NET_BUFFER_LISTNET_BUFFER的集合,NET_BUFFER表示一个数据包,该结构如下:
在这里插入图片描述

NET_BUFFER其实就是使用MDL来描述数据包的真实类容,因此对于NET_BUFFER_LIST的全部结构可以描述为如下:
在这里插入图片描述

对于NET_BUFFER_LISTNET_BUFFER提供了如下宏来操作该结构的成员:

#define NET_BUFFER_LIST_NEXT_NBL(_NBL)              ((_NBL)->Next)
#define NET_BUFFER_LIST_FIRST_NB(_NBL)              ((_NBL)->FirstNetBuffer)#define NET_BUFFER_NEXT_NB(_NB)                     ((_NB)->Next)
#define NET_BUFFER_FIRST_MDL(_NB)                   ((_NB)->MdlChain)
#define NET_BUFFER_DATA_LENGTH(_NB)                 ((_NB)->DataLength)
#define NET_BUFFER_DATA_OFFSET(_NB)                 ((_NB)->DataOffset)
#define NET_BUFFER_CURRENT_MDL(_NB)                 ((_NB)->CurrentMdl)
#define NET_BUFFER_CURRENT_MDL_OFFSET(_NB)          ((_NB)->CurrentMdlOffset)

我们需要对接收或者发送的数据包进行处理,都是解析NET_BUFFER_LIST的过程。

4. ndisprot实例

对于NDIS协议驱动,WDK提供了一个示例ndisprot,该示例展示了协议驱动的工作原理,该实例提供如下功能:

  1. ndisprot驱动可以绑定到网卡上面。
  2. 通过NdisprotReceiveNetBufferLists接收底层的网络数据包,并将其放入队列中。
  3. 用户层程序可以通过ReadFile读取协议驱动的网络数据包。
  4. 用户层程序可以通过WriteFile往网络协议驱动写入数据包,协议驱动通过NdisSendNetBufferLists将数据包发送到底层小端口驱动。

该协议否是可以支持网络通信呢?本人没有进行实验验证,但是从原理上来说是可行的,只是它是一个面向非连接,并且没有校验的原始通信手段的协议。

对于该驱动我们可以简单的使用如下方式手动安装和验证:
在这里插入图片描述

5. 总结

对于NDIS协议层驱动平时我们的使用场景不多,我们也没有能力(也没必要)设计一个完整的网络协议驱动。但是协议驱动是我们后面NDIS过滤驱动的基础,NDIS过滤驱动可以帮助我们获取本机接收到的网络帧数据包,并且对以太帧数据包进行过滤(例如ARP数据包等)。

因此NDIS协议驱动还是非常值得我们去学习的,它是NDIS过滤驱动的基础,也是以太帧数据包过滤的重要手段。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/211564.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【DPDK】Trace Library

概述 跟踪是一种用于了解运行中的软件系统中发生了什么的技术。用于跟踪的软件被称为跟踪器,在概念上类似于磁带记录器。记录时,放置在软件源代码中的特定检测点会生成保存在巨大磁带上的事件:跟踪文件。稍后可以在跟踪查看器中打开跟踪文件…

SpringSecurity的默认登录页的使用

SpringSecurity的默认登录页的使用 01 前期准备 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mysql驱动--><dependency><grou…

【S32K3环境搭建】-0.3-S32DS安装实时驱动RTD(Real-Time Driver)

目录 1 什么是“实时驱动RTD(Real-Time Driver)” 2 安装“实时驱动RTD(Real-Time Driver)” 2.1 方法一&#xff1a;通过S32DS Extensions and Updates安装“实时驱动RTD(Real-Time Driver)” 2.2 方法二&#xff1a;通过Install New Software…安装“实时驱动RTD(Real-Ti…

【海思SS528 | VO】MPP媒体处理软件V5.0 | 视频输出模块——学习笔记

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Docker本地部署Firefox火狐浏览器并远程访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

zookeeper集群 +kafka集群

1.zookeeper kafka3.0之前依赖于zookeeper zookeeper是一个开源&#xff0c;分布式的架构&#xff0c;提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构 存储和管理数据&#xff0c;分布式节点上的服务接受观察者的注册&#xff0c…

ATFX汇市:加央行即将公布12月利率决议结果,大概率维持5%不变

ATFX汇市&#xff1a;2023年美联储的8次利率决议计划&#xff0c;已经公布7次结果&#xff0c;其中有四次加息&#xff0c;三次暂停加息&#xff0c;并且近两次的决议结果都是不加息。美联储的货币政策对其他国家中央银行的决策具有指导作用&#xff0c;尤其是经济数据与美国共…

Android的前台服务

概述 前台服务是用户主动意识到的一种服务&#xff0c;因此在内存不足时&#xff0c;系统也不会考虑将其终止。前台服务必须为状态栏提供通知&#xff0c;将其放在运行中的标题下方。这意味着除非将服务停止或从前台移除&#xff0c;否则不能清除该通知。 在 Android 8.0&…

CPP-SCNUOJ-Problem P24. [算法课贪心] 跳跃游戏

Problem P24. [算法课贪心] 跳跃游戏 给定一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度 判断你是否能够到达最后一个下标。 输入 输入一行数组nums 输出 输出true/fasle 样例 标准输入 2 3 1 …

【网络奇缘】- 计算机网络|分层结构|深入学习ISO模型

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 回顾链接&#xff1a;http://t.csdnimg.cn/nRRzR 这篇文章是关于深入学习OSI模型七层结构&#xff0c; “书山…

如何解决syntaxerror: more than 255 arguments 报错

如何解决syntaxerror: more than 255 arguments 报错 问题背景解释解决方案 问题背景 今天拼接特征的时候&#xff0c;突然代码报错syntaxerror: more than 255 arguments &#xff0c;看了一下感觉这个报错非常有意思&#xff0c;估计平时也是没机会碰到&#xff0c;和大家分…

Java LeetCode篇-深入了解二叉树经典解法(三种方式实现:获取二叉树的最大深度)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 对称二叉树 1.1 判断对称二叉树实现思路 1.2 代码实现&#xff1a;判断对称二叉树 2.0 二叉树的最大深度 2.1 使用递归实现获取二叉树的最大深度思路 2.2 代码实…

Hello World

世界上最著名的程序 from fastapi import FastAPIapp FastAPI()app.get("/") async def root():return {"message": "Hello World"}app.get("/hello/{name}") async def say_hello(name: str):return {"message": f"…

漏洞扫描服务是什么

漏洞扫描服务是维护网络安全的重要一环。通过定期或实时的漏洞扫描&#xff0c;组织可以及时发现并修复可能存在的安全威胁&#xff0c;增强自身网络的安全性。在选择漏洞扫描服务时&#xff0c;需要明确自身的需求和目标&#xff0c;并选择合适的工具和服务提供商。只有这样&a…

flutter使用动态路由传参的最小案例

flutter中使用动态路由传递参数的封装案例&#xff0c;子组件页面只需要接收arguments参数即可&#xff0c;参数是一个map&#xff0c;里面包含有所需要的参数&#xff0c;类似于json。在MaterialApp中配置onGenerateRoute&#xff0c;然后动态判断传递参数&#xff1a; route…

【数据结构】——堆排序

前言&#xff1a;我们已经学习了堆以及实现了堆&#xff0c;那么我们就来给堆进行排序。我们怎么来进行排序呢&#xff1f;这一次我们就来解决这个问题。 如果我们堆排序要求排序&#xff0c;我们是建立大堆还是小堆呢&#xff0c;如果我们建的小堆的话&#xff0c;那我们在排序…

玩转大数据9:机器学习在大数据分析中的应用

1. 引言 在大数据时代&#xff0c;机器学习在大数据分析中扮演着至关重要的角色。本文介绍机器学习在大数据分析中的重要性和应用场景&#xff0c;并探讨Java中可用的机器学习库和框架。 2. 机器学习的基本概念和算法 机器学习是当今人工智能领域的一个关键分支&#xff0c;…

Flink(九)【时间语义与水位线】

前言 2023-12-02-20:05&#xff0c;终于写完啦&#xff0c;最近状态不错。刚写完又收到了她的消息哈哈哈哈&#xff0c;开心。 再去全力打拼一次&#xff0c;奋战一场&#xff0c;就算最后打了败仗也无所谓&#xff0c;至少你留下了足迹。 《解忧杂货店》 1、时间语义 …

webpack学习-1.起步

webpack学习-1.起步 1.基础设置2.配置文件的引入3.总结 1.基础设置 首先 webpack是干嘛的呢&#xff0c;用官网的一张图 Webpack 是一个现代的静态模块打包工具。它主要用于将前端应用程序中的各种资源&#xff08;例如 JavaScript、CSS、图片等&#xff09;打包成一个或多个…

Linux 进程地址空间

文章目录 进程地址空间进程地址空间结构页表虚拟内存写时拷贝 进程地址空间 进程地址空间难以定义&#xff0c;因为它更像是一个中间件。 程序从磁盘中加载到内存&#xff0c;程序的执行需要硬件资源&#xff0c;所以每个程序启动时会创建至少一条进程&#xff0c;进程作为组…