Hi yes i added the VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE thing to secondary vmexec control and i tested my hypervisor driver on both my main machine and vmware virtual machine.
on my host machine, when i run the driver my system slowly gets corrupted. For the first 5 seconds its fine then i get some weird errors like filesystem error code -1235653 then my background disappears and then the taskbar and at the end, there is only a white screen and my cursor.
anyways, as im still learning hypervisors i have no intent of publishing this anyway so heres the full code
#pragma once
#include <intrin.h>
#include <ntifs.h>
#include "memory.h"
#include "ia32.h"
#define KERNEL_STACK_SIZE 0x6000
#include "vmx_structs.h"
typedef struct vmx_region
{
unsigned long revision_id;
unsigned char data[PAGE_SIZE - sizeof(unsigned long)];
} vmx_region;
typedef struct vcpu_data
{
PHYSICAL_ADDRESS vmxon_region_physical;
PHYSICAL_ADDRESS vmcs_region_physical;
vmx_region* vmxon_region;
vmx_region* vmcs_region;
void* host_stack;
bool vmx_enabled;
void* msr_bitmap;
PHYSICAL_ADDRESS physical_msr_bitmap;
vmx_capabilities caps;
CONTEXT ctx_frame;
special_registers_t special_registers;
volatile vmx_state vmx_state;
} vcpu_data;
namespace vmx
{
extern "C" void vm_restore_context(CONTEXT* ctx);
unsigned long get_vmx_revision_id()
{
auto vmx_basic = __readmsr(MSR_IA32_VMX_BASIC);
unsigned long revision_id = vmx_basic & 0x7FFFFFFF;
DbgPrint("vmx revision id 0x%X\n", revision_id);
return revision_id;
}
unsigned long adjust_controls(unsigned long requested, std::uint64_t msr_value)
{
requested |= (msr_value & 0xFFFFFFFF);
requested &= (msr_value >> 32);
return requested;
}
extern "C"
{
void capture_ctx(CONTEXT* context);
DECLSPEC_NORETURN void restore_ctx(CONTEXT* context);
void vmexit_handler();
DECLSPEC_NORETURN void vmentry_handler(PCONTEXT context);
}
void vmx_resume()
{
__vmx_vmresume();
ULONG64 error = 0;
__vmx_vmread(VM_INSTRUCTION_ERROR, &error);
DbgPrint("vmresume failed with error 0x%llx\n", error);
__debugbreak();
}
void vmx_handle_cpuid(vp_state* vp_state)
{
INT32 cpu_info[4];
if ((vp_state->vp_regs->Rax == 0x41414141) &&
(vp_state->vp_regs->Rcx == 0x42424242) &&
((vmx_read(GUEST_CS_SELECTOR) & RPL_MASK) == DPL_SYSTEM))
{
vp_state->exit_vm = TRUE;
return;
}
__cpuidex(cpu_info, (INT32)vp_state->vp_regs->Rax, (INT32)vp_state->vp_regs->Rcx);
if (vp_state->vp_regs->Rax == 1)
{
cpu_info[2] |= (1 << 31);
}
vp_state->vp_regs->Rax = cpu_info[0];
vp_state->vp_regs->Rbx = cpu_info[1];
vp_state->vp_regs->Rcx = cpu_info[2];
vp_state->vp_regs->Rdx = cpu_info[3];
}
void vmx_handle_invd(vp_state* vp_state)
{
__wbinvd();
}
void vmx_handle_xsetbv(vp_state* vp_state)
{
_xsetbv((UINT32)vp_state->vp_regs->Rcx,
((UINT64)vp_state->vp_regs->Rdx << 32) | (UINT32)vp_state->vp_regs->Rax);
}
void vmx_handle_vmcall(vp_state* vp_state)
{
ULONG32 hypercall_number = (ULONG32)(vp_state->vp_regs->Rcx & 0xFFFF);
switch (hypercall_number)
{
case 0x1337:
vp_state->exit_vm = TRUE;
break;
default:
DbgPrint("unknown hypercall 0x%x\n", hypercall_number);
break;
}
}
void vmx_handle_cr_access(vp_state* vp_state)
{
UINT64 exit_qual = vp_state->exit_qualification;
UINT64 cr_num = (exit_qual >> 0) & 0xF;
UINT64 access_type = (exit_qual >> 4) & 0x3;
UINT64 reg_num = (exit_qual >> 8) & 0xF;
PULONG64 reg_ptr = (PULONG64)&vp_state->vp_regs->Rax + reg_num;
switch (access_type)
{
case 0:
switch (cr_num)
{
case 0:
vmx_write(GUEST_CR0, *reg_ptr);
vmx_write(CR0_READ_SHADOW, *reg_ptr);
break;
case 3:
vmx_write(GUEST_CR3, *reg_ptr);
break;
case 4:
vmx_write(GUEST_CR4, *reg_ptr);
vmx_write(CR4_READ_SHADOW, *reg_ptr);
break;
}
break;
case 1:
switch (cr_num)
{
case 0:
*reg_ptr = vmx_read(GUEST_CR0);
break;
case 3:
*reg_ptr = vmx_read(GUEST_CR3);
break;
case 4:
*reg_ptr = vmx_read(GUEST_CR4);
break;
}
break;
}
}
void vmx_handle_msr_read(vp_state* vp_state)
{
UINT32 msr = (UINT32)vp_state->vp_regs->Rcx;
UINT64 msr_value = __readmsr(msr);
vp_state->vp_regs->Rax = msr_value & 0xFFFFFFFF;
vp_state->vp_regs->Rdx = msr_value >> 32;
}
void vmx_handle_msr_write(vp_state* vp_state)
{
UINT32 msr = (UINT32)vp_state->vp_regs->Rcx;
UINT64 msr_value = ((UINT64)vp_state->vp_regs->Rdx << 32) | (UINT32)vp_state->vp_regs->Rax;
__writemsr(msr, msr_value);
}
void vmx_handle_rdtsc(vp_state* vp_state)
{
UINT64 tsc = __rdtsc();
vp_state->vp_regs->Rax = tsc & 0xFFFFFFFF;
vp_state->vp_regs->Rdx = tsc >> 32;
}
void vmx_handle_rdtscp(vp_state* vp_state)
{
UINT32 aux;
UINT64 tsc = __rdtscp(&aux);
vp_state->vp_regs->Rax = tsc & 0xFFFFFFFF;
vp_state->vp_regs->Rdx = tsc >> 32;
vp_state->vp_regs->Rcx = aux;
}
void vmx_handle_exit(vp_state* vp_state)
{
bool advance_rip = true;
switch (vp_state->exit_reason)
{
case EXIT_REASON_CPUID:
vmx_handle_cpuid(vp_state);
break;
case EXIT_REASON_INVD:
case EXIT_REASON_WBINVD:
vmx_handle_invd(vp_state);
break;
case EXIT_REASON_XSETBV:
vmx_handle_xsetbv(vp_state);
break;
case EXIT_REASON_VMCALL:
vmx_handle_vmcall(vp_state);
break;
case EXIT_REASON_CR_ACCESS:
vmx_handle_cr_access(vp_state);
break;
case EXIT_REASON_MSR_READ:
vmx_handle_msr_read(vp_state);
break;
case EXIT_REASON_MSR_WRITE:
vmx_handle_msr_write(vp_state);
break;
case EXIT_REASON_RDTSC:
vmx_handle_rdtsc(vp_state);
break;
case EXIT_REASON_RDTSCP:
vmx_handle_rdtscp(vp_state);
break;
case EXIT_REASON_EXCEPTION_NMI:
{
UINT32 intr_info = (UINT32)vmx_read(VM_EXIT_INTR_INFO);
UINT32 vector = intr_info & 0xFF;
UINT32 intr_type = (intr_info >> 8) & 0x7;
UINT32 error_valid = (intr_info >> 11) & 0x1;
UINT32 valid = (intr_info >> 31) & 0x1;
if (valid)
{
UINT32 error_code = 0;
if (error_valid)
error_code = (UINT32)vmx_read(VM_EXIT_INTR_ERROR_CODE);
UINT32 instr_len = (UINT32)vmx_read(VM_EXIT_INSTRUCTION_LEN);
vmx_write(0x00004016, intr_info);
if (error_valid)
vmx_write(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
vmx_write(VM_ENTRY_INSTRUCTION_LEN, instr_len);
advance_rip = false;
}
break;
}
default:
DbgPrint("unhandled exit reason 0x%x at rip 0x%llx\n",
vp_state->exit_reason, vp_state->guest_rip);
vp_state->exit_vm = TRUE;
advance_rip = false;
break;
}
if (advance_rip && !vp_state->exit_vm)
{
vp_state->guest_rip += vmx_read(VM_EXIT_INSTRUCTION_LEN);
vmx_write(GUEST_RIP, vp_state->guest_rip);
}
}
extern "C" DECLSPEC_NORETURN void vmentry_handler(PCONTEXT context)
{
vp_state guest_context;
vcpu_data* vp_data;
context->Rcx = *(UINT64*)((uintptr_t)context - sizeof(context->Rcx));
vp_data = (vcpu_data*)((uintptr_t)(context + 1) - KERNEL_STACK_SIZE);
guest_context.guest_eflags = vmx::vmx_read(GUEST_RFLAGS);
guest_context.guest_rip = vmx::vmx_read(GUEST_RIP);
guest_context.guest_rsp = vmx::vmx_read(GUEST_RSP);
guest_context.exit_reason = vmx::vmx_read(VM_EXIT_REASON) & 0xFFFF;
guest_context.exit_qualification = vmx::vmx_read(EXIT_QUALIFICATION);
guest_context.vp_regs = context;
guest_context.exit_vm = FALSE;
vmx::vmx_handle_exit(&guest_context);
if (guest_context.exit_vm != FALSE)
{
context->Rax = (uintptr_t)vp_data >> 32;
context->Rbx = (uintptr_t)vp_data & 0xFFFFFFFF;
context->Rcx = 0x43434343;
__writecr3(vmx::vmx_read(GUEST_CR3));
context->Rsp = guest_context.guest_rsp;
context->Rip = (UINT64)guest_context.guest_rip;
context->EFlags = (UINT32)guest_context.guest_eflags;
__vmx_off();
vp_data->vmx_enabled = false;
vp_data->vmx_state = VMX_STATE_OFF;
DbgPrint("exited vmx root mode on core %lu\n", KeGetCurrentProcessorNumber());
}
else
{
context->Rsp += sizeof(context->Rcx);
context->Rip = (UINT64)vmx::vmx_resume;
}
vm_restore_context(context);
}
ULONG vmx_get_segment_access_rights(USHORT selector)
{
if (selector == 0)
return 0x10000;
ULONG_PTR gdt_base = get_gdt_base();
struct segment_descriptor_entry
{
USHORT limit_low;
USHORT base_low;
UCHAR base_mid;
UCHAR access;
UCHAR granularity;
UCHAR base_high;
} *descriptor;
descriptor = (segment_descriptor_entry*)(gdt_base + (selector & ~7));
ULONG access_rights = descriptor->access;
access_rights |= (descriptor->granularity & 0xF0) << 4;
return access_rights;
}
void convert_gdt_entry(void* gdt_base, std::uint16_t selector, PVMX_GDTENTRY64 gdt_entry)
{
PKGDTENTRY64 gdt_entry_1;
if ((selector == 0) || (selector & SELECTOR_TABLE_INDEX) != 0)
{
gdt_entry->Limit = gdt_entry->AccessRights = 0;
gdt_entry->Base = 0;
gdt_entry->Selector = 0;
gdt_entry->Bits.Unusable = TRUE;
return;
}
gdt_entry_1 = (PKGDTENTRY64)((uintptr_t)gdt_base + (selector & ~RPL_MASK));
gdt_entry->Selector = selector;
gdt_entry->Limit = __segmentlimit(selector);
gdt_entry->Base = ((gdt_entry_1->Bytes.BaseHigh << 24) |
(gdt_entry_1->Bytes.BaseMiddle << 16) |
(gdt_entry_1->BaseLow)) & 0xFFFFFFFF;
gdt_entry->Base |= ((gdt_entry_1->Bits.Type & 0x10) == 0) ?
((uintptr_t)gdt_entry_1->BaseUpper << 32) : 0;
gdt_entry->AccessRights = 0;
gdt_entry->Bytes.Flags1 = gdt_entry_1->Bytes.Flags1;
gdt_entry->Bytes.Flags2 = gdt_entry_1->Bytes.Flags2;
gdt_entry->Bits.Reserved = 0;
gdt_entry->Bits.Unusable = !gdt_entry_1->Bits.Present;
}
void capture_registers(special_registers_t* special_registers)
{
special_registers->Cr0 = __readcr0();
special_registers->Cr3 = __readcr3();
special_registers->Cr4 = __readcr4();
special_registers->DebugControl = __readmsr(MSR_DEBUG_CTL);
special_registers->MsrGsBase = __readmsr(MSR_GS_BASE);
special_registers->KernelDr7 = __readdr(7);
asm_sgdt(&special_registers->Gdtr.Limit);
__sidt(&special_registers->Idtr.Limit);
special_registers->Tr = get_tr();
special_registers->Ldtr = get_ldtr();
}
void read_vmx_capabilities(vmx_capabilities* caps)
{
caps->vmx_basic = __readmsr(MSR_IA32_VMX_BASIC);
caps->pinbased_ctls = __readmsr(MSR_IA32_VMX_PINBASED_CTLS);
caps->procbased_ctls = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS);
caps->exit_ctls = __readmsr(MSR_IA32_VMX_EXIT_CTLS);
caps->entry_ctls = __readmsr(MSR_IA32_VMX_ENTRY_CTLS);
caps->misc = __readmsr(MSR_IA32_VMX_MISC);
caps->cr0_fixed0 = __readmsr(MSR_IA32_VMX_CR0_FIXED0);
caps->cr0_fixed1 = __readmsr(MSR_IA32_VMX_CR0_FIXED1);
caps->cr4_fixed0 = __readmsr(MSR_IA32_VMX_CR4_FIXED0);
caps->cr4_fixed1 = __readmsr(MSR_IA32_VMX_CR4_FIXED1);
caps->vmcs_enum = __readmsr(MSR_IA32_VMX_VMCS_ENUM);
caps->procbased_ctls2 = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS2);
if (caps->vmx_basic & (1ULL << 55))
{
caps->true_pinbased_ctls = __readmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS);
caps->true_procbased_ctls = __readmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS);
caps->true_exit_ctls = __readmsr(MSR_IA32_VMX_TRUE_EXIT_CTLS);
caps->true_entry_ctls = __readmsr(MSR_IA32_VMX_TRUE_ENTRY_CTLS);
}
else
{
caps->true_pinbased_ctls = caps->pinbased_ctls;
caps->true_procbased_ctls = caps->procbased_ctls;
caps->true_exit_ctls = caps->exit_ctls;
caps->true_entry_ctls = caps->entry_ctls;
}
}
bool setup_vmcs(vcpu_data* vcpu, CONTEXT* ctx)
{
if (!vcpu)
return false;
special_registers_t registers;
capture_registers(®isters);
read_vmx_capabilities(&vcpu->caps);
registers.Cr0 &= vcpu->caps.cr0_fixed1;
registers.Cr0 |= vcpu->caps.cr0_fixed0;
registers.Cr4 &= vcpu->caps.cr4_fixed1;
registers.Cr4 |= vcpu->caps.cr4_fixed0;
__writecr0(registers.Cr0);
__writecr4(registers.Cr4);
vmx_write(VMCS_LINK_POINTER, ~0ULL);
vmx_write(MSR_BITMAP, vcpu->physical_msr_bitmap.QuadPart);
vmx_write(PIN_BASED_VM_EXEC_CONTROL,
adjust_controls(0, vcpu->caps.true_pinbased_ctls));
vmx_write(CPU_BASED_VM_EXEC_CONTROL,
adjust_controls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS,
vcpu->caps.true_procbased_ctls));
//VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE
vmx_write(SECONDARY_VM_EXEC_CONTROL,
adjust_controls(SECONDARY_EXEC_ENABLE_RDTSCP | SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_XSAVES | 0x04000000,
vcpu->caps.procbased_ctls2));
vmx_write(VM_EXIT_CONTROLS,
adjust_controls(VM_EXIT_ACK_INTR_ON_EXIT | VM_EXIT_HOST_ADDR_SPACE_SIZE,
vcpu->caps.true_exit_ctls));
vmx_write(VM_ENTRY_CONTROLS,
adjust_controls(VM_ENTRY_IA32E_MODE, vcpu->caps.true_entry_ctls));
vmx_write(EXCEPTION_BITMAP, 0);
VMX_GDTENTRY64 vmx_gdt_entry;
convert_gdt_entry(registers.Gdtr.Base, ctx->SegCs, &vmx_gdt_entry);
vmx_write(GUEST_CS_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_CS_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_CS_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_CS_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegSs, &vmx_gdt_entry);
vmx_write(GUEST_SS_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_SS_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_SS_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_SS_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegDs, &vmx_gdt_entry);
vmx_write(GUEST_DS_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_DS_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_DS_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_DS_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegEs, &vmx_gdt_entry);
vmx_write(GUEST_ES_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_ES_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_ES_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_ES_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegFs, &vmx_gdt_entry);
vmx_write(GUEST_FS_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_FS_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_FS_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_FS_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegGs, &vmx_gdt_entry);
vmx_write(GUEST_GS_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_GS_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_GS_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_GS_BASE, registers.MsrGsBase);
convert_gdt_entry(registers.Gdtr.Base, registers.Tr, &vmx_gdt_entry);
vmx_write(GUEST_TR_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_TR_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_TR_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_TR_BASE, vmx_gdt_entry.Base);
convert_gdt_entry(registers.Gdtr.Base, registers.Ldtr, &vmx_gdt_entry);
vmx_write(GUEST_LDTR_SELECTOR, vmx_gdt_entry.Selector);
vmx_write(GUEST_LDTR_LIMIT, vmx_gdt_entry.Limit);
vmx_write(GUEST_LDTR_AR_BYTES, vmx_gdt_entry.AccessRights);
vmx_write(GUEST_LDTR_BASE, vmx_gdt_entry.Base);
vmx_write(GUEST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
vmx_write(GUEST_GDTR_LIMIT, registers.Gdtr.Limit);
vmx_write(GUEST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);
vmx_write(GUEST_IDTR_LIMIT, registers.Idtr.Limit);
vmx_write(CR0_READ_SHADOW, registers.Cr0);
vmx_write(GUEST_CR0, registers.Cr0);
vmx_write(GUEST_CR3, registers.Cr3);
vmx_write(GUEST_CR4, registers.Cr4);
vmx_write(CR4_READ_SHADOW, registers.Cr4);
vmx_write(GUEST_IA32_DEBUGCTL, registers.DebugControl);
vmx_write(GUEST_DR7, registers.KernelDr7);
vmx_write(GUEST_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));
vmx_write(GUEST_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
vmx_write(GUEST_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));
vmx_write(GUEST_RSP, ctx->Rsp);
vmx_write(GUEST_RIP, ctx->Rip);
vmx_write(GUEST_RFLAGS, ctx->EFlags);
vmx_write(GUEST_ACTIVITY_STATE, 0);
vmx_write(GUEST_INTERRUPTIBILITY_INFO, 0);
vmx_write(GUEST_PENDING_DBG_EXCEPTIONS, 0);
vmx_write(HOST_CS_SELECTOR, ctx->SegCs & ~RPL_MASK);
vmx_write(HOST_SS_SELECTOR, ctx->SegSs & ~RPL_MASK);
vmx_write(HOST_DS_SELECTOR, ctx->SegDs & ~RPL_MASK);
vmx_write(HOST_ES_SELECTOR, ctx->SegEs & ~RPL_MASK);
vmx_write(HOST_FS_SELECTOR, ctx->SegFs & ~RPL_MASK);
vmx_write(HOST_GS_SELECTOR, ctx->SegGs & ~RPL_MASK);
vmx_write(HOST_TR_SELECTOR, registers.Tr & ~RPL_MASK);
convert_gdt_entry(registers.Gdtr.Base, ctx->SegFs, &vmx_gdt_entry);
vmx_write(HOST_FS_BASE, vmx_gdt_entry.Base);
vmx_write(HOST_GS_BASE, registers.MsrGsBase);
convert_gdt_entry(registers.Gdtr.Base, registers.Tr, &vmx_gdt_entry);
vmx_write(HOST_TR_BASE, vmx_gdt_entry.Base);
vmx_write(HOST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
vmx_write(HOST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);
vmx_write(HOST_CR0, registers.Cr0);
vmx_write(HOST_CR3, registers.Cr3);
vmx_write(HOST_CR4, registers.Cr4);
vmx_write(HOST_IA32_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));
vmx_write(HOST_IA32_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
vmx_write(HOST_IA32_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));
C_ASSERT((KERNEL_STACK_SIZE - sizeof(CONTEXT)) % 16 == 0);
vmx_write(HOST_RSP, (uintptr_t)vcpu->host_stack + KERNEL_STACK_SIZE - sizeof(CONTEXT));
vmx_write(HOST_RIP, (uintptr_t)vmexit_handler);
DbgPrint("vmcs setup completed on core %lu\n", KeGetCurrentProcessorNumber());
return true;
}
vcpu_data* allocate_vmx_regions()
{
vcpu_data* cpu_data = reinterpret_cast<vcpu_data*>(
ExAllocatePool(NonPagedPool, sizeof(vcpu_data)));
if (!cpu_data)
{
DbgPrint("failed to allocate vcpu data\n");
return nullptr;
}
RtlZeroMemory(cpu_data, sizeof(vcpu_data));
cpu_data->vmxon_region = reinterpret_cast<vmx_region*>(
memory::MmAllocateIndependentPages(PAGE_SIZE));
if (!cpu_data->vmxon_region)
{
DbgPrint("failed to allocate vmxon region\n");
ExFreePool(cpu_data);
return nullptr;
}
cpu_data->vmcs_region = reinterpret_cast<vmx_region*>(
memory::MmAllocateIndependentPages(PAGE_SIZE));
if (!cpu_data->vmcs_region)
{
DbgPrint("failed to allocate vmcs region\n");
memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
ExFreePool(cpu_data);
return nullptr;
}
cpu_data->host_stack = ExAllocatePool(NonPagedPool, KERNEL_STACK_SIZE);
if (!cpu_data->host_stack)
{
DbgPrint("failed to allocate host stack\n");
memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
memory::MmFreeIndependentPages(cpu_data->vmcs_region, PAGE_SIZE);
ExFreePool(cpu_data);
return nullptr;
}
cpu_data->msr_bitmap = ExAllocatePool(NonPagedPool, PAGE_SIZE);
if (!cpu_data->msr_bitmap)
{
DbgPrint("failed to allocate msr bitmap\n");
ExFreePool(cpu_data->host_stack);
memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
memory::MmFreeIndependentPages(cpu_data->vmcs_region, PAGE_SIZE);
ExFreePool(cpu_data);
return nullptr;
}
RtlZeroMemory(cpu_data->vmxon_region, PAGE_SIZE);
RtlZeroMemory(cpu_data->vmcs_region, PAGE_SIZE);
RtlZeroMemory(cpu_data->host_stack, KERNEL_STACK_SIZE);
RtlZeroMemory(cpu_data->msr_bitmap, PAGE_SIZE);
cpu_data->vmxon_region_physical = MmGetPhysicalAddress(cpu_data->vmxon_region);
cpu_data->vmcs_region_physical = MmGetPhysicalAddress(cpu_data->vmcs_region);
cpu_data->physical_msr_bitmap = MmGetPhysicalAddress(cpu_data->msr_bitmap);
unsigned long rev_id = get_vmx_revision_id();
cpu_data->vmxon_region->revision_id = rev_id;
cpu_data->vmcs_region->revision_id = rev_id;
return cpu_data;
}
bool execute_vmclear(vcpu_data* cpu_data)
{
int status = __vmx_vmclear(
reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));
if (status != 0)
{
DbgPrint("vmclear failed with status %d\n", status);
return false;
}
return true;
}
bool execute_vmxon(vcpu_data* cpu_data)
{
ULONG64 cr0 = __readcr0();
ULONG64 cr4 = __readcr4();
ULONG64 cr0_fixed0 = __readmsr(MSR_IA32_VMX_CR0_FIXED0);
ULONG64 cr0_fixed1 = __readmsr(MSR_IA32_VMX_CR0_FIXED1);
ULONG64 cr4_fixed0 = __readmsr(MSR_IA32_VMX_CR4_FIXED0);
ULONG64 cr4_fixed1 = __readmsr(MSR_IA32_VMX_CR4_FIXED1);
cr0 &= cr0_fixed1;
cr0 |= cr0_fixed0;
cr4 &= cr4_fixed1;
cr4 |= cr4_fixed0;
__writecr0(cr0);
__writecr4(cr4);
unsigned char status = __vmx_on(
(unsigned long long*) & cpu_data->vmxon_region_physical.QuadPart);
if (status != 0)
{
DbgPrint("vmxon failed with status %d\n", status);
return false;
}
cpu_data->vmx_enabled = true;
return true;
}
bool execute_vmptrld(vcpu_data* cpu_data)
{
int status = __vmx_vmptrld(
reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));
if (status != 0)
{
DbgPrint("vmptrld failed with status %d\n", status);
__vmx_off();
return false;
}
return true;
}
unsigned long long initialize_vmx_on_core(unsigned long long argument)
{
unsigned long core_id = KeGetCurrentProcessorNumber();
vcpu_data* cpu_data = g_cpu_data[core_id];
if (cpu_data == nullptr)
{
if (!vmx::enable_vmx())
{
DbgPrint("failed to enable vmx on core %lu\n", core_id);
return 0;
}
cpu_data = vmx::allocate_vmx_regions();
if (!cpu_data)
{
DbgPrint("failed to allocate vmx regions on core %lu\n", core_id);
return 0;
}
cpu_data->vmx_state = VMX_STATE_OFF;
if (!vmx::execute_vmxon(cpu_data))
{
DbgPrint("failed to execute vmxon on core %lu\n", core_id);
return 0;
}
if (!vmx::execute_vmclear(cpu_data))
{
DbgPrint("failed to execute vmclear on core %lu\n", core_id);
return 0;
}
if (!vmx::execute_vmptrld(cpu_data))
{
DbgPrint("failed to execute vmptrld on core %lu\n", core_id);
return 0;
}
g_cpu_data[core_id] = cpu_data;
}
cpu_data->ctx_frame.ContextFlags = CONTEXT_ALL;
RtlCaptureContext(&cpu_data->ctx_frame);
if (cpu_data->vmx_state == VMX_STATE_OFF)
{
capture_registers(&cpu_data->special_registers);
if (!vmx::setup_vmcs(cpu_data, &cpu_data->ctx_frame))
{
DbgPrint("failed to setup vmcs on core %lu\n", core_id);
return 0;
}
cpu_data->vmx_state = VMX_STATE_TRANSITION;
int status = __vmx_vmlaunch();
cpu_data->vmx_state = VMX_STATE_OFF;
ULONG64 error = 0;
__vmx_vmread(VM_INSTRUCTION_ERROR, &error);
DbgPrint("vmlaunch failed on core %lu with error 0x%llx\n", core_id, error);
return 0;
}
else if (cpu_data->vmx_state == VMX_STATE_TRANSITION)
{
cpu_data->vmx_state = VMX_STATE_ON;
DbgPrint("vmlaunch succeeded on core %lu\n", core_id);
vm_restore_context(&cpu_data->ctx_frame);
}
return 0;
}
bool virtualise_all_cores()
{
if (!vmx::is_vmx_supported())
{
DbgPrint("vmx not supported\n");
return false;
}
g_core_count = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
DbgPrint("initializing vmx on %lu cores\n", g_core_count);
KeIpiGenericCall(initialize_vmx_on_core, 0);
ULONG virtualized_cores = 0;
for (ULONG i = 0; i < g_core_count; i++)
{
if (g_cpu_data[i] && g_cpu_data[i]->vmx_state == VMX_STATE_ON)
{
virtualized_cores++;
}
}
if (virtualized_cores == g_core_count)
{
DbgPrint("successfully virtualized all %lu cores\n", g_core_count);
return true;
}
else
{
DbgPrint("virtualized %lu out of %lu cores\n", virtualized_cores, g_core_count);
return false;
}
}
unsigned long long turn_off_vmx_on_core(unsigned long long argument)
{
unsigned long core_id = KeGetCurrentProcessorNumber();
vcpu_data* cpu_data = g_cpu_data[core_id];
if (cpu_data && cpu_data->vmx_enabled)
{
__vmx_off();
cpu_data->vmx_enabled = false;
cpu_data->vmx_state = VMX_STATE_OFF;
DbgPrint("disabled vmx on core %lu\n", core_id);
}
return 0;
}
bool devirtualise_all_cores()
{
DbgPrint("disabling vmx on all cores\n");
KeIpiGenericCall(turn_off_vmx_on_core, 0);
for (ULONG i = 0; i < g_core_count; i++)
{
if (g_cpu_data[i])
{
if (g_cpu_data[i]->msr_bitmap)
ExFreePool(g_cpu_data[i]->msr_bitmap);
if (g_cpu_data[i]->host_stack)
ExFreePool(g_cpu_data[i]->host_stack);
if (g_cpu_data[i]->vmcs_region)
memory::MmFreeIndependentPages(g_cpu_data[i]->vmcs_region, PAGE_SIZE);
if (g_cpu_data[i]->vmxon_region)
memory::MmFreeIndependentPages(g_cpu_data[i]->vmxon_region, PAGE_SIZE);
ExFreePool(g_cpu_data[i]);
g_cpu_data[i] = nullptr;
}
}
return true;
}
}
some parts were taken from simplevisor and hyperbone
vm_restore_context PROC
push rbp
push rsi
push rdi
sub rsp, 30h
mov rbp, rsp
movaps xmm0, xmmword ptr [rcx+1A0h]
movaps xmm1, xmmword ptr [rcx+1B0h]
movaps xmm2, xmmword ptr [rcx+1C0h]
movaps xmm3, xmmword ptr [rcx+1D0h]
movaps xmm4, xmmword ptr [rcx+1E0h]
movaps xmm5, xmmword ptr [rcx+1F0h]
movaps xmm6, xmmword ptr [rcx+200h]
movaps xmm7, xmmword ptr [rcx+210h]
movaps xmm8, xmmword ptr [rcx+220h]
movaps xmm9, xmmword ptr [rcx+230h]
movaps xmm10, xmmword ptr [rcx+240h]
movaps xmm11, xmmword ptr [rcx+250h]
movaps xmm12, xmmword ptr [rcx+260h]
movaps xmm13, xmmword ptr [rcx+270h]
movaps xmm14, xmmword ptr [rcx+280h]
movaps xmm15, xmmword ptr [rcx+290h]
ldmxcsr dword ptr [rcx+34h]
mov ax, [rcx+42h]
mov [rsp+20h], ax
mov rax, [rcx+98h] ; RSP
mov [rsp+18h], rax
mov eax, [rcx+44h]
mov [rsp+10h], eax
mov ax, [rcx+38h]
mov [rsp+08h], ax
mov rax, [rcx+0F8h] ; RIP
mov [rsp+00h], rax ;
mov rax, [rcx+78h]
mov rdx, [rcx+88h]
mov r8, [rcx+0B8h]
mov r9, [rcx+0C0h]
mov r10, [rcx+0C8h]
mov r11, [rcx+0D0h]
cli
mov rbx, [rcx+90h]
mov rsi, [rcx+0A8h]
mov rdi, [rcx+0B0h]
mov rbp, [rcx+0A0h]
mov r12, [rcx+0D8h]
mov r13, [rcx+0E0h]
mov r14, [rcx+0E8h]
mov r15, [rcx+0F0h]
mov rcx, [rcx+80h]
iretq
vm_restore_context ENDP
PUBLIC vmexit_handler
vmexit_handler PROC
push rcx
lea rcx, [rsp + 8h]
call capture_ctx
jmp vmentry_handler
vmexit_handler ENDP
END
PUBLIC capture_ctx
capture_ctx PROC
pushfq
mov [rcx + 78h], rax
mov [rcx + 80h], rcx
mov [rcx + 88h], rdx
mov [rcx + 0B8h], r8
mov [rcx + 0C0h], r9
mov [rcx + 0C8h], r10
mov [rcx + 0D0h], r11
mov word ptr [rcx + 38h], cs
mov word ptr [rcx + 3Ah], ds
mov word ptr [rcx + 3Ch], es
mov word ptr [rcx + 42h], ss
mov word ptr [rcx + 3Eh], fs
mov word ptr [rcx + 40h], gs
mov [rcx + 90h], rbx
mov [rcx + 0A0h], rbp
mov [rcx + 0A8h], rsi
mov [rcx + 0B0h], rdi
mov [rcx + 0D8h], r12
mov [rcx + 0E0h], r13
mov [rcx + 0E8h], r14
mov [rcx + 0F0h], r15
lea rax, [rsp + 10h]
mov [rcx + 98h], rax
mov rax, [rsp + 8]
mov [rcx + 0F8h], rax
mov eax, [rsp]
mov [rcx + 44h], eax
add rsp, 8
ret
capture_ctx ENDP