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)启用了自测试,还会执行 CryptoSelftest、AllowedIpsSelftest、PacketCounterSelftest 和 RatelimiterSelftest。

卸载例程 Unload 依次调用 DeviceUnload、WskUnload、PeerUnload、RatelimiterUnload、AllowedIpsUnload、RcuUnload、MemUnload 释放资源。

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

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

3.1 核心数据结构 WG_DEVICE

定义在 device.h 中,包含:

  • MiniportAdapterHandle -- NDIS 适配器句柄。
  • Statistics -- 接口统计信息(收发字节、包数等)。
  • ItemsInFlight -- 引用计数,用于控制暂停/重启时的资源释放。
  • 多个环形队列(EncryptQueue、DecryptQueue、HandshakeRxQueue)用于异步处理。
  • 多核工作队列(EncryptThreads、DecryptThreads、HandshakeTxThreads、HandshakeRxThreads)用于并行加解密和握手。
  • 套接字指针 Sock4 和 Sock6(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 适配器状态管理

DeviceStart 和 DeviceStop 分别启动/停止设备,其中 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_PEER 和 WG_IOCTL_ALLOWED_IP 结构。若缓冲区不足,返回 STATUS_BUFFER_OVERFLOW 并给出所需大小。
  • WG_IOCTL_SET(METHOD_IN_DIRECT):设置配置。输入为一个 WG_IOCTL_INTERFACE 后接若干 WG_IOCTL_PEER 和 WG_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,调用 SetListenPort、SetPrivateKey,并循环处理每个对等体(SetPeer)。SetPeer 中会查找或创建对等体,更新预共享密钥、端点、持久保活间隔,并调用 AllowedIpsInsertV4/V6 或 Remove 更新路由表。

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

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

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

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

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

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

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

6. 自测试与调试

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

7. 总结

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

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

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

相关推荐
kakakahahahaha21 小时前
Win10开始菜单打不开排查:explorer、AppX注册、SFC、服务与用户配置修复
windows·电脑·笔记本电脑·内容运营·软件需求
ITHAOGE151 天前
下载 | Windows Server 2016最新纯净版ISO映像!(集成9月更新、标准版、数据中心版、14393.9514)
windows·科技·微软·电脑
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
懒人81 天前
Windows11系统优化指南
windows·经验分享
Vex2une1 天前
Windows 磁盘管理发展史:从 FDISK 分区表到存储池与虚拟磁盘
windows·gpt·磁盘管理·mbr·fdisk·动态磁盘·储存池
今夕资源网1 天前
从夯到拉模拟器RankPix windows本地版 exe
windows·exe·从夯到拉·从夯到拉模拟器·rankpix·本地版
kakakahahahaha1 天前
Windows系统时间校准指南:time命令、date命令、w32tm /resync与CMOS排查
windows·笔记本电脑·软件需求
沫璃染墨1 天前
《从零入门Linux系统篇(五十八):线程篇·十一——线程安全与死锁详解:从可重入到多锁管理》
linux·服务器·开发语言·c++·驱动开发·安全·架构
ebiobiz1 天前
Zig 工具链编译 STM32 开发指南
驱动开发·stm32·嵌入式硬件
一技安身1 天前
【mysql】Windows MySQL 8.4.10 绿色免安装喂饭教程
windows·mysql·adb