diff --git a/binaries/bluepill.sys b/binaries/bluepill.sys index d5e06ba..c9fc14a 100644 Binary files a/binaries/bluepill.sys and b/binaries/bluepill.sys differ diff --git a/drv_entry.cpp b/drv_entry.cpp index 3ded756..a60fc31 100644 --- a/drv_entry.cpp +++ b/drv_entry.cpp @@ -46,10 +46,21 @@ auto drv_entry(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) -> N // copy the guest IDT entries... memcpy(idt::table, (void*)idt_value.base_address, idt_value.limit); - // change gp, pf, and de to vmxroot handlers... - idt::table[general_protection] = idt::create_entry(hv::idt_addr_t{ __gp_handler }, idt::ist_idx::gp); - idt::table[page_fault] = idt::create_entry(hv::idt_addr_t{ __pf_handler }, idt::ist_idx::pf); - idt::table[divide_error] = idt::create_entry(hv::idt_addr_t{ __de_handler }, idt::ist_idx::de); + idt::table[general_protection] = + idt::create_entry(hv::idt_addr_t + { __gp_handler }, idt::ist_idx::gp); + + idt::table[page_fault] = + idt::create_entry(hv::idt_addr_t + { __pf_handler }, idt::ist_idx::pf); + + idt::table[divide_error] = + idt::create_entry(hv::idt_addr_t + { __de_handler }, idt::ist_idx::de); + + idt::table[non_maskable_interrupt] = + idt::create_entry(hv::idt_addr_t + { __nmi_handler }, idt::ist_idx::nmi); // used for SEH in vmxroot fault handler... idt::image_base = driver_object->DriverStart; diff --git a/exit_handler.cpp b/exit_handler.cpp index db2eb4b..10028da 100644 --- a/exit_handler.cpp +++ b/exit_handler.cpp @@ -39,6 +39,44 @@ auto exit_handler(hv::pguest_registers regs) -> void regs->rdx = result[3]; break; } + case VMX_EXIT_REASON_NMI_WINDOW: + { + vmentry_interrupt_information interrupt{}; + interrupt.interruption_type = interruption_type::non_maskable_interrupt; + interrupt.vector = EXCEPTION_NMI; + interrupt.valid = true; + + __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags); + __vmx_vmwrite(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE, NULL); + + ia32_vmx_procbased_ctls_register procbased_ctls; + ia32_vmx_pinbased_ctls_register pinbased_ctls; + + __vmx_vmread(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &procbased_ctls.flags); + __vmx_vmread(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, &pinbased_ctls.flags); + + procbased_ctls.nmi_window_exiting = false; + pinbased_ctls.virtual_nmi = false; + + __vmx_vmwrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, procbased_ctls.flags); + __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinbased_ctls.flags); + return; // dont advance rip... + } + case VMX_EXIT_REASON_EXCEPTION_OR_NMI: + { + ia32_vmx_procbased_ctls_register procbased_ctls; + ia32_vmx_pinbased_ctls_register pinbased_ctls; + + __vmx_vmread(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &procbased_ctls.flags); + __vmx_vmread(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, &pinbased_ctls.flags); + + procbased_ctls.nmi_window_exiting = true; + pinbased_ctls.virtual_nmi = true; + + __vmx_vmwrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, procbased_ctls.flags); + __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinbased_ctls.flags); + return; // dont advance rip... + } case VMX_EXIT_REASON_EXECUTE_XSETBV: { hv::msr_split value{}; @@ -65,9 +103,11 @@ auto exit_handler(hv::pguest_registers regs) -> void __except (EXCEPTION_EXECUTE_HANDLER) { vmentry_interrupt_information interrupt{}; - interrupt.flags = interruption_type::hardware_exception; + interrupt.interruption_type = interruption_type::hardware_exception; interrupt.vector = EXCEPTION_GP_FAULT; + interrupt.valid = true; + interrupt.deliver_error_code = true; __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags); __vmx_vmwrite(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE, g_vcpu->error_code); @@ -94,9 +134,11 @@ auto exit_handler(hv::pguest_registers regs) -> void __except (EXCEPTION_EXECUTE_HANDLER) { vmentry_interrupt_information interrupt{}; - interrupt.flags = interruption_type::hardware_exception; + interrupt.interruption_type = interruption_type::hardware_exception; interrupt.vector = EXCEPTION_GP_FAULT; + interrupt.valid = true; + interrupt.deliver_error_code = true; __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags); __vmx_vmwrite(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE, g_vcpu->error_code); @@ -110,11 +152,6 @@ auto exit_handler(hv::pguest_registers regs) -> void value.high = regs->rdx; __try - { - __writemsr(regs->rcx, value.value); - break; - } - __except (EXCEPTION_EXECUTE_HANDLER) { /* EXCEPTION WARNING: @@ -122,10 +159,17 @@ auto exit_handler(hv::pguest_registers regs) -> void If the value in ECX specifies a reserved or unimplemented MSR address. #UD If the LOCK prefix is used. */ + __writemsr(regs->rcx, value.value); + break; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { vmentry_interrupt_information interrupt{}; - interrupt.flags = interruption_type::hardware_exception; + interrupt.interruption_type = interruption_type::hardware_exception; interrupt.vector = EXCEPTION_GP_FAULT; + interrupt.valid = true; + interrupt.deliver_error_code = true; __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags); __vmx_vmwrite(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE, g_vcpu->error_code); @@ -213,7 +257,7 @@ auto exit_handler(hv::pguest_registers regs) -> void else { vmentry_interrupt_information interrupt{}; - interrupt.flags = interruption_type::hardware_exception; + interrupt.interruption_type = interruption_type::hardware_exception; interrupt.vector = EXCEPTION_INVALID_OPCODE; interrupt.valid = true; @@ -232,12 +276,11 @@ auto exit_handler(hv::pguest_registers regs) -> void case VMX_EXIT_REASON_EXECUTE_VMXON: { vmentry_interrupt_information interrupt{}; - interrupt.flags = interruption_type::hardware_exception; + interrupt.interruption_type = interruption_type::hardware_exception; interrupt.vector = EXCEPTION_INVALID_OPCODE; interrupt.valid = true; __vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags); - // manual says there will never be an error code... so just put null... __vmx_vmwrite(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE, NULL); return; // dont advance rip... } diff --git a/hv_types.hpp b/hv_types.hpp index 44783ca..4f2cd0e 100644 --- a/hv_types.hpp +++ b/hv_types.hpp @@ -253,7 +253,7 @@ namespace hv u64 rax; } guest_registers, * pguest_registers; - typedef struct _idt_regs_t + typedef struct _idt_regs_ecode_t { u64 r15; u64 r14; @@ -277,7 +277,32 @@ namespace hv ::rflags rflags; u64 rsp; u64 ss_selector; - } idt_regs_t, *pidt_regs_t; + } idt_regs_ecode_t, *pidt_regs_ecode_t; + + typedef struct _idt_regs_t + { + u64 r15; + u64 r14; + u64 r13; + u64 r12; + u64 r11; + u64 r10; + u64 r9; + u64 r8; + u64 rbp; + u64 rdi; + u64 rsi; + u64 rdx; + u64 rcx; + u64 rbx; + u64 rax; + + u64 rip; + u64 cs_selector; + ::rflags rflags; + u64 rsp; + u64 ss_selector; + } idt_regs_t, * pidt_regs_t; union msr_split { @@ -657,7 +682,10 @@ namespace hv u64 vmcs_phys; u64 vmxon_phys; u64 host_stack; + u64 error_code; + u64 nmi_code; + tss64 tss; segment_descriptor_64* gdt; } vcpu_ctx, * pvcpu_ctx; diff --git a/ia32.hpp b/ia32.hpp index 04f1484..10ca493 100644 --- a/ia32.hpp +++ b/ia32.hpp @@ -10156,6 +10156,8 @@ typedef union #define IA32_VMX_PROCBASED_CTLS 0x00000482 typedef union { + uint64_t flags; + struct { uint64_t reserved1 : 2; @@ -10429,8 +10431,6 @@ typedef union #define IA32_VMX_PROCBASED_CTLS_ACTIVATE_SECONDARY_CONTROLS(_) (((_) >> 31) & 0x01) uint64_t reserved7 : 32; }; - - uint64_t flags; } ia32_vmx_procbased_ctls_register; diff --git a/idt.cpp b/idt.cpp index 753a0fa..d818b95 100644 --- a/idt.cpp +++ b/idt.cpp @@ -1,6 +1,7 @@ #include "idt.hpp" +#include "debug.hpp" -auto seh_handler(hv::pidt_regs_t regs) -> void +auto seh_handler_ecode(hv::pidt_regs_ecode_t regs) -> void { g_vcpu->error_code = regs->error_code; const auto rva = regs->rip - reinterpret_cast(idt::image_base); @@ -45,6 +46,68 @@ auto seh_handler(hv::pidt_regs_t regs) -> void } } +// probably should make a single SEH handler instead of having 2... +// maybe instead of passing the stack (rsp), just pass RIP and have seh_handler +// calc the catch block and return the address... +auto seh_handler(hv::pidt_regs_t regs) -> void +{ + const auto rva = regs->rip - reinterpret_cast(idt::image_base); + const auto nt_headers = reinterpret_cast( + reinterpret_cast(idt::image_base) + + reinterpret_cast(idt::image_base)->e_lfanew); + + const auto exception = + &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; + + const auto functions = + reinterpret_cast( + reinterpret_cast(idt::image_base) + exception->VirtualAddress); + + for (auto idx = 0; idx < exception->Size / sizeof(RUNTIME_FUNCTION); ++idx) + { + const auto function = &functions[idx]; + if (!(rva >= function->BeginAddress && rva < function->EndAddress)) + continue; + + const auto unwind_info = + reinterpret_cast( + reinterpret_cast(idt::image_base) + function->UnwindData); + + if (!(unwind_info->Flags & UNW_FLAG_EHANDLER)) + continue; + + const auto scope_table = + reinterpret_cast( + reinterpret_cast(&unwind_info->UnwindCode[ + (unwind_info->CountOfCodes + 1) & ~1]) + sizeof(u32)); + + for (auto entry = 0; entry < scope_table->Count; ++entry) + { + const auto scope_record = &scope_table->ScopeRecords[entry]; + if (rva >= scope_record->BeginAddress && rva < scope_record->EndAddress) + { + regs->rip = reinterpret_cast(idt::image_base) + scope_record->JumpTarget; + return; + } + } + } +} + +auto nmi_handler(hv::pidt_regs_t regs) -> void +{ + ia32_vmx_procbased_ctls_register procbased_ctls; + ia32_vmx_pinbased_ctls_register pinbased_ctls; + + __vmx_vmread(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, &procbased_ctls.flags); + __vmx_vmread(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, &pinbased_ctls.flags); + + procbased_ctls.nmi_window_exiting = true; + pinbased_ctls.virtual_nmi = true; + + __vmx_vmwrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, procbased_ctls.flags); + __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinbased_ctls.flags); +} + namespace idt { auto create_entry(hv::idt_addr_t idt_handler, u8 ist_index) -> hv::idt_entry_t diff --git a/idt.hpp b/idt.hpp index c296567..0efdaaf 100644 --- a/idt.hpp +++ b/idt.hpp @@ -8,13 +8,17 @@ extern "C" void __gp_handler(void); extern "C" void __pf_handler(void); extern "C" void __de_handler(void); +extern "C" void __nmi_handler(void); + +extern "C" void nmi_handler(hv::pidt_regs_t regs); extern "C" void seh_handler(hv::pidt_regs_t regs); +extern "C" void seh_handler_ecode(hv::pidt_regs_ecode_t regs); namespace idt { __declspec(allocate(".idt")) inline hv::idt_entry_t table[256]; - enum ist_idx : u8 { de = 4, pf = 5, gp = 6}; + enum ist_idx : u8 { nmi = 3, de = 4, pf = 5, gp = 6}; inline void* image_base = nullptr; // used for SEH... auto create_entry(hv::idt_addr_t idt_handler, u8 ist_index) -> hv::idt_entry_t; diff --git a/idt_handlers.asm b/idt_handlers.asm index 10c2df0..36524fb 100644 --- a/idt_handlers.asm +++ b/idt_handlers.asm @@ -1,7 +1,92 @@ extern seh_handler : proc +extern nmi_handler : proc +extern seh_handler_ecode : proc .code +__nmi_handler proc + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rcx, rsp + sub rsp, 20h + call nmi_handler + add rsp, 20h + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + iretq +__nmi_handler endp + +; #DE has no error code... __de_handler proc + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rcx, rsp + sub rsp, 20h + call seh_handler + add rsp, 20h + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + iretq +__de_handler endp + +; PF and GP have error code... __pf_handler proc __gp_handler proc push rax @@ -22,7 +107,7 @@ __gp_handler proc mov rcx, rsp sub rsp, 20h - call seh_handler + call seh_handler_ecode add rsp, 20h pop r15 @@ -45,5 +130,4 @@ __gp_handler proc iretq __gp_handler endp __pf_handler endp -__de_handler endp end \ No newline at end of file diff --git a/vmcs.cpp b/vmcs.cpp index cce725f..60569ac 100644 --- a/vmcs.cpp +++ b/vmcs.cpp @@ -162,6 +162,7 @@ namespace vmcs if (vmx_basic.vmx_controls) { msr_fix_value.flags = __readmsr(IA32_VMX_TRUE_PINBASED_CTLS); + pinbased_ctls.nmi_exiting = true; pinbased_ctls.flags &= msr_fix_value.allowed_1_settings; pinbased_ctls.flags |= msr_fix_value.allowed_0_settings; __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinbased_ctls.flags); @@ -188,6 +189,7 @@ namespace vmcs else { msr_fix_value.flags = __readmsr(IA32_VMX_PINBASED_CTLS); + pinbased_ctls.nmi_exiting = true; pinbased_ctls.flags &= msr_fix_value.allowed_1_settings; pinbased_ctls.flags |= msr_fix_value.allowed_0_settings; __vmx_vmwrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinbased_ctls.flags);