Intel VT学习笔记(三)—— VMCS(上)
Intel VT学习笔记(三)—— VMCS前言VMCS参考资料前言在上一篇笔记中,我们学习了如何进入VMX模式,接下来,我们将学习与虚拟机开始交互前需要做的一些准备工作VMCS参考资料VT虚拟化架构编写视频教程①~⑥课周鹤《VT技术入门》系列视频教程github项目:VT_Learngithub项目: HyperPlatformIntel开发手册 卷3:Chapter 23 ~ Chapter
Intel VT学习笔记(三)—— VMCS(上)
要点回顾
在上一篇笔记中,我们学习了如何进入VMX模式,并申请了一块4KB的内存,用于保存VMM相关信息。
接下来,我们将学习与虚拟机开始交互前需要做的一些准备工作。
VMCS
全称:virtual-machine control data structures
描述:可参考Intel开发手册卷3第24章。
- 逻辑处理器在VMX操作时使用虚拟机控制数据结构(virtual-machine control data structures,VMCSs)。它们管理进入和退出VMX non-root(虚拟机入口和虚拟机出口)的过渡,以及VMX non-root中的处理器行为。这个结构由新的指令VMCLEAR、VMPTRLD、VMREAD和VMWRITE操作。
- VMX需要为每个虚拟机分配一个VMCS结构,将虚拟机信息保存在一个4KB对齐的内存页上(称作VMCS region),这块内存的第11:0位必须为0。
- 同一时刻,可以存在多个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小节)
- Guest-State Area
- Host-State Area
- VM-Control Fields
- VM-Exit Information Fields
其中,控制域又可以分为三个部分:
- VM-Execution Control Fields
- VM-Exit Control Fields
- 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信息如何填写将留到下一篇学习。
参考资料
- VT虚拟化架构编写视频教程①~⑥课
- 周壑《VT技术入门》系列视频教程
- github项目:VT_Learn
- github项目: HyperPlatform
- Intel开发手册 卷3:Chapter 23 ~ Chapter 33
- x86内部函数列表
更多推荐
所有评论(0)