NDIS协议驱动开发指南

news2024/12/24 12:47:31

文章目录

  • 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.coloradmin.cn/o/1289226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Could not resolve all files for configuration ‘:app:debugCompileClasspath‘.

修改前 修改后 maven {url https://developer.huawei.com/repo/}

3DSEE:AI驱动的3D模型语义搜索引擎

3DSEE (3D SEmantic Engine)是基于 AI 技术的 3D 模型语义搜索引擎,可以自动提取 3D 模型内涵的语义信息并存储入库,以帮助用户使用自然语言或关键字高效地检索 3D 模型。3DSEE 提供完善的二次开发 API,无论使用Java、…

键盘打字盲打练习系列之反复练习——3

一.欢迎来到我的酒馆 盲打,反复练习! 目录 一.欢迎来到我的酒馆二.数字符号键位指法 二.数字符号键位指法 前面的一个章节重点介绍了主键盘区字母键位的指法:基准键位指法、QWERTY字母键位指法、ZXCVBNM字母键位指法。大概练习一天的时间&…

【力扣】240.搜索二维矩阵

题目意思是从该矩阵之中查找出是否有和target一样的值,若有则返回true,无则返回false。这里我用的是java。总共有三种方法,分别是暴力解题法(能过),二分查找法(就是将二维数组拆分成m个二维数组…

2.8寸 ILI9341 TFTLCD 学习移植到STM32F103C8T6

2.8寸 ILI9341 TFTLCD 学习移植到STM32F103C8T6 文章目录 2.8寸 ILI9341 TFTLCD 学习移植到STM32F103C8T6前言第1章 LCD简介1.1 LCD硬件接口介绍 第2章 LCD指令介绍第3章 LCD 8080驱动方式3.1 8080写时序3.2 8080读时序 第4章 LCD 驱动代码部分4.1 修改代码部分4.2 代码工程下载…

【力扣】54. 螺旋矩阵

题解: 这里当然就不能只是通过双循环来解题了,因为会烦死。这道题的关键点在于左右边界的确定,参考官方解题法在这里写出我的解题思路。 首先看图,螺旋且顺时针,所以我们可以先遍历从左至右的,上边界加一…

[原创][2]探究C#多线程开发细节-“线程的无顺序性“

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delph…

【2023全网最全教程】web自动化测试入门

一、自动化测试基本介绍 1 自动化测试概述: 什么是自动化测试?一般说来所有能替代人工测试的方式都属于自动化测试,即通过工具和脚本来模拟人执行用例的过程。 2 自动化测试的作用 减少软件测试时间与成本改进软件质量通过扩大测试覆盖率…

大数据:sql,数据挖掘刷题

大数据:sql 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发,测开 测开的话,你就得学数据库,sql,oracle,尤其sql要学&…

Java 线程池到底是如何复用线程的

原理概述 其实 Java 线程池的实现原理很简单,说白了就是一个线程集合 workerSet 和一个阻塞队列 workQueue。 当用户向线程池提交一个任务时,线程池会先将任务放入 workQueue 中。workerSet 中的线程会不断的从 workQueue 中获取线程然后执行。当 work…

U3 词法分析

文章目录 一、定义1、任务2、构造3、输出形式 二、(有穷状态)自动机三、正则文法和状态图四、词法分析程序流程1、单词及内部表示2、词法分析程序需要引用的公共(全局)变量和过程 五、自动机1、DFA(确定的有穷状态自动…

深耕中国成分,柳丝木打造茶小光IP赋能乡村振兴

随着全球可持续性发展理念的增强,越来越多品牌在行商业化道路之外,开始思考自身的社会责任,不断参与公益行动,践行ESG理念【Environmental(环境)、Social(社会)和 Governance &#…

HASH 哈希算法之MD5 算法

1. 哈希算法&#xff0c;用C 写的 #include <iostream> #include <iomanip> #include <cstring> #include <openssl/md5.h> #include <stdio.h>using namespace std;int main() {string str "hello world";unsigned char digest[MD5…

没有实权的PM如何做好项目管理?

在一些公司中&#xff0c;项目经理&#xff08;PM&#xff09;可能并没有实权&#xff0c;这种情况下如何做好项目管理呢&#xff1f;实际上&#xff0c;即使没有实权&#xff0c;PM仍然可以通过一些方法来确保项目的顺利进行。 首先&#xff0c;PM可以通过建立良好的沟通渠道来…

软著项目推荐 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

《opencv实用探索·十一》opencv之Prewitt算子边缘检测,Roberts算子边缘检测和Sobel算子边缘检测

1、前言 边缘检测&#xff1a; 图像边缘检测是指在图像中寻找灰度、颜色、纹理等变化比较剧烈的区域&#xff0c;它们可能代表着物体之间的边界或物体内部的特征。边缘检测是图像处理中的一项基本操作&#xff0c;可以用于人脸识别、物体识别、图像分割等多个领域。 边缘检测…

Streamlit框架的定制化

Streamlit框架的定制化 最近做了一个关于streamlit框架的项目&#xff0c;颇有感触&#xff0c;所以在这里记录一下。 什么是streamlit? Streamlit 是一个python的WEB UI库&#xff0c;它做了高度的封装以便于不懂后前端开发的人员也能轻松构建画面。你可以从官网进行详细的…

你真的掌握结构体了么?结构体习题(C语言)

前言 上一期博客我们学习了结构体的相关知识&#xff08;上期链接&#xff09;&#xff0c;但是学了不练也是不行的&#xff0c;我们今天讲给大家分享两道有点恶心的题目&#xff0c;让大家来加深对结构体的理解&#xff0c;那么话不多说我们现在开始吧&#xff01; 第一题 有…

zabbix 进阶

zabbix的字段发现机制&#xff1a; zabbix客户端主动和服务端联系&#xff0c;将自己的地址和端口发送服务端实现字段添加监控主机。 客户端是主动一方。 缺点&#xff1a;自定义网段中主机数量太多&#xff0c;登记耗时会很久&#xff0c;而且这个自动发现机制不是很稳定。…

Hadoop学习笔记(HDP)-Part.20 安装Flume

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …