KERNEL VIRTUALIZATION / NESTED HVM
Nested HVM 拓扑变化
KSword HVM 不引导第二个 Windows。它对已经在跑的那个系统做一次后期接管:VMLAUNCH 之后,同一个执行上下文原地继续,只是从此运行在 VMX non-root,而 KSword 在 VMX root 处理它产生的每一次 VM exit。下面这台演示机逐步走完这个过程。
DEMO
七步拓扑演示
演示按 KSwordDEV/KSword@main 的 HVM 协议与嵌套架构文档编排。读数取自文档记录的 2 vCPU 嵌套 Hyper-V 靶机实测,不是裸机结论;本页不执行任何真实虚拟化操作。
它不启动第二个 Windows
这是最容易读反的一点。VMLAUNCH 之后没有重启、没有第二个内核、桌面上什么都不会发生——原本那个 Windows 的执行上下文原地往下跑,只是从此处在 VMX non-root 模式。演示里那一行「Windows」自始至终是同一个节点,第 5 步只是往它上面插了一层。
正因为看不见,它也最容易被误判成「没生效」。所以判据不能靠肉眼:RESIDENT_ACTIVE 是状态位不是心跳,权威判据是 residentProcessorCount > 0,更硬的判据是 CR4.VMXE——蓝屏转储里这一位为 0,就意味着崩溃时这一层根本没在跑。
一次性来宾 vs 常驻
驱动里有两种进入 VMX 的方式,差别不是程度而是种类。
维度
一次性来宾(one-shot guest)
常驻(resident)
谁在 non-root 里跑
驱动自己造的一小段测试代码
正在运行的这个 Windows
持续多久
几条指令,立刻 VMXOFF
一直,直到显式停止
能观测什么
只有那段测试代码
全机器的每一次 VM exit
用途
证明 VMX 能进能出
真正的监控与拦截
停掉常驻之后 CPU 回到 VMX 关闭状态,EPT 不再被硬件遵守,所有基于 EPT 的能力——隐蔽 Hook、分离视图、执行域、R-1 进程处置——全部同时失效。不是降级,是那一层不在了。
这也解释了两件反直觉的事:EPT 规则、分离视图和 R-1 处置只能在常驻停着时安装(常驻期间退出路径不持锁就在读这些表),所以界面上「装一条处置」实际是 停常驻 → 装 → 重启常驻;而 sc stop 在常驻期间返回 1052,因为卸载守卫在拦——映像正在 VMX root 里被执行,卸载它等于把机器打死。
嵌套带来的三条硬约束
嵌套不是「和裸机一样,只是慢一点」。外层决定了内层能看见什么,下面三条都是在靶机上实测撞出来的。
没有 Monitor Trap Flag
嵌套 Hyper-V 不向来宾通告 MTF。默认那套「写叶 + monitor-trap 恢复」的隐蔽 Hook 后端因此根本装不上,只有 EPTP 切换后端能工作——它只要求 execute-only EPT 叶(IA32_VMX_EPT_VPID_CAP bit 0),而那一位在同一台机器上可用。这是选那套后端的唯一理由:能力,不是性能。
跨核 TLB 失效要自己做
转发给 L0 的 flush hypercall 只让 L0 去失效它认为的那个 L1 VP,而那个 VP 正在跑我们的 L2,兄弟核上的陈旧翻译不会被清掉。这个缺陷只在「我们插在 Windows 和 Hyper-V 中间」时存在。修法是私有 VMX-root IDT + 广播 NMI,并且私有 IDT 是发 NMI 的前提而不是优化——第一次只加 NMI 广播就撞了 0x80 NMI_HARDWARE_FAILURE。
必须显式声明允许嵌套
检测到外层已有 hypervisor 而请求里没带 ALLOW_NESTED 时,驱动直接拒绝启动常驻,而不是静默降级——静默降级出来的东西没人能判断它到底在保护什么。常驻成功时会打上 RESIDENT_NESTED,界面明确显示「作为 L1 运行,性能与可用能力均降级」。
跨核失效:开关一次,现象跟着开关一次
判据不用「跑很久没崩」那种统计实验,而是确定性的:VirtualProtect 返回的语义就是「所有处理器都已看到新保护」。主线程把一页改成 PAGE_NOACCESS 并等它返回,之后绑在别的处理器上的线程再去读——读成功就是用了一条本该已被失效的映射。
轮次
常驻核数
窗口内取样
违规
基线
0
10,429,398
0
常驻(修复前)
2
180,208,570
174,787,358(97%)
基线
0
10,644,062
0
常驻(修复后)×3
2
6,434,264 / 6,952,434 / 6,782,150
0 / 0 / 0
除了违规计数归零,取样量级也从 1.8 亿掉回 600–700 万、与基线同量级——读会正常触发访问违例才会这么慢。只看「违规 = 0」可能是探针失灵,量级一起对上才说明真的在走页表。这个缺陷同时解释了此前三种没有归因的崩溃:0x139 (0x1D)、0x0A 与 0x50。
未启用 VPID 时 VM entry 会失效与 VPID 0000H 关联的线性映射,广播 NMI 才能治好跨核失效。一旦为了性能启用 VPID,那次 entry 什么都不刷,修法会静默失效——失效形态就是上表那个 97%,不会有任何报错。所以这两件事必须一起改。
为什么开着 VBS 的物理机走不通
这不是策略问题,是一条硬事实。开着内存完整性或 Credential Guard 任一项就会拉起 Hyper-V;Hyper-V 独占 VMX root,并且对根分区隐藏 CPUID.1:ECX[5]。于是连「这台机器有 VT-x」都探测不到,PREPARE 就失败了——根本走不到任何一处 HYPERVISOR_PRESENT 判断。
关键在于根分区拿不到嵌套 VMX。Hyper-V 确实支持把虚拟化扩展暴露给 guest(Set-VMProcessor -ExposeVirtualizationExtensions),但那是给它的 guest 分区用的,根分区不在其列——而 KswordARK 驱动就跑在根分区。
因此可选项只有两条,都需要重启,都由使用者自己决定:关掉内存完整性并 bcdedit /set hypervisorlaunchtype off 在本机跑(代价是失去 HVCI 与 Credential Guard),或者保留 VBS、在开了 ExposeVirtualizationExtensions 的子分区里跑。产品侧维持 fail-closed,并把原因如实报出:NestedNotAllowed 与 HypervisorConflict 是分开的两态,因为前者用户自己能解决。
不做的是:探测到 VBS 就尝试抢占 VMX root,或者绕过 CPUID 隐藏去猜 VMX 是否真的存在。两者都是拿使用者的机器去赌一个无法验证的假设。
边界
常驻 HVM 是实验性后端,仅适用于已获授权的实验与诊断环境。启动前请保存工作:VMCS、EPT、固件、Hyper-V/VBS、嵌套虚拟化或处理器实现异常都可能导致系统不稳定、蓝屏或必须重启。
隐蔽 Hook、分离视图与 R-1 进程处置不是安全边界:它们失败即放行,目标若能让代码页换一个客户物理页,就不在被拒绝的那一页上了。这是 R0 之外的一条处置通路,不是用来对抗一个知道它存在的对手。上面的读数来自嵌套靶机,路线图中多项裸机验收仍未完成。



