前阵子帮一个做安全软件的团队排查问题。他们的过滤驱动在自家测试机上一切正常,发给客户后设备管理器直接黄叹号,事件查看器 System 日志刷出 Event ID 219,应用层拿到的错误是 0xC0000428(STATUS_INVALID_IMAGE_HASH)。客户那台机器开着 Secure Boot,组策略又把 VBS / HVCI 打开了。团队负责人第一句话是"可我都用 EV 代码签名证书签过了啊"。问题就在这儿:在 64 位 Windows 上,内核驱动真不是你签了名就能跑,得微软签过、还得 HVCI 认,缺一样都白搭。
一、从开机到驱动加载,中间卡了四道关
这事得从按下电源说起。驱动不是凭空进内核的,系统从开机到把你的 .sys 映射进去,中间有四道关,一道比一道硬:
| 阶段 | 守门者 | 校验什么 | 失败表现 |
| UEFI 启动 | Secure Boot | Bootloader / OS Loader 是否由微软(或白名单密钥)签名 | 直接拒绝启动,进入恢复 |
| 内核初始化 | VBS / 虚拟化层 | 把 Code Integrity 策略搬进 hypervisor 保护的内存 | CI 策略不可被内核篡改 |
| 驱动映射 | DSE(驱动签名强制) | 驱动与 .cat 是否链到 Microsoft Trusted Root | 0xC0000428 , Event 219 |
| 页属性落地 | HVCI(内存完整性) | 内核代码页是否满足 W^X 等兼容性 | Code Integrity 事件拦截,驱动不加载 |
说白了,Secure Boot 管的是启动这条链可不可信,HVCI 管的是跑起来之后内核里的代码干不干净。SecureBoot 给HVCI 提供了一个内核改不了的执行环境,HVCI才在最后一刻对驱动做裁决。两头一卡,结论就一句:驱动既得是微软签的,又得HVCI 兼容。
二、先从签名说起:DSE 早就不认你自己的签名了
先说签名这道。早年的64 位 Windows 允许驱动拿 CA 交叉证书(cross-certificate)签了就加载,这条路在 Windows 10 1607(2016 年)被微软堵死:从那以后,新的内核模式驱动必须走 Windows 硬件开发者中心(WHCP),由微软对你的目录文件 .cat 签名,才能链到 Microsoft Trusted Root。老的交叉证书慢慢到期,到 2021 年前后基本就没人管用了。
所以你拿 EV 代码证书亲手给 .sys 签了名也没用,信任链根本到不了微软根,DSE 二话不说拦下来。真正能让 DSE 放行的,是带 MicrosoftWindows Hardware Compatibility Publisher 签名的那个 .cat,也就是过完 WHQL / HLK 之后微软发给你的那张。
开发阶段可以用 bcdedit /set testsigning on 临时放行测试签名,但那是带水印、自降安全等级的调试模式,上生产想都别想。而且 Secure Boot 一旦严格开着,这条后门也未必好使。
三、再是 HVCI:它压根不看你签名对不对
不少团队签名这关过了,到了开了 HVCI 的客户机器上还是挂,因为 HVCI 看的不是签名。
HVCI(Hypervisor-ProtectedCode Integrity)靠hypervisor 兜着,对内核模式代码立了条死规矩:任何内存页不能又写又执行(W^X),内核代码必须是静态的、签过名的。驱动映射进内核的时候它会挨个查节(section)的属性,下面几种情况直接拦:
运行时申请一块 RWX 内存塞代码进去,比如自解密壳、JIT 那类;
把数据页改成可执行页,某些HS 防护、热补丁、inline hook 框架常干这种事;
用了不兼容 HVCI 的分配/ 映射接口,或者依赖写完还能执行的节。
这种失败在 Microsoft-Windows-CodeIntegrity/Operational 日志里会记一条"驱动因不兼容 HVCI 被阻止",病因不是签名,是内存完整性。一句话:签了名不等于能加载,HVCI 认才算数。
四、为什么 WHQL 是绕不过去的那个口子
把这两关放一块就清楚了。DSE 要的是签名链到微软根,只有交 WHCP 拿到微软签的 .cat 才满足;HVCI要的是代码内存兼容,而 HLK 的测试项里本来就带 HVCI 兼容性检查,能过测基本等于代码层面达标。
所以"过WHQL"本质上就是同时满足这两点的唯一正规路子:微软的签名解决信任链,HLK 的兼容性测试解决 HVCI。那些"自己签了再关掉校验"的野路子,要么只在测试模式里混得下去,要么直接撞上客户的安全基线。
五、正经走一遍 WHCP(HLK 路径)
最常见的是 HLK 全测这条路,步骤大概这样:
1. 身份和 EV 证书
先在 Windows 硬件开发者中心注册组织,手里得有一张有效的 EV 代码签名证书,后面给测试结果包签名、向微软证明你是你,都靠它。
2. 搭 HLK 环境
把 HLK控制器(Studio / Controller)和目标测试机部署好,要覆盖的 Windows 版本尽量都覆盖,至少 Win10 / 11 主流版本别漏。
3. 跑测试,出 .hlkx
对驱动跑 WHCP 相关测试家族,HVCI 兼容性那几项别跳过,跑完导出 .hlkx。
4. 交仪表板
用 EV 代码签名证书给 .hlkx 签上名,传上硬件开发者中心等微软审。
5. 拿回签名 .cat
微软签完你下回来,跟 .sys、INF一起打包发。安装时 INF 里引用这个.cat,DSE 就放行了。
早些年纯软件驱动还能走attestation 签名,现在那个口子基本关了,都往 HLK 收。新项目别纠结,直接 HLK。
六、代码里真正要改的几处
HVCI 拦得最多的其实不是签名,是老写法。提交之前先把自己代码过一遍:
先把 RWX 内存清掉:代码页标 NX(不可写),数据页可写但不可执行;要可执行的内存池用带 NX 语义的标志去分配。
运行时改可执行代码的事别干了:inline hook、热补丁、自修改代码在 HVCI 下基本活不成,换微软给的回调 / 过滤框架(WFP、minifilter、ETW 这些)。
分配接口用对:涉及可执行内存优先走带 NX 语义的接口,别再抱着老 ExAllocatePool的写法不放。
节属性别搞野的:节表老老实实按标准来,可执行节不要顺手叠个可写标志。
早点把 HVCI 测试接进 CI:别等到了客户机器上才暴露。
七、翻车了怎么定位
| 你看到的现象 | 实际是什么 | 往哪使劲 |
| Event ID 219 + 0xC0000428 | 镜像哈希无效,签名链不到微软根 | 走 WHCP 拿微软签名 .cat |
| CodeIntegrity/Operational 里写 " 不兼容 HVCI" | 驱动内存不满足 W^X | 清掉 RWX / 自修改代码,过 HLK 的 HVCI 项 |
| 测试机正常、客户机挂 | 客户开了 Secure Boot + VBS / HVCI | 按生产安全基线重新测 |
| testsigning 下能加载 | 只是测试签名被放行 | 调试态而已,必须换成微软签名 |
| 关了 Secure Boot 还是被拦 | DSE 跟 Secure Boot 开不开没关系 | 还是签名的问题 |
Secure Boot 和 HVCI 不是两个互不搭界的安全开关,是从固件一路到内核的一条信任链:前者把启动链焊死、给后者一个内核改不动的底座,后者在驱动进内核那一刻做最后的内存完整性裁决。在这条链下面,"驱动必须过 WHQL"真不是走形式,而是技术上唯一能让 DSE 和 HVCI 同时放你过的口子。把 HLK(连同 HVCI 兼容性)提前塞进构建流水线,比在客户现场救火便宜太多。以上是我们团队在驱动合规上踩过的一些坑的整理,具体测试项和签名政策以微软 WHCP 文档和你目标 Windows 版本为准。