windows 驱动实例分析系列: wireguard-nt驱动分析-driver篇(一)

WireGuardNT 驱动代码解析 - 第一篇:设备管理与IOCTL接口

1. 概述

WireGuardNT 驱动(driver/ 目录)是 WireGuard 在 Windows NT 内核中的核心实现,负责处理 VPN 隧道的数据包加解密、路由、对等体管理以及网络接口操作。本系列文档将驱动代码分为四个部分进行解析,本文为第一部分,涵盖设备初始化、适配器管理、IOCTL 控制接口以及与网络状态指示器(NSI)的交互。

主要涉及以下源文件:

  • main.c -- 驱动入口点(DriverEntry)和卸载例程。
  • device.c / device.h -- 定义 WG_DEVICE 结构,实现 NDIS 微型端口驱动回调(初始化、暂停、重启、发送、接收、OID 请求等)。
  • ioctl.c / ioctl.h -- 实现用户态与驱动通信的 IOCTL 接口(获取/设置配置、适配器状态、读取日志)。
  • nsi.c / nsi.h -- 与 Windows 网络状态指示器(NSI)集成,监控 MTU 变化并拦截 NSI 设置请求。
  • wireguard.inf -- 驱动安装信息文件。
  • wireguard.rc / wireguard.ico -- 资源文件。

2. 驱动入口与初始化(main.c)

DriverEntry 是驱动的初始化入口,其流程如下:

  1. 调用 ExInitializeDriverRuntime(DrvRtPoolNxOptIn) 启用非执行池保护。
  2. 调用 CryptoDriverEntry() 检测 CPU 特性(SSSE3、AVX、AVX2、AVX512 等),为后续加密算法选择最佳实现。
  3. 调用 NoiseDriverEntry() 预计算 Noise 握手协议所需的初始哈希和链密钥(基于固定字符串)。
  4. 调用 MemDriverEntry() 初始化内存池(NET_BUFFER 和 NET_BUFFER_LIST 缓存)。
  5. 调用 RcuDriverEntry() 初始化 RCU 机制(每 CPU 的 DPC 和清理线程)。
  6. 调用 AllowedIpsDriverEntry() 初始化 AllowedIPs 查找树的节点缓存。
  7. 调用 RatelimiterDriverEntry() 初始化速率限制器(GC 线程和哈希表)。
  8. 调用 PeerDriverEntry() 初始化对等体对象的后备列表(lookaside list)。
  9. 调用 DeviceDriverEntry() 注册 NDIS 微型端口驱动(NdisMRegisterMiniportDriver)。
  10. 调用 IoctlDriverEntry()NsiDriverEntry() 分别挂接 IOCTL 派遣例程和 NSI 过滤。

若调试版本(DBG)启用了自测试,还会执行 CryptoSelftestAllowedIpsSelftestPacketCounterSelftestRatelimiterSelftest

卸载例程 Unload 依次调用 DeviceUnloadWskUnloadPeerUnloadRatelimiterUnloadAllowedIpsUnloadRcuUnloadMemUnload 释放资源。

3. NDIS 微型端口驱动(device.c)

device.c 实现了 NDIS 微型端口驱动所需的回调函数,并管理 WG_DEVICE 结构(每个适配器一个实例)。

3.1 核心数据结构 WG_DEVICE

定义在 device.h 中,包含:

  • MiniportAdapterHandle -- NDIS 适配器句柄。
  • Statistics -- 接口统计信息(收发字节、包数等)。
  • ItemsInFlight -- 引用计数,用于控制暂停/重启时的资源释放。
  • 多个环形队列(EncryptQueueDecryptQueueHandshakeRxQueue)用于异步处理。
  • 多核工作队列(EncryptThreadsDecryptThreadsHandshakeTxThreadsHandshakeRxThreads)用于并行加解密和握手。
  • 套接字指针 Sock4Sock6(RCU 保护)。
  • 静态身份标识 StaticIdentity(公钥/私钥)。
  • Cookie 检查器 CookieChecker
  • 对等体哈希表 PeerHashtable 和索引哈希表 IndexHashtable
  • AllowedIPs 表 PeerAllowedIps
  • 设备锁 DeviceUpdateLock 和套接字更新锁 SocketUpdateLock
  • 对等体列表 PeerList 和计数 NumPeers
  • 接口索引、LUID、属主进程、监听端口、UP/DOWN 状态等。

3.2 NDIS 回调函数

  • InitializeEx :创建 WG_DEVICE 实例,初始化所有队列、锁、哈希表、工作线程,并调用 RegisterAdapter 注册适配器属性。若成功,调用 NsiActivate 激活 NSI 监控。

  • HaltEx:反向清理,关闭套接字、移除所有对等体、销毁工作线程、释放内存。

  • Pause :在适配器暂停时调用,等待所有飞行中的包完成,调用 DeviceStop 停止设备(清空密钥、清除握手状态)。

  • Restart :恢复适配器,重新初始化 ItemsInFlight,并调用 DeviceStart 启动对等体的待发送包和保活定时器。

  • SendNetBufferLists :处理上层协议栈发来的数据包。对每个 NBL,检查协议(IPv4/IPv6),提取 IP 头,通过 AllowedIpsLookupDst 查找目标对等体,然后将包加入对等体的暂存队列(StagedPacketQueue),并触发 PacketSendStagedPackets 发送。

  • OidRequest :处理 OID 查询和设置请求,例如返回厂商 ID、速度、统计信息、链路状态等。对于 OID_GEN_CURRENT_PACKET_FILTER 等设置,仅验证长度并返回成功;对于 OID_PNP_SET_POWER,若进入 D1 以上状态则调用 RcuBarrier

  • ReturnNetBufferLists :释放接收到的 NBL(由 PacketReceive 处理后调用)。

3.3 适配器状态管理

DeviceStartDeviceStop 分别启动/停止设备,其中 DeviceStart 遍历所有对等体,发送暂存包和保活包;DeviceStop 清除所有对等体的密钥和握手状态,并清空握手队列。

DeviceIndicateConnectionStatus 用于向 NDIS 报告链路连接状态(UP/DOWN),并更新链路速度。

4. IOCTL 接口(ioctl.c / ioctl.h)

用户态 DLL(wireguard.dll)通过 IOCTL 与驱动通信。驱动在 IoctlDriverEntry 中替换 IRP_MJ_DEVICE_CONTROL 的派遣例程,并设置安全描述符(允许 SYSTEM 和 Administrators 完全访问)。

定义的控制代码(ioctl.h):

  • WG_IOCTL_GET(METHOD_OUT_DIRECT):获取当前适配器配置(接口信息、对等体列表、AllowedIPs)。返回 WG_IOCTL_INTERFACE 结构及其后跟随的 WG_IOCTL_PEERWG_IOCTL_ALLOWED_IP 结构。若缓冲区不足,返回 STATUS_BUFFER_OVERFLOW 并给出所需大小。
  • WG_IOCTL_SET(METHOD_IN_DIRECT):设置配置。输入为一个 WG_IOCTL_INTERFACE 后接若干 WG_IOCTL_PEERWG_IOCTL_ALLOWED_IP。驱动解析并应用配置(设置监听端口、私钥、添加/删除对等体、更新 AllowedIPs)。
  • WG_IOCTL_SET_ADAPTER_STATE(METHOD_BUFFERED):设置适配器状态(UP/DOWN)或查询当前状态。
  • WG_IOCTL_READ_LOG_LINE(METHOD_BUFFERED):读取下一条日志条目(环形缓冲区)。

关键辅助函数:

  • HasAccess:使用安全描述符检查调用者权限。
  • WgDeviceFromFdo:从设备对象提取 WG_DEVICE 指针(利用 NdisWdfGetAdapterContextFromAdapterHandle)。

Get 操作 :遍历对等体和 AllowedIPs,填充输出缓冲区。注意使用锁保护(DeviceUpdateLock 和各个子锁)。

Set 操作 :解析 WG_IOCTL_INTERFACE,调用 SetListenPortSetPrivateKey,并循环处理每个对等体(SetPeer)。SetPeer 中会查找或创建对等体,更新预共享密钥、端点、持久保活间隔,并调用 AllowedIpsInsertV4/V6Remove 更新路由表。

SetAdapterState :根据命令调用 UpDown,内部会创建/销毁 UDP 套接字、更新链路状态、启动/停止定时器。

ReadLogLine :从 LOG_RING 中读取一条日志(非阻塞等待,可被设备移除事件中断)。

5. NSI 集成(nsi.c / nsi.h)

为了监控和调节接口 MTU,驱动挂钩了 NSI(网络状态指示器)的 IOCTL_NSI_SET_ALL_PARAMETERS 调用。NsiDriverEntry 替换了 IRP_MJ_PNPIRP_MJ_DEVICE_CONTROL 的派遣例程,并在首次有设备激活时附加到 \Device\Nsi 设备对象。

  • NsiActivate :将设备加入全局 DeviceList,若列表原本为空,则调用 Attach 挂载过滤设备。
  • NsiDeactivate :从列表中移除,若列表变空则调用 Detach 卸载过滤。

过滤派遣例程 FilterDispatch 拦截 IOCTL_NSI_SET_ALL_PARAMETERS,提取出 MIB_IPINTERFACE_ROW(包括接口 LUID 和 MTU),然后调用原始的 NSI 请求,并在成功后回调 IpInterfaceChangeNotification,更新设备的 Mtu4Mtu6(根据地址族)。该回调也通过 NotifyRouteChange2 注册,以响应路由变化。

通过这种方式,驱动能感知上层设置的 MTU,并调整数据包填充大小,确保不超过 MTU。

6. 自测试与调试

在调试版本中,main.c 中会执行一系列自测试(加密、AllowedIPs、计数器、速率限制器),这些测试定义在 selftest/ 目录下的文件中(如 chacha20poly1305.callowedips.ccounter.cratelimiter.c)。测试失败将导致驱动加载失败(返回 STATUS_INTERNAL_ERROR)。

7. 总结

第一部分的代码构成了驱动的基础骨架,负责:

  • 驱动的加载、初始化、卸载。
  • NDIS 微型端口驱动的注册和回调实现。
  • 用户态 IOCTL 接口,实现配置的获取、设置及适配器状态控制。
  • 与 NSI 集成,自适应 MTU 变化。

后续文档将深入探讨加密与 Noise 协议、数据包收发路径以及辅助模块。

相关推荐
百事牛科技2 小时前
Excel部分单元格禁止修改:锁住该锁的,放开该放的
windows·excel
水饺编程2 小时前
第5章,[Win32 章节] :创建、选择和删除画笔
c语言·c++·windows·visual studio
沫璃染墨3 小时前
《从零入门Linux系统篇(二十九):文件篇·二——深入文件描述符:从文件描述符表到重定向,再到Shell实现》
linux·运维·服务器·c++·驱动开发·系统架构
曹牧4 小时前
C#与Java后台交互
java·windows·microsoft·c#
꯭爿꯭巎꯭4 小时前
视频画质修复软件
windows
凯哥Java5 小时前
ChatGPT Windows 客户端突然打不开?提示找不到 Codex CLI,我这样解决了
windows·chatgpt
Jaixln_HRF6 小时前
率能SS6635E 单通道4.5A/30V直流电机驱动芯片,高压大电流,用于电子锁/玩具/机器人
驱动开发·嵌入式硬件·机器人·硬件工程
Jaixln_HRF6 小时前
率能SS6623E 单通道4.3A/20V直流电机驱动芯片,超低导通电阻110mΩ,用于电子锁/玩具/机器人
驱动开发·嵌入式硬件·机器人·硬件工程
薛定猫AI1 天前
OpenAI Codex本地完整配置教程|(Windows/Mac/Linux全平台,修复鉴权报错)
linux·windows·macos