Intel VT学习笔记(四)—— VMCS(下)
Intel VT學習筆記(四)—— VMCS(下)
- 要點回顧
- VM-Exit Information
- Guest state fields
- 代碼實現(xiàn)
- 參考資料
要點回顧
在上一篇中,我們了解了如何設置VMCS字段,以及如何通過錯誤碼排查錯誤字段,并最終成功執(zhí)行了VMLAUNCH指令,進入了guest,但是由于由于目前并未填充Guest state fields,因此直接從Guest中跳出到Host中。
本篇將學習如何排查相關錯誤以及如何填寫Guest域信息。
VM-Exit Information
描述:如果執(zhí)行虛擬機代碼時直接跳出到了Host,可在Host中讀取錯誤碼與寄存器信息來排查錯誤。
GUEST_REGS g_GuestRegs; //GUEST_REGS的定義將在完整代碼中給出static void VMMEntryPointEbd(void) {ULONG ExitReason;ExitReason = Vmx_VmRead(VM_EXIT_REASON);Log("ExitReason", ExitReason);g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);Log("g_GuestRegs.esp", g_GuestRegs.esp);Log("g_GuestRegs.eip", g_GuestRegs.eip);__asm int 3; }void __declspec(naked) VMMEntryPoint(void) {__asm{//需要設置fs和gs,否則無法正常運行mov ax, fsmov fs, axmov ax, gsmov gs, ax}//盡量不要在裸函數(shù)中定義局部變量,或實現(xiàn)太多功能,最好封裝成函數(shù)VMMEntryPointEbd(); }執(zhí)行結果:
可以看到,讀取到的錯誤碼為ExitReason等于0x80000021,esp和eip均為0。
VM-Exit錯誤碼信息可參考Intel開發(fā)手冊卷3第24.9小節(jié)。
這里說,如果錯誤碼的第31位被設置,第15:0比特位表示具體的錯誤碼,附錄C列舉了這些錯誤碼所對應的含義。
可以看到,當31位1時,錯誤碼0x21對應的含義是「invalid guest state」,具體原因是未通過26.3.1節(jié)的檢查。
Guest state fields
描述:關于Guest state fields信息如何填寫可參考Intel開發(fā)手冊卷3第24.4節(jié)。
上圖中說明需要加載的寄存器有:
- 控制寄存器:Cr0、Cr3、Cr4
- 調試寄存器:Dr7
- 通用寄存器:RSP
- 指令指針寄存器:RIP
- 標志寄存器:RFLAGS
- 段寄存器:CS、SS、DS、ES、FS、GS、LDTR、TR
其中,段寄存器需要加載selector、base、limit。
部分段寄存器是比較重要的,可以在進入Guest前設置,例如CS和TR,其它可以等到進入Guest后再設置。
圖24-2解釋了段寄存器屬性第16位的作用,為0表示可用,為1表示不可用。
因此,除了CS和TR,可先將其它段寄存器的第16位置1,即設置成不可用狀態(tài),這樣在加載時就不會出錯。
除此之外,還需要設置GDTR和LDTR的基址和邊界。
以及幾個關鍵的MSR寄存器。
最后,需要注意的是,存在兩個特殊的字段,用于在需要時指向Shadow VMCS,若不需要使用,需要將其置為0xffffffff(參考Intel開發(fā)手冊卷3第31.6小節(jié))。
Shadow VMCS可理解為VMCS的拓展(參考Intel開發(fā)手冊卷3第24.10小節(jié))。
代碼實現(xiàn)
//vtasm.h #ifndef VTASM_H #define VTASM_Htypedef union {struct{unsigned SSE3 : 1;unsigned PCLMULQDQ : 1;unsigned DTES64 : 1;unsigned MONITOR : 1;unsigned DS_CPL : 1;unsigned VMX : 1;unsigned SMX : 1;unsigned EIST : 1;unsigned TM2 : 1;unsigned SSSE3 : 1;unsigned Reserved : 22;};}_CPUID_ECX;typedef struct _IA32_FEATURE_CONTROL_MSR {unsigned Lock : 1; // Bit 0 is the lock bit - cannot be modified once lock is setunsigned Reserved1 : 1; // Undefinedunsigned EnableVmxon : 1; // Bit 2. If this bit is clear, VMXON causes a general protection exceptionunsigned Reserved2 : 29; // Undefinedunsigned Reserved3 : 32; // Undefined} IA32_FEATURE_CONTROL_MSR;typedef struct _VMX_BASIC_MSR {unsigned RevId : 32;unsigned szVmxOnRegion : 12;unsigned ClearBit : 1;unsigned Reserved : 3;unsigned PhysicalWidth : 1;unsigned DualMonitor : 1;unsigned MemoryType : 4;unsigned VmExitInformation : 1;unsigned Reserved2 : 9; } VMX_BASIC_MSR, * PVMX_BASIC_MSR;typedef union {struct{unsigned PE : 1;unsigned MP : 1;unsigned EM : 1;unsigned TS : 1;unsigned ET : 1;unsigned NE : 1;unsigned Reserved_1 : 10;unsigned WP : 1;unsigned Reserved_2 : 1;unsigned AM : 1;unsigned Reserved_3 : 10;unsigned NW : 1;unsigned CD : 1;unsigned PG : 1;//unsigned Reserved_64:32;};}_CR0;typedef union {struct {unsigned VME : 1;unsigned PVI : 1;unsigned TSD : 1;unsigned DE : 1;unsigned PSE : 1;unsigned PAE : 1;unsigned MCE : 1;unsigned PGE : 1;unsigned PCE : 1;unsigned OSFXSR : 1;unsigned PSXMMEXCPT : 1;unsigned UNKONOWN_1 : 1; //These are zerounsigned UNKONOWN_2 : 1; //These are zerounsigned VMXE : 1; //It's zero in normalunsigned Reserved : 18; //These are zero//unsigned Reserved_64:32;}; }_CR4;typedef union {struct{unsigned CF : 1;unsigned Unknown_1 : 1; //Always 1unsigned PF : 1;unsigned Unknown_2 : 1; //Always 0unsigned AF : 1;unsigned Unknown_3 : 1; //Always 0unsigned ZF : 1;unsigned SF : 1;unsigned TF : 1;unsigned IF : 1;unsigned DF : 1;unsigned OF : 1;unsigned TOPL : 2;unsigned NT : 1;unsigned Unknown_4 : 1;unsigned RF : 1;unsigned VM : 1;unsigned AC : 1;unsigned VIF : 1;unsigned VIP : 1;unsigned ID : 1;unsigned Reserved : 10; //Always 0//unsigned Reserved_64:32; //Always 0}; }_EFLAGS;void Asm_CPUID(ULONG uFn, PULONG uRet_EAX, PULONG uRet_EBX, PULONG uRet_ECX, PULONG uRet_EDX);ULONG64 Asm_ReadMsr(ULONG uIndex);ULONG Asm_GetEflags(); ULONG Asm_GetCs(); ULONG Asm_GetDs(); ULONG Asm_GetEs(); ULONG Asm_GetFs(); ULONG Asm_GetGs(); ULONG Asm_GetSs(); ULONG Asm_GetTr();ULONG Asm_GetGdtBase(); ULONG Asm_GetIdtBase(); ULONG Asm_GetGdtLimit(); ULONG Asm_GetIdtLimit();ULONG Asm_GetCr0(); ULONG Asm_GetCr3(); ULONG Asm_GetCr4();void Asm_SetCr4(ULONG uNewCr4);void Vmx_VmxOn(ULONG LowPart, ULONG HighPart); void Vmx_VmxOff();void Vmx_VmClear(ULONG LowPart, ULONG HighPart); void Vmx_VmPtrld(ULONG LowPart, ULONG HighPart); ULONG Vmx_VmRead(ULONG uField); void Vmx_VmWrite(ULONG uField, ULONG uValue); void Vmx_VmLaunch(); void Vmx_VmCall();#endif //vtasm.asm .686p .model flat, stdcall option casemap:none.data.codeAsm_CPUID Proc uses ebx esi edi fn:dword, ret_eax:dword, ret_ebx:dword, ret_ecx:dword, ret_edx:dwordmov eax, fncpuidmov esi, ret_eaxmov dword ptr [esi], eaxmov esi, ret_ebxmov dword ptr [esi], ebxmov esi, ret_ecxmov dword ptr [esi], ecxmov esi, ret_edxmov dword ptr [esi], edxret Asm_CPUID EndpAsm_ReadMsr Proc Index:dwordmov ecx,Indexrdmsrret Asm_ReadMsr EndpAsm_GetCr0 Procmov eax, cr0ret Asm_GetCr0 EndpAsm_GetCr3 Procmov eax, cr3ret Asm_GetCr3 EndpAsm_GetCr4 Procmov eax, cr4ret Asm_GetCr4 EndpAsm_SetCr4 Proc NewCr4:dwordmov eax,NewCr4mov cr4, eaxret Asm_SetCr4 EndpVmx_VmxOn Proc LowPart:dword,HighPart:dwordpush HighPartpush LowPartVmxon qword ptr [esp]add esp,8ret Vmx_VmxOn EndpVmx_VmxOff ProcVmxoffret Vmx_VmxOff EndpAsm_GetEflags PROCpushfdpop eaxret Asm_GetEflags ENDPVmx_VmClear Proc LowPart:dword,HighPart:dwordpush HighPartpush LowPartvmclear qword ptr [esp]add esp,8ret Vmx_VmClear endpVmx_VmPtrld Proc LowPart:dword,HighPart:dwordpush HighPartpush LowPartvmptrld qword ptr [esp]add esp,8ret Vmx_VmPtrld endpVmx_VmRead Proc uses ecx Field:dwordmov eax,Fieldvmread ecx,eaxmov eax,ecxret Vmx_VmRead endpVmx_VmWrite Proc uses ecx Field:dword,Value:dwordmov eax,Fieldmov ecx,Valuevmwrite eax,ecxret Vmx_VmWrite endpAsm_GetCs PROCmov eax, csret Asm_GetCs ENDPAsm_GetDs PROCmov eax, dsret Asm_GetDs ENDPAsm_GetEs PROCmov eax, esret Asm_GetEs ENDPAsm_GetSs PROCmov eax, ssret Asm_GetSs ENDPAsm_GetFs PROCmov eax, fsret Asm_GetFs ENDPAsm_GetGs PROCmov eax, gsret Asm_GetGs ENDPAsm_GetTr PROCstr eaxret Asm_GetTr ENDPAsm_GetGdtBase PROCLOCAL gdtr[10]:BYTEsgdt gdtrmov eax, dword PTR gdtr[2]ret Asm_GetGdtBase ENDPAsm_GetGdtLimit PROCLOCAL gdtr[10]:BYTEsgdt gdtrmov ax, WORD PTR gdtr[0]ret Asm_GetGdtLimit ENDPAsm_GetIdtBase PROCLOCAL idtr[10]:BYTEsidt idtrmov eax, dword PTR idtr[2]ret Asm_GetIdtBase ENDPAsm_GetIdtLimit PROCLOCAL idtr[10]:BYTEsidt idtrmov ax, WORD PTR idtr[0]ret Asm_GetIdtLimit ENDPVmx_VmLaunch Procvmlaunchret Vmx_VmLaunch endpVmx_VmCall Procvmcallret Vmx_VmCall endpEND //vtsystem.h #ifndef VTSYSTEM_H #define VTSYSTEM_H #include <ntddk.h>/*MSR definition*/ #define MSR_IA32_FEATURE_CONTROL 0x03a #define MSR_IA32_VMX_BASIC 0x480 #define MSR_IA32_VMX_PINBASED_CTLS 0x481 #define MSR_IA32_VMX_PROCBASED_CTLS 0x482 #define MSR_IA32_VMX_EXIT_CTLS 0x483 #define MSR_IA32_VMX_ENTRY_CTLS 0x484#define MSR_IA32_SYSENTER_CS 0x174 #define MSR_IA32_SYSENTER_ESP 0x175 #define MSR_IA32_SYSENTER_EIP 0x176typedef struct _VMX_CPU {PVOID pVMXONRegion;PHYSICAL_ADDRESS pVMXONRegion_PA;PVOID pVMCSRegion;PHYSICAL_ADDRESS pVMCSRegion_PA;PVOID pStack;BOOLEAN bVTStartSuccess; }VMX_CPU, * PVMX_CPU;/* VMCS Encordings */ enum {VIRTUAL_PROCESSOR_ID = 0x00000000,POSTED_INTR_NV = 0x00000002,GUEST_ES_SELECTOR = 0x00000800,GUEST_CS_SELECTOR = 0x00000802,GUEST_SS_SELECTOR = 0x00000804,GUEST_DS_SELECTOR = 0x00000806,GUEST_FS_SELECTOR = 0x00000808,GUEST_GS_SELECTOR = 0x0000080a,GUEST_LDTR_SELECTOR = 0x0000080c,GUEST_TR_SELECTOR = 0x0000080e,GUEST_INTR_STATUS = 0x00000810,HOST_ES_SELECTOR = 0x00000c00,HOST_CS_SELECTOR = 0x00000c02,HOST_SS_SELECTOR = 0x00000c04,HOST_DS_SELECTOR = 0x00000c06,HOST_FS_SELECTOR = 0x00000c08,HOST_GS_SELECTOR = 0x00000c0a,HOST_TR_SELECTOR = 0x00000c0c,IO_BITMAP_A = 0x00002000,IO_BITMAP_A_HIGH = 0x00002001,IO_BITMAP_B = 0x00002002,IO_BITMAP_B_HIGH = 0x00002003,MSR_BITMAP = 0x00002004,MSR_BITMAP_HIGH = 0x00002005,VM_EXIT_MSR_STORE_ADDR = 0x00002006,VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007,VM_EXIT_MSR_LOAD_ADDR = 0x00002008,VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009,VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a,VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b,TSC_OFFSET = 0x00002010,TSC_OFFSET_HIGH = 0x00002011,VIRTUAL_APIC_PAGE_ADDR = 0x00002012,VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013,APIC_ACCESS_ADDR = 0x00002014,APIC_ACCESS_ADDR_HIGH = 0x00002015,POSTED_INTR_DESC_ADDR = 0x00002016,POSTED_INTR_DESC_ADDR_HIGH = 0x00002017,EPT_POINTER = 0x0000201a,EPT_POINTER_HIGH = 0x0000201b,EOI_EXIT_BITMAP0 = 0x0000201c,EOI_EXIT_BITMAP0_HIGH = 0x0000201d,EOI_EXIT_BITMAP1 = 0x0000201e,EOI_EXIT_BITMAP1_HIGH = 0x0000201f,EOI_EXIT_BITMAP2 = 0x00002020,EOI_EXIT_BITMAP2_HIGH = 0x00002021,EOI_EXIT_BITMAP3 = 0x00002022,EOI_EXIT_BITMAP3_HIGH = 0x00002023,VMREAD_BITMAP = 0x00002026,VMWRITE_BITMAP = 0x00002028,XSS_EXIT_BITMAP = 0x0000202C,XSS_EXIT_BITMAP_HIGH = 0x0000202D,GUEST_PHYSICAL_ADDRESS = 0x00002400,GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401,VMCS_LINK_POINTER = 0x00002800,VMCS_LINK_POINTER_HIGH = 0x00002801,GUEST_IA32_DEBUGCTL = 0x00002802,GUEST_IA32_DEBUGCTL_HIGH = 0x00002803,GUEST_IA32_PAT = 0x00002804,GUEST_IA32_PAT_HIGH = 0x00002805,GUEST_IA32_EFER = 0x00002806,GUEST_IA32_EFER_HIGH = 0x00002807,GUEST_IA32_PERF_GLOBAL_CTRL = 0x00002808,GUEST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002809,GUEST_PDPTR0 = 0x0000280a,GUEST_PDPTR0_HIGH = 0x0000280b,GUEST_PDPTR1 = 0x0000280c,GUEST_PDPTR1_HIGH = 0x0000280d,GUEST_PDPTR2 = 0x0000280e,GUEST_PDPTR2_HIGH = 0x0000280f,GUEST_PDPTR3 = 0x00002810,GUEST_PDPTR3_HIGH = 0x00002811,GUEST_BNDCFGS = 0x00002812,GUEST_BNDCFGS_HIGH = 0x00002813,HOST_IA32_PAT = 0x00002c00,HOST_IA32_PAT_HIGH = 0x00002c01,HOST_IA32_EFER = 0x00002c02,HOST_IA32_EFER_HIGH = 0x00002c03,HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04,HOST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002c05,PIN_BASED_VM_EXEC_CONTROL = 0x00004000,CPU_BASED_VM_EXEC_CONTROL = 0x00004002,EXCEPTION_BITMAP = 0x00004004,PAGE_FAULT_ERROR_CODE_MASK = 0x00004006,PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008,CR3_TARGET_COUNT = 0x0000400a,VM_EXIT_CONTROLS = 0x0000400c,VM_EXIT_MSR_STORE_COUNT = 0x0000400e,VM_EXIT_MSR_LOAD_COUNT = 0x00004010,VM_ENTRY_CONTROLS = 0x00004012,VM_ENTRY_MSR_LOAD_COUNT = 0x00004014,VM_ENTRY_INTR_INFO_FIELD = 0x00004016,VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018,VM_ENTRY_INSTRUCTION_LEN = 0x0000401a,TPR_THRESHOLD = 0x0000401c,SECONDARY_VM_EXEC_CONTROL = 0x0000401e,PLE_GAP = 0x00004020,PLE_WINDOW = 0x00004022,VM_INSTRUCTION_ERROR = 0x00004400,VM_EXIT_REASON = 0x00004402,VM_EXIT_INTR_INFO = 0x00004404,VM_EXIT_INTR_ERROR_CODE = 0x00004406,IDT_VECTORING_INFO_FIELD = 0x00004408,IDT_VECTORING_ERROR_CODE = 0x0000440a,VM_EXIT_INSTRUCTION_LEN = 0x0000440c,VMX_INSTRUCTION_INFO = 0x0000440e,GUEST_ES_LIMIT = 0x00004800,GUEST_CS_LIMIT = 0x00004802,GUEST_SS_LIMIT = 0x00004804,GUEST_DS_LIMIT = 0x00004806,GUEST_FS_LIMIT = 0x00004808,GUEST_GS_LIMIT = 0x0000480a,GUEST_LDTR_LIMIT = 0x0000480c,GUEST_TR_LIMIT = 0x0000480e,GUEST_GDTR_LIMIT = 0x00004810,GUEST_IDTR_LIMIT = 0x00004812,GUEST_ES_AR_BYTES = 0x00004814,GUEST_CS_AR_BYTES = 0x00004816,GUEST_SS_AR_BYTES = 0x00004818,GUEST_DS_AR_BYTES = 0x0000481a,GUEST_FS_AR_BYTES = 0x0000481c,GUEST_GS_AR_BYTES = 0x0000481e,GUEST_LDTR_AR_BYTES = 0x00004820,GUEST_TR_AR_BYTES = 0x00004822,GUEST_INTERRUPTIBILITY_INFO = 0x00004824,GUEST_ACTIVITY_STATE = 0X00004826,GUEST_SYSENTER_CS = 0x0000482A,VMX_PREEMPTION_TIMER_VALUE = 0x0000482E,HOST_IA32_SYSENTER_CS = 0x00004c00,CR0_GUEST_HOST_MASK = 0x00006000,CR4_GUEST_HOST_MASK = 0x00006002,CR0_READ_SHADOW = 0x00006004,CR4_READ_SHADOW = 0x00006006,CR3_TARGET_VALUE0 = 0x00006008,CR3_TARGET_VALUE1 = 0x0000600a,CR3_TARGET_VALUE2 = 0x0000600c,CR3_TARGET_VALUE3 = 0x0000600e,EXIT_QUALIFICATION = 0x00006400,GUEST_LINEAR_ADDRESS = 0x0000640a,GUEST_CR0 = 0x00006800,GUEST_CR3 = 0x00006802,GUEST_CR4 = 0x00006804,GUEST_ES_BASE = 0x00006806,GUEST_CS_BASE = 0x00006808,GUEST_SS_BASE = 0x0000680a,GUEST_DS_BASE = 0x0000680c,GUEST_FS_BASE = 0x0000680e,GUEST_GS_BASE = 0x00006810,GUEST_LDTR_BASE = 0x00006812,GUEST_TR_BASE = 0x00006814,GUEST_GDTR_BASE = 0x00006816,GUEST_IDTR_BASE = 0x00006818,GUEST_DR7 = 0x0000681a,GUEST_RSP = 0x0000681c,GUEST_RIP = 0x0000681e,GUEST_RFLAGS = 0x00006820,GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822,GUEST_SYSENTER_ESP = 0x00006824,GUEST_SYSENTER_EIP = 0x00006826,HOST_CR0 = 0x00006c00,HOST_CR3 = 0x00006c02,HOST_CR4 = 0x00006c04,HOST_FS_BASE = 0x00006c06,HOST_GS_BASE = 0x00006c08,HOST_TR_BASE = 0x00006c0a,HOST_GDTR_BASE = 0x00006c0c,HOST_IDTR_BASE = 0x00006c0e,HOST_IA32_SYSENTER_ESP = 0x00006c10,HOST_IA32_SYSENTER_EIP = 0x00006c12,HOST_RSP = 0x00006c14,HOST_RIP = 0x00006c16, };typedef struct _GUEST_REGS {ULONG eax;ULONG ecx;ULONG edx;ULONG ebx;ULONG esp;ULONG ebp;ULONG esi;ULONG edi;ULONG eip;ULONG eflags; }GUEST_REGS, * PGUEST_REGS;extern VMX_CPU g_VMXCPU;//檢查當前處理器是否支持VT BOOLEAN IsVTEnabled(); //開啟VT NTSTATUS StartVirtualTechnology(); //關閉VT NTSTATUS StopVirtualTechnology();#define Log(message,value) {{KdPrint(("[MinVT] %-40s [%p]\n",message,value));}}#endif //vtsystem.c #include "vtsystem.h" #include "vtasm.h" #include "exithandler.h"VMX_CPU g_VMXCPU;BOOLEAN IsVTEnabled() {ULONG uRet_EAX, uRet_ECX, uRet_EDX, uRet_EBX;_CPUID_ECX uCPUID;_CR0 uCr0;_CR4 uCr4;IA32_FEATURE_CONTROL_MSR msr;//1. CPUIDAsm_CPUID(1, &uRet_EAX, &uRet_EBX, &uRet_ECX, &uRet_EDX);*((PULONG)&uCPUID) = uRet_ECX;if (uCPUID.VMX != 1){Log("ERROR: 這個CPU不支持VT!", 0);return FALSE;}// 2. MSR*((PULONG)&msr) = (ULONG)Asm_ReadMsr(MSR_IA32_FEATURE_CONTROL);if (msr.Lock != 1){Log("ERROR:VT指令未被鎖定!", 0);return FALSE;}// 3. CR0 CR4*((PULONG)&uCr0) = Asm_GetCr0();*((PULONG)&uCr4) = Asm_GetCr4();if (uCr0.PE != 1 || uCr0.PG != 1 || uCr0.NE != 1){Log("ERROR:這個CPU沒有開啟VT!", 0);return FALSE;}if (uCr4.VMXE == 1){Log("ERROR:這個CPU已經開啟了VT!", 0);Log("可能是別的驅動已經占用了VT,你必須關閉它后才能開啟。", 0);return FALSE;}Log("SUCCESS:這個CPU支持VT!", 0);return TRUE; }static ULONG VmxAdjustControls(ULONG Ctl, ULONG Msr) {LARGE_INTEGER MsrValue;MsrValue.QuadPart = Asm_ReadMsr(Msr);Ctl &= MsrValue.HighPart; /* bit == 0 in high word ==> must be zero */Ctl |= MsrValue.LowPart; /* bit == 1 in low word ==> must be one */return Ctl; }static ULONG GetSegBase(ULONG Select) {ULONG GdtBase = Asm_GetGdtBase();ULONGLONG Descriptor = *(PULONGLONG)(GdtBase + Select);ULONG Base = 0;Base |= (Descriptor & 0xff00000000000000) >> 32;Base |= (Descriptor & 0x000000ff00000000) >> 16;Base |= (Descriptor & 0x00000000ffff0000) >> 16;return Base; }static ULONG GetSegLimit(ULONG Select) {ULONG GdtBase = Asm_GetGdtBase();ULONGLONG Descriptor = *(PULONGLONG)(GdtBase + Select);ULONG Limit = 0;Limit |= (Descriptor & 0x000f000000000000) >> 32;Limit |= Descriptor & 0x000000000000ffff;//if Desc.G == 1 ? 4kb : byteif (Descriptor & 0x0080000000000000){Limit <<= 12;Limit |= 0xfff;}return Limit; }static ULONG GetSegAR(ULONG Select) {ULONG GdtBase = Asm_GetGdtBase();ULONGLONG Descriptor = *(PULONGLONG)(GdtBase + Select);ULONG AR = 0;AR |= (Descriptor & 0x00f0ff0000000000) >> 40;return AR; }void __declspec(naked) GuestEntry() {__asm {mov ax, esmov es, axmov ax, dsmov ds, axmov ax, fsmov fs, axmov ax, gsmov gs, axmov ax, ssmov ss, ax}Vmx_VmCall(); }void SetupVMCS() {ULONG GdtBase, IdtBase;GdtBase = Asm_GetGdtBase();IdtBase = Asm_GetIdtBase();//// 1.Guest State Area//Vmx_VmWrite(GUEST_CR0, Asm_GetCr0());Vmx_VmWrite(GUEST_CR3, Asm_GetCr3());Vmx_VmWrite(GUEST_CR4, Asm_GetCr4());Vmx_VmWrite(GUEST_DR7, 0x400);Vmx_VmWrite(GUEST_RFLAGS, Asm_GetEflags() & ~0x200); //cliVmx_VmWrite(GUEST_ES_SELECTOR, Asm_GetEs() & 0xFFF8);Vmx_VmWrite(GUEST_CS_SELECTOR, Asm_GetCs() & 0xFFF8);Vmx_VmWrite(GUEST_DS_SELECTOR, Asm_GetDs() & 0xFFF8);Vmx_VmWrite(GUEST_FS_SELECTOR, Asm_GetFs() & 0xFFF8);Vmx_VmWrite(GUEST_GS_SELECTOR, Asm_GetGs() & 0xFFF8);Vmx_VmWrite(GUEST_SS_SELECTOR, Asm_GetSs() & 0xFFF8);Vmx_VmWrite(GUEST_TR_SELECTOR, Asm_GetTr() & 0xFFF8);// 重要的段寄存器信息需要在進入Guest前加載Vmx_VmWrite(GUEST_CS_AR_BYTES, GetSegAR(Asm_GetCs() & 0xFFF8));Vmx_VmWrite(GUEST_CS_BASE, GetSegBase(Asm_GetCs() & 0xFFF8));Vmx_VmWrite(GUEST_CS_LIMIT, GetSegLimit(Asm_GetCs() & 0xFFF8));Vmx_VmWrite(GUEST_TR_AR_BYTES, GetSegAR(Asm_GetTr() & 0xFFF8));Vmx_VmWrite(GUEST_TR_BASE, GetSegBase(Asm_GetTr() & 0xFFF8));Vmx_VmWrite(GUEST_TR_LIMIT, GetSegLimit(Asm_GetTr() & 0xFFF8));// 其他寄存器可在進入Guest后加載,先將屬性的第16位置1,即不可用狀態(tài)Vmx_VmWrite(GUEST_ES_AR_BYTES, 0x10000);Vmx_VmWrite(GUEST_FS_AR_BYTES, 0x10000);Vmx_VmWrite(GUEST_DS_AR_BYTES, 0x10000);Vmx_VmWrite(GUEST_SS_AR_BYTES, 0x10000);Vmx_VmWrite(GUEST_GS_AR_BYTES, 0x10000);Vmx_VmWrite(GUEST_LDTR_AR_BYTES, 0x10000);// CS、ESP、EIPVmx_VmWrite(GUEST_SYSENTER_CS, Asm_ReadMsr(MSR_IA32_SYSENTER_CS) & 0xFFFFFFFF);Vmx_VmWrite(GUEST_SYSENTER_ESP, Asm_ReadMsr(MSR_IA32_SYSENTER_ESP) & 0xFFFFFFFF);Vmx_VmWrite(GUEST_SYSENTER_EIP, Asm_ReadMsr(MSR_IA32_SYSENTER_EIP) & 0xFFFFFFFF); // KiFastCallEntry// GDTRVmx_VmWrite(GUEST_GDTR_BASE, GdtBase);Vmx_VmWrite(GUEST_GDTR_LIMIT, Asm_GetGdtLimit());// LDTRVmx_VmWrite(GUEST_IDTR_BASE, IdtBase);Vmx_VmWrite(GUEST_IDTR_LIMIT, Asm_GetIdtLimit());Vmx_VmWrite(GUEST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x1000); // Guest 臨時棧Vmx_VmWrite(GUEST_RIP, (ULONG)GuestEntry); // 客戶機的入口點// Link Shadow VMCSVmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff);Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff);//// 2.Host State Area//Vmx_VmWrite(HOST_CR0, Asm_GetCr0());Vmx_VmWrite(HOST_CR3, Asm_GetCr3());Vmx_VmWrite(HOST_CR4, Asm_GetCr4());Vmx_VmWrite(HOST_ES_SELECTOR, Asm_GetEs() & 0xFFF8);Vmx_VmWrite(HOST_CS_SELECTOR, Asm_GetCs() & 0xFFF8);Vmx_VmWrite(HOST_DS_SELECTOR, Asm_GetDs() & 0xFFF8);Vmx_VmWrite(HOST_FS_SELECTOR, Asm_GetFs() & 0xFFF8);Vmx_VmWrite(HOST_GS_SELECTOR, Asm_GetGs() & 0xFFF8);Vmx_VmWrite(HOST_SS_SELECTOR, Asm_GetSs() & 0xFFF8);Vmx_VmWrite(HOST_TR_SELECTOR, Asm_GetTr() & 0xFFF8);Vmx_VmWrite(HOST_TR_BASE, GetSegBase(Asm_GetTr() & 0xFFF8));Vmx_VmWrite(HOST_GDTR_BASE, GdtBase);Vmx_VmWrite(HOST_IDTR_BASE, IdtBase);Vmx_VmWrite(HOST_IA32_SYSENTER_CS, Asm_ReadMsr(MSR_IA32_SYSENTER_CS) & 0xFFFFFFFF);Vmx_VmWrite(HOST_IA32_SYSENTER_ESP, Asm_ReadMsr(MSR_IA32_SYSENTER_ESP) & 0xFFFFFFFF);Vmx_VmWrite(HOST_IA32_SYSENTER_EIP, Asm_ReadMsr(MSR_IA32_SYSENTER_EIP) & 0xFFFFFFFF); // KiFastCallEntryVmx_VmWrite(HOST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x2000); //Host 臨時棧Vmx_VmWrite(HOST_RIP, (ULONG)VMMEntryPoint); //這里定義我們的VMM處理程序入口//// 3.虛擬機運行控制域//Vmx_VmWrite(PIN_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS));Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0, MSR_IA32_VMX_PROCBASED_CTLS));//// 4.VMEntry運行控制域//Vmx_VmWrite(VM_ENTRY_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_ENTRY_CTLS));//// 5.VMExit運行控制域//Vmx_VmWrite(VM_EXIT_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_EXIT_CTLS)); }NTSTATUS StartVirtualTechnology() {PVOID pVMXONRegion;PVOID pVMCSRegion;PVOID pStack;VMX_BASIC_MSR Msr;ULONG uRevId;_CR4 uCr4;_EFLAGS uEflags;if (!IsVTEnabled())return STATUS_NOT_SUPPORTED;//VMXE*((PULONG)&uCr4) = Asm_GetCr4();uCr4.VMXE = 1;Asm_SetCr4(*((PULONG)&uCr4));//VMX version*((PULONG)&Msr) = (ULONG)Asm_ReadMsr(MSR_IA32_VMX_BASIC);uRevId = Msr.RevId;//VMXON regionpVMXONRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //4KBif (!pVMXONRegion){Log("ERROR:申請VMXON內存區(qū)域失敗!", 0);return STATUS_MEMORY_NOT_ALLOCATED;}RtlZeroMemory(pVMXONRegion, 0x1000);g_VMXCPU.pVMXONRegion = pVMXONRegion;g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(pVMXONRegion);*((PULONG)g_VMXCPU.pVMXONRegion) = uRevId;//VMXONVmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart, g_VMXCPU.pVMXONRegion_PA.HighPart);// if CF = 0*((PULONG)&uEflags) = Asm_GetEflags();if (uEflags.CF != 0){Log("ERROR:VMXON指令調用失敗!", 0);return STATUS_UNSUCCESSFUL;}Log("SUCCESS:VMXON指令調用成功!", 0);//VMCSpVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //4KBif (!pVMCSRegion){Log("ERROR:申請VMCS內存區(qū)域失敗!", 0);return STATUS_MEMORY_NOT_ALLOCATED;}RtlZeroMemory(pVMCSRegion, 0x1000);*((PULONG)pVMCSRegion) = uRevId;g_VMXCPU.pVMCSRegion = pVMCSRegion;g_VMXCPU.pVMCSRegion_PA = MmGetPhysicalAddress(pVMCSRegion);Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);Vmx_VmPtrld(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);//Stack,一半給Guest用,一半給Host用pStack = ExAllocatePoolWithTag(NonPagedPool, 0x2000, 'stck');if (!pStack){Log("ERROR:申請Stack內存區(qū)域失敗!", 0);return STATUS_MEMORY_NOT_ALLOCATED;}RtlZeroMemory(pStack, 0x2000);g_VMXCPU.pStack = pStack;//SetupSetupVMCS(); //設置VMCS字段//LaunchVmx_VmLaunch();//===================================================//正常情況下,VMLAUNCH執(zhí)行后,CPU會進入虛擬機中//如果走到這里,說明執(zhí)行失敗//===================================================Log("ERROR:VmLaunch指令調用失敗!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", Vmx_VmRead(VM_INSTRUCTION_ERROR))return STATUS_SUCCESS; }NTSTATUS StopVirtualTechnology() {_CR4 uCr4;Vmx_VmxOff();*((PULONG)&uCr4) = Asm_GetCr4();uCr4.VMXE = 0;Asm_SetCr4(*((PULONG)&uCr4));ExFreePool(g_VMXCPU.pVMXONRegion);ExFreePool(g_VMXCPU.pVMCSRegion);ExFreePool(g_VMXCPU.pStack);Log("SUCCESS:VMXOFF指令調用成功!", 0);return STATUS_SUCCESS; } //exithandler.h #ifndef EXITHANDLER_H #define EXITHANDLER_Hvoid VMMEntryPoint(void);#endif //exithandler.c #include "exithandler.h" #include "vtsystem.h" #include "vtasm.h"GUEST_REGS g_GuestRegs;static void VMMEntryPointEbd(void) {ULONG ExitReason;ExitReason = Vmx_VmRead(VM_EXIT_REASON);Log("ExitReason", ExitReason);g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);Log("g_GuestRegs.esp", g_GuestRegs.esp);Log("g_GuestRegs.eip", g_GuestRegs.eip);__asm int 3; }void __declspec(naked) VMMEntryPoint(void) {__asm{//需要設置fs和gs,否則無法正常運行mov ax, fsmov fs, axmov ax, gsmov gs, ax}Log("VM Exit", 0);//盡量不要在裸函數(shù)中定義局部變量,或實現(xiàn)太多功能,最好封裝成函數(shù)VMMEntryPointEbd(); } //driver.c #include <ntddk.h> #include "vtasm.h" #include "vtsystem.h"VOID DriverUnload(PDRIVER_OBJECT pDriver) {StopVirtualTechnology();DbgPrint("Driver unload. \r\n"); }NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) {DbgPrint("Driver load. \r\n");StartVirtualTechnology();pDriver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }運行結果:
查手冊能夠得知導致產生錯誤碼0x12的原因是執(zhí)行了VMCALL,說明已經成功進入了Guest并執(zhí)行。
此時,EIP指向VMCALL指令。
并且在棧頂位置存在值,即VMCALL的返回地址。
參考資料
- VT虛擬化架構編寫視頻教程①~⑥課
- 周鶴《VT技術入門》系列視頻教程
- github項目:VT_Learn
- github項目: HyperPlatform
- Intel開發(fā)手冊 卷3:Chapter 23 ~ Chapter 33
- x86內部函數(shù)列表
總結
以上是生活随笔為你收集整理的Intel VT学习笔记(四)—— VMCS(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: glibc-2.23学习笔记(二)——
- 下一篇: Intel VT学习笔记(五)—— 调试