vmxon到vmlaunch期间
一、为每个虚拟机分配4kb大小空间
Intel手册第三卷31.6
在VMX功能MSR IA32_VMX_BASIC指定的大小的非分页内存中创建一个VMCS区域,对齐为4 kb
把前31位初始化为版本号,清除前四个字节的第31位
二、vmclear
通过提供的guest-VMCS地址来执行VMCLEAR
vmclear 通过检测EFLAG.cf位与EFLAG.ZF位来判断vmclear是否正确执行
三、VMPTRLD
通过提供的guest-VMCS地址来执行VMPTRLD
这将使用新的VMCS区域的物理地址初始化工作VMCS指针
四、VMWRITE
发出一个vmwrite序列来初始化工作的VMCS中的各个主机状态区域字段,初始化将在后续VM退出客户机时设置VMM的上下文和入口点。主机状态字段包括控制寄存器(CRO, CR3和CR4),段寄存器(CS, SS, DS, ES, FS, GS和TR)的选择器字段,和基址字段(FS, GS, TR, GDTR和IDTR;RSP、RIP和控制快速系统调用的msr)
使用vmwrite在VMCS中设置虚拟机退出控制字段、虚拟机进入控制字段和虚拟机执行控制字段。应该注意确保各个字段的设置与VMX能力msr报告的相应控件允许的O和1设置相匹配(见附录A)。任何与能力msr报告的设置不一致的设置将导致VM条目失败。
使用VMWRITE在工作的VMCS中初始化各种来宾状态区域字段。这将在VM入口时为来宾执行设置上下文和入口点。第27章描述了客户机状态的加载和处理器对VM条目进行的检查,以保护和执行virtual-8086客户机
五、VMLAUNCH
执行vmlaunch后,通过检查EFLAG寄存器的ZF位或CF位来检测是否运行成功,
VM-instruction错误字段将会包含错误代码
六、VMCSs的概念(附)
VMCSs:virtual-machine control data structures虚拟机控制结构
Intel手册第三卷24.1
逻辑处理器在VMX操作时使用虚拟机控制数据结构(VMCSs)。它们管理进入和退出VMX非root操作(虚拟机入口和虚拟机出口),以及VMX非root操作中的处理器行为。这个结构由新的指令VMCLEAR、VMPTRLD、VMREAD和VMWRITE操作。
每个guest对应一个这样的结构
七、VMX的root与no-root的概念(附)
处理器对虚拟化的支持是通过一种称为VMX操作的处理器操作形式提供的。VMX操作有两种:VMX根操作和VMX非根操作。通常,VMM将在VMX根操作中运行,而客户软件将在VMX非根操作中运行。
VMX根操作和VMX非根操作之间的转换称为VMX转换。VMX转换有两种。过渡到VMX非根操作称为VM条目。从VMX的非根操作过渡到VMX的根操作称为虚拟机退出。
VMX根操作中的处理器行为与VMX操作之外的处理器行为非常相似。主要的区别是一组新的指令(VMX指令)是可用的,并且可以加载到某些控制寄存器的值是有限的(参见第23.8节)。
VMX非根操作中的处理器行为受到限制和修改,以促进虚拟化。
VMX非根操作和VMX转换由一种称为虚拟机控制结构(VMCS)的数据结构控制。
通过称为VMCS指针(每个逻辑处理器一个)的处理器状态组件来管理对VMCS的访问。VMCS指针的值为该VMCS的64位地址。VMCS指针的读写使用VMPTRST和VMPTRLD指令。
VMM通过VMREAD、VMWRITE和VMCLEAR指令配置VMCS。
VMM可以为它支持的每个虚拟机使用不同的vmcs。对于具有多个逻辑处理器(虚拟处理器)的虚拟机,VMM可以为每个虚拟处理器使用不同的VMCS。
八、执行失败返回的错误号
解决错误号为7的control错误
vm control fields vm控制域:
九、vm execution control
1、Pin-Based
基于pin的虚拟机执行控制构成了一个32位的向量,该字段中所有其他的位都被保留,一些为0,一些为1。软件应该参考VMX能力MSRs ia32_vmx_pinbased_ctl和ia32_vmx_true_pinbased_ctl来确定如何设置预留位。如果预留位设置不正确,将导致后续的虚拟机表项失败
A.3.1中介绍了MSR寄存器 ia32_vmx_pinbased_ctl,它是下标为481h的控制寄存器
它的后32位决定了32位向量必须置1的位,前32位决定了32位向量必须置0的位
必须置1的位:1.2.4
2、Processor-Based
一些必须设置的位依赖于IA32_VMX_PROCBASED_CTLS msr寄存器
1,4,5,6,7,13,14,15,16,26位必须设为1
也就是说读写CR3的时候必须退出虚拟机
其他的位我们也可以自己去设置,如第23位的调试域...
再如第28位,如果我们把这一位设置为0,虚拟机里的MSR寄存器都为0,如果虚拟机想要操作MSR寄存器的话,就要被宿主机拦截
十、vm exit control
24.7.1
依赖于IA32_VMX_EXIT_CTLS寄存器
下表为483的msr寄存器
十一、vm entry control
依赖于IA32_VMX_ENTRY_CTLS寄存器,这是下标为484的msr寄存器
解决错误号为8的host错误
Host state fields 主机状态域 它决定了虚拟机中断或发生异常怎样返回Host或 返回Host的哪个地址
十二、HOST-STATE AREA
24.5
我们需要在虚拟机中向这些寄存器中添值
如CR0 CR3 CR4寄存器,RSP,RIP,寄存器,各种段选择子(除了LDTR)
FS,GS,TR,GDTR,IDTR段寄存器的基址
CS,ESP,EIP寄存器的值
这里需要 & 0xfff8的原因是,切换成0环段选择子
...目前用到的代码文件
主要代码:
vtsystem.c:
#include "vtsystem.h"
#include "vtasm.h"
#include "exithandler.h"
VMX_CPU g_VMXCPU;
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 fields
//2、Host state fields
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); //调成0环的段选择子//
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, 0x80042000); //tr寄存器在5号段选择子上,直接查,为0x80042000// 直接填充为tr的基地址//
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、vm-control fields
// 3.1、vm execution control
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));
// 3.2、vm exit control
Vmx_VmWrite(VM_EXIT_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_EXIT_CTLS));
// 3.3、vm entry control
Vmx_VmWrite(VM_ENTRY_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_ENTRY_CTLS));
}
NTSTATUS StartVirtualTechnology()
{
_CR4 uCr4;
_EFLAGS uEflags;
if(!IsVTEnabled())
{
return STATUS_UNSUCCESSFUL;
}
*((PULONG)&uCr4) = Asm_GetCr4();
uCr4.VMXE = 1; //第一步,开锁完成//
Asm_SetCr4(*((PULONG)&uCr4));
g_VMXCPU.pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'tlsn');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k//
RtlZeroMemory(g_VMXCPU.pVMXONRegion, 0x1000);
*(PULONG)g_VMXCPU.pVMXONRegion = 1;// //
g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMXONRegion);//virtual add to phy add//
Vmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart,g_VMXCPU.pVMXONRegion_PA.HighPart);//第二步,开启VmxOn模式//
*((PULONG)&uEflags) = Asm_GetEflags();
if(uEflags.CF != 0)
{
Log("Error: VMXON指令调用失败!", 0);
ExFreePool(g_VMXCPU.pVMXONRegion);
return STATUS_UNSUCCESSFUL;
}
Log("vmcon success", 0);
//为虚拟机分配4kb//
g_VMXCPU.pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmcs');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k//
RtlZeroMemory(g_VMXCPU.pVMCSRegion, 0x1000);
*(PULONG)g_VMXCPU.pVMCSRegion = 1;//
g_VMXCPU.pVMCSRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMCSRegion);//virtual add to phy add//
Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart); //第三步拔电源//
//第四步,选中机器 (我们可能有很多虚拟机,我们要进行选择)//
Vmx_VmPtrld(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);
g_VMXCPU.pStack = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'stck'); //栈//
RtlZeroMemory(g_VMXCPU.pVMCSRegion, 0x1000);
Log("host_stack: ", g_VMXCPU.pStack);
//第五步,设置vmcs(vmwrite) ,装机//设置大量寄存器信息//
SetupVMCS();
//第六步...
Vmx_VmLaunch(); //执行成功,直接跳走,不再向下执行,执行失败,EFLAG的CF或ZF位置1// VM - instruction错误字段将会包含错误代码//
Log("ERROR: VmLaunch指令调用失败!!!!!!",Vmx_VmRead(VM_INSTRUCTION_ERROR)); //从错误字段中读取错误号// Intel第三卷 30-4中可以查看错误号//
return STATUS_SUCCESS;
}
NTSTATUS StopVirtualTechnology()
{
_CR4 uCr4;
Vmx_VmxOff(); //关柜门
*((PULONG)&uCr4) = Asm_GetCr4();
uCr4.VMXE = 0; //关锁//
Asm_SetCr4(*((PULONG)&uCr4));
ExFreePool(g_VMXCPU.pVMXONRegion);//在内核分配的内存需要我们自己手动释放//
DbgPrint("VT Stop");
return STATUS_SUCCESS;
}
BOOLEAN IsVTEnabled() //判断主机能不能开启vt//
{
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;
}
阶段实验:
完美断下
十三、Guest state fields
上图的实验会报 0x80000012错误
1、错误号的查看
24.9.1
如我们的错误码第31位为1,说明VM进入失败
2、设置寄存器
Intel 第三卷24.4
加载寄存器信息的方法我都写到了代码中
代码如下
vtsystem.c
#include "vtsystem.h"
#include "vtasm.h"
#include "exithandler.h"
VMX_CPU g_VMXCPU;
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 __declspec(naked) GuestEntry() //guest入口//
{
//加载段选择子与基址//
__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();//vmcall功能就是回到host//
}
void SetupVMCS()
{
ULONG GdtBase, IdtBase;
GdtBase = Asm_GetGdtBase();
IdtBase = Asm_GetIdtBase();
//1、Guest state fields
Vmx_VmWrite(GUEST_CR0, Asm_GetCr0());
Vmx_VmWrite(GUEST_CR3, Asm_GetCr3());
Vmx_VmWrite(GUEST_CR4, Asm_GetCr4());
Vmx_VmWrite(GUEST_DR7, 0x400);//Dr7调试寄存器默认就是0x400//
Vmx_VmWrite(GUEST_RFLAGS, Asm_GetEflags() & ~0x200); //eflag寄存器关中断//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);
Vmx_VmWrite(GUEST_ES_AR_BYTES, 0x10000); //Intel手册第三卷 Table 24-2 把这些段选择子设为不可用的段选择子需要第16位置1//
Vmx_VmWrite(GUEST_FS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性//
Vmx_VmWrite(GUEST_DS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性//
Vmx_VmWrite(GUEST_SS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性//
Vmx_VmWrite(GUEST_GS_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性//
Vmx_VmWrite(GUEST_LDTR_AR_BYTES, 0x10000); //防止CPU当作可用,而加载错误的属性//
Vmx_VmWrite(GUEST_CS_AR_BYTES, 0xc09b); //直接读的1号段描述符//
Vmx_VmWrite(GUEST_CS_BASE, 0);
Vmx_VmWrite(GUEST_CS_LIMIT, 0xffffffff);
Vmx_VmWrite(GUEST_TR_AR_BYTES, 0x008b); //5号//
Vmx_VmWrite(GUEST_TR_BASE, 0x80042000);
Vmx_VmWrite(GUEST_TR_LIMIT, 0x20ab);
Vmx_VmWrite(GUEST_GDTR_BASE, GdtBase);
Vmx_VmWrite(GUEST_GDTR_LIMIT, Asm_GetGdtLimit());
Vmx_VmWrite(GUEST_IDTR_BASE, IdtBase);
Vmx_VmWrite(GUEST_IDTR_LIMIT, Asm_GetIdtLimit());
//Vmx_VmWrite(GUEST_IA32_DEBUGCTL, Asm_ReadMsr(MSR_IA32_DEBUGCTL) & 0xFFFFFFFF);
//Vmx_VmWrite(GUEST_IA32_DEBUGCTL_HIGH, Asm_ReadMsr(MSR_IA32_DEBUGCTL) >> 32);
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
Vmx_VmWrite(GUEST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x1000); //Guest 临时栈 //上半4kb给guest使用//
Vmx_VmWrite(GUEST_RIP, (ULONG)GuestEntry); // 客户机的入口点
Vmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff); //不使用shadow vmcs,都置成f//
Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff); //不使用shadow vmcs,都置成f//
//2、Host state fields
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); //Intel规定后三位必须为0//
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, 0x80042000); //tr寄存器在5号段选择子上,直接查,为0x80042000// 直接填充为tr的基地址//
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、vm-control fields
// 3.1、vm execution control
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));
// 3.2、vm exit control
Vmx_VmWrite(VM_EXIT_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_EXIT_CTLS));
// 3.3、vm entry control
Vmx_VmWrite(VM_ENTRY_CONTROLS, VmxAdjustControls(0, MSR_IA32_VMX_ENTRY_CTLS));
}
NTSTATUS StartVirtualTechnology()
{
_CR4 uCr4;
_EFLAGS uEflags;
if(!IsVTEnabled())
{
return STATUS_UNSUCCESSFUL;
}
*((PULONG)&uCr4) = Asm_GetCr4();
uCr4.VMXE = 1; //第一步,开锁完成//
Asm_SetCr4(*((PULONG)&uCr4));
g_VMXCPU.pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'tlsn');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k//
RtlZeroMemory(g_VMXCPU.pVMXONRegion, 0x1000);
*(PULONG)g_VMXCPU.pVMXONRegion = 1;// //
g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMXONRegion);//virtual add to phy add//
Vmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart,g_VMXCPU.pVMXONRegion_PA.HighPart);//第二步,开启VmxOn模式//
*((PULONG)&uEflags) = Asm_GetEflags();
if(uEflags.CF != 0)
{
Log("Error: VMXON指令调用失败!", 0);
ExFreePool(g_VMXCPU.pVMXONRegion);
return STATUS_UNSUCCESSFUL;
}
Log("vmcon success", 0);
//为虚拟机分配4kb//
g_VMXCPU.pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmcs');//最多分配4k,还要满足4k大小对齐//那这里直接分配4k//
RtlZeroMemory(g_VMXCPU.pVMCSRegion, 0x1000);
*(PULONG)g_VMXCPU.pVMCSRegion = 1;//
g_VMXCPU.pVMCSRegion_PA = MmGetPhysicalAddress(g_VMXCPU.pVMCSRegion);//virtual add to phy add//
Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart); //第三步拔电源//
//第四步,选中机器 (我们可能有很多虚拟机,我们要进行选择)//
Vmx_VmPtrld(g_VMXCPU.pVMCSRegion_PA.LowPart, g_VMXCPU.pVMCSRegion_PA.HighPart);
g_VMXCPU.pStack = ExAllocatePoolWithTag(NonPagedPool, 0x2000, 'stck'); //栈//
RtlZeroMemory(g_VMXCPU.pVMCSRegion, 0x2000);
Log("host_stack: ", g_VMXCPU.pStack);
//第五步,设置vmcs(vmwrite) ,装机//设置大量寄存器信息//
SetupVMCS();
//第六步...
Vmx_VmLaunch(); //执行成功,直接跳走,不再向下执行,执行失败,EFLAG的CF或ZF位置1// VM - instruction错误字段将会包含错误代码//
Log("ERROR: VmLaunch指令调用失败!!!!!!",Vmx_VmRead(VM_INSTRUCTION_ERROR)); //从错误字段中读取错误号// Intel第三卷 30-4中可以查看错误号//
return STATUS_SUCCESS;
}
NTSTATUS StopVirtualTechnology()
{
_CR4 uCr4;
Vmx_VmxOff(); //关柜门
*((PULONG)&uCr4) = Asm_GetCr4();
uCr4.VMXE = 0; //关锁//
Asm_SetCr4(*((PULONG)&uCr4));
ExFreePool(g_VMXCPU.pVMXONRegion);//在内核分配的内存需要我们自己手动释放//
DbgPrint("VT Stop");
return STATUS_SUCCESS;
}
BOOLEAN IsVTEnabled() //判断主机能不能开启vt//
{
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;
}
实验
我们u一下guest的eip
可以发现guest还停止vmcall的位置
另外,之前报0x80000012错误通过处理Guest state fields已经解决了第31位进入VM失败的错误,现在虽然任然有错误,但已经不是进入虚拟机错误了...
十四、VMCS SHADOW的概念(附)
24.10
小总结
1、我们还需要为虚拟机分配最大为4kb,且4kb大小对齐的非分页内存,同时满足前四个字节为版本号
2、Wmx_VmClear
3、Vmx_VmPtrld选中机器
4、Vmx_VmLaunch装机
(1)、Guest state fields
(2)、Host state fields
(3)、vm-control fields
(3.1)vm execution control
(3.2)vm exit control
(3.3)vm entry control
参考文章
这三篇都是比较基础且不错的文章
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现