Intel VT学习笔记(八)—— 开启EPT

要点回顾

上一篇中,我们学习了什么是EPT模式,以及EPT模式是如何在GPA和HPA之间进行转换的。

在本篇,我们即将学习的是32-bit分页模式(10-10-12分页)下如何开启EPT。

在讲解正题之前,周壑老师抛出了几个小问题。
问题一:为什么在退出VT模式后,使用PCHUNTER查看GDT表会蓝屏?
问题二:进入VT模式时通过EFLAGS关闭了外部中断(IF位清零),切换到HOST时需要再手动关闭一下吗?

这里我们就直接说答案了,大家也可以之后动手实验一下。
问题一答案:因为在退出VT模式时,GDT.Limit和IDT.Limit会被设置为0xFFFF,可以在退出时手动设置回去(可参考Intel开发手册卷3第27.5.2小节)。
在这里插入图片描述
问题二答案: 不需要,因为从Guest切换到Host时,RIP、RSP会从VMCS相应的字段读取,而EFLAGS除了部分关键的标志位必须置1以外,其余部分会被清零(可参考Intel开发手册卷3第27.5.2小节)。
在这里插入图片描述

32-bit分页模式开启EPT

描述:32-bit分页模式即10-10-12分页模式,接下来将学习如何在该模式下开启EPT

设置VMCS字段

IA32_VMX_PROCBASED_CTLS2 MSR字段

IA32_VMX_PROCBASED_CTLS2 MSR寄存器中包含了与开启VT相关的标志位(参考Intel开发手册卷3附录A.3.3和表24-7)
在这里插入图片描述

IA32_VMX_PROCBASED_CTLS MSR字段

只有当IA32_VMX_PROCBASED_CTLS MSR的第31位(最后一位)为1时,IA32_VMX_PROCBASED_CTLS2 MSR寄存器才会生效(参考Intel开发手册卷3表24-6)。
在这里插入图片描述

EPTP字段

最关键的是设置EPTP字段,格式如下(参考Intel开发手册卷3第24.6.11小节):
在这里插入图片描述
首先除了后12位比特位为属性位以外,剩下的部分为物理地址,直接写入即可。

属性位第2:0位表示是否开启缓存,但不能随意设置,需要参考IA32_VMX_EPT_VPID_CAP MSR寄存器的相关标志位(详见Intel开发手册卷3附录A.10),只有当它的第8位为1时,才能关闭缓存(UC),当14位为1时,才能开启缓存(WB)。
在这里插入图片描述
然后属性位的第5:3位表示当前页表的极数,值比实际页数少1(比如是4级页表,就这个字段写3),这个值也不是随便写的,同样要参考IA32_VMX_EPT_VPID_CAP MSR寄存器,如果它的第6位为1,那么一定是4级页表。
在这里插入图片描述
最后看一下笔者实验的环境中IA32_VMX_EPT_VPID_CAP MSR寄存器的值。
在这里插入图片描述
可以看到,第6位为1,说明当前是4级页表,第14位为1,说明必须开启缓存。

代码实现

//for EPT
EPTP = MmGetPhysicalAddress(MyEptInitialization()).QuadPart;
Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0x80000000, MSR_IA32_VMX_PROCBASED_CTLS));
Vmx_VmWrite(SECONDARY_VM_EXEC_CONTROL, VmxAdjustControls(0x2, MSR_IA32_VMX_PROCBASED_CTLS2));
Vmx_VmWrite(EPT_POINTER, (EPTP | 6 | (3 << 3)) & 0xFFFFFFFF);
Vmx_VmWrite(EPT_POINTER_HIGH, (EPTP | 6 | (3 << 3)) >> 32);

运行结果:
在这里插入图片描述
此时产生了此前未处理过的退出码02,对应的含义为「Triple fault」。

结果分析

看一下当前EIP的位置:
在这里插入图片描述
不难发现,此时刚进入GuestEntry,但是执行第一句指令就导致了VM-Exit。

根据周壑老师的描述,三重错误是由于GPA转HPA的过程中出现了问题导致的。

细分来说

  1. 第一次GPA转HPA失败时,产生第一次异常,尝试进入对应的中断处理程序
  2. 进入中断处理程序失败,产生第二次异常,尝试进入二次异常处理程序
  3. 进入二次异常处理程序依然失败,也就导致了三重错误。

具体GPA转HPA失败的原因嘛。。。大家检查下自己是不是忘记切换到32-bit分页模式了【大无语事件】。

知识点复习:修改boot.ini中的启动项配置以切换分页模式

//PAE模式
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional Kernel Debugging" /noexecute=optin /fastdetect /debug /debugport=com1 /baudrate=115200
//32-bit模式
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional Kernel Debugging" /execute=optin /fastdetect /debug /debugport=com1 /baudrate=115200

修改好后,重启生效,再次尝试开启EPT,结果如下:
在这里插入图片描述
那么这里直接成功开启了EPT模式。

本来根据周壑老师的进度,这里应该会产生一个0x30的退出码,这是由于周壑老师此时映射的物理内存比较少(只有1G),而系统尝试访问一个比较大的物理地址(视频里是APIC的地址,意为高级可编程中断控制器,位于内核的KfRaiseIrql函数,作用是当系统为多核时负责多核之间的调度),因此出现了这个问题,而我们在上一篇直接使用了修复后的代码(映射了4G),因此没有出现这个问题。

PAE分页模式开启EPT

描述:PAE分页模式即2-9-9-12分页模式,相比32-bit分页模式而言,想要开启EPT还需要在前者基础上做一些额外的事情。

设置PDPTE字段

描述:在PAE分页模式下,如果开启EPT,需要在VMCS中设置与PDPTE相关的寄存器字段,共4个(参考Intel开发手册卷3第24.4节)。
在这里插入图片描述
没错,PDPTE也是有相关的寄存器作为高速缓存的,它的作用于段寄存器的base类似,避免每次要使用时都进行索引。

PDPTE格式如下(参考Intel开发手册卷3第4.4节):
在这里插入图片描述
每当切换Cr3时,CPU会把四个PDPTE里的值(即PDE的地址)写到高速缓存寄存器中,提高运行效率。

思考:VMCS字段该如何设置,才能让进程访问到PDPTE每项的物理地址呢?
答案:通过虚拟地址0xC0600000。

在Windows操作系统中,PDE的基址为0xC0600000,我们就可以通过这个特性访问4个PDE。

代码实现

//for EPT with PAE
Vmx_VmWrite(GUEST_PDPTR0, MmGetPhysicalAddress((PVOID)0xc0600000).LowPart | 1);	//最后一位为有效位
Vmx_VmWrite(GUEST_PDPTR0_HIGH, MmGetPhysicalAddress((PVOID)0xc0600000).HighPart);
Vmx_VmWrite(GUEST_PDPTR1, MmGetPhysicalAddress((PVOID)0xc0601000).LowPart | 1);
Vmx_VmWrite(GUEST_PDPTR1_HIGH, MmGetPhysicalAddress((PVOID)0xc0601000).HighPart);
Vmx_VmWrite(GUEST_PDPTR2, MmGetPhysicalAddress((PVOID)0xc0602000).LowPart | 1);
Vmx_VmWrite(GUEST_PDPTR2_HIGH, MmGetPhysicalAddress((PVOID)0xc0602000).HighPart);
Vmx_VmWrite(GUEST_PDPTR3, MmGetPhysicalAddress((PVOID)0xc0603000).LowPart | 1);
Vmx_VmWrite(GUEST_PDPTR3_HIGH, MmGetPhysicalAddress((PVOID)0xc0603000).HighPart);

运行结果(记得切换回PAE模式):
在这里插入图片描述

结果分析

可以看到,驱动运行后能够成功开启了VT,但是在执行csrss.exe进程途中出现了错误(继续往下运行会走向蓝屏)。

产生这种现象的原因是由于在切换线程时,Cr3寄存器的值会被切换,那也就意味着PDPTE的值也会发生变化。但是由于我们在VMCS中设置的PDPTE字段的值是固定的,所以一旦发生线程切换,下一个线程就无法正常寻址了,因此就会出现这种错误。对应的解决方式是在处理Cr3访问时,切换到对应进程的Cr3,然后同步更新一下4个PDPTE的值。

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 <-- reg32
		Vmx_VmWrite(GUEST_CR3, *(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister));
		Asm_SetCr3(Vmx_VmRead(GUEST_CR3));	//切换到对应进程的Cr3
		Vmx_VmWrite(GUEST_PDPTR0, MmGetPhysicalAddress((PVOID)0xc0600000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR0_HIGH, MmGetPhysicalAddress((PVOID)0xc0600000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR1, MmGetPhysicalAddress((PVOID)0xc0601000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR1_HIGH, MmGetPhysicalAddress((PVOID)0xc0601000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR2, MmGetPhysicalAddress((PVOID)0xc0602000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR2_HIGH, MmGetPhysicalAddress((PVOID)0xc0602000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR3, MmGetPhysicalAddress((PVOID)0xc0603000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR3_HIGH, MmGetPhysicalAddress((PVOID)0xc0603000).HighPart);
	}
	else {                            // reg32 <-- CR3
		*(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister) = Vmx_VmRead(GUEST_CR3);
	}
}

完整代码

//vtasm.h
#ifndef VTASM_H
#define VTASM_H

typedef 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 set
	unsigned Reserved1 : 1;		// Undefined
	unsigned EnableVmxon : 1;		// Bit 2. If this bit is clear, VMXON causes a general protection exception
	unsigned Reserved2 : 29;	// Undefined
	unsigned 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 zero
		unsigned UNKONOWN_2 : 1;		//These are zero
		unsigned VMXE : 1;			//It's zero in normal
		unsigned Reserved : 18;		//These are zero
		//unsigned Reserved_64:32;
	};
}_CR4;

typedef union
{
	struct
	{
		unsigned CF : 1;
		unsigned Unknown_1 : 1;	//Always 1
		unsigned PF : 1;
		unsigned Unknown_2 : 1;	//Always 0
		unsigned AF : 1;
		unsigned Unknown_3 : 1;	//Always 0
		unsigned 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();

void Asm_SetGdt(ULONG base, USHORT limit);
void Asm_SetIdt(ULONG base, USHORT limit);

ULONG Asm_GetCr0();
ULONG Asm_GetCr3();
ULONG Asm_GetCr4();

void Asm_SetCr3(ULONG uNewCr3);
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

.code

Asm_CPUID	Proc	uses ebx esi edi fn:dword, ret_eax:dword, ret_ebx:dword, ret_ecx:dword, ret_edx:dword
        mov	eax, fn
        cpuid
        mov	esi, ret_eax
        mov	dword ptr [esi], eax
        mov	esi, ret_ebx
        mov	dword ptr [esi], ebx
        mov	esi, ret_ecx
        mov	dword ptr [esi], ecx
        mov	esi, ret_edx
        mov	dword ptr [esi], edx
        ret
Asm_CPUID 	Endp



Asm_IN      Proc    uRet_EAX:dword, uPort:dword
        mov eax, uRet_EAX
        mov edx, uPort
        in  eax, dx
Asm_IN		Endp


Asm_ReadMsr		Proc	Index:dword
        mov	ecx,Index
        rdmsr
        ret
Asm_ReadMsr		Endp

Asm_GetCr0		Proc
        mov 	eax, cr0
        ret
Asm_GetCr0 		Endp

Asm_GetCr3		Proc
        mov 	eax, cr3
        ret
Asm_GetCr3 		Endp

Asm_GetCr4		Proc
        mov 	eax, cr4
        ret
Asm_GetCr4 		Endp

Asm_SetCr3		Proc 	NewCr3:dword
        mov 	eax, NewCr3
        mov	cr3, eax
        ret
Asm_SetCr3 		Endp

Asm_SetCr4		Proc	NewCr4:dword
        mov 	eax,NewCr4
        mov 	cr4, eax
        ret
Asm_SetCr4 		Endp

Vmx_VmxOn Proc LowPart:dword,HighPart:dword
        push HighPart
        push LowPart
        Vmxon qword ptr [esp]
        add esp,8
        ret
Vmx_VmxOn Endp

Vmx_VmxOff Proc
        Vmxoff
        ret
Vmx_VmxOff Endp

Asm_GetEflags PROC
        pushfd
        pop		eax
        ret
Asm_GetEflags ENDP

Vmx_VmClear Proc LowPart:dword,HighPart:dword
        push HighPart
        push LowPart
        vmclear qword ptr [esp]
        add esp,8
        ret
Vmx_VmClear endp

Vmx_VmPtrld Proc LowPart:dword,HighPart:dword
        push HighPart
        push LowPart
        vmptrld qword ptr [esp]
        add esp,8
        ret
Vmx_VmPtrld endp

Vmx_VmRead Proc uses ecx Field:dword
        mov eax,Field
        vmread ecx,eax
        mov eax,ecx
        ret
Vmx_VmRead endp

Vmx_VmWrite Proc uses ecx Field:dword,Value:dword
        mov eax,Field
        mov ecx,Value
        vmwrite eax,ecx
        ret
Vmx_VmWrite endp

Asm_GetCs PROC
        mov		eax, cs
        ret
Asm_GetCs ENDP

Asm_GetDs PROC
        mov		eax, ds
        ret
Asm_GetDs ENDP

Asm_GetEs PROC
        mov		eax, es
        ret
Asm_GetEs ENDP

Asm_GetSs PROC
        mov		eax, ss
        ret
Asm_GetSs ENDP

Asm_GetFs PROC
        mov		eax, fs
        ret
Asm_GetFs ENDP

Asm_GetGs PROC
        mov		eax, gs
        ret
Asm_GetGs ENDP

Asm_GetTr PROC
        str	eax
        ret
Asm_GetTr ENDP

Asm_GetGdtBase PROC
        LOCAL	gdtr[10]:BYTE
        sgdt	gdtr
        mov		eax, dword PTR gdtr[2]
        ret
Asm_GetGdtBase ENDP

Asm_GetGdtLimit PROC
        LOCAL	gdtr[10]:BYTE
        sgdt	gdtr
        mov		ax, WORD PTR gdtr[0]
        ret
Asm_GetGdtLimit ENDP

Asm_GetIdtBase PROC
        LOCAL	idtr[10]:BYTE
        sidt	idtr
        mov		eax, dword PTR idtr[2]
        ret
Asm_GetIdtBase ENDP

Asm_GetIdtLimit PROC
        LOCAL	idtr[10]:BYTE
        sidt	idtr
        mov		ax, WORD PTR idtr[0]
        ret
Asm_GetIdtLimit ENDP

Asm_SetGdt PROC base:dword, limit:word
        LOCAL   gdt[6]:BYTE
        mov     ax, limit
        mov     word ptr gdt[0], ax
        mov     eax, base
        mov     dword ptr gdt[2], eax
        lgdt    fword ptr gdt
        ret
Asm_SetGdt ENDP

Asm_SetIdt PROC base:dword, limit:word
        LOCAL   idt[6]:BYTE
        mov     ax, limit
        mov     word ptr idt[0], ax
        mov     eax, base
        mov     dword ptr idt[2], eax
        lgdt    fword ptr idt
        ret
Asm_SetIdt ENDP

Vmx_VmLaunch Proc
        vmlaunch
        ret
Vmx_VmLaunch endp

Vmx_VmCall Proc
        vmcall
        ret
Vmx_VmCall endp

END
//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_VMX_PROCBASED_CTLS2    0x48b

#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           28


typedef 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. CPUID
    Asm_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 : byte
    if (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, es
        mov es, ax

        mov ax, ds
        mov ds, ax

        mov ax, fs
        mov fs, ax

        mov ax, gs
        mov gs, ax

        mov ax, ss
        mov ss, ax

        mov esp, g_back_esp
        jmp g_back_eip
    }
}

ULONG64 EPTP;
extern ULONG64* MyEptInitialization();

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);    //cli
    
    Vmx_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、EIP
    Vmx_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

    // GDTR
    Vmx_VmWrite(GUEST_GDTR_BASE, GdtBase);
    Vmx_VmWrite(GUEST_GDTR_LIMIT, Asm_GetGdtLimit());
    // LDTR
    Vmx_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 VMCS
    Vmx_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));

    // GDTR
    Vmx_VmWrite(HOST_GDTR_BASE, GdtBase);
    // LDTR
    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); // KiFastCallEntry

    Vmx_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));

    //for EPT
    EPTP = MmGetPhysicalAddress(MyEptInitialization()).QuadPart;
    Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, VmxAdjustControls(0x80000000, MSR_IA32_VMX_PROCBASED_CTLS));
    Vmx_VmWrite(SECONDARY_VM_EXEC_CONTROL, VmxAdjustControls(0x2, MSR_IA32_VMX_PROCBASED_CTLS2));
    Vmx_VmWrite(EPT_POINTER, (EPTP | 6 | (3 << 3)) & 0xFFFFFFFF);
    Vmx_VmWrite(EPT_POINTER_HIGH, (EPTP | 6 | (3 << 3)) >> 32);
    //for EPT with PAE
    Vmx_VmWrite(GUEST_PDPTR0, MmGetPhysicalAddress((PVOID)0xc0600000).LowPart | 1);
    Vmx_VmWrite(GUEST_PDPTR0_HIGH, MmGetPhysicalAddress((PVOID)0xc0600000).HighPart);
    Vmx_VmWrite(GUEST_PDPTR1, MmGetPhysicalAddress((PVOID)0xc0601000).LowPart | 1);
    Vmx_VmWrite(GUEST_PDPTR1_HIGH, MmGetPhysicalAddress((PVOID)0xc0601000).HighPart);
    Vmx_VmWrite(GUEST_PDPTR2, MmGetPhysicalAddress((PVOID)0xc0602000).LowPart | 1);
    Vmx_VmWrite(GUEST_PDPTR2_HIGH, MmGetPhysicalAddress((PVOID)0xc0602000).HighPart);
    Vmx_VmWrite(GUEST_PDPTR3, MmGetPhysicalAddress((PVOID)0xc0603000).LowPart | 1);
    Vmx_VmWrite(GUEST_PDPTR3_HIGH, MmGetPhysicalAddress((PVOID)0xc0603000).HighPart);

    //
    // 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 region
    pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //4KB
    if (!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;

    //VMXON
    Vmx_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);

    //VMCS
    pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //4KB
    if (!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;

    //Setup
    SetupVMCS();    //设置VMCS字段

    //Launch
    Vmx_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;

extern void MyEptFree();

NTSTATUS StopVirtualTechnology()
{
    _CR4 uCr4;
    UCHAR GDT[6];

    g_vmcall_arg = 'STOP';

    __asm {
        pushad
        pushfd
        mov g_stop_esp, esp
        mov g_stop_eip, offset STOPVT
    }

    Vmx_VmCall();

STOPVT:

    __asm {
        popfd
        popad
    }

    // Set VMXE with 0
    *((PULONG)&uCr4) = Asm_GetCr4();
    uCr4.VMXE = 0;
    Asm_SetCr4(*((PULONG)&uCr4));

    // Repair Gdt&Idt Limit
    Asm_SetGdt(Asm_GetGdtBase(), 0x3ff);
    Asm_SetIdt(Asm_GetGdtBase(), 0x7ff);

    ExFreePool(g_VMXCPU.pVMXONRegion);
    ExFreePool(g_VMXCPU.pVMCSRegion);
    ExFreePool(g_VMXCPU.pStack);

    MyEptFree();

    Log("SUCCESS:关闭VT成功!", 0);

    return STATUS_SUCCESS;
}
//exithandler.h
#ifndef EXITHANDLER_H
#define EXITHANDLER_H

void 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模拟执行CPUID
	else 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_esp
			jmp 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 <-- reg32
		Vmx_VmWrite(GUEST_CR3, *(PULONG)((ULONG)&g_GuestRegs + 4 * movcrGeneralPurposeRegister));
		Asm_SetCr3(Vmx_VmRead(GUEST_CR3));	//切换到对应进程的Cr3
		Vmx_VmWrite(GUEST_PDPTR0, MmGetPhysicalAddress((PVOID)0xc0600000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR0_HIGH, MmGetPhysicalAddress((PVOID)0xc0600000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR1, MmGetPhysicalAddress((PVOID)0xc0601000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR1_HIGH, MmGetPhysicalAddress((PVOID)0xc0601000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR2, MmGetPhysicalAddress((PVOID)0xc0602000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR2_HIGH, MmGetPhysicalAddress((PVOID)0xc0602000).HighPart);
		Vmx_VmWrite(GUEST_PDPTR3, MmGetPhysicalAddress((PVOID)0xc0603000).LowPart | 1);
		Vmx_VmWrite(GUEST_PDPTR3_HIGH, MmGetPhysicalAddress((PVOID)0xc0603000).HighPart);
	}
	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);
		Log("g_GuestRegs.eip", g_GuestRegs.eip);
		__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, eax
		mov g_GuestRegs.ecx, ecx
		mov g_GuestRegs.edx, edx
		mov g_GuestRegs.ebx, ebx
		mov g_GuestRegs.esp, esp
		mov g_GuestRegs.ebp, ebp
		mov g_GuestRegs.esi, esi
		mov g_GuestRegs.edi, edi

		pushfd
		pop eax
		mov g_GuestRegs.eflags, eax

		//需要设置fs和gs,否则无法正常运行
		mov ax, fs
		mov fs, ax
		mov ax, gs
		mov gs, ax
	}

	//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数
	VMMEntryPointEbd();

	__asm {
		mov  eax, g_GuestRegs.eax
		mov  ecx, g_GuestRegs.ecx
		mov  edx, g_GuestRegs.edx
		mov  ebx, g_GuestRegs.ebx
		mov  esp, g_GuestRegs.esp
		mov  ebp, g_GuestRegs.ebp
		mov  esi, g_GuestRegs.esi
		mov  edi, g_GuestRegs.edi

		//vmresume
		__emit 0x0f
		__emit 0x01
		__emit 0xc3
	}
}
//ept.c
#include <ntddk.h>
#define PAGE_SIZE 0x1000

ULONG64* ept_PML4T;

static PVOID *pagesToFree;
static int index = 0;

void InitEptPagesPool()
{
    pagesToFree = ExAllocatePoolWithTag(NonPagedPool, 12*1024*1024, 'ept');
    if(!pagesToFree)
        __asm int 3
    RtlZeroMemory(pagesToFree, 12*1024*1024);
}

static ULONG64* AllocateOnePage()
{
    PVOID page;
    page = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'ept');
    if(!page)
        __asm int 3
    RtlZeroMemory(page, PAGE_SIZE);
    pagesToFree[index] = page;
    index++;
    return (ULONG64 *)page;
}

ULONG64* MyEptInitialization()
{
    ULONG64 *ept_PDPT, *ept_PDT, *ept_PT;
    PHYSICAL_ADDRESS FirstPtePA, FirstPdePA, FirstPdptePA;
    int a, b, c;

    InitEptPagesPool();
    ept_PML4T = AllocateOnePage();
    ept_PDPT = AllocateOnePage();
    FirstPdptePA = MmGetPhysicalAddress(ept_PDPT);
    *ept_PML4T = (FirstPdptePA.QuadPart) + 7;
    for (a = 0; a < 4; a++)
    {
        ept_PDT = AllocateOnePage();
        FirstPdePA = MmGetPhysicalAddress(ept_PDT);
        *ept_PDPT = (FirstPdePA.QuadPart) + 7;
        ept_PDPT++;
        for (b = 0; b < 512; b++)
        {
            ept_PT = AllocateOnePage();
            FirstPtePA = MmGetPhysicalAddress(ept_PT);
            *ept_PDT = (FirstPtePA.QuadPart) + 7;
            ept_PDT++;
            for (c = 0; c < 512; c++)
            {
                *ept_PT  = ((a << 30) | (b << 21) | (c << 12) | 0x37) & 0xFFFFFFFF;
                ept_PT++;
            }
        }
    }

    return ept_PML4T;
}

void MyEptFree()
{
    int i;
    for (i = 0; i < index; i++) {
        ExFreePool(pagesToFree[i]);
    }
    ExFreePool(pagesToFree);
    index = 0;
}
//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;

extern void InitEptPagesPool();
extern ULONG64* MyEptInitialization();

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
	DbgPrint("Driver load. \r\n");

	pDriver->DriverUnload = DriverUnload;

/*
	InitEptPagesPool();
	ULONG64* ept_PML4T = MyEptInitialization();
	Log("ept_PML4T", ept_PML4T);
	__asm int 3;
*/

	__asm
	{
		pushad
		pushfd
		mov g_back_esp, esp
		mov g_back_eip, offset BACK
	}

	StartVirtualTechnology();	//开启VT

BACK:

	__asm
	{
		popfd
		popad
	}

	//Log("Return To DriverEntry", 0);

	return STATUS_SUCCESS;
}

运行结果
在这里插入图片描述
现在,已经能够成功在PAE分页模式和32-bit分页模式下开启EPT模式了,这份代码是两者通用的,切换到32-bit分页模式时,系统不会去检查与PAE相关的字段,因此不会产生错误。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值