第11章 其他主题

收尾工具箱:驱动签名、Driver Verifier、Native API、传统过滤驱动与设备通知,以及三条技术路线的支持边界。

学习目标

  • 能解释驱动签名的测试/发布两条路径,以及 Driver Verifier 的使用边界(测试机 + 目标驱动)
  • 能改出“设备到达通知”的注册与回调,并完成用后注销
  • 能回答:为什么 SSDT 挂钩在现代 Windows 上不可行?新代码应该选哪条路线?

为什么大楼交付前要体检、发证、装监控

想象大楼里新开一家公司(你的驱动要进驻系统)。搬进来之前有三道手续:体检(查身体有没有暗病——内存乱用、锁没锁好)、发通行证(没有有效证件,64 位大门直接拒入)、装监控(新设备接进来要有人知道)。大楼里还有几条老路和一条禁区:老路(旧式过滤驱动)还在但没人维护,禁区(改大楼结构图)一碰就触发警报。

没有这些手续会怎样?带病上工的程序一次越界就把整栋楼搞崩;没证的程序根本进不了门;U 盘插进来没人响应,外设全成摆设。

这一章是收尾工具箱:体检(Driver Verifier)、发证(驱动签名)、近路(Native API)、旧地图(传统过滤驱动)、监控(设备通知),最后标出禁区(SSDT 挂钩)——知道什么不能做,和知道怎么做一样重要。

驱动签名:进 64 位大门的通行证

是驱动的“身份证”。开发调试阶段用测试签名:bcdedit /set testsigning on 打开测试模式 + 自签证书签名——但这会永久降低这台机器的安全边界,只许用在可丢弃的测试机。发布阶段用正式签名:EV 证书签名后提交微软门户,拿回 attestation 签名(微软背书),这是兼容 HVCI(内存完整性)的唯一途径。

一个常见混淆:测试签名的驱动换到生产机直接拒绝加载,报“无法验证此驱动软件的发布者”——因为生产机不认你的测试证书。

Driver Verifier:内核自带的体检仪

把驱动放进“放大镜”下跑:内存越界、错误 IRQL、锁顺序问题、引用泄漏,任何一条规则被违反就当场蓝屏,并借崩溃转储定位到责任代码——与其在用户现场随机蓝屏,不如在测试机上有预谋地蓝屏。

铁律两条:只在测试机开(全局开启性能下降 3-5 倍甚至无法启动);只对目标驱动开(verifier /driver MyDriver.sys)。违规转储用调试器的 !analyze -v 就能找到出错栈。

Native API:绕开 Win32 的近路

是 Win32 之下的“原始通道”:CreateFile 最终调用 NtCreateFileReadFile 最终调用 NtReadFile。用户模式程序可以直接调用 ntdll.dll 导出的这些函数,拿到 Win32 层不暴露的参数(如完整对象属性、标志位)。

风险在于:Native API 是未承诺稳定的内部接口,参数语义随版本变动,且签名行为与 Win32 不同(如不自动初始化 OBJECT_ATTRIBUTES)。能用 Win32 就用 Win32,Native API 只在确实需要底层控制时用。

传统过滤驱动:还在但没人维护的老路

是第 10 章 Minifilter 的前身:不依赖过滤器管理器,自己写整套 MajorFunction 分发例程,自己管理设备栈附加、IRP 传递、名字缓冲……所有基础设施都要手搓,与新版文件系统兼容性差。

现在写新代码没有理由再走这条路:Minifilter 把基础设施全包了(第 10 章),传统过滤驱动只作为“读老代码”的迁移知识保留。判断标准很简单——新工程选受支持框架,老代码才谈迁移。

设备通知:大楼的入住监控

让驱动对热插拔有反应:U 盘插入 → 设备到达事件 → 你的回调被调用 → 打开设备接口、完成初始化。没有它,即插即用设备对驱动来说“来了也不知道”。

注册时指定事件类别(设备接口变化)、设备接口 GUID(只关心某类设备)和回调函数;返回的通知句柄必须在卸载时 IoUnregisterPlugPlayNotification 注销——和前面所有回调一样,注销是纪律的一部分。

动手:走一遍交付流水线

下面的可视化把整章工具画成一张图:上部是交付流水线五个阶段(开发 → Verifier → 测试签名 → 发布签名 → 部署通知),中部是 Verifier 工作台三步骤,下部是三条技术路线。点阶段框看说明;打开“全局 Verifier”开关模拟误操作;点路线三列看支持边界。

猜一猜:把“全局 Verifier”开关打开——结果条会变成什么颜色?提示文字说明了什么恢复手段?为什么“只查目标驱动”才是正确姿势?

⚡ 驱动交付工具箱:体检 → 签名 → 部署 → 选路线
驱动交付流水线:体检 → 签名 → 部署(点击阶段查看)① 开发源码 + 符号② Verifier测试机体检③ 测试签名自签证书④ 发布签名EV + 微软⑤ 部署通知设备到达Driver Verifier 工作台(只在测试机,只查目标驱动)verifier /flags 0x1 /driver MyDriver.sys → 违规当场 bug check → 转储定位① 选择驱动verifier /driver 目标.sys② 选择规则池 / IRQL / 锁 / 引用③ 检测结果违规 → 蓝屏 + 定位正确:只对目标驱动验证,违规可定位、系统不受拖累路线选择:新代码该怎么写?Minifilter / WDF受支持 · 推荐第 10 章小过滤 + 框架对象传统过滤驱动遗留 · 仅兼容老代码迁移理解,新代码别写SSDT 挂钩不支持 · 蓝屏PatchGuard 检测,当场 bug check当前选择:④ 发布签名 · 路线「Minifilter / WDF」受支持 · 推荐经验:交付前过完 ②③④ 三步,生产机保持 testsigning off
Driver Verifier

Driver Verifier 是内核自带的"体检仪":对目标驱动开启内存池、IRQL、锁、引用计数等规则检查,任何违规当场 bug check(蓝屏)并定位到出错驱动。只能在测试机上、只对目标驱动开——全局开启会让系统慢 3-5 倍甚至无法启动。违规时的崩溃转储配合调试器 !analyze 就能找到责任代码。

代码对照:五件工具的用法

这一节按原书结构拆解五段:设备通知注册、Verifier 验证流程、测试/发布签名、Native API 调用、新旧两代过滤框架对比。五段覆盖本章全部工具的关键用法,完整工程见章末来源链接。

段一:设备通知 —— 注册与回调

设备通知三步:填订阅参数(回调 + 上下文)→ IoRegisterPlugPlayNotification 注册(声明关心“设备接口到达”事件、按 GUID 过滤设备类别)→ 卸载时用返回的句柄注销。回调里区分事件类型(到达/移除),到达时打开接口初始化。

// 注册设备通知:只关心 USB 设备接口到达
NTSTATUS RegisterDeviceNotification(PDRIVER_OBJECT Drv)
{
    DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS params = { 0 };
    params.Callback = OnDeviceNotify;   // 通知回调
    params.Context = nullptr;
 
    return IoRegisterPlugPlayNotification(
        EventCategoryDeviceInterfaceChange,
        PNPNOTIFY_DEVICE_INTERFACE_ARRIVAL,
        (PUNICODE_STRING)&GUID_DEVINTERFACE_USB_DEVICE,
        Drv->DriverObject,
        &params, &g_notifyHandle);      // 注销凭证
}
 
VOID OnDeviceNotify(PVOID Context,
    PDEVICE_NOTIFY_CALLBACK_CONTEXT Ctx,
    PDEVICE_NOTIFY_PARAMETERS Params)
{
    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(Ctx);
    if (Params->Event == DeviceInterfaceArrival) {
        // 新设备到了:打开接口、完成初始化
        DbgPrint("Device arrived: %wZ\n",
            &Params->DeviceInterface);
    }
}

段二:Verifier —— 只查目标,违规即蓝

Verifier 是命令行工具,没有内核 API。标准流程:测试机管理员对目标驱动开启(/flags 指定规则组合,0x1 是内存池检查)→ 跑测试场景 → 违规当场蓝屏 → 用转储定位责任代码 → verifier /reset 结束(重启生效)。

:: 测试机上,只验证目标驱动(管理员)
verifier /flags 0x1 /driver MyDriver.sys
 
:: 跑压力场景;违规时系统立即蓝屏
:: 重启后收集崩溃转储:!analyze -v
:: 转储中责任模块就是违规驱动
 
:: 结束验证(重启后生效)
verifier /reset
 
:: 误开全局后无法启动:安全模式进入后
verifier /bootmode 1   (或 /reset 恢复)

段三:签名 —— 测试自签,发布走门户

签名流程分两轨:测试轨(测试机开 testsigning + signtool 自签证书)与发布轨(EV 证书 SHA-256 签名 + 时间戳 + 提交微软门户拿 attestation 签名)。发布轨的 attestation 签名是 HVCI 兼容的前提,缺它驱动在开启内存完整性的机器上被拒。

:: 测试签名(仅测试机)
bcdedit /set testsigning on
signtool sign /f MyTestCert.pfx /p pass MyDriver.sys
 
:: 发布签名(生产):EV 证书 + SHA-256 + 时间戳
signtool sign /fd sha256 /a /ph
    /tr http://timestamp.digicert.com MyDriver.sys
 
:: 提交微软硬件开发者门户 → attestation 签名
:: (HVCI/内存完整性兼容的唯一途径)

段四:Native API —— 用户模式直接走底层

Win32 的 CreateFile 内部就是调 NtCreateFile。直接调用原生 API 能拿到 Win32 不暴露的参数(对象属性、标志位),但必须自己初始化 OBJECT_ATTRIBUTES、自己处理 IO_STATUS_BLOCK——接口未承诺稳定,能不用就不用。

// 用户模式直接调用原生 API(ntdll.dll)
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\??\\C:\\tmp\\data.bin");
 
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &name,
    OBJ_CASE_INSENSITIVE, nullptr, nullptr);
 
IO_STATUS_BLOCK io;
HANDLE hFile = nullptr;
NTSTATUS st = NtCreateFile(&hFile,
    FILE_GENERIC_READ, &oa, &io, nullptr,
    FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
    FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
    nullptr, 0);
if (!NT_SUCCESS(st)) return st;

段五:新旧两代过滤框架 —— 一个逻辑,两种写法

传统过滤驱动与 Minifilter 实现的是同一件事(在文件 I/O 路径上插一手),但写法天差地别:老框架手写整套 MajorFunction 分发和 IRP 传递,新框架只声明回调表,基础设施全由过滤器管理器承包。

// 老框架:接管 IRP 分发例程,全部手写
NTSTATUS DriverEntry(PDRIVER_OBJECT Drv, PUNICODE_STRING)
{
    Drv->MajorFunction[IRP_MJ_CREATE] = FsFilterCreate;
    Drv->MajorFunction[IRP_MJ_READ]   = FsFilterRead;
    Drv->MajorFunction[IRP_MJ_WRITE]  = FsFilterWrite;
    Drv->DriverUnload = FsFilterUnload;
    return STATUS_SUCCESS;
}
 
// 每个分发例程都要:查栈位置 → 处理 →
// IoCallDriver 下传 → IoCompletion 回调收尾

容易踩的坑

小结

  • 驱动签名两轨:测试轨(testsigning + 自签)限测试机,发布轨(EV + attestation)才进生产
  • Driver Verifier 只对目标驱动开在测试机,违规当场蓝屏、转储定位
  • Native API 是 Win32 之下的底层通道,能用 Win32 就别直接用
  • 传统过滤驱动是遗留路线,新代码一律 Minifilter;SSDT 挂钩是 PatchGuard 禁区
  • 设备通知经 IoRegisterPlugPlayNotification 注册,卸载用句柄注销

练习

问题 1: 在 Demo 中打开“全局 Verifier”开关,观察结果条与恢复提示。为什么“违规当场蓝屏”恰恰是 Verifier 的价值,而不是它的缺点?

问题 2: 你的驱动要交付给开启 HVCI(内存完整性)的客户机器。给出完整的签名与发布检查清单,并说明为什么测试签名路径在这里不够用。

问题 3(独立实现题): 实现 USB 设备到达通知的三段:1)写出 RegisterDeviceNotification——订阅设备接口变化事件、按 USB 设备 GUID 过滤、保存通知句柄;2)写出 OnDeviceNotify 回调——到达时打印接口名、移除时打印移除;3)写出卸载注销片段——注销通知句柄并判空。要求回调区分到达/移除事件。

知识点对照

11.1 驱动程序签名

驱动程序签名用微软认可的证书签署驱动,系统启动时校验签名的合法性、完整性与发布来源,未签名驱动无法加载。

11.2 驱动程序验证器

驱动程序验证器强制检查内存分配、锁的使用与 IRQL 规范,非常适合在测试阶段开启并使用。

11.3 使用原生API

使用原生API直接调用 Nt 系列系统服务,可以绕过 Win32 层的部分限制与封装,直达内核。

11.4 过滤驱动程序

过滤驱动程序附加在设备栈上,拦截并处理来自上下层的 I/O 请求,从而实现对设备的监控与扩展功能。

11.4.1 过滤驱动程序的实现

过滤驱动程序的实现要点是复制目标设备对象并挂接到设备栈上,再把请求转发给下一层处理,同时保存原设备指针。

11.4.2 附加过滤器

附加过滤器在驱动加载时创建过滤设备对象,并将其附加到目标设备栈的顶部开始拦截工作,卸载时再摘除。

11.5 设备监视器

设备监视器用过滤驱动观察设备栈上的所有 IRP 请求,并记录每个请求的处理结果与最终状态信息。

11.5.1 增加过滤设备

增加过滤设备时创建过滤设备对象,并将其附加到目标设备栈之上,完成挂接并开始接收 I/O 请求。

11.5.2 移除过滤设备

移除过滤设备时先从设备栈上脱离,再删除过滤设备对象,并释放其占用的全部资源,恢复设备栈的原状。

11.5.3 初始化和卸载

初始化和卸载阶段负责注册与注销设备接口,管理过滤设备从创建到删除的整个生命周期过程,保证栈完整性。

11.5.4 处理请求

处理请求在过滤器的分发例程中执行,根据既定规则决定放行、修改或完成该请求,然后返回处理状态。

11.6 驱动程序挂钩

驱动程序挂钩通过替换函数指针或系统服务表来拦截系统调用,从而实现行为监控与安全防护功能,但易被检测。

11.7 内核库

内核库提供通用的内核函数集合,驱动程序可以静态链接并复用其中的代码,从而避免重复开发工作。

11.4.3 在任意时刻附加过滤器

在任意时刻附加过滤器需要枚举设备栈并用 IoAttachDeviceToDeviceStack 安全挂接。

11.4.4 过滤器的清理

过滤器的清理在卸载或设备移除时执行,先脱离设备栈再删除过滤设备对象,并释放占用的相关资源。

11.4.5 基于硬件的过滤驱动程序的更多内容

基于硬件的过滤驱动程序的更多内容涉及总线、端口与功能驱动之间的分层职责划分和协作关系,这也是本章的核心要点。

11.5.5 测试驱动程序

测试驱动程序用工具向目标设备发送请求,观察过滤设备记录的数据流是否完整正确,验证通过,这也是本章的核心要点。

11.5.6 请求的结果

请求的结果在完成例程中读取状态与返回数据,并记录到监视日志中供用户后续查看分析,这也是本章的核心要点。

11.8 总结

本章总结了签名、验证器、原生 API 与传统过滤驱动等其他主题的核心要点和落地实践经验分享。

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

驱动签名

对驱动文件做数字签名,64 位 Windows 强制要求。测试签名(自签证书)只限测试机;发布签名(EV + 微软 attestation)才是生产通道。相当于大楼发的通行证:临时访客证只让进测试楼,正式员工证才能进生产区。

Driver Verifier

Windows 内置的驱动验证工具:对目标驱动开启内存池、IRQL、锁、引用计数等规则检查,违规当场蓝屏并借转储定位。相当于大楼给员工做体检:有暗病当场查出来,总比入职后随时倒下强。

Native API

内核导出的底层系统服务接口(Nt*/Zw* 函数),Win32 API 内部就是调用它们实现的。相当于大楼的内部员工通道:Win32 是前台,Native API 是直达后台的楼梯——能用前台就走前台,内部通道不承诺稳定。

传统过滤驱动

Minifilter 出现之前的老式文件系统过滤驱动:自己接管 IRP 分发例程、自己管理设备栈。相当于旧式仓库管理员:一个人管全部流程,费力且易错;现在有了分工明确的值班岗体系(Minifilter),新仓库不再用旧办法。

设备通知

驱动通过 IoRegisterPlugPlayNotification 注册的回调机制,在设备到达、移除或接口变化时收到内核通知。相当于大楼的入住监控:新设备(U 盘)插进来,系统第一时间通知相关岗位,撤岗时注销通知。

讨论

评论区加载中…