1.查询CPU对VMX的支持
- cpuid 指令,查询1号功能,返回的ECX[5]==1
-
#define CPUID_ECX_VMX_ABILITY (1<<5) ULONG64 CheckCPUID() { int ctx[4] = { 0 };//顺序:eax,ebx,ecx,edx __cpuid(ctx, 1); return ctx[2] & CPUID_ECX_VMX_ABILITY; //Bit-5 }
- rdmsr 指令,查询BIOS是否已开启对VMX的支持
-
#define MSR_IA32_FEATURE_CONTROL 0x3A ULONG64 CheckMsr() { MSR_IA32_FEATURE_CONTROL_REGISTER msr = { 0 }; msr.value.QuadPart = __readmsr(MSR_IA32_FEATURE_CONTROL); return msr.Bits.Lock; }
2.开启VMX
- 修正CR0和CR4,设置CR4.VMXE=1
-
#define MSR_IA32_VMX_CR0_FIXED0 0x486 #define MSR_IA32_VMX_CR0_FIXED1 0x487 #define MSR_IA32_VMX_CR4_FIXED0 0x488 #define MSR_IA32_VMX_CR4_FIXED1 0x489 ULONG64 FixCr0() { IA32_CR0 uCr0 = { 0 }; uCr0.value.QuadPart = __readcr0(); uCr0.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR0_FIXED0); uCr0.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR0_FIXED1); __writecr0(uCr0.value.QuadPart); return TRUE; } ULONG64 FixCr4() { IA32_CR4 uCr4 = { 0 }; uCr4.value.QuadPart = __readcr4(); uCr4.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR4_FIXED0); uCr4.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR4_FIXED1); uCr4.Bits.VMXE = 1; __writecr4(uCr4.value.QuadPart); return TRUE; }
3.申请内存
- VMON区,需要页对齐,并且需要得到它的物理地址
- VMCS区,需要页对齐,并且需要得到它的物理地址
- VMM栈,可以的话申请几个页,避免栈溢出
-
ULONG64 CreateVM(UCHAR CpuIndex) { psVM[CpuIndex].pOnVa = ExAllocatePool2(POOL_FLAG_NON_PAGED, PAGE_SIZE, 'VMON'); if (!psVM[CpuIndex].pOnVa) { return FALSE; } RtlZeroMemory(psVM[CpuIndex].pOnVa, PAGE_SIZE); psVM[CpuIndex].pOnPa = MmGetPhysicalAddress(psVM[CpuIndex].pOnVa); psVM[CpuIndex].pCsVa = ExAllocatePool2(POOL_FLAG_NON_PAGED, PAGE_SIZE, 'VMCS'); if (!psVM[CpuIndex].pCsVa) { return FALSE; } RtlZeroMemory(psVM[CpuIndex].pCsVa, PAGE_SIZE); psVM[CpuIndex].pCsPa = MmGetPhysicalAddress(psVM[CpuIndex].pCsVa); psVM[CpuIndex].pStack = ExAllocatePool2(POOL_FLAG_NON_PAGED, STACK_SIZE, 'VMSK'); if (!psVM[CpuIndex].pStack) { return FALSE; } RtlZeroMemory(psVM[CpuIndex].pStack, STACK_SIZE); return TRUE; }
4.设置VMXON区,执行VMXON
- 只需要填写一个版本号
- 执行vmxon(VMON区的物理地址);
-
ULONG64 InitVmxON(UCHAR CpuIndex, ULONG RevisonId) { *((PULONG)psVM[CpuIndex].pOnVa) = RevisonId; if (__vmx_on(&psVM[CpuIndex].pOnPa.QuadPart)) { KdPrint(("vmx_on 失败!\n")); return FALSE; } return TRUE; }
5.设置VMXCS区
- 5.1.初始化VMCS
-
ULONG64 InitVmxCS(UCHAR CpuIndex, ULONG RevisonId) { *((PULONG)psVM[CpuIndex].pCsVa) = RevisonId; if (__vmx_vmclear(&psVM[CpuIndex].pCsPa.QuadPart)) return FALSE; if (__vmx_vmptrld(&psVM[CpuIndex].pCsPa.QuadPart)) return FALSE; return TRUE; }
- 2.设置GUEST运行环境
- 控制寄存器:CR0,CR3,CR4
- 调试寄存器:DR7
- 段寄存器:ES,FS,DS,CS,SS,GS,TR,LDTR
- GS基址,FS基址
- GDT基址,IDT基址
- GDT段限,IDT段限
- 状态寄存器:RFLAGS
- SYSENTER_CS,SYSENTER_ESP,SYSENTER_EIP
- RIP,RSP
需要保存GUEST当前的RSP和GUEST当前下一条指令的RIP,以便操作完成后回到GUEST的代码流程,就像没有发生过什么事一样
-
ULONG64 SetGuestState(ULONG64 GuestRip, ULONG64 GuestRsp) { ULONG64 ret = 0; ULONG64 GdtBase = _readBaseGDT(); ULONG64 IdtBase = _readBaseIDT(); ret += __vmx_vmwrite(GUEST_CR0, __readcr0()); ret += __vmx_vmwrite(GUEST_CR3, __readcr3()); ret += __vmx_vmwrite(GUEST_CR4, __readcr4()); ret += __vmx_vmwrite(GUEST_DR7, __readdr(7)); ret += SetGuestSeg(ES, _readES(), GdtBase); ret += SetGuestSeg(FS, _readFS(), GdtBase); ret += SetGuestSeg(DS, _readDS(), GdtBase); ret += SetGuestSeg(CS, _readCS(), GdtBase); ret += SetGuestSeg(SS, _readSS(), GdtBase); ret += SetGuestSeg(GS, _readGS(), GdtBase); ret += SetGuestSeg(TR, _readTR(), GdtBase); ret += SetGuestSeg(LDTR, _readLDTR(), GdtBase); ret += __vmx_vmwrite(GUEST_FS_BASE, __readmsr(MSR_IA32_FS_BASE)); ret += __vmx_vmwrite(GUEST_GS_BASE, __readmsr(MSR_IA32_GS_BASE)); ret += __vmx_vmwrite(GUEST_GDTR_BASE, GdtBase); ret += __vmx_vmwrite(GUEST_GDTR_LIMIT, _readLimitGDT()); ret += __vmx_vmwrite(GUEST_IDTR_BASE, IdtBase); ret += __vmx_vmwrite(GUEST_IDTR_LIMIT, _readLimitIDT()); ret += __vmx_vmwrite(GUEST_RFLAGS, __readeflags()); ret += __vmx_vmwrite(GUEST_RIP, GuestRip); ret += __vmx_vmwrite(GUEST_RSP, GuestRsp); ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP)); ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); return ret; }
- 3.设置HOST运行环境
- 控制寄存器:CR0,CR3,CR4
- 段寄存器:ES,FS,DS,CS,SS,GS,TR
- GS基址,FS基址,TR基址
- GDT基址,IDT基址
- SYSENTER_CS,SYSENTER_ESP,SYSENTER_EIP
- RIP,RSP
HOST的RIP就是VM退出后跳转到VMM的入口地址,所以我们要写一个VMM入口函数,保存VM的现场状态,回到VM后才能继续像什么也没发生过一样继续执行
-
ULONG64 SetHostState(ULONG64 HostRip, ULONG64 HostRsp) { SEG seg = { 0 }; ULONG64 ret = 0; ULONG64 GdtBase = _readBaseGDT(); ULONG64 IdtBase = _readBaseIDT(); ULONG64 TR = GetSegInfo(&seg, _readTR(), GdtBase); ret += __vmx_vmwrite(HOST_CR0, __readcr0()); ret += __vmx_vmwrite(HOST_CR3, __readcr3()); ret += __vmx_vmwrite(HOST_CR4, __readcr4()); ret += __vmx_vmwrite(HOST_RIP, HostRip); ret += __vmx_vmwrite(HOST_RSP, HostRsp); ret += __vmx_vmwrite(HOST_CS_SELECTOR, _readCS() & 0xF8); ret += __vmx_vmwrite(HOST_SS_SELECTOR, _readSS() & 0xF8); ret += __vmx_vmwrite(HOST_DS_SELECTOR, _readDS() & 0xF8); ret += __vmx_vmwrite(HOST_ES_SELECTOR, _readES() & 0xF8); ret += __vmx_vmwrite(HOST_FS_SELECTOR, _readFS() & 0xF8); ret += __vmx_vmwrite(HOST_GS_SELECTOR, _readGS() & 0xF8); ret += __vmx_vmwrite(HOST_TR_SELECTOR, _readTR() & 0xF8); ret += __vmx_vmwrite(HOST_FS_BASE, __readmsr(MSR_IA32_FS_BASE)); ret += __vmx_vmwrite(HOST_GS_BASE, __readmsr(MSR_IA32_GS_BASE)); ret += __vmx_vmwrite(HOST_TR_BASE, TR ? seg.Base : 0); ret += __vmx_vmwrite(HOST_GDTR_BASE, GdtBase); ret += __vmx_vmwrite(HOST_IDTR_BASE, IdtBase); ret += __vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); ret += __vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP)); ret += __vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); return ret; }
- 4.设置执行控制
先设置最基本的的条件
-
ULONG64 SetVmxExecutionCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) { UCHAR ret = 0; ULONG64 value = 0; MSR_IA32_VMX_PINBASED_CTLS_REGISTER PinBased = { 0 }, SetPinBase = { 0 }; MSR_IA32_VMX_PROCBASED_CTLS_REGISTER Primary = { 0 }, SetPrimary = { 0 }; MSR_IA32_VMX_PROCBASED_CTLS2_REGISTER Secondry = { 0 }, SetSecondry = { 0 }; MSR_IA32_VMX_PROCBASED_CTLS3_REGISTER Tertiary = { 0 }; PinBased.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS) : __readmsr(MSR_IA32_VMX_PINBASED_CTLS); /*在这里设置《PinBased》*/ value = SetControls(SetPinBase.value.QuadPart, PinBased.value); ret += __vmx_vmwrite(PIN_BASED_VM_EXECUTION_CONTROLS, value); Primary.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) : __readmsr(MSR_IA32_VMX_PROCBASED_CTLS); /*在这里设置《Primary》*/ SetPrimary.Bits->MSRmap = TRUE; SetPrimary.Bits->RDTSC = TRUE; SetPrimary.Bits->Secondary = TRUE; value = SetControls(SetPrimary.value.QuadPart, Primary.value); ret += __vmx_vmwrite(PRIMARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, value); if (SetPrimary.Bits->Secondary) { Secondry.value.QuadPart = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS2); /*在这里设置《Secondry》*/ SetSecondry.Bits->XSAVES_XRSTORS = TRUE; SetSecondry.Bits->INVPCID = TRUE; SetSecondry.Bits->RDTSCP = TRUE; value = SetControls(SetSecondry.value.QuadPart, Secondry.value); ret += __vmx_vmwrite(SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, value); } ret += __vmx_vmwrite(VMCS_LINK_POINTER_FULL, ~0Ull); ret += __vmx_vmwrite(VIRTUAL_PROCESSOR_IDENTIFIER, CpuIndex); return ret; }
- 5.设置VM退出控制
先设置最基本的的条件
-
ULONG64 SetVmxExitCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) { ULONG64 ret = 0; ULONG64 value = 0; MSR_IA32_VMX_EXIT_CTLS_REGISTER VmExit = { 0 }, SetExit = { 0 }; VmExit.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_EXIT_CTLS) : __readmsr(MSR_IA32_VMX_EXIT_CTLS); /*在这里设置《VmExit》*/ SetExit.Bits->HOST_addr_size = TRUE; value = SetControls(SetExit.value.QuadPart, VmExit.value); ret += __vmx_vmwrite(PRIMARY_VM_EXIT_CONTROLS, value); return ret; }
- 6.设置VM进入控制
先设置最基本的的条件
-
ULONG64 SetVmxEntryCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) { ULONG64 ret = 0; ULONG64 value = 0; MSR_IA32_VMX_ENTRY_CTLS_REGISTER VmEntry = { 0 }, SetEntry = { 0 }; VmEntry.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_ENTRY_CTLS) : __readmsr(MSR_IA32_VMX_ENTRY_CTLS); /*在这里设置《VmEntry》*/ SetEntry.Bits->Guest_IA32e = TRUE; value = SetControls(SetEntry.value.QuadPart, VmEntry.value); ret += __vmx_vmwrite(VM_ENTRY_CONTROLS, value); return ret; }
- 7.编写VMM处理函数
此函数只做一些准备工作。
-
ULONG64 HostEntry(PGUESTREG pGuestRegs) { __vmx_vmread(VIRTUAL_PROCESSOR_IDENTIFIER, &pGuestRegs->CpuIndex); __vmx_vmread(EXIT_REASON, (size_t*) & pGuestRegs->exitReason); __vmx_vmread(GUEST_RIP, &pGuestRegs->GuestRip); __vmx_vmread(GUEST_RSP, &pGuestRegs->GuestRsp); __vmx_vmread(GUEST_RFLAGS, &pGuestRegs->GuestFlag); __vmx_vmread(VM_EXIT_INSTRUCTION_LENGTH, &pGuestRegs->InstrLen); pGuestRegs->eProcess = PsGetCurrentProcess(); pGuestRegs->eThread = PsGetCurrentThread(); return DispatchHandler(pGuestRegs); }
调用 DispatchHandler 来处理事件。以下只是处理一些必须要处理的指令,CPU不同可能会有差异。处理方法就是查看CPU手册,模仿指令行为。这里就省略了,以免读者看得不清晰
-
ULONG64 DispatchHandler(PGUESTREG pGuestRegs) { EXIT_REASON_FIELDS* exit = (EXIT_REASON_FIELDS*)&pGuestRegs->exitReason; ULONG64 CpuIndex = pGuestRegs->CpuIndex; ULONG64 GuestRip = pGuestRegs->GuestRip; ULONG64 GuestRsp = pGuestRegs->GuestRsp; ULONG64 InstrLen = pGuestRegs->InstrLen; switch (exit->Bits.Basic) { case EXIT_REASON_CPUID: { ... break; } case EXIT_REASON_RDMSR: { ... break; } case EXIT_REASON_WRMSR: { ... break; } case EXIT_REASON_RDTSC: { ... break; } case EXIT_REASON_RDTSCP: { ... break; } default: DbgBreakPoint(); break; } return TRUE; }
6.执行VM
- 执行vmlaunch();
如果上述设置没有出现问题。代码会接着在我们设置好的GUEST.RIP处继续运行。直到触发VM退出条件,即可进入我们设置好的VMM.RIP(HostEntry)处开始执行
<源码下载>
<源码>