MSR 是什么RAX、RBX、RCX等这些寄存器大家都很熟悉.它们数量有限,编号固定在逆向的过程中他是我们的老熟人。但 x86 处理器内部还有一大堆不参与日常运算但控制着处理器行为本身的寄存器——比如要不要开启长模式、“syscall 要跳到哪个地址”、“APIC 基地址在哪”、“性能计数器怎么配置”。这类寄存器如果也按一般寄存器的方式编码进指令里指令集会膨胀到无法维护而且这些寄存器很多是厂商/型号相关的Intel 和 AMD 之间、甚至 Intel 不同代际的 CPU 之间都可能不一样。于是 Intel 和 AMD 都设计了一套独立于通用寄存器的寄存器空间通过专门的指令RDMSR/WRMSR来访问这就是MSR(Model Specific Register模型特定寄存器)。MSR 解决的问题可扩展性:MSR 用一个 32 位的索引(index/address)去访问理论上有 2^32 个槽位可用远超通用寄存器的数量上限方便处理器一代代往里加新功能而不用改指令编码。权限隔离:RDMSR/WRMSR都是特权指令只能在 Ring 0 执行用户态代码完全碰不到天然把控制处理器底层行为这件事锁在了内核态。厂商/型号差异化:不同 CPU 型号可以有自己独有的 MSR(所以叫 ModelSpecific)同时保留一批架构 MSR(Architectural MSR)作为跨代际、跨型号都保证存在且语义一致的基础设施比如IA32_EFER、IA32_APIC_BASE。可以把 MSR 理解成处理器暴露出来的一整套配置寄存器阵列——每一个 MSR 都掌管着处理器某一块具体的行为从长模式开关到 syscall 跳转地址到虚拟化能力探测全部都在这里。MSR 的访问方式RDMSR / WRMSR; 读取 MSR: ECX 指定索引 mov ecx, 0xC0000080 ; IA32_EFER rdmsr ; 写入 MSR: ECX 指定索引 mov ecx, 0xC0000080 mov eax, 0x00000d01 mov edx, 0x00000000 wrmsr寻址方式很简单粗暴:ECX 里放 32 位索引值永远是 64 位通过 EDX:EAX 高低位拼接。这个模式从 Pentium 时代一直延续到现在非常稳定。对应到 C 层面一般会封装成这样的 intrinsic:UINT64__readmsr(unsignedlongRegister);void__writemsr(unsignedlongRegister,UINT64 Value);在 Windows 内核驱动里可以直接调用这两个 intrinsic(定义在intrin.h)不需要手写内联汇编。访问权限与异常RDMSR/WRMSR是 CPL0-only 指令用户态执行会触发#GP。即便是在 Ring 0访问一个不存在的 MSR 索引同样会#GP——这也是很多反调试/反虚拟化检测的手段之一:故意读一个只有在 VMX root 模式或者特定 hypervisor 里才会被模拟出来的 MSR观察是否#GP,或者观察返回值是否符合真实硬件的预期。常见且重要的 MSRMSR 数量非常多这里只列出与内核开发、虚拟化关系最密切的几类。长模式与系统调用相关MSR索引作用IA32_EFER0xC0000080长模式开关(LME/LMA)、系统调用扩展开关(SCE)、NX 位开关(NXE)IA32_STAR0xC000008132 位 syscall 使用的 CS/SS 段选择子IA32_LSTAR0xC000008264 位syscall指令跳转的目标地址本质就是内核态系统调用入口的 RIPIA32_CSTAR0xC0000083兼容模式下的 syscall 目标地址IA32_FMASK0xC0000084syscall 时 RFLAGS 的掩码决定哪些标志位在进入内核态时被清零其中IA32_LSTAR是逆向和 hook 领域的常客——用户态一条syscall指令会跳转到内核的哪个地址。比如在Windows中这个值就指向了KiSyscall64这个函数的的序言也就是头部。篡改它就等于劫持了整个系统Syscall的入口。段基址相关MSR索引作用IA32_FS_BASE0xC0000100FS 段基址64 位下常用来指向 TEBIA32_GS_BASE0xC0000101GS 段基址64 位下内核态常用来指向 KPCR/per-CPU 数据IA32_KERNEL_GS_BASE0xC0000102配合swapgs指令在用户态/内核态切换时交换 GS 基址这三个 MSR 本质上是给段基址这种传统上由段描述符提供的信息开了个后门——64 位模式下段限长检查基本形同虚设但 FS/GS 的基址部分仍然有用所以 Intel 专门用 MSR 承载它配合swapgs实现用户态/内核态各自独立的 per-CPU 上下文指针。APIC 与中断相关IA32_APIC_BASE(0x1B)控制 Local APIC 的映射基址以及 APIC 是否处于 x2APIC 模式、是否被全局禁用。这个 MSR 在虚拟化里也很关键因为 guest 对它的读写往往需要 VMM 介入模拟才能正确反映 vAPIC 的状态。内存类型相关MTRR(Memory Type Range Registers)也是一组 MSR,比如IA32_MTRR_DEF_TYPE、IA32_MTRR_PHYSBASEn/PHYSMASKn,它们决定物理地址范围的默认缓存类型(UC/WB/WC 等)。EPT 的 memory type 字段(还记得上一篇 EPT 文章里EPTP.MemoryType和EPT_PTE.EPTMemoryType吗)在实际生效时会和 MTRR 的设定做合并判断这也是为什么很多 hypervisor 在初始化 EPT 之前要先读一遍 guest 的 MTRR 配置。虚拟化能力相关IA32_FEATURE_CONTROL: bit 0 是 lock 位bit 2 是 “Enable VMX outside SMX” 位。必须先设置 VMX enable 位并锁定才能执行VMXON,如果 BIOS 里锁定了这个 MSR 却没开 VMX 位VMXON会直接失败。IA32_VMX_BASIC: 分配 VMXON Region 和 VMCS Region 时写入的 revision id 就来自这个 MSR 的低 31 位。IA32_VMX_PINBASED_CTLS/IA32_VMX_PROCBASED_CTLS/IA32_VMX_PROCBASED_CTLS2/IA32_VMX_EXIT_CTLS/IA32_VMX_ENTRY_CTLS等一整组: 这些 MSR 描述了当前 CPU允许哪些 VMX 控制位被置 1 或置 0写 VMCS 的控制字段之前必须用这些 MSR 做能力校验否则VMLAUNCH/VMRESUME会因为控制字段不合法而失败。可以看到MSR 不只是处理器配置项这么简单——它同时也是 VMX 这一整套虚拟化扩展对外暴露能力边界的接口。没有这些 capability MSRVMM 甚至不知道当前 CPU 支不支持某个特性那是非常危险的。MSR 与虚拟化的关系VM-exit在 VMX non-root 模式(guest)下执行RDMSR/WRMSR,默认情况下(取决于 Processor-Based VM-Execution Controls 里的Use MSR bitmaps位是否开启)会无条件触发 VM-exit,退到 VMM 里由 VMM 决定:是把这次访问转发给真实硬件执行(读写宿主机上真正的 MSR);还是拦截并模拟一个虚假的值(比如向 guest 隐藏某些和虚拟化相关的 MSR,或者伪造性能计数器数值反调试)。允许直接访问需要拦截透传模拟/隐藏Guest 执行 RDMSR/WRMSRMSR Bitmap 是否放行?硬件直接执行无 VM-exitVM-exit (Exit Reason: RDMSR/WRMSR)VMM 读取 Exit Qualification 中的 MSR indexVMM 如何处理VMM 自己执行 RDMSR/WRMSR 后回填 Guest 寄存器VMM 构造虚假值写回 Guest RAX/RDXVM-entry 恢复 GuestMSR Bitmap如果每次 guest 的 MSR 访问都无差别地触发 VM-exit,性能损耗会很明显(尤其是一些高频读取的 MSR,比如某些 guest 内部会频繁读 TSC 相关或者性能计数器相关的 MSR)。所以 VMX 提供了MSR Bitmap机制让 VMM 可以精细控制哪些 MSR 的读/写需要拦截哪些可以放行。MSR Bitmap 是一块4KB大小、物理地址对齐的内存区域内部分成 4 个 1KB 的子区域:typedefstruct_MSR_BITMAP{UINT8 ReadLowMsrs[1024];// 0x00000000 - 0x00001FFF 的读拦截位图UINT8 ReadHighMsrs[1024];// 0xC0000000 - 0xC0001FFF 的读拦截位图UINT8 WriteLowMsrs[1024];// 0x00000000 - 0x00001FFF 的写拦截位图UINT8 WriteHighMsrs[1024];// 0xC0000000 - 0xC0001FFF 的写拦截位图}MSR_BITMAP,*PMSR_BITMAP;每个 MSR 索引对应位图里的一个 bit:置 1 表示这个 MSR 的这类访问需要 VM-exit,置 0 表示直接放行不 trap。[!WARNING]注意范围只覆盖了0x00000000-0x00001FFF和0xC0000000-0xC0001FFF这两段——落在这两段之外的 MSR 索引只要开启了Use MSR bitmaps访问就必定触发 VM-exit,没有绕过的余地。MSR Bitmap 的物理地址通过 VMCS 的MSR_BITMAP_ADDRESS字段配置同时需要在 Processor-Based VM-Execution Controls 里置位Use MSR bitmaps,这套机制才会生效。VM-Entry / VM-Exit 的 MSR 载入与保存区除了拦截 guest 主动执行的RDMSR/WRMSR,VMX 还提供了另一套机制——在 VM-entry/VM-exit 这两个边界时刻自动批量加载/保存一组 MSR,不需要 guest 自己执行任何指令也不需要 VMM 手写RDMSR/WRMSR去逐个搬运。这对应 VMCS 里三个区域:VM-Entry MSR-Load Area:VM-entry 时VMM 提前准备好一组guest 应该具有的 MSR 值硬件在进入 guest 之前自动写入。VM-Exit MSR-Store Area:VM-exit 发生的瞬间硬件自动把 guest 当前的这组 MSR 值保存下来供 VMM 后续查看或者在下次 VM-entry 时恢复。VM-Exit MSR-Load Area:VM-exit 发生后硬件自动把这组 MSR 恢复成host 应有的值避免 host 代码在 root 模式下被 guest 遗留的 MSR 状态干扰。三个区域的条目格式是一样的:typedefstruct_VMX_MSR_ENTRY{UINT32 MsrIndex;// MSR 索引UINT32 Reserved;// 保留位必须为 0UINT64 MsrData;// 64 位 MSR 值}VMX_MSR_ENTRY,*PVMX_MSR_ENTRY;典型的用法是把IA32_EFER、IA32_PAT这类host 和 guest 必须严格区分、且切换开销可以接受批量搬运的 MSR 放进这三个区域,让硬件自动处理,而不是每次都靠 VM-exit 陷入再手动RDMSR/WRMSR。相比之下MSR Bitmap 拦截更适合 guest 会主动、频繁访问且 VMM 需要按需决定是否模拟的场景两者是互补关系而不是取代关系。总结解决的问题访问方式与虚拟化的关系MSR处理器底层配置/状态的可扩展存取RDMSR/WRMSR,ECX 给索引EDX:EAX 给值VMX 自身的开关、能力探测都靠 MSR;guest 对 MSR 的访问可以被 MSR Bitmap 精细拦截也可以通过 VM-Entry/VM-Exit MSR 区自动批量搬运MSR 像是处理器整体行为的控制面板——从要不要开长模式到 syscall 跳去哪到 VMX 能力边界在哪几乎处理器每一个模式开关都藏在某个 MSR 里。对 hypervisor 开发而言MSR 既是启动 VMX 的必经之路也是 guest/host 边界上一处需要精细拦截和模拟的关键接口理解它是继续往下做 VM-exit 处理、反检测、syscall hook 这些方向的基础。参考Intel SDM Volume 3, Chapter 2 - Model-Specific Registers (MSRs)附感兴趣可以来我的博客看看很多东西只能在这里进行交流分享