-
-
[原创]深度理解 VMX root vs VMX non-root
-
-
[原创]深度理解 VMX root vs VMX non-root

一、两个世界:VMX root vs VMX non-root 的本质隔离
Intel VT-x 把处理器分成两种运行态:
关键约束是:non-root 的代码无法直接调用 VMX root 的函数,两者地址空间上是连续的,但执行权限完全隔离。要从 non-root 进入 root,只有一条合法路径 —— 触发 VM-exit。
以 !epthook 为例:
EPT hook 的核心是把目标页的内容复制到一个新位置,在新页上写入 0xcc 断点,然后清除原 EPT 页表项的 Read/Write 位,只保留 Execute 位,并把该 PTE 指向新页的物理地址。
这意味着你需要直接操作 EPT 页表(二级页表结构)。如果在 VMX non-root 里做这件事,会有几个严重问题:
因此架构上规定:EPT 表结构的写入操作,必须在 VMX root 中完成。
从 VMX non-root 进入 VMX root 的唯一主动方式是执行 VMCALL 指令。它在 non-root 中执行时**无条件触发 EXIT_REASON_VMCALL(exit reason 18)**的 VM-exit,处理器切到 VMX root,HyperDbg 的 VM-exit handler 接管。
HyperDbg 的调用约定是:
这本质上是一套用户自定义超级调用(hypercall)系统,类比 Linux 的 syscall 表:non-root 把"我要做什么"编码进寄存器,VMX root 的 handler 里做 switch(VmcallNumber) 分发到对应的操作。
为什么要 CALL_ID 而不是直接函数指针?
当调试器需要暂停 debuggee 时,用户态应用发 IOCTL 到内核,从 IOCTL 中执行 VMCALL 进入所有核心的 VMX root 模式。在 root 模式中,IF 标志被清零,不允许中断,所有核心就这样被暂停,以轮询模式等待来自调试器的命令。
对于其他核心的通知,HyperDbg 通过 XAPIC/X2APIC 向它们发送 NMI。由于配置了 NMI exiting,这些 NMI 触发 VM-exit,其他核进入 root 后在 spinlock 上自旋等待,实现了全核暂停。
!epthook2 等命令不在 vmx root 触发,保持在 vmx non-root 运行,这样省去了 VM-exit/VM-entry 的切换开销,速度更快。但脚本引擎中访问 @rip、@rsp、@cs 等寄存器依赖 VMREAD/VMWRITE,这两条指令在 non-root 不可用,因此 non-root 模式下脚本访问这些寄存器会导致 BSOD。
一句话总结这个架构的本质:HyperDbg 利用 VMCALL+CALL_ID 构造了一套从 ring 0(VMX non-root)向 ring -1(VMX root)的受控超级调用系统,目的是把所有需要原子性、需要操作 EPT 硬件结构的操作安全地转移到 hypervisor 层执行,同时把内存分配、参数构造等高级操作留在 non-root 完成,两侧各司其职。
[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于 1天前
被4seaynl编辑
,原因: