news 2026/8/18 13:05:39

windows 驱动实例分析系列: wintun驱动分析-driver篇(二)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
windows 驱动实例分析系列: wintun驱动分析-driver篇(二)

一、驱动入口与 NDIS 注册

DriverEntry中,除了填充NDIS_MINIPORT_DRIVER_CHARACTERISTICS外,还特别注入了IRP_MJ_DEVICE_CONTROLIRP_MJ_CLOSEIRP_MJ_PNP的自定义处理函数。这些处理函数会在用户态调用DeviceIoControl或进程关闭文件句柄时被调用,用于注册/注销环形缓冲区及清理。


二、适配器初始化(TunInitializeEx

当 NDIS 需要启动一个 Wintun 适配器实例时,调用此函数。

流程详解:

  1. 分配上下文:使用ExAllocatePoolZero在非分页池分配TUN_CTX,并清零。
  2. 保存 NDIS 句柄Ctx->MiniportAdapterHandle = MiniportAdapterHandle
  3. 获取设备对象:调用NdisMGetDeviceProperty取得FunctionalDeviceObject,并将Ctx存入DeviceObject->Reserved,以便后续 IRP 分发使用。
  4. 初始化同步对象
    • 事件Device.Disconnected(初始为 signaled,表示未连接)。
    • 自旋锁Device.Send.LockDevice.Receive.Lock
    • 事件Device.Receive.ActiveNbls.Empty(初始 signaled)。
    • 资源锁Device.RegistrationLock
  5. 分配 NBL 池:调用NdisAllocateNetBufferListPool,指定fAllocateNetBuffer = TRUE,池标签为'wtun'
  6. 设置 NDIS 属性:填充NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTESNDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES
    • 介质类型:NdisMediumIP(直接处理 IP 包,无以太网头)。
    • MTU:0xFFFF(最大 IP 包大小)。
    • 链路速度:100 Gbps(虚拟,实际受用户态处理限制)。
    • 支持 OID 列表:包括统计、电源管理、链路参数等。
    • 电源管理能力:不支持唤醒。
    • 标志:允许意外移除。
  7. 通过NdisMSetMiniportAttributes两次调用分别注册这两组属性。
  8. 返回NDIS_STATUS_SUCCESS。此时适配器处于“已停止”状态,Running = 0(初始值)。

三、暂停与重启

3.1TunPause

当 NDIS 要求暂停(例如系统进入低功耗状态或正在卸载驱动)时调用:

  • Running置为 0(使用WriteRelease)。
  • 获取TransitionLock的独占锁并立即释放,使所有正在进行的读路径(持有共享锁)能感知到Running的变化。
  • 等待Device.Receive.ActiveNbls.Empty事件,确保所有已指示的接收 NBL 都已返回(避免使用已释放的 MDL)。
  • 返回NDIS_STATUS_SUCCESS

3.2TunRestart

恢复运行时调用:

  • Running置为 1(WriteRelease)。
  • 返回成功。

四、适配器停止(TunHaltEx

当适配器被永久移除(如用户手动删除设备)时调用:

  • 调用TunUnregisterBuffers强制注销(TUN_FORCE_UNREGISTRATION),断开连接、释放资源。
  • 再次获取TransitionLock独占锁,确保所有读者已退出。
  • 释放 NBL 池。
  • 清空MiniportAdapterHandleFunctionalDeviceObject->Reserved
  • 删除资源锁Device.RegistrationLock
  • 释放TUN_CTX内存。

五、缓冲区的注册与注销

5.1 注册(TunRegisterBuffers

由用户态DeviceIoControlTUN_IOCTL_REGISTER_RINGS)触发,该 IOCTL 最终会调用到此函数。

核心步骤:

  1. 获取Device.RegistrationLock独占锁,检查是否已有OwningFileObject,若有则返回STATUS_ALREADY_INITIALIZED
  2. 从 IRP 的输入缓冲区解析TUN_REGISTER_RINGS结构(支持 32/64 位跨进程)。
  3. 校验参数
    • 计算Capacity = TUN_RING_CAPACITY(RingSize),必须介于 128 KiB 和 64 MiB 之间,且为 2 的幂。
    • 检查事件句柄(TailMoved)是否有效,并通过ObReferenceObjectByHandle获取内核事件对象指针(用于驱动唤醒)。
  4. 锁定用户内存
    • 分配 MDL,调用MmProbeAndLockPages将用户空间环形缓冲区锁定在物理内存中。
    • 使用MmGetSystemAddressForMdlSafe获取内核地址空间映射(NonPagedPool,且带MdlMappingNoExecute)。
  5. 读取初始 Tail 指针:从用户环中读取当前 Tail 并保存到Ctx->Device.Send.RingTail
  6. 创建接收线程:调用PsCreateSystemThread创建内核线程TunProcessReceiveData,负责从接收环读取数据并转发给 NDIS。
  7. 保存进程 IDCtx->Device.OwningProcessId = PsGetCurrentProcessId()
  8. 加入全局设备列表:获取TunDispatchDeviceListLock,将Ctx->Device.Entry插入链表(用于后续孤儿检测)。
  9. 指示链接状态为已连接:调用TunIndicateStatus(MediaConnectStateConnected)
  10. 释放RegistrationLock,返回成功。

5.2 注销(TunUnregisterBuffers

WintunEndSession或进程退出、设备移除等触发:

  • 获取RegistrationLock独占锁,确认OwningFileObject与调用者匹配(若为强制注销,忽略匹配)。
  • 从全局设备列表中移除自身。
  • 指示链接状态为断开。
  • 置位Device.Disconnected事件,并获取TransitionLock独占锁,使所有读路径感知到断开。
  • 等待接收线程退出(通过ObReferenceObjectByHandle获取线程对象,KeWaitForSingleObject等待)。
  • 关闭接收线程句柄。
  • 将发送环的Tail设为MAXULONG,并触发Send.TailMoved事件,让可能的等待者退出。
  • 解锁用户内存(MmUnlockPages)、释放 MDL、取消引用事件对象。
  • 清空OwningFileObject,释放锁。

六、进程退出通知(TunProcessNotification

注册了PsSetCreateProcessNotifyRoutine,在进程退出时被回调:

  • 如果Create == FALSE(进程退出),则遍历全局设备列表,查找OwningProcessId == ProcessId的设备。
  • 若找到,则调用TunUnregisterBuffers(TUN_FORCE_UNREGISTRATION)强制断开该进程持有的适配器,避免资源泄漏。

七、设备对象的 IRP 处理

7.1TunDispatchDeviceControl

拦截IRP_MJ_DEVICE_CONTROL,仅处理TUN_IOCTL_REGISTER_RINGS,其他交给 NDIS 默认处理。

  • 首先进行安全访问检查(SeAccessCheck),确保调用者有FILE_WRITE_DATA权限。
  • 获取TunDispatchCtxGuard共享锁(保护DeviceObject->Reserved),调用TunRegisterBuffers

7.2TunDispatchClose

拦截IRP_MJ_CLOSE,当用户态关闭设备句柄时触发:

  • 获取TunDispatchCtxGuard共享锁,调用TunUnregisterBuffers并传入当前FileObject
  • 将原关闭处理函数(NdisDispatchClose)的结果返回。

7.3TunDispatchPnp

拦截IRP_MJ_PNP,主要处理IRP_MN_QUERY_REMOVE_DEVICEIRP_MN_SURPRISE_REMOVAL

在此函数中,驱动会尝试强制关闭仍持有设备句柄的其他进程。具体做法:

  • 获取RegistrationLock
  • 如果当前存在OwningFileObject且不是本文件对象,则枚举系统句柄表(ZwQuerySystemInformation),查找与OwningFileObject相同的对象句柄,并关闭它们(通过ObCloseHandle)。
  • 这样做的目的是防止其他进程持有句柄导致设备无法移除。
  • 最终交给NdisDispatchPnp完成剩余 PnP 处理。

八、总结

本文档详细描述了驱动的初始化、暂停、重启、关闭、缓冲区注册与注销,以及进程退出通知和 IRP 拦截。这些机制共同保证了适配器的生命周期管理、资源安全和跨进程共享的正确性,特别是通过进程通知和句柄枚举,有效避免了“僵尸适配器”问题。下一篇文章将聚焦最关键的数据路径——发送与接收。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/18 13:04:50

从百店到万店,连锁企业网络运维如何从人盯设备走向零接触部署

一、先搞懂:为什么门店越多,网络越“盯不过来”打个比方,一家餐厅开到10家分店,收银、点餐、监控出了问题,一个网管坐在总部打电话就能远程解决。可当门店扩张到100家、500家甚至上千家,情况就完全不同了—…

作者头像 李华
网站建设 2026/8/18 13:04:39

国际版外卖系统代码深讲:多币种订单、支付回调与状态机

中东县城创业者评估「国际版外卖系统」时,成本过高常见于工程债:币种写死在金额字段、支付回调只打日志不推进状态、阿语/英语文案绑在 if-else。全套高预算项目若缺少这些边界,第二个月就会同时出现外包变更单、财务拼表与客服改单。下文用表…

作者头像 李华
网站建设 2026/8/18 13:04:02

5 分钟救回你的暗黑2角色:d2s-editor 存档编辑器完整使用指南

5 分钟救回你的暗黑2角色:d2s-editor 存档编辑器完整使用指南 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 如果你玩过《暗黑破坏神2》或重制版 D2R,一定体验过那种"角色练到一半突然读不了档&quo…

作者头像 李华
网站建设 2026/8/18 13:03:27

计算机毕业设计之高校篮球训练管理系统的设计与实现

信息技术是当今社会发展的重要方向之一,它已经深入到各个行业中。随着计算机技术的发展,信息技术已经从传统的数据处理转变为网络信息的处理和交互。在管理方面,通过信息管理技术,系统可以快速的处理大量的数据,并且能…

作者头像 李华
网站建设 2026/8/18 13:01:15

比亚迪DM-i双擎四驱技术解析:从电四驱原理到宋Pro真实体验

1. 从“双擎四驱”说起:一次动力架构的认知刷新最近在帮朋友选车,他拿着手机给我看,屏幕上赫然是“宋Pro DM-i 荣耀版”的配置单,嘴里念叨着“双擎四驱”和“综合功率323马力”。他问我:“这‘双擎’我知道&#xff0c…

作者头像 李华
网站建设 2026/8/18 12:59:55

林沛群机器人之运动学笔记—5

5-1 机械手臂轨迹规划引言 机械手臂轨迹规划在机械臂任务操作过程当中不可或缺,抓取物体拿到指定位置,这个过程除了IK,就还包括机械臂的轨迹规划。这里给出轨迹的定义,轨迹:机械手臂(的末端点或操作点&…

作者头像 李华