Intel VT学习笔记(四)—— VMCS(下)

Intel VT学习笔记(四)—— VMCS(下)

要点回顾

上一篇中,我们了解了如何设置VMCS字段,以及如何通过错误码排查错误字段,并最终成功执行了VMLAUNCH指令,进入了guest,但是由于由于目前并未填充Guest state fields,因此直接从Guest中跳出到Host中。

本篇将学习如何排查相关错误以及如何填写Guest域信息。

VM-Exit Information

描述:如果执行虚拟机代码时直接跳出到了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, fs
		mov fs, ax
		mov ax, gs
		mov gs, ax
	}
	
	//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数
	VMMEntryPointEbd();
}

执行结果:
在这里插入图片描述
可以看到,读取到的错误码为ExitReason等于0x80000021,esp和eip均为0。

VM-Exit错误码信息可参考Intel开发手册卷3第24.9小节。
在这里插入图片描述这里说,如果错误码的第31位被设置,第15:0比特位表示具体的错误码,附录C列举了这些错误码所对应的含义。
在这里插入图片描述可以看到,当31位1时,错误码0x21对应的含义是「invalid guest state」,具体原因是未通过26.3.1节的检查。

Guest state fields

描述:关于Guest state fields信息如何填写可参考Intel开发手册卷3第24.4节。
在这里插入图片描述
上图中说明需要加载的寄存器有:

  • 控制寄存器: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,即设置成不可用状态,这样在加载时就不会出错。

除此之外,还需要设置GDTR和LDTR的基址和边界。
在这里插入图片描述
以及几个关键的MSR寄存器。
在这里插入图片描述
最后,需要注意的是,存在两个特殊的字段,用于在需要时指向Shadow VMCS,若不需要使用,需要将其置为0xffffffff(参考Intel开发手册卷3第31.6小节)。
在这里插入图片描述
Shadow VMCS可理解为VMCS的拓展(参考Intel开发手册卷3第24.10小节)。
在这里插入图片描述

代码实现

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

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

.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_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_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

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_SYSENTER_CS            0x174
#define MSR_IA32_SYSENTER_ESP           0x175
#define MSR_IA32_SYSENTER_EIP           0x176

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"

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  */
    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
    }

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

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

    //
    // 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:VMXON指令调用失败!", 0);
        return STATUS_UNSUCCESSFUL;
    }

    Log("SUCCESS:VMXON指令调用成功!", 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;
}

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_H

void 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, fs
		mov fs, ax
		mov ax, gs
		mov gs, ax
	}

	Log("VM Exit", 0);

	//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数
	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的原因是执行了VMCALL,说明已经成功进入了Guest并执行。
在这里插入图片描述
此时,EIP指向VMCALL指令。
在这里插入图片描述
并且在栈顶位置存在值,即VMCALL的返回地址。
在这里插入图片描述
在这里插入图片描述

参考资料

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值