首页
社区
课程
招聘
Hypervisor From Scratch – 第 3 部分:设置我们的第一个虚拟机
发表于: 2024-3-30 15:16 8787

Hypervisor From Scratch – 第 3 部分:设置我们的第一个虚拟机

2024-3-30 15:16
8787

这是教程“ Hypervisor From Scratch ”的第三部分。在这一部分中,我们将继续学习虚拟机管理程序以及如何开始创建自定义 VMM。在上一部分中,我们学习了如何制作 WDK 驱动程序来处理用户模式请求并启用处理器中的 VMX 位。在这一部分中,我们扩展驱动程序并向 VMM 添加 VMX 功能。最后,我们在VMM中使用不同的VT-x指令。

在这一部分中,我们演示如何从 Windows 用户模式( IOCTL Dispatcher )与 VMM 交互,然后解决亲和性和在特定核心中运行代码的问题。 最后,我们熟悉初始化 VMXON 区域VMCS 区域 ,然后将虚拟机管理程序加载到每个核心中并实现自定义函数以使用虚拟机管理程序指令以及与虚拟机控制数据结构 ( VMCS ) 相关的许多内容。

一些实现源自HyperBone(带钩子的简约 VT-X 虚拟机管理程序)、Satoshi TandaHyperPlatformhvpp (我的朋友Petr Beneš的出色工作)。

IRP MJ 函数中对我们来说最重要的函数是 DrvIoctlDispatcher 或 (IRP_MJ_DEVICE_CONTROL) 主要函数,这是因为可以使用特定的 IOCTL 编号从用户模式调用该函数,这意味着我们可以在驱动程序中拥有特殊的代码并实现与此代码相对应的独特功能,然后通过了解代码(来自用户模式),我们可以要求我们的驱动程序执行请求,这样我们就可以从内核请求某些功能。

IOCTL结构。
图片描述
有一个定义 IOCTL 的约定,如此处 所述 。 IOCTL 是一个 32 位数字。 前两位低位代表“传输类型”,可以是 METHOD_OUT_DIRECT、METHOD_IN_DIRECT、METHOD_BUFFERED 或 METHOD_NEITHER。

下一组从 2 到 13 的位定义“功能代码”。 高位被称为“自定义位”。 这用于确定用户定义的 IOCTL 与系统定义的 IOCTL。 这意味着 0x800 及更大的功能代码是为 Windows 消息自定义定义的。

接下来的两位定义发出 IOCTL 所需的访问权限。 如果未使用正确的访问权限打开句柄,则 I/O 管理器可以通过这种方式拒绝 IOCTL 请求。 访问类型有 FILE_READ_DATA、FILE_WRITE_DATA 等。

最后几位代表写入 IOCTL 的设备类型。 高位再次代表用户定义的值。

我们可以使用下面定义的宏来创建我们的 IOCTL 代码。

例如,可以定义以下IOCTL代码。

现在让我们实现调度 IOCTL 代码的函数。

请注意, PAGED_CODE() 宏确保调用线程以足够低的 IRQL 运行以允许分页,该宏用于确保启用分页,例如,当前执行不在 DISPATCH_LEVEL 下。 不用担心,我们将在以后的部分中详细讨论 IRQL。

下一步是检查输入缓冲区和输出缓冲区的长度。 我们将检查它,因为我们需要确保用户为内核提供缓冲区并期望接收缓冲区。 以下代码从 IO_STACK_LOCATION 获取输入和输出缓冲区 长度

PrintIrpInfo 是这样的

如果您还记得上一部分中 我们 使用CreateFile创建了句柄 (HANDLE) ,现在我们可以使用 DeviceIoControl使用先前的句柄并调用 DrvIoctlDispatcher或( IRP_MJ_DEVICE_CONTROL )以及我们在内核中提供的缓冲区。

我们已经完成了 WDK 基础知识! 现在是时候看看我们如何使用 Windows 来构建我们的 VMM 了。

使用虚拟机管理程序时,与特殊逻辑处理器的关联性是主要考虑因素之一。

在我的 Intel Core i7 6820HQ 中,我有四个物理核心,每个核心可以同时运行两个线程(由于超线程); 因此,我们有八个逻辑处理器,当然还有八组所有寄存器(包括通用寄存器和MSR寄存器),更重要的是,八组VMCS和VMXON区域等。所以我们应该配置我们的VMM来工作八个逻辑处理器。

要获取逻辑处理器的数量,我们可以使用 KeQueryActiveProcessorCount(0)。 然后我们应该将 KAFFINITY 掩码传递给 KeSetSystemAffinityThread,它设置当前线程的系统关联性。

KAFFINITY 掩码可以使用简单的幂函数进行配置:

之后,我们应该使用以下代码来更改处理器的亲和性,并分别在所有逻辑核心中运行我们的代码:

这样,我们就可以在不同的逻辑核心中运行我们的代码。 现在,让我们看看虚拟机管理程序所需的其他基本功能。

VMXON 区域和 VMCS 区域(见下文)使用物理地址作为 VMXON 和 VMPTRLD 指令的操作数,因此我们应该创建函数将虚拟地址转换为物理地址:

只要我们不能直接使用物理地址在保护模式下进行修改,我们就必须将物理地址转换为虚拟地址。

在上一部分中,我们从用户模式查询虚拟机管理程序是否存在,但我们也应该考虑从内核模式检查虚拟机管理程序。 这减少了将来出现内核错误的可能性,或者可能存在使用锁定 禁用虚拟机管理程序的情况。 顺便说一句,以下代码检查 IA32_FEATURE_CONTROL MSR(MSR 地址 3AH)以查看 锁定位 是否已设置。

上述函数中使用的结构声明如下:

VMX 中使用多个区域来处理虚拟机状态。 在本部分中,我们将介绍 VMXON 区域和 VMCS 区域。

在执行 VMXON 之前,我们应该分配一个自然对齐的 4 KB 内存区域,我们的逻辑处理器将使用它来支持 VMX 操作。 该区域称为 VMXON 区域 。 VMXON 区域的地址(VMXON 指针)在 VMXON 指令的操作数中提供。

VMM 应该为每个逻辑处理器使用不同的 VMXON 区域; 否则,行为是“未定义的”。

请注意,VMX 操作要求以下位在 VMX 操作中为 1:CR0.PE、CR0.NE、CR0.PG 和 CR4.VMXE。 CR0.PE 和 CR0.PG 的限制意味着 VMX 操作仅在分页保护模式下受支持。 因此,guest软件不能在未分页保护模式或实地址模式下运行。

现在我们正在配置虚拟机管理程序,我们应该有一个描述虚拟机状态的全局变量。 为此目的创建了以下结构。 目前,我们有两个名为( VMXON_REGIONVMCS_REGION )的字段,但我们将来会添加新字段并增强此结构。

当然,还有一个全局变量:

以下函数(在“ Memory.c ”中)分配VMXON区域并使用分配区域的指针执行VMXON指令。

我们来解释一下上面的函数。 在上面的函数中,我们使用了 MmAllocateContiguousMemory分配连续且对齐的页面。 我们还可以使用 MmAllocateContiguousMemorySpecifyCache指定分配内存的缓存类型。

您可以阅读 链接以了解不同类型的内存缓存。

为了确保 VMX 操作中的正确行为,我们应该在回写式缓存内存中维护 VMCS 区域和相关结构。 或者,我们可以将这些区域或结构中的任何一个映射到 UC(未缓存)内存类型。 除非必要,否则强烈建议不要这样做,因为这会导致使用这些结构的转换性能受到显着影响。

Writeback是一种每次发生变化时将数据写入缓存的存储方法,但仅在指定的时间间隔或在特定条件下才将数据写入主存中的相应位置。 可缓存或不可缓存可以通过分页结构 (PTE) 和内存类型范围寄存器 (MTRR) 中的缓存禁用位来确定,这在本系列的第 7 部分中进行了详细描述。

顺便说一句,我们分配了8192字节,因为不能保证Windows分配对齐的内存,以便我们能找到一块8196字节对齐的4096字节。 (通过对齐,我的意思是物理地址应该可以被4096整除,无需任何提醒)。

根据我的经验, MmAllocateContigouslyMemory 分配始终是对齐的。 可能是因为PFN中每个页都是分配4096字节的,只要我们需要4096字节,就对齐了。

如果您对页框号 (PFN) 感兴趣,您可以阅读 Inside Windows Page Frame Number (PFN) – Part 1 Inside Windows Page Frame Number (PFN) – Part 2

现在我们应该将分配的内存地址转换为其物理地址并确保其对齐。

MmAllocateContigouslyMemory 分配的内存未初始化。 内核模式驱动程序必须首先将此内存设置为零,我们使用 RtlSecureZeroMemory 来实现此目的。

来自英特尔手册(24.11.5 VMXON Region):

获取修订标识符 因此,让我们从IA32_VMX_BASIC_MSR 并将其写入 VMXON 区域。

最后一部分用于执行VMXON指令

__vmx_on 是执行VMXON 的内部函数。 状态码显示不同的含义。

如果我们使用 VMXON 设置 VMXON 区域并且失败,则状态等于 1。如果没有任何 VMCS,则状态等于 2,如果操作成功,则状态为零。 如果我们执行上述代码两次而不执行 VMXOFF,则会出现错误。

现在,VMXON 区域已准备就绪,我们可以开始了。

逻辑处理器在 VMX 操作时使用虚拟机控制数据结构 (VMCS)。 它们管理进出 VMX non-root操作(VM 进入和 VM-exit)的转换以及 VMX non-root操作中的处理器行为。 该结构由 VMCLEAR、VMPTRLD、VMREAD 和 VMWRITE 指令操作。
图片描述
上图说明了 VMCS 区域中 VMX 操作的生命周期。

VMM 应该使用不同的 VMCS 区域,因此我们需要设置逻辑处理器关联并多次运行初始化例程。

VMCS 所在的位置称为“VMCS 区域”。

VMCS 区域是

该指针不得设置超出处理器物理地址宽度的位(我们可以通过在 EAX 中使用 80000008H 执行 CPUID 来确定处理器的物理地址宽度。物理地址宽度在 EAX 的位 7:0 中返回。)

处理器中可能同时存在多个 VMCS,但当前只有其中一个处于活动状态,并且 VMLAUNCH、VMREAD、VMRESUME 和 VMWRITE 指令仅在当前 VMCS 上运行。

使用 VMPTRLD 设置逻辑处理器上的当前 VMCS。

VMCLEAR指令的存储器操作数也是VMCS的地址。 执行指令后,VMCS 在逻辑处理器上既不活动也不当前。 如果VMCS在逻辑处理器上是当前的,则逻辑处理器不再具有当前的VMCS。

VMPTRST 负责给出当前 VMCS 指针,如果当前没有 VMCS,它存储值 FFFFFFFFFFFFFFFFH。

VMCS 的启动状态决定了该 VMCS 应该使用哪个 VM 进入指令。 VMLAUNCH指令需要启动状态为“clear”的VMCS; VMRESUME 指令需要启动状态为“已启动”的 VMCS。 逻辑处理器在相应的VMCS区域中维护VMCS的启动状态。

如果当前VMCS的启动状态为“清除”,则成功执行VMLAUNCH指令将启动状态更改为“已启动”。

VMCLEAR指令的存储器操作数是VMCS的地址。 执行指令后,VMCS 的启动状态为“清除”。

没有其他方法可以修改 VMCS 的启动状态(无法使用 VMWRITE 进行修改),并且没有直接的方法来发现它(无法使用 VMREAD 进行读取)。

下图说明了 VMCS 区域的内容。
图片描述
以下代码负责分配 VMCS Region :

上面的代码与 VMXON Region 完全相同,只是 __vmx_vmptrld 而不是 __vmx_on__vmx_vmptrld 是 VMPTRLD 指令的内部函数。

在VMCS中,我们应该在执行VMPTRLD之前从MSR_IA32_VMX_BASIC中找到修订标识符并将其写入VMCS Region中。

MSR_IA32_VMX_BASIC 定义如下。

配置完上述区域后,现在是时候考虑当用户模式应用程序不再维护驱动程序句柄时的 DrvClose 了。 这时,我们应该终止VMX并释放我们之前分配的所有内存。

以下函数负责执行 VMXOFF,然后调用 MmFreeContigouslyMemory 来释放分配的内存:

请记住将 VMXON 和 VMCS Region 转换为虚拟地址,因为 MmFreeContigouslyMemory 接受虚拟地址; 否则,会导致 BSOD。

好的,快完成了!

让我们为代码创建一个测试用例,首先是一个通过所有逻辑处理器启动 VMXON 和 VMCS 区域的函数。

上面的函数应该从 IRP MJ CREATE 调用,所以让我们将 DrvCreate 修改为:

并将 DrvClose 修改为:

现在,运行代码。 在创建句柄的情况下(您可以看到我们的区域已成功分配)。
图片描述
当我们 调用CloseHandle时: 从用户模式
图片描述

在这一部分中,我们了解了不同类型的 IOCTL 调度。 我们在 Windows 中看到了不同的功能来管理 VMM,我们初始化了 VMXON 区域和 VMCS 区域,然后终止了它们。

将来,我们将重点关注扩展页表 (EPT)、VMCS 以及可以在 VMCS 区域中执行以控制guest软件的不同操作。

[1] 英特尔® 64 和 IA-32 架构软件开发人员手册合并第 3 卷 ( https://software.intel.com/en-us/articles/intel-sdm )

[2] Windows 驱动程序示例 ( https://github.com/Microsoft/Windows-driver-samples )

[3] 驱动程序开发第 2 部分:IOCTL 实现简介 ( https://www.codeproject.com/Articles/9575/Driver-Development-Part-2-Introduction-to-Implemen )

[3] 超平台( https://github.com/tandasat/HyperPlatform

[4] PAGED_CODE 宏 ( https://technet.microsoft.com/en-us/ff558773(v=vs.96) )

[5]HVPP( https://github.com/wbenny/hvpp

[6] HyperBone 项目 ( https://github.com/DarthTon/HyperBone )

[7] 内存缓存类型 ( https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ne-wdm-_memory_caching_type )

[8] 什么是回写缓存? ( https://whatis.techtarget.com/definition/write-back

[9] I/O 控制代码的缓冲区描述 ( https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-io-control-codes )

[10] 定义 I/O 控制代码 ( https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-io-control-codes )

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
#define IOCTL_SIOCTL_METHOD_BUFFERED \
    CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_SIOCTL_METHOD_BUFFERED \
    CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
IrpStack     = IoGetCurrentIrpStackLocation(Irp);
InBufLength  = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
OutBufLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
 
if (!InBufLength || OutBufLength < DataLen)
{
    NtStatus = STATUS_INVALID_PARAMETER;
    goto End;
}
IrpStack     = IoGetCurrentIrpStackLocation(Irp);
InBufLength  = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
OutBufLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
 
if (!InBufLength || OutBufLength < DataLen)
{
    NtStatus = STATUS_INVALID_PARAMETER;
    goto End;
}
VOID PrintIrpInfo(PIRP Irp)
{
    PIO_STACK_LOCATION  IrpStack;
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
    PAGED_CODE();
 
    DbgPrint("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n",
        Irp->AssociatedIrp.SystemBuffer);
    DbgPrint("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n",
        IrpStack->Parameters.DeviceIoControl.Type3InputBuffer);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.InputBufferLength = %d\n",
        IrpStack->Parameters.DeviceIoControl.InputBufferLength);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.OutputBufferLength = %d\n",
        IrpStack->Parameters.DeviceIoControl.OutputBufferLength);
    return;
}
VOID PrintIrpInfo(PIRP Irp)
{
    PIO_STACK_LOCATION  IrpStack;
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
    PAGED_CODE();
 
    DbgPrint("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n",
        Irp->AssociatedIrp.SystemBuffer);
    DbgPrint("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n",
        IrpStack->Parameters.DeviceIoControl.Type3InputBuffer);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.InputBufferLength = %d\n",
        IrpStack->Parameters.DeviceIoControl.InputBufferLength);
    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.OutputBufferLength = %d\n",
        IrpStack->Parameters.DeviceIoControl.OutputBufferLength);
    return;
}
char  OutputBuffer[1000];
char  InputBuffer[1000];
ULONG BytesReturned;
BOOL  Result;
 
//
// Performing METHOD_BUFFERED
//
StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_BUFFERED");
 
printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");
 
memset(OutputBuffer, 0, sizeof(OutputBuffer));
 
Result = DeviceIoControl(Handle,
                         (DWORD)IOCTL_SIOCTL_METHOD_BUFFERED,
                         &InputBuffer,
                         (DWORD)strlen(InputBuffer) + 1,
                         &OutputBuffer,
                         sizeof(OutputBuffer),
                         &BytesReturned,
                         NULL);
 
if (!Result)
{
    printf("Error in DeviceIoControl : %d", GetLastError());
    return false;
}
printf("    OutBuffer (%d): %s\n", BytesReturned, OutputBuffer);
char  OutputBuffer[1000];
char  InputBuffer[1000];
ULONG BytesReturned;
BOOL  Result;
 
//
// Performing METHOD_BUFFERED
//
StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_BUFFERED");
 
printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");
 
memset(OutputBuffer, 0, sizeof(OutputBuffer));
 
Result = DeviceIoControl(Handle,
                         (DWORD)IOCTL_SIOCTL_METHOD_BUFFERED,
                         &InputBuffer,
                         (DWORD)strlen(InputBuffer) + 1,
                         &OutputBuffer,
                         sizeof(OutputBuffer),
                         &BytesReturned,
                         NULL);
 
if (!Result)
{
    printf("Error in DeviceIoControl : %d", GetLastError());
    return false;
}
printf("    OutBuffer (%d): %s\n", BytesReturned, OutputBuffer);
int
MathPower(int Base, int Exponent)
{
    int Result = 1;
    for (;;)
    {
        if (Exponent & 1)
        {
            Result *= Base;
        }
 
        Exponent >>= 1;
        if (!Exponent)
        {
            break;
        }
        Base *= Base;
    }
    return Result;
}
int
MathPower(int Base, int Exponent)
{
    int Result = 1;
    for (;;)
    {
        if (Exponent & 1)
        {
            Result *= Base;
        }
 
        Exponent >>= 1;
        if (!Exponent)
        {
            break;
        }
        Base *= Base;
    }
    return Result;
}
KAFFINITY AffinityMask;
for (size_t i = 0; i < KeQueryActiveProcessors(); i++)
{
    AffinityMask = MathPower(2, i);
    KeSetSystemAffinityThread(AffinityMask);
 
    DbgPrint("=====================================================");
    DbgPrint("Current thread is executing in %d th logical processor.", i);
 
    // run code here
}
KAFFINITY AffinityMask;
for (size_t i = 0; i < KeQueryActiveProcessors(); i++)
{
    AffinityMask = MathPower(2, i);
    KeSetSystemAffinityThread(AffinityMask);
 
    DbgPrint("=====================================================");
    DbgPrint("Current thread is executing in %d th logical processor.", i);
 
    // run code here
}
UINT64
VirtualToPhysicalAddress(void * Va)
{
    return MmGetPhysicalAddress(Va).QuadPart;
}
UINT64
VirtualToPhysicalAddress(void * Va)
{
    return MmGetPhysicalAddress(Va).QuadPart;
}
UINT64
PhysicalToVirtualAddress(UINT64 Pa)
{
    PHYSICAL_ADDRESS PhysicalAddr;
    PhysicalAddr.QuadPart = Pa;
 
    return MmGetVirtualForPhysical(PhysicalAddr);
}
UINT64
PhysicalToVirtualAddress(UINT64 Pa)
{
    PHYSICAL_ADDRESS PhysicalAddr;
    PhysicalAddr.QuadPart = Pa;
 
    return MmGetVirtualForPhysical(PhysicalAddr);
}
BOOLEAN
IsVmxSupported()
{
    CPUID Data = {0};
 
    //
    // Check for the VMX bit
    //
    __cpuid((int *)&Data, 1);
    if ((Data.ecx & (1 << 5)) == 0)
        return FALSE;
 
    IA32_FEATURE_CONTROL_MSR Control = {0};
    Control.All                      = __readmsr(MSR_IA32_FEATURE_CONTROL);
 
    //
    // BIOS lock check
    //
    if (Control.Fields.Lock == 0)
    {
        Control.Fields.Lock        = TRUE;
        Control.Fields.EnableVmxon = TRUE;
        __writemsr(MSR_IA32_FEATURE_CONTROL, Control.All);
    }
    else if (Control.Fields.EnableVmxon == FALSE)
    {
        DbgPrint("[*] VMX locked off in BIOS");
        return FALSE;
    }
 
    return TRUE;
}
BOOLEAN
IsVmxSupported()
{
    CPUID Data = {0};
 
    //
    // Check for the VMX bit
    //
    __cpuid((int *)&Data, 1);
    if ((Data.ecx & (1 << 5)) == 0)
        return FALSE;
 
    IA32_FEATURE_CONTROL_MSR Control = {0};
    Control.All                      = __readmsr(MSR_IA32_FEATURE_CONTROL);
 
    //
    // BIOS lock check
    //
    if (Control.Fields.Lock == 0)
    {
        Control.Fields.Lock        = TRUE;
        Control.Fields.EnableVmxon = TRUE;
        __writemsr(MSR_IA32_FEATURE_CONTROL, Control.All);
    }
    else if (Control.Fields.EnableVmxon == FALSE)
    {
        DbgPrint("[*] VMX locked off in BIOS");
        return FALSE;
    }
 
    return TRUE;
}
typedef union _IA32_FEATURE_CONTROL_MSR
{
    ULONG64 All;
    struct
    {
        ULONG64 Lock : 1;                // [0]
        ULONG64 EnableSMX : 1;           // [1]
        ULONG64 EnableVmxon : 1;         // [2]
        ULONG64 Reserved2 : 5;           // [3-7]
        ULONG64 EnableLocalSENTER : 7;   // [8-14]
        ULONG64 EnableGlobalSENTER : 1;  // [15]
        ULONG64 Reserved3a : 16;         //
        ULONG64 Reserved3b : 32;         // [16-63]
    } Fields;
} IA32_FEATURE_CONTROL_MSR, *PIA32_FEATURE_CONTROL_MSR;
 
typedef struct _CPUID
{
    int eax;
    int ebx;
    int ecx;
    int edx;
} CPUID, *PCPUID;
typedef union _IA32_FEATURE_CONTROL_MSR
{
    ULONG64 All;
    struct
    {
        ULONG64 Lock : 1;                // [0]
        ULONG64 EnableSMX : 1;           // [1]
        ULONG64 EnableVmxon : 1;         // [2]
        ULONG64 Reserved2 : 5;           // [3-7]
        ULONG64 EnableLocalSENTER : 7;   // [8-14]
        ULONG64 EnableGlobalSENTER : 1;  // [15]
        ULONG64 Reserved3a : 16;         //
        ULONG64 Reserved3b : 32;         // [16-63]
    } Fields;
} IA32_FEATURE_CONTROL_MSR, *PIA32_FEATURE_CONTROL_MSR;
 
typedef struct _CPUID
{
    int eax;
    int ebx;
    int ecx;
    int edx;
} CPUID, *PCPUID;
typedef struct _VIRTUAL_MACHINE_STATE
{
    UINT64 VmxonRegion; // VMXON region
    UINT64 VmcsRegion;  // VMCS region
} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;
typedef struct _VIRTUAL_MACHINE_STATE
{
    UINT64 VmxonRegion; // VMXON region
    UINT64 VmcsRegion;  // VMCS region
} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;
extern VIRTUAL_MACHINE_STATE* g_GuestState;
extern VIRTUAL_MACHINE_STATE* g_GuestState;
BOOLEAN
AllocateVmxonRegion(IN VIRTUAL_MACHINE_STATE * GuestState)
{
    // at IRQL > DISPATCH_LEVEL memory allocation routines don't work
    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
        KeRaiseIrqlToDpcLevel();
 
    PHYSICAL_ADDRESS PhysicalMax = {0};
    PhysicalMax.QuadPart         = MAXULONG64;
 
    int    VMXONSize = 2 * VMXON_SIZE;
    BYTE * Buffer    = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region
 
    PHYSICAL_ADDRESS Highest = {0}, Lowest = {0};
    Highest.QuadPart = ~0;
 
    // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached);
 
    if (Buffer == NULL)
    {
        DbgPrint("[*] Error : Couldn't Allocate Buffer for VMXON Region.");
        return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer);
 
    // zero-out memory
    RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE);
    UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
 
    UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
 
    DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer);
    DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer);
    DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer);
 
    // get IA32_VMX_BASIC_MSR RevisionId
 
    IA32_VMX_BASIC_MSR basic = {0};
 
    basic.All = __readmsr(MSR_IA32_VMX_BASIC);
 
    DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier);
 
    // Changing Revision Identifier
    *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier;
 
    int Status = __vmx_on(&AlignedPhysicalBuffer);
    if (Status)
    {
        DbgPrint("[*] VMXON failed with status %d\n", Status);
        return FALSE;
    }
 
    g_GuestState->VmxonRegion = AlignedPhysicalBuffer;
 
    return TRUE;
}
BOOLEAN
AllocateVmxonRegion(IN VIRTUAL_MACHINE_STATE * GuestState)
{
    // at IRQL > DISPATCH_LEVEL memory allocation routines don't work
    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
        KeRaiseIrqlToDpcLevel();
 
    PHYSICAL_ADDRESS PhysicalMax = {0};
    PhysicalMax.QuadPart         = MAXULONG64;
 
    int    VMXONSize = 2 * VMXON_SIZE;
    BYTE * Buffer    = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region
 
    PHYSICAL_ADDRESS Highest = {0}, Lowest = {0};
    Highest.QuadPart = ~0;
 
    // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached);
 
    if (Buffer == NULL)
    {
        DbgPrint("[*] Error : Couldn't Allocate Buffer for VMXON Region.");
        return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer);
 
    // zero-out memory
    RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE);
    UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
 
    UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
 
    DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer);
    DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer);
    DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer);
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-3-31 17:17 被zhang_derek编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (2)
雪    币: 3004
活跃值: (30861)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-3-30 22:27
1
雪    币: 4216
活跃值: (6208)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
受益匪浅,3Q
2024-6-30 23:06
0
游客
登录 | 注册 方可回帖
返回
//