1.cpuid 指令:
EFLAGS寄存器中的ID标志(位21)表示对CPUID指令的支持。如果一个软件程序可以设置和清除这个标志,那么执行该程序的处理器支持CPUID指令。这条指令在非64位模式和64位模式下操作相同。
CPUID在EAX、EBX、ECX和EDX寄存器中返回处理器识别和特征信息。
![](https://i-blog.csdnimg.cn/blog_migrate/8e91e578e36f40e944cde5ddee210876.png)
__cpuid 函数在 #include <intrin.h>
intel提供了非常方便的头文件
传入数组,功能号为1
就能得到相关的信息,我们这里只关注返回后,ecx的第5个位,这里为了简单就做了下与操作。(后面如果有深入VT的额外课程,会使用其他功能号的位,intel手册上cpuid指令有祥细的说明,可将这些定义为结构体,注释起来方便使用,一劳永逸)
返回值是真就代表支持VMX.
![](https://i-blog.csdnimg.cn/blog_migrate/3d232f421387a9930cf3c949b8b83a06.png)
2.__rdmsr 指令 -- 从特定型号寄存器读取
cpu 有很多特殊寄存器 统称为MSR
MSR_IA32_FEATURE_CONTROL -- 地址是0x3A.(手册上第四卷有祥细的说明)
指令同样也在intel提供的头文件里。
第0位说明:
锁定位(R/WO):(1 = 锁定)。设置后,锁定此 MSR 不被写入;写入此位将导致GP(0)。
注意:一旦设置了锁定位,就无法修改此寄存器的内容。
因此,在配置对英特尔虚拟化技术的支持之后,以及在将控制权转移到选项 ROM 或操作系统之前,必须设置锁定位。因此,一旦设置了锁定位,当 PWRGOOD 未取消断言时,整个IA32_FEATURE_CONTROL内容将在整个 RESET 中保留。
![](https://i-blog.csdnimg.cn/blog_migrate/0edf4e2649aef87c3a8816435732a9cb.png)
结构体我已经定义好了(一劳永逸)。我们这里要检查锁定位是否为0,为0的话VMXON会失败,这个地方可在BIOS设置开启,大家型号可能不一样,网上查一下自己的bios怎么开启叭~。
返回值是真表示可以开启VMX。
设置CR4:
在系统软件可以进入 VMX 操作之前,它通过设置 CR4 启用 VMX。VMXE[位 13] = 1。然后通过执行 VMXON 指令进入 VMX 操作。如果未开启此位,VMXON 会导致无效操作码异常(#UD)。一旦进入 VMX 操作,就无法清除 CR4.VMXE(请参见第 23.8 节)。系统软件通过执行 VMXOFF 指令离开 VMX 操作。执行 VMXOFF 后,可以在 VMX 操作之外清除 VMXE。
![](https://i-blog.csdnimg.cn/blog_migrate/8298bc21a0d730cd70ff31b1bd6e1dc5.png)
这一步只是检查VMXE是否已经开启,未对它进行设置,如果未开启的话,就由我们来开启。
下面修正CR4,CR0设置会开启VMXE。
修正CR0,CR4:
VMX 操作对处理器操作施加了限制。具体如下:
* 在 VMX 操作中,处理器可能会将 CR0 和 CR4 中的某些位固定为特定值,而不支持其他值。如果这些位中的任何一个包含不受支持的值,则 VMXON 将失败(请参见第 30 章中的“VMXON-Enter VMX 操作”)。在 VMX 操作(包括 VMX 根操作)中使用任何 CLTS、LMSW 或 MOV CR 指令时,尝试将这些位之一设置为不受支持的值会导致常规保护异常。VM Entry或 VM Exit无法将这些位中的任何一个设置为不受支持的值。软件应参考 VMX 功能 MSR IA32_VMX_CR0_FIXED0和IA32_VMX_CR0_FIXED1,以确定如何修复 CR0 中的位(请参阅附录 A.7)。对于 CR4,软件应参考 VMX 功能 MSR IA32_VMX_CR4_FIXED0和IA32_VMX_CR4_FIXED1(请参阅附录 A.8)。
IA32_VMX_CR0_FIXED0MSR(索引 486H)和 IA32_VMX_CR0_FIXED1 MSR(索引487H)指示如何在 VMX 操作中设置 CR0 中的位。它们报告 CR0 中允许在 VMX 操作中分别为 0 和 1 的位。如果位 X 在IA32_VMX_CR0_FIXED0中为 1,则CR0 的位在 VMX 操作中固定为 1。同样,如果位 X 在 IA32_VMX_CR0_FIXED1 中为0,则 CR0 位在 VMX 操作中固定为 0。总是这样,如果位 X 是 1 IA32_VMX_CR0_FIXED0,那么该位也是 1 IA32_VMX_CR0_FIXED1;如果位 X 在IA32_VMX_CR0_FIXED1中为 0,则该位在IA32_VMX_CR0_FIXED0中也是 0。因此,CR0 中的每个位要么固定为 0(两个 MSR 中的值均为 0),要么固定为 1(两个 MSR 中的值均为 1),要么灵活(IA32_VMX_CR0_FIXED0 为 0,IA32_VMX_CR0_FIXED1中为 1)。
How to 理解?
也就是说 CR0_FIXED0 中的任意一位是1,CR0的该位就必须是1,CR0_FIXED1 中的任意一位是0,CR0的该位就必须是0
不关心CR0_FIXED0 中的0值的位,不关心CR0_FIXED1 中的1值的位。
组合起来就是以下:
![](https://i-blog.csdnimg.cn/blog_migrate/c4b73b05cc14e9d2366081db8c4e3652.png)
CR0_FIXED0=486H,CR0_FIXED1=487H。
CR4也同样需要一份这样的代码,CR4_FIXED0=488H,CR4_FIXED1=489H。
每个CPU核都有自己CR0,CR4哦,所以每个核都要设置。
以上我们已经检查并开启了VMX。进入下一节。
#include <ntifs.h>
#include <intrin.h>
#define CPUID_ECX_VMX_ABILITY 5
#define MSR_IA32_FEATURE_CONTROL 0x3A
#define MSR_IA32_VMX_CR0_FIXED0 0x486
#define MSR_IA32_VMX_CR0_FIXED1 0x487
#define MSR_IA32_VMX_CR4_FIXED0 0x488
#define MSR_IA32_VMX_CR4_FIXED1 0x489
#pragma pack(1)
typedef union {
struct {
ULONG Lock : 1; //bit-0 Lock bit (0 = unlocked, 1 = locked). When set to '1' further writes to this MSR are blocked.
ULONG EnableVmxInsideSmx : 1; //bit-1 Enable VMX in SMX operation.
ULONG EnableVmxOutsideSmx : 1; //bit-2 Enable VMX outside SMX operation.
ULONG Reserved1 : 5; //bit-3:7 Reserved
ULONG SenterLocalFunctionEnables : 7; //bit-8:14 SENTER Local Function Enables: When set, each bit in the field represents an enable control for a corresponding SENTER function.
ULONG SenterGlobalEnable : 1; //bit-15 SENTER Global Enable: Must be set to ‘1’ to enable operation of GETSEC[SENTER].
ULONG Reserved2 : 1; //bit-16 Reserved
ULONG SgxLaunchControlEnable : 1; //bit-17 SGX Launch Control Enable: Must be set to ‘1’ to enable runtime re-configuration of SGX Launch Control via the IA32_SGXLEPUBKEYHASHn MSR.
ULONG SgxEnable : 1; //bit-18 SGX Global Enable: Must be set to ‘1’ to enable Intel SGX leaf functions.
ULONG Reserved3 : 1; //bit-19 Reserved
ULONG LmceOn : 1; //bit-20 LMCE On: When set, system software can program the MSRs associated with LMCE to configure delivery of some machine check exceptions to a single logical processor.
ULONG Reserved4 : 11; //bit-21:31 Reserved
ULONG Reserved5 : 32; //bit-32:64 Reserved
} Bits;
LARGE_INTEGER value;
} MSR_IA32_FEATURE_CONTROL_REGISTER, * PMSR_IA32_FEATURE_CONTROL_REGISTER;
typedef union {
struct {
ULONG PE : 1; ///< Protection Enable.
ULONG MP : 1; ///< Monitor Coprocessor.
ULONG EM : 1; ///< Emulation.
ULONG TS : 1; ///< Task Switched.
ULONG ET : 1; ///< Extension Type.
ULONG NE : 1; ///< Numeric Error.
ULONG Reserved_0 : 10; ///< Reserved.
ULONG WP : 1; ///< Write Protect.
ULONG Reserved_1 : 1; ///< Reserved.
ULONG AM : 1; ///< Alignment Mask.
ULONG Reserved_2 : 10; ///< Reserved.
ULONG NW : 1; ///< Mot Write-through.
ULONG CD : 1; ///< Cache Disable.
ULONG PG : 1; ///< Paging.
} Bits;
LARGE_INTEGER value;
} IA32_CR0, * PCR0;
typedef union {
struct {
ULONG64 VME : 1; //Bit-0
ULONG64 PVI : 1; //Bit-1
ULONG64 TSD : 1; //Bit-2
ULONG64 DE : 1; //Bit-3
ULONG64 PSE : 1; //Bit-4
ULONG64 PAE : 1; //Bit-5
ULONG64 MCE : 1; //Bit-6
ULONG64 PGE : 1; //Bit-7
ULONG64 PCE : 1; //Bit-8
ULONG64 OSFXSR : 1; //Bit-9
ULONG64 OSXMMEXCPT : 1; //Bit-10
ULONG64 UMIP : 1; //Bit-11
ULONG64 LA57 : 1; //Bit-12
ULONG64 VMXE : 1; //Bit-13
ULONG64 SMXE : 1; //Bit-14
ULONG64 NOP_15 : 1; //Bit-15
ULONG64 FSGSBASE : 1; //Bit-16
ULONG64 PCIDE : 1; //Bit-17
ULONG64 OSXSAVE : 1; //Bit-18
ULONG64 KL : 1; //Bit-19
ULONG64 SMEP : 1; //Bit-20
ULONG64 SMAP : 1; //Bit-21
ULONG64 PKE : 1; //Bit-22
ULONG64 CET : 1; //Bit-23
ULONG64 PKS : 1; //Bit-24
ULONG64 NOP_25_31 : 7; //These are zero
ULONG64 Reserved_64 : 32;
}Bits;
LARGE_INTEGER value;
}IA32_CR4, * PCR4;
#pragma pack()
IA32_CR0 SaveCr0[10] = { 0 };
IA32_CR4 SaveCr4[10] = { 0 };
ULONG64 CheckCPUID() {
int ctx[4] = { 0 };//顺序:eax,ebx,ecx,edx
__cpuid(ctx, 1);
return ctx[2] & CPUID_ECX_VMX_ABILITY; //Bit-5
}
ULONG64 SetCr0(ULONG64 IndexCpu) {
IA32_CR0 uCr0 = { 0 };
uCr0.value.QuadPart = __readcr0();
SaveCr0[IndexCpu].value.QuadPart = uCr0.value.QuadPart;
uCr0.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR0_FIXED0);
uCr0.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR0_FIXED1);
__writecr0(uCr0.value.QuadPart);
return TRUE;
}
ULONG64 SetCr4(ULONG64 IndexCpu) {
IA32_CR4 uCr4 = { 0 };
uCr4.value.QuadPart = __readcr4();
SaveCr4[IndexCpu].value.QuadPart = uCr4.value.QuadPart;
uCr4.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR4_FIXED0);
uCr4.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR4_FIXED1);
__writecr4(uCr4.value.QuadPart);
return TRUE;
}
ULONG64 CheckMsr() {
MSR_IA32_FEATURE_CONTROL_REGISTER msr = { 0 };
msr.value.QuadPart = __readmsr(MSR_IA32_FEATURE_CONTROL);
return msr.Bits.Lock;
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
for (ULONG CpuIndex = 0; CpuIndex < KeNumberProcessors; CpuIndex++)
{
KeSetSystemAffinityThread((KAFFINITY)(1 << CpuIndex));
__writecr0(SaveCr0[CpuIndex].value.QuadPart);
__writecr4(SaveCr4[CpuIndex].value.QuadPart);
KdPrint(("CPU:[%d],关闭VMX\n", CpuIndex));
KeRevertToUserAffinityThread();
}
ULONG64 Cr4 = __readcr4();
KdPrint(("卸载驱动,当前CR4.VMEX=%I64X,1==打开,0==关闭\n", ((IA32_CR4*)&Cr4)->Bits.VMXE));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegisterPath)
{
if (CheckCPUID() == FALSE) {
KdPrint(("CPU不支持\n"));
}
if (CheckMsr() == FALSE) {
KdPrint(("BIOS未开启VMX\n"));
}
for (ULONG CpuIndex = 0; CpuIndex < KeNumberProcessors; CpuIndex++)
{
KeSetSystemAffinityThread((KAFFINITY)(1 << CpuIndex));
SetCr0(CpuIndex);
SetCr4(CpuIndex);
KdPrint(("CPU:[%d],开启VMX\n", CpuIndex));
KeRevertToUserAffinityThread();
}
ULONG64 Cr4 = __readcr4();
KdPrint(("加载驱动,当前CR4.VMEX=%I64X,1==打开,0==关闭\n", ((IA32_CR4*)&Cr4)->Bits.VMXE));
pDriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}