要点回顾

上一篇笔记中,我们学习了如何进入VMX模式,并申请了一块4KB的内存,用于保存VMM相关信息。

接下来,我们将学习与虚拟机开始交互前需要做的一些准备工作。

VMCS

全称:virtual-machine control data structures
描述:可参考Intel开发手册卷3第24章。

  1. 逻辑处理器在VMX操作时使用虚拟机控制数据结构(virtual-machine control data structures,VMCSs)。它们管理进入和退出VMX non-root(虚拟机入口和虚拟机出口)的过渡,以及VMX non-root中的处理器行为。这个结构由新的指令VMCLEAR、VMPTRLD、VMREAD和VMWRITE操作。
  2. VMX需要为每个虚拟机分配一个VMCS结构,将虚拟机信息保存在一个4KB对齐的内存页上(称作VMCS region),这块内存的第11:0位必须为0。
  3. 同一时刻,可以存在多个active VMCS,但最多只能存在一个current VMCS。

VMCLEAR:初始化VMCS
VMPTRLD:选中一个VMCS作为current VMCS,需要提供其物理地址作为参数,执行之后,其他VMCS切换到active状态
VMWRITE:用于设置VMCS的各个字段,可以理解为填入VMCS的基本信息。由于在不同版本系统中相同字段的位置可能不同,因此没有采用直接对内存进行读写的方式来设置字段。

设置字段

VMCS字段列表如下(不同版本,各字段的位置可能不同),也可以参考Intel开发手册卷3附录B
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

错误排查

描述:设置好VMCS字段后,可尝试执行VMLAUNCH指令进行启动,如果失败,可以通过错误码进行排查。
在这里插入图片描述
各错误码含义可参考Intel开发手册卷3第30.4小节
在这里插入图片描述
以错误码7为例,含义是「VM entry with invalid control field(s)」,就是说控制域字段存在问题。

Fields

描述:VMCS能够大致分为以下几个域(具体可参考Intel开发手册卷3第24.4~24.9小节)

  1. Guest-State Area
  2. Host-State Area
  3. VM-Control Fields
  4. VM-Exit Information Fields

其中,控制域又可以分为三个部分:

  1. VM-Execution Control Fields
  2. VM-Exit Control Fields
  3. VM-Entry Information Fields

Host-State Area

描述:Host-State域用于记录部分CPU的状态信息,因为CPU是在VMM与虚拟机中来回切换运行的,所以在VMCS中既要记录主机CPU的状态,也要记录虚拟机CPU的状态,这样才能保证虚拟机正常运行。

根据Intel手册,目前Host-State域需要设置的字段:

  • 控制寄存器:Cr0、Cr3、Cr4
  • 段寄存器:CS、SS、DS、ES、FS、GS、TR
  • 基址段寄存器:FS、GS、TR、GDTR、IDTR
  • MSR寄存器:IA32_SYSENTER_CS、IA32_SYSENTER_ESP、IA32_SYSENTER_EIP

VM-Control Fields

描述:用于控制虚拟机行为,例如中断方式,是否启用EPT,启用I/O端口,MSR访问限制等等。

控制域相关字段不能够随意设置,需要参考MSR寄存器。

以Pin-Based VM-Execution Control Fields为例,对应PIN_BASED_VM_EXEC_CONTROL字段,它的值参考IA32_VMX_PINBASED_CTLS MSR寄存器(偏移为481H,可参考Intel开发手册卷3附录A.3.1),前32位表示哪些比特位位可以为1,后32位表示哪些比特位必须为1(各比特位含义可参考Intel开发手册卷3第24.6小节)。
在这里插入图片描述
因此,在设置PIN_BASED_VM_EXEC_CONTROL字段时,必须将该字段与IA32_VMX_PINBASED_CTLS MSR的前32位进行与运算,与其后32位进行或运算,以保证关键的比特位不出错,其他相关字段同理。

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;
}

代码实现

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

#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_GetIdtBase PROC
        LOCAL	idtr[10]:BYTE
        sidt	idtr
        mov		eax, dword PTR idtr[2]
        ret
Asm_GetIdtBase ENDP

Vmx_VmLaunch Proc
        vmlaunch
        ret
Vmx_VmLaunch 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,
};

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;
}

void SetupVMCS()
{
    ULONG GdtBase, IdtBase;

    GdtBase = Asm_GetGdtBase();
    IdtBase = Asm_GetIdtBase();

    //
    // 1.Guest State Area
    //

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

    // Get Tr.Base
    ULONGLONG TSS_Descriptor = *(PULONGLONG)(GdtBase + (Asm_GetTr() & 0xFFF8));
    ULONG Tr_Base = 0;

    Tr_Base |= (TSS_Descriptor & 0xff00000000000000) >> 32;
    Tr_Base |= (TSS_Descriptor & 0x000000ff00000000) >> 16;
    Tr_Base |= (TSS_Descriptor & 0x00000000ffff0000) >> 16;

    //DbgPrint("TSS Descriptor = %I64x \r\n", TSS_Descriptor);
    //DbgPrint("Tr.Base = %x \r\n", Tr_Base);

    Vmx_VmWrite(HOST_TR_BASE, Tr_Base);

    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) + 0x1000);     //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
    pStack = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'stck'); //4KB就够用了
    if (!pStack)
    {
        Log("ERROR:申请Stack内存区域失败!", 0);
        return STATUS_MEMORY_NOT_ALLOCATED;
    }
    RtlZeroMemory(pStack, 0x1000);

    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"

void __declspec(naked) VMMEntryPoint(void)
{
	__asm
	{
		//需要设置fs和gs,否则无法正常运行
		mov ax, fs
		mov fs, ax
		mov ax, gs
		mov gs, ax
	}

	Log("VM Exit", 0);

	__asm int 3;
}
//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;
}

运行结果:
在这里插入图片描述

可以看到,现在VMLAUNCH已经执行成功,但是系统直接走到了退出部分,这是由于目前并未给到有关Guest的任何信息,因此刚进入Guest就立马退出了。

关于Guest信息如何填写将留到下一篇学习。

参考资料

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐