
简介VMCSVirtual Machine Control Structure虚拟机控制结构是 Intel VT-x 技术中最核心的数据结构几乎所有 VMX 操作都围绕它展开。管理 VMCS 的内存布局、字段分类和生命周期管理是编写 Hypervisor 的必要前提。VMCS 是什么VMCS 是一块由处理器管理的、位于物理内存中的数据结构用于保存Guest客户机的完整处理器状态包括通用寄存器之外的几乎所有架构状态CR0/CR3/CR4、段寄存器、RIP、RSP、RFLAGS、调试寄存器、MSR 等。Host宿主机/Hypervisor的处理器状态VM-exit 发生时处理器应恢复到的状态。VM 执行控制域决定哪些指令/事件会触发 VM-exit例如 CR3 访问、CPUID、RDMSR/WRMSR、I/O 端口访问、EPT violation 等。VM-entry / VM-exit 控制域控制进入和退出虚拟机时的行为如是否加载 MSR、是否注入事件等。只读的 VM-exit 信息域记录最近一次 VM-exit 的原因和相关细节exit reason、exit qualification、guest linear address 等。每一个逻辑处理器不是核心数可以关联多个 VMCS但同一时刻只能有一个处于active活动状态。VMCS 的内存区域格式VMCS 本质上是一段 4KB 对齐的物理内存VMXON region 同理。它的大小由IA32_VMX_BASICMSR 报告通常不超过 4KB实际有效字节数由处理器实现决定。内存布局大致分为两部分---------------------------------- | VMCS Revision Identifier (4B) | | VMX-Abort Indicator (4B) | ---------------------------------- | VMCS Data | | (guest-state / host-state / | | control fields / exit info...) | ----------------------------------这里的 Data 区域是由Intel决定的我们无需知道每一个字段具体的偏移。具体来说Revision Identifier每次分配 VMCS 内存后必须先从IA32_VMX_BASICMSR 的低 31 位读取当前处理器支持的 revision ID写入 VMCS 区域的前 4 字节否则VMPTRLD/VMLAUNCH会失败。数据区格式对软件不透明软件不能直接用偏移量读写这块内存,必须通过VMREAD/VMWRITE指令并通过encoding编码值来访问具体字段,而不是内存偏移。这是 Intel 为了保持向后兼容和跨微架构灵活性而设计的抽象层。VMCS 的状态机一个 VMCS 在其生命周期中会处于以下几种状态之一状态说明Active通过VMPTRLD加载到某个逻辑处理器上,该处理器上执行的VMREAD/VMWRITE/VMLAUNCH/VMRESUME会作用于这个 VMCSCurrent与 active 含义基本重合,表示当前生效的 VMCS,是 VMLAUNCH/VMRESUME 的隐式操作对象Clear通过VMCLEAR显式清除,表示该 VMCS 不再与任何处理器关联,处理器缓存的相关状态被写回内存Launched该 VMCS 已经成功执行过一次VMLAUNCH,之后必须使用VMRESUME而非再次VMLAUNCH重要约束一个 VMCS 同一时刻只能在一个逻辑处理器上 active。多核环境下每个 VMCS 在迁移到另一个逻辑处理器使用前必须先在原处理器上执行VMCLEAR否则会因为处理器内部缓存VMCS cache不一致导致未定义行为。这也是多核 Hypervisor 中常见的 “VMCS 迁移” 陷阱之一。VMCS 字段VMCS 的数据字段可分为六大类。Intel SDM Volume 3C Chapter 24 有完整定义。每个字段有唯一的 encodingencoding 的位域本身还编码了字段的宽度16/32/64/natural-width和类型control/read-only/guest-state/host-state。Guest-State保存 VM-entry 时加载、VM-exit 时保存的 guest 处理器状态,包括控制寄存器CR0、CR3、CR4部分位受 CR0/CR4 Guest/Host Mask 与 Read Shadow 影响调试寄存器 DR7段寄存器CS/SS/DS/ES/FS/GS/LDTR/TR及其 selector、base、limit、access rightsGDTR / IDTR base 和 limitRSP、RIP、RFLAGS部分 MSRIA32_SYSENTER_CS/ESP/EIP、IA32_EFER、IA32_PAT等若相应 VM-entry/exit control 开启Guest 非寄存器状态activity stateactive / HLT / shutdown / wait-for-SIPI、interruptibility state、pending debug exceptions、VMCS link pointerHost-StateVM-exit 发生时处理器会将执行状态切换为该区域中保存的值。基本对称于 guest-state 中的寄存器项但不包含段寄存器的 base/limit/access rightsVM-exit 时段寄存器按固定规则重新加载也不保存 RFLAGSVM-exit 后 RFLAGS 由处理器按架构定义的固定值设置。如果对syscall熟悉的就会发现Guest - Host 的切换就像是 User - Krnel 一样。VM-Execution Control Fields决定 guest 运行期间哪些操作会触发 VM-exit是 Hypervisor 拦截逻辑的核心配置主要包括Pin-Based VM-Execution Controls外部中断、NMI 相关的拦截开关Processor-Based VM-Execution ControlsPrimary / SecondaryCR3-load/store exiting、CR8 exitingMOV-DR exiting、使用 I/O bitmapRDTSC/RDTSCP exitingactivate secondary controls位决定是否解析 Secondary 控制域Secondary 控制域中常用的有Enable EPT、Enable VPID、Unrestricted Guest、Enable RDTSCP、Enable INVPCID、APIC-Access 相关虚拟化、Enable XSAVES/XRSTORS等Exception Bitmap32 位位图决定哪些异常#PF/#GP/#UD 等需要 VM-exitI/O Bitmap A/B、MSR Bitmap细粒度控制特定端口/MSR 访问是否 exitCR0/CR4 Guest/Host Mask 与 Read Shadow实现虚拟化控制寄存器某些位的关键机制,允许 Hypervisor 让 guest 看到与实际硬件不同的 CR0/CR4 值EPT PointerEPTP指向 guest 的扩展页表根,是实现内存虚拟化和 EPT Hook 的入口VPID为 guest 的 TLB 条目打标签,避免每次 VM-entry/exit 都全局刷新 TLBVM-Exit Control Fields控制 VM-exit 发生时的行为例如Host address-space size是否切换到 64 位模式是否在 VM-exit 时保存/加载特定 MSRVM-exit MSR-store/load area是否应答外部中断的 acknowledge interrupt on exitVM-Entry Control Fields控制 VM-entry 时的行为例如Guest address-space sizeVM-entry 时是否加载 MSRVM-Entry Interruption-Information Field用于向 guest 注入中断/异常/软件中断,是实现异常转发、中断注入的关键字段VM-Exit Information FieldsVM-exit 发生后由处理器自动填写Hypervisor 在 VM-exit handler 中读取这些字段来判断发生了什么典型字段Exit Reasonexit 的具体原因如 EPT violation、CPUID、RDMSR、CR access 等编号Exit Qualification与 exit reason 配套的附加信息例如 EPT violation 时具体是读/写/执行权限缺失Guest-Linear/Physical AddressEPT violation 或分页相关 exit 时的地址信息VM-Exit Interruption Information / Error Codeexit 由异常/NMI 引起时的具体信息Instruction Length触发 exit 的指令长度常用于 VM-exit handler 中推进 guest RIPVMCS 与 EPT 的关系对于做 EPT Hook / 内存虚拟化的场景VMCS 中与之直接相关的字段主要是Secondary Processor-Based Controls 中的 Enable EPT 位必须置位才能启用 EPTEPT Pointer (EPTP)指向 EPT PML4 表的物理地址同时编码了 memory type 和 page-walk lengthINVEPT指令用于在修改 EPT 页表后使相关 TLB/paging-structure cache 失效避免处理器使用陈旧的转换缓存EPT-violation 也就是EXIT_REASON_EPT_VIOLATION会触发 VM-exitExit Qualification 中会标明是读/写/执行访问违规Guest-Physical Address 字段给出触发违规的 GPA这是实现EPT Hook的基础。VOIDMainVmexitHandler(PGUEST_REGS GuestRegs){ULONG ExitReason0;__vmx_vmread(VM_EXIT_REASON,ExitReason);ULONG ExitQualification0;__vmx_vmread(EXIT_QUALIFICATION,ExitQualification);DbgPrint(\nVM_EXIT_REASION 0x%x\n,ExitReason0xffff);DbgPrint(\EXIT_QUALIFICATION 0x%x\n,ExitQualification);switch(ExitReason){caseEXIT_REASON_VMCLEAR:caseEXIT_REASON_VMPTRLD:caseEXIT_REASON_VMPTRST:caseEXIT_REASON_VMREAD:caseEXIT_REASON_VMRESUME:caseEXIT_REASON_VMWRITE:caseEXIT_REASON_VMXOFF:caseEXIT_REASON_VMXON:caseEXIT_REASON_VMLAUNCH:{break;}caseEXIT_REASON_HLT:{DbgPrint([*] Execution of HLT detected... \n);AsmVmxoffAndRestoreState();break;}caseEXIT_REASON_EXCEPTION_NMI:{break;}caseEXIT_REASON_CPUID:{break;}caseEXIT_REASON_INVD:{break;}caseEXIT_REASON_VMCALL:{break;}caseEXIT_REASON_CR_ACCESS:{break;}caseEXIT_REASON_MSR_READ:{break;}caseEXIT_REASON_MSR_WRITE:{break;}caseEXIT_REASON_EPT_VIOLATION:{break;}default:{// DbgBreakPoint();break;}}}就像这样。EPT Hook被广泛应用到外挂辅助驱动中是现代的反作弊领域的关键技术之一。不过Intel最初设计时应该是没想过EPT Hook这一点的。参考Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3C, Chapter 24Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3C, Chapter 25-28https://github.com/DarthTon/HyperBonehttps://github.com/ionescu007/SimpleVisorhttps://github.com/hyperdbg/hyperdbg