欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

Intel VT学习笔记(六)—— VM-Exit Handler

发布时间:2025/3/21 46 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Intel VT学习笔记(六)—— VM-Exit Handler 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

Intel VT学习笔记(六)—— VM-Exit Handler

  • Reutrn To DriverEntry
  • VM-Exit Handler
    • External interrupt
    • I/O instruction
    • Control-register accesses
    • CPUID
    • VMCALL
  • 完整代码
  • 参考资料

Reutrn To DriverEntry

描述:当开启VT后,就可以从DriverEntry中返回了,但应该怎么做呢?

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) {DbgPrint("Driver load. \r\n");pDriver->DriverUnload = DriverUnload;StartVirtualTechnology(); //开启VT//=========================================//how do I get here?//=========================================return STATUS_SUCCESS; }

实现思路

  • 在开启VT前,将当前的CPU状态保存在栈里,然后记录ESP。
  • 使用类似于在StartVirtualTechnology函数下方打标签的方式,保存其地址作为返回地址。
  • 开启VT后,将EIP指向记录的返回地址,并将ESP指向之前保存的CPU状态。
  • 恢复CPU状态,继续向下执行。
  • 代码实现(在Guest中执行INT 3或VMCALL都可以):

    //driver.c NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) {DbgPrint("Driver load. \r\n");pDriver->DriverUnload = DriverUnload;__asm{pushadpushfdmov g_ret_esp, espmov g_ret_eip, offset RET_EIP}StartVirtualTechnology(); //开启VTRET_EIP:__asm{popfdpopad}Log("Return To DriverEntry .", 0);return STATUS_SUCCESS; } //exithandler.c extern ULONG g_ret_esp; extern ULONG g_ret_eip;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{mov esp, g_ret_espjmp g_ret_eip} }

    执行结果:

    可以看到,CPU已经成功回到了DriverEntry,并能够关闭VT以及卸载驱动。

    VM-Exit Handler

    描述:当Guest触发VM-Exit时,会进入Host进行处理。如果需要在Return To DriverEntry后使系统保持VT模式,则需要将返回代码写在Guest中,并在VM-Exit Handler中对各类VM-Exit事件进行处理。

    代码实现:

    //vtsystem.c ... 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, axmov esp, g_ret_espjmp g_ret_eip} } ... //exithandler.c ... static void VMMEntryPointEbd(void) {ULONG ExitReason;ULONG ExitInstructionLength;ULONG GuestResumeEIP;ExitReason = Vmx_VmRead(VM_EXIT_REASON); //获取退出码ExitInstructionLength = Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN); //获取指令长度Log("ExitReason", ExitReason);g_GuestRegs.eflags = Vmx_VmRead(GUEST_RFLAGS);g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);Log("g_GuestRegs.eflags", g_GuestRegs.eflags);Log("g_GuestRegs.esp", g_GuestRegs.esp);Log("g_GuestRegs.eip", g_GuestRegs.eip);switch(ExitReason){case ...: //需要处理的退出码//处理程序break;...default:Log("not handled reason: %p", ExitReason);__asm int 3;break;}__asm INT 3; }void __declspec(naked) VMMEntryPoint(void) {__asm{//需要设置fs和gs,否则无法正常运行mov ax, fsmov fs, axmov ax, gsmov gs, ax}Log("VM Exit", 0);//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数VMMEntryPointEbd(); } ...

    External interrupt

    描述:通过设置,可以使外部中断触发VM-Exit。

    令人感到奇怪的是,明明在设置VMCS字段的时候已经将TF位置0了,却仍然捕捉到了外部中断。

    Vmx_VmWrite(GUEST_RFLAGS, Asm_GetEflags() & ~0x200); //cli

    运行结果:

    由于笔刚接触这一块,是跟着周壑老师的视频做的实验,而周壑老师做实验的时候并没有遇到01错误,因此一下子给笔者整不会了,折磨了很久,谷歌百度均无果,最终通过查手册解决(手册YYDS),原来是控制寄存器的设置出了问题。

    查手册卷3附录C,退出码1对应的信息是外部中断,原因是控制域中相应位被设置。

    翻阅控制域相关部分(详见24.6.1),在「Pin-Based VM-Execution Controls」找到关于外部中断的详细说明。

    这里说,如果IA32_VMX_PINBASED_CTLS MSR的第0位为1,表示外部中断将触发VM-Exit,并且ELFAGS的TF位失效。

    找到原因了,那么接下来,只要在设置IA32_VMX_PINBASED_CTLS MSR的时候将第0位置0即可。

    当然,如果需要手动处理外部中断,可以忽略这一步。

    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 */if (MSR_IA32_VMX_PINBASED_CTLS == Msr){Ctl &= ~1;} }

    运行结果:

    可以看到,退出码01已经消失了,此时出现了一个新的退出码1E。

    I/O instruction

    描述:访问I/O时触发了VM-Exit,对应退出码1E。

    退出码1E的出现也是在意料之外的,和01类似,也是由于MSR寄存器的设置出现了问题。

    从手册对该退出码的描述可以看到,除了访问I/O,还需要满足两个条件。

  • 对应MSR的「use I/O bitmaps」位为0以及「unconditional I/O exiting」位为1
  • 「use I/O bitmaps」VM执行控制为1,与I/O指令访问的一个端口相关的位图中有一位为1。
  • 第二个条件不太理解是什么意思,但是第一个条件我们可以手动给他取消。

    关于「use I/O bitmaps」和「unconditional I/O exiting」标志位可查阅Intel手册卷3第24.6.2小节,是IA32_VMX_PROCBASED_CTLS MSR寄存器的第24比特位和第25比特位。。

    目前无需了解其具体含义,把它们全部置0先。

    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 */if (MSR_IA32_VMX_PINBASED_CTLS == Msr) //关闭外部中断信号{Ctl &= ~1;}if (MSR_IA32_VMX_PROCBASED_CTLS == Msr) //关闭I/O访问信号{Ctl &= ~0x03000000;}return Ctl; }

    执行结果:

    好了,现在退出码1E也消失了,捕获到的退出码变成了1C,总算可以进入正题了。

    Control-register accesses

    描述:当Guest试图使用控制寄存器时会触发该事件,对应的退出码为0x1C。

    处理方式:当Guest试图使用控制寄存器,并触发VM-Exit来到Host后,Host通过读取相应的信息,帮助Guest完成对应的功能,然后回到触发该事件的下一句指令。

    ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION);

    关于「Control-register accesses」错误的具体信息可参考Intel开发手册卷3第27.2小节。


    值得注意的是,在实现Handler的过程中,只拦截Cr3即可。

    具体思路:

  • 如果分析错误信息后发现是操作的话,就把对应的值写到VMCS相应的字段中。
  • 如果发现是操作的话,就先将其读到g_GuestRegs全局变量的对应寄存器成员中,然后在Resume前将其写到寄存器当中,调用Resume的时候会自动写到VMCS相应的字段中。
  • 代码实现:

    void HandleCrAccess() {ULONG movcrControlRegister;ULONG movcrAccessType;ULONG movcrOperandType; //只是读了,没有使用,通常都是寄存器ULONG movcrGeneralPurposeRegister;ULONG movcrLMSWSourceData;ULONG ExitQualification;ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION); //获取具体信息movcrControlRegister = (ExitQualification & 0x0000000F); //控制寄存器下标movcrAccessType = ((ExitQualification & 0x00000030) >> 4); //读/写movcrOperandType = ((ExitQualification & 0x00000040) >> 6); //寄存器/内存movcrGeneralPurposeRegister = ((ExitQualification & 0x00000F00) >> 8); //寄存器下标if (movcrControlRegister != 3) { // not for cr3__asm int 3}if (movcrAccessType == 0) { // CR3 <-- reg32Vmx_VmWrite(GUEST_CR3, *(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister));}else { // reg32 <-- CR3*(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister) = Vmx_VmRead(GUEST_CR3);} }

    执行结果:

    可以看到系统在不断访问控制寄存器,让我们看看是谁干的。

    破案了,原来是线程切换的时候访问了Cr3。

    由于系统在不断地切换线程,因此也就在不断地输出调试信息,这个时候系统会处于假卡死状态,无法正常操作。

    把输出调试信息的代码注释掉之后,系统就变得流畅了起来,但是这并不代表已经完全没问题了。

    当进行特定操作时,仍然会触发其他原因导致的VM-Exit,例如当打开一个进程时,会触发退出码0A(CPUID)。

    CPUID

    描述:汇编指令,用于获取CPU的相关信息,当Guest调用该指令时,会触发VM-Exit信号。

    处理方法:在Host中模拟执行,并返回结果。

    void HandleCPUID() {//特殊处理,用于测试VT是否开启,注意保证参数的独立性。if (g_GuestRegs.eax == 'Mini'){g_GuestRegs.ebx = 0x88888888;g_GuestRegs.ecx = 0x11111111;g_GuestRegs.edx = 0x12345678;}//正常情况,替Guest模拟执行CPUIDelse Asm_CPUID(g_GuestRegs.eax, &g_GuestRegs.eax, &g_GuestRegs.ebx, &g_GuestRegs.ecx, &g_GuestRegs.edx); }

    执行结果(CPUID的调用频次不高,开启调试信息问题不大):

    可以发现已经能够正常处理CPUID指令,然后将EAX改为「Mini」的ASCII码,再调用CPUID试试。

    ECX、EDX、EBX返回的结果正是Handle处理的结果,说明现在系统确实是处于VT模式。

    VMCALL

    描述:执行VMCALL指令时,能够触发一次VM-Exit。

    使用场景:当需要退出VT模式时,如果直接调用VMXOFF指令,由于Guest将没有权限执行,因此将会出错。可行的思路是先使用VMCALL切换到VMX root模式,由Host来执行退出部分的代码。

    代码实现

    ULONG g_vmcall_arg; ULONG g_stop_esp, g_stop_eip;void HandleVmCall() {if (g_vmcall_arg == 'SVT'){Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);Vmx_VmxOff();__asm {mov esp, g_stop_espjmp g_stop_eip}}else {__asm int 3} }

    处理完这几个退出码之后,基本就初步实现了一个最小VT框架。如果后续遇到其它退出码,可再通过查手册解决。

    完整代码

    //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);void Asm_IN(ULONG uRet_EAX, ULONG uPort);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_IN Proc uRet_EAX:dword, uPort:dwordmov eax, uRet_EAXmov edx, uPortin eax, dx Asm_IN 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 0x176 #define MSR_IA32_DEBUGCTL 0x1d9//VMX Exit Reasons #define EXIT_REASON_CPUID 10 #define EXIT_REASON_VMCALL 18 #define EXIT_REASON_CR_ACCESS 28typedef 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"extern ULONG g_back_esp; extern ULONG g_back_eip;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 */if (MSR_IA32_VMX_PINBASED_CTLS == Msr) //关闭外部中断信号{Ctl &= ~1;}if (MSR_IA32_VMX_PROCBASED_CTLS == Msr) //关闭I/O访问信号{Ctl &= ~0x03000000;}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, axmov esp, g_back_espjmp g_back_eip} }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,即不可用状态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内存区域失败!", 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:开启VT失败!", 0);return STATUS_UNSUCCESSFUL;}Log("SUCCESS:开启VT成功!", 0);//VMCSpVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //4KBif (!pVMCSRegion){Log("ERROR:申请VMCS内存区域失败!", 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内存区域失败!", 0);return STATUS_MEMORY_NOT_ALLOCATED;}RtlZeroMemory(pStack, 0x2000);g_VMXCPU.pStack = pStack;//SetupSetupVMCS(); //设置VMCS字段//LaunchVmx_VmLaunch();//===================================================//正常情况下,VMLAUNCH执行后,CPU会进入虚拟机中//如果走到这里,说明执行失败//===================================================Log("ERROR:VmLaunch指令调用失败!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", Vmx_VmRead(VM_INSTRUCTION_ERROR))return STATUS_SUCCESS; }extern ULONG g_vmcall_arg; extern ULONG g_stop_esp; extern ULONG g_stop_eip;NTSTATUS StopVirtualTechnology() {_CR4 uCr4;g_vmcall_arg = 'STOP';__asm {pushadpushfdmov g_stop_esp, espmov g_stop_eip, offset STOP}Vmx_VmCall();STOP:__asm {popfdpopad}*((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:关闭VT成功!", 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;void HandleCPUID() {//特殊处理,用于测试VT是否开启,注意保证参数的独立性。if (g_GuestRegs.eax == 'Mini'){g_GuestRegs.ebx = 0x88888888;g_GuestRegs.ecx = 0x11111111;g_GuestRegs.edx = 0x12345678;}//正常情况,替Guest模拟执行CPUIDelse Asm_CPUID(g_GuestRegs.eax, &g_GuestRegs.eax, &g_GuestRegs.ebx, &g_GuestRegs.ecx, &g_GuestRegs.edx); }ULONG g_vmcall_arg; ULONG g_stop_esp, g_stop_eip;void HandleVmCall() {if (g_vmcall_arg == 'STOP'){Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);Vmx_VmxOff();__asm {mov esp, g_stop_espjmp g_stop_eip}}else {__asm int 3} }void HandleCrAccess() {ULONG movcrControlRegister;ULONG movcrAccessType;ULONG movcrOperandType; //只是读了,没有使用,通常都是寄存器ULONG movcrGeneralPurposeRegister;ULONG movcrLMSWSourceData;ULONG ExitQualification;ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION); //获取具体信息movcrControlRegister = (ExitQualification & 0x0000000F); //控制寄存器下标movcrAccessType = ((ExitQualification & 0x00000030) >> 4); //读/写movcrOperandType = ((ExitQualification & 0x00000040) >> 6); //寄存器/内存movcrGeneralPurposeRegister = ((ExitQualification & 0x00000F00) >> 8); //寄存器下标if (movcrControlRegister != 3) { // not for cr3__asm int 3}if (movcrAccessType == 0) { // CR3 <-- reg32Vmx_VmWrite(GUEST_CR3, *(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister));}else { // reg32 <-- CR3*(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister) = Vmx_VmRead(GUEST_CR3);} }static void VMMEntryPointEbd(void) {ULONG ExitReason;ULONG ExitInstructionLength;ULONG GuestResumeEIP;ExitReason = Vmx_VmRead(VM_EXIT_REASON);ExitInstructionLength = Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN);g_GuestRegs.eflags = Vmx_VmRead(GUEST_RFLAGS);g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);//Log("ExitReason", ExitReason);//Log("g_GuestRegs.eip", g_GuestRegs.eip);switch (ExitReason){case EXIT_REASON_CPUID:HandleCPUID();Log("EXIT_REASON_CPUID", 0);break;case EXIT_REASON_VMCALL:HandleVmCall();Log("EXIT_REASON_VMCALL", 0);break;case EXIT_REASON_CR_ACCESS:HandleCrAccess();//Log("EXIT_REASON_CR_ACCESS", 0);break;default:Log("not handled reason: %p", ExitReason);__asm int 3;break;}//Resume:GuestResumeEIP = g_GuestRegs.eip + ExitInstructionLength;Vmx_VmWrite(GUEST_RIP, GuestResumeEIP);Vmx_VmWrite(GUEST_RSP, g_GuestRegs.esp);Vmx_VmWrite(GUEST_RFLAGS, g_GuestRegs.eflags); }void __declspec(naked) VMMEntryPoint(void) {__asm{mov g_GuestRegs.eax, eaxmov g_GuestRegs.ecx, ecxmov g_GuestRegs.edx, edxmov g_GuestRegs.ebx, ebxmov g_GuestRegs.esp, espmov g_GuestRegs.ebp, ebpmov g_GuestRegs.esi, esimov g_GuestRegs.edi, edipushfdpop eaxmov g_GuestRegs.eflags, eax//需要设置fs和gs,否则无法正常运行mov ax, fsmov fs, axmov ax, gsmov gs, ax}//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数VMMEntryPointEbd();__asm {mov eax, g_GuestRegs.eaxmov ecx, g_GuestRegs.ecxmov edx, g_GuestRegs.edxmov ebx, g_GuestRegs.ebxmov esp, g_GuestRegs.espmov ebp, g_GuestRegs.ebpmov esi, g_GuestRegs.esimov edi, g_GuestRegs.edi//vmresume__emit 0x0f__emit 0x01__emit 0xc3} } //driver.c #include <ntddk.h> #include "vtasm.h" #include "vtsystem.h"VOID DriverUnload(PDRIVER_OBJECT pDriver) {StopVirtualTechnology();DbgPrint("Driver unload. \r\n"); }ULONG g_back_esp; ULONG g_back_eip;NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) {DbgPrint("Driver load. \r\n");pDriver->DriverUnload = DriverUnload;__asm{pushadpushfdmov g_back_esp, espmov g_back_eip, offset BACK}StartVirtualTechnology(); //开启VTBACK:__asm{popfdpopad}//Log("Return To DriverEntry", 0);return STATUS_SUCCESS; }

    执行结果

    参考资料

    • VT虚拟化架构编写视频教程①~⑥课
    • 周鹤《VT技术入门》系列视频教程
    • github项目:VT_Learn
    • github项目: HyperPlatform
    • Intel开发手册 卷3:Chapter 23 ~ Chapter 33
    • x86内部函数列表

    总结

    以上是生活随笔为你收集整理的Intel VT学习笔记(六)—— VM-Exit Handler的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。