111 _In_
bool read_access);
143 static void VmmpIoWrapper(_In_
bool to_memory, _In_
bool is_string,
144 _In_ SIZE_T size_of_access, _In_
unsigned short port,
145 _Inout_
void *address, _In_
unsigned long count);
152 _Inout_
void *context);
158 _In_
bool deliver_error_code,
159 _In_ ULONG32 error_code);
180 #pragma warning(push) 181 #pragma warning(disable : 28167) 184 const auto guest_irql = KeGetCurrentIrql();
185 const auto guest_cr8 =
IsX64() ? __readcr8() : 0;
186 if (guest_irql < DISPATCH_LEVEL) {
187 KeRaiseIrqlToDpcLevel();
189 NT_ASSERT(stack->
reserved == MAXULONG_PTR);
211 if (guest_context.
irql < DISPATCH_LEVEL) {
212 KeLowerIrql(guest_context.
irql);
217 __writecr8(guest_context.
cr8);
233 const auto processor = KeGetCurrentProcessorNumberEx(
nullptr);
238 history.ip = guest_context->
ip;
239 history.exit_reason = exit_reason;
327 reinterpret_cast<ULONG_PTR>(guest_context),
328 guest_context->
ip, 0);
337 reinterpret_cast<ULONG_PTR>(guest_context),
338 guest_context->
ip, qualification);
346 reinterpret_cast<ULONG_PTR>(guest_context),
347 guest_context->
ip, 0);
356 const auto interruption_type =
370 "GuestIp= %016Ix, #PF Fault= %016Ix Code= 0x%2x", guest_context->
ip,
371 fault_address, fault_code.
all);
376 const auto error_code =
381 guest_context->
ip, error_code);
410 unsigned int cpu_info[4] = {};
411 const auto function_id =
static_cast<int>(guest_context->
gp_regs->
ax);
412 const auto sub_function_id =
static_cast<int>(guest_context->
gp_regs->
cx);
414 __cpuidex(reinterpret_cast<int *>(cpu_info), function_id, sub_function_id);
416 if (function_id == 1) {
418 CpuFeaturesEcx cpu_features = {
static_cast<ULONG_PTR
>(cpu_info[2])};
420 cpu_info[2] =
static_cast<int>(cpu_features.
all);
423 cpu_info[0] =
'PpyH';
426 guest_context->
gp_regs->
ax = cpu_info[0];
427 guest_context->
gp_regs->
bx = cpu_info[1];
428 guest_context->
gp_regs->
cx = cpu_info[2];
429 guest_context->
gp_regs->
dx = cpu_info[3];
438 ULARGE_INTEGER tsc = {};
439 tsc.QuadPart = __rdtsc();
440 guest_context->
gp_regs->
dx = tsc.HighPart;
441 guest_context->
gp_regs->
ax = tsc.LowPart;
450 unsigned int tsc_aux = 0;
451 ULARGE_INTEGER tsc = {};
452 tsc.QuadPart = __rdtscp(&tsc_aux);
453 guest_context->
gp_regs->
dx = tsc.HighPart;
454 guest_context->
gp_regs->
ax = tsc.LowPart;
464 ULARGE_INTEGER value = {};
465 value.LowPart =
static_cast<ULONG
>(guest_context->
gp_regs->
ax);
466 value.HighPart =
static_cast<ULONG
>(guest_context->
gp_regs->
dx);
467 _xsetbv(static_cast<ULONG>(guest_context->
gp_regs->
cx), value.QuadPart);
491 const auto msr =
static_cast<Msr>(guest_context->
gp_regs->
cx);
493 bool transfer_to_vmcs =
false;
498 transfer_to_vmcs =
true;
502 transfer_to_vmcs =
true;
506 transfer_to_vmcs =
true;
510 transfer_to_vmcs =
true;
514 transfer_to_vmcs =
true;
518 transfer_to_vmcs =
true;
524 const auto is_64bit_vmcs =
528 LARGE_INTEGER msr_value = {};
530 if (transfer_to_vmcs) {
539 guest_context->
gp_regs->
ax = msr_value.LowPart;
540 guest_context->
gp_regs->
dx = msr_value.HighPart;
542 msr_value.LowPart =
static_cast<ULONG
>(guest_context->
gp_regs->
ax);
543 msr_value.HighPart =
static_cast<ULONG
>(guest_context->
gp_regs->
dx);
544 if (transfer_to_vmcs) {
546 UtilVmWrite64(vmcs_field, static_cast<ULONG_PTR>(msr_value.QuadPart));
548 UtilVmWrite(vmcs_field, static_cast<ULONG_PTR>(msr_value.QuadPart));
569 ULONG_PTR base_value = 0;
573 base_value = *register_used;
577 ULONG_PTR index_value = 0;
581 index_value = *register_used;
584 index_value = index_value;
587 index_value = index_value * 2;
590 index_value = index_value * 4;
593 index_value = index_value * 8;
601 ULONG_PTR segment_base = 0;
613 auto operation_address =
614 segment_base + base_value + index_value + displacement;
617 operation_address &= MAXULONG;
623 const auto vmm_cr3 = __readcr3();
624 __writecr3(guest_cr3);
627 auto descriptor_table_reg =
reinterpret_cast<Idtr *
>(operation_address);
628 switch (static_cast<GdtrOrIdtrInstructionIdentity>(
632 descriptor_table_reg->limit =
637 descriptor_table_reg->limit =
664 ULONG_PTR operation_address = 0;
667 const auto register_used =
669 operation_address =
reinterpret_cast<ULONG_PTR
>(register_used);
672 ULONG_PTR base_value = 0;
676 base_value = *register_used;
680 ULONG_PTR index_value = 0;
684 index_value = *register_used;
687 index_value = index_value;
690 index_value = index_value * 2;
693 index_value = index_value * 4;
696 index_value = index_value * 8;
704 ULONG_PTR segment_base = 0;
716 operation_address = segment_base + base_value + index_value + displacement;
719 operation_address &= MAXULONG;
726 const auto vmm_cr3 = __readcr3();
727 __writecr3(guest_cr3);
730 auto selector =
reinterpret_cast<USHORT *
>(operation_address);
731 switch (static_cast<LdtrOrTrInstructionIdentity>(
783 if (debugl_register == 4 || debugl_register == 5) {
790 }
else if (debugl_register == 4) {
801 Dr6 guest_dr6 = {__readdr(6)};
812 __writedr(6, guest_dr6.
all);
828 const auto register_used =
830 const auto direction =
837 const auto value64 =
static_cast<ULONG64
>(*register_used);
838 if ((debugl_register == 6 || debugl_register == 7) && (value64 >> 32)) {
848 switch (debugl_register) {
850 case 0: __writedr(0, *register_used);
break;
851 case 1: __writedr(1, *register_used);
break;
852 case 2: __writedr(2, *register_used);
break;
853 case 3: __writedr(3, *register_used);
break;
862 Dr6 write_value = {*register_used};
866 __writedr(6, write_value.
all);
871 Dr7 write_value = {*register_used};
884 switch (debugl_register) {
885 case 0: *register_used = __readdr(0);
break;
886 case 1: *register_used = __readdr(1);
break;
887 case 2: *register_used = __readdr(2);
break;
888 case 3: *register_used = __readdr(3);
break;
889 case 6: *register_used = __readdr(6);
break;
914 const auto string_address =
reinterpret_cast<void *
>(
917 static_cast<unsigned long>((is_rep) ? guest_context->
gp_regs->
cx : 1);
919 (is_string) ? string_address : &guest_context->
gp_regs->
ax;
921 SIZE_T size_of_access = 0;
922 const char *suffix =
"";
923 switch (static_cast<IoInstSizeOfAccess>(
940 guest_context->
ip, port, (is_in ?
"IN" :
"OUT"),
941 (is_string ?
"S" :
""),
942 (is_string ? suffix :
""));
944 VmmpIoWrapper(is_in, is_string, size_of_access, port, address, count);
949 const auto update_count = (is_rep) ? guest_context->
gp_regs->
cx : 1;
950 const auto update_size = update_count * size_of_access;
951 const auto update_register =
955 *update_register = *update_register - update_size;
957 *update_register = *update_register + update_size;
969 _Use_decl_annotations_
static void VmmpIoWrapper(
bool to_memory,
bool is_string,
970 SIZE_T size_of_access,
973 unsigned long count) {
974 NT_ASSERT(size_of_access == 1 || size_of_access == 2 || size_of_access == 4);
979 const auto vmm_cr3 = __readcr3();
980 __writecr3(guest_cr3);
986 switch (size_of_access) {
987 case 1: __inbytestring(port, reinterpret_cast<UCHAR*>(address), count);
break;
988 case 2: __inwordstring(port, reinterpret_cast<USHORT*>(address), count);
break;
989 case 4: __indwordstring(port, reinterpret_cast<ULONG*>(address), count);
break;
993 switch (size_of_access) {
994 case 1: *
reinterpret_cast<UCHAR*
>(address) = __inbyte(port);
break;
995 case 2: *
reinterpret_cast<USHORT*
>(address) = __inword(port);
break;
996 case 4: *
reinterpret_cast<ULONG*
>(address) = __indword(port);
break;
1002 switch (size_of_access) {
1003 case 1: __outbytestring(port, reinterpret_cast<UCHAR*>(address), count);
break;
1004 case 2: __outwordstring(port, reinterpret_cast<USHORT*>(address), count);
break;
1005 case 4: __outdwordstring(port, reinterpret_cast<ULONG*>(address), count);
break;
1009 switch (size_of_access) {
1010 case 1: __outbyte(port, *reinterpret_cast<UCHAR*>(address));
break;
1011 case 2: __outword(port, *reinterpret_cast<USHORT*>(address));
break;
1012 case 4: __outdword(port, *reinterpret_cast<ULONG*>(address));
break;
1018 __writecr3(vmm_cr3);
1028 const auto register_used =
1042 Cr0 cr0 = {*register_used};
1043 cr0.
all &= cr0_fixed1.
all;
1044 cr0.
all |= cr0_fixed0.
all;
1061 static_cast<USHORT>(KeGetCurrentProcessorNumberEx(
nullptr) + 1));
1079 Cr4 cr4 = {*register_used};
1080 cr4.
all &= cr4_fixed1.
all;
1081 cr4.
all |= cr4_fixed0.
all;
1090 guest_context->
cr8 = *register_used;
1113 *register_used = guest_context->
cr8;
1156 const auto hypercall_number =
1158 const auto context =
reinterpret_cast<void *
>(guest_context->
gp_regs->
dx);
1160 switch (hypercall_number) {
1175 *
reinterpret_cast<void **
>(context) =
1197 const auto invalidate_address =
1199 __invlpg(invalidate_address);
1201 static_cast<USHORT>(KeGetCurrentProcessorNumberEx(
nullptr) + 1),
1202 invalidate_address);
1217 UNREFERENCED_PARAMETER(guest_context);
1224 reinterpret_cast<ULONG_PTR>(ept_pt_entry), 0);
1230 ULONG_PTR *register_used =
nullptr;
1233 case 0: register_used = &guest_context->
gp_regs->
ax;
break;
1234 case 1: register_used = &guest_context->
gp_regs->
cx;
break;
1235 case 2: register_used = &guest_context->
gp_regs->
dx;
break;
1236 case 3: register_used = &guest_context->
gp_regs->
bx;
break;
1237 case 4: register_used = &guest_context->
gp_regs->
sp;
break;
1238 case 5: register_used = &guest_context->
gp_regs->
bp;
break;
1239 case 6: register_used = &guest_context->
gp_regs->
si;
break;
1240 case 7: register_used = &guest_context->
gp_regs->
di;
break;
1241 #if defined(_AMD64_) 1242 case 8: register_used = &guest_context->
gp_regs->r8;
break;
1243 case 9: register_used = &guest_context->
gp_regs->r9;
break;
1244 case 10: register_used = &guest_context->
gp_regs->r10;
break;
1245 case 11: register_used = &guest_context->
gp_regs->r11;
break;
1246 case 12: register_used = &guest_context->
gp_regs->r12;
break;
1247 case 13: register_used = &guest_context->
gp_regs->r13;
break;
1248 case 14: register_used = &guest_context->
gp_regs->r14;
break;
1249 case 15: register_used = &guest_context->
gp_regs->r15;
break;
1254 return register_used;
1361 UNREFERENCED_PARAMETER(guest_context);
1380 Gdtr gdtr = {
static_cast<USHORT
>(gdt_limit), gdt_base};
1381 Idtr idtr = {
static_cast<USHORT
>(idt_limit), idt_base};
1386 const auto result_ptr =
reinterpret_cast<ProcessorData **
>(context);
1392 const auto exit_instruction_length =
1394 const auto return_address = guest_context->
ip + exit_instruction_length;
1411 guest_context->
gp_regs->
cx = return_address;
1427 bool deliver_error_code, ULONG32 error_code) {
1435 if (deliver_error_code) {
1445 if (guest_cr3 & 1) {
1446 static const long kDirectoryTableBaseOffsetX64 = 0x28;
1447 static const long kDirectoryTableBaseOffsetX86 = 0x18;
1448 auto process =
reinterpret_cast<PUCHAR
>(PsGetCurrentProcess());
1451 *
reinterpret_cast<PULONG_PTR
>(process + kDirectoryTableBaseOffsetX64);
1454 *
reinterpret_cast<PULONG_PTR
>(process + kDirectoryTableBaseOffsetX86);
ULONG_PTR port_number
[16:31]
A triple fault VM-exit occurred.
struct SegmentSelector::@7 fields
static void VmmpIndicateSuccessfulVmcall(_In_ GuestContext *guest_context)
static void VmmpHandleXsetbv(_Inout_ GuestContext *guest_context)
static void VmmpIndicateUnsuccessfulVmcall(_In_ GuestContext *guest_context)
HypercallNumber
Available command numbers for VMCALL.
struct EptData * ept_data
A pointer to EPT related data.
static DECLSPEC_NORETURN void VmmpHandleUnexpectedExit(_Inout_ GuestContext *guest_context)
static DECLSPEC_NORETURN void VmmpHandleTripleFault(_Inout_ GuestContext *guest_context)
ULONG_PTR access_type
[4:5]
ULONG_PTR pf
[2] Parity flag
struct MovCrQualification::@39 fields
VmxStatus UtilVmWrite(VmcsField field, ULONG_PTR field_value)
ULONG32 interruption_type
[8:10]
struct IoInstQualification::@38 fields
static void VmmpHandleLdtrOrTrAccess(_Inout_ GuestContext *guest_context)
static ULONG g_vmmp_next_history_index[kVmmpNumberOfProcessors]
InterruptionType
See: Format of the VM-Entry Interruption-Information Field.
static void VmmpHandleVmExit(_Inout_ GuestContext *guest_context)
EptCommonEntry * EptGetEptPtEntry(EptData *ept_data, ULONG64 physical_address)
ULONG_PTR gp_register
[8:11]
#define HYPERPLATFORM_COMMON_BUG_CHECK(hp_bug_check_code, param1, param2, param3)
Issues a bug check.
static void VmmpHandleVmCallTermination(_In_ GuestContext *guest_context, _Inout_ void *context)
constexpr bool IsX64()
Checks if a system is x64.
Represents a stack layout after PUSHAD.
#define HYPERPLATFORM_COMMON_DBG_BREAK()
Sets a break point that works only when a debugger is present.
ULONG32 not_used
[31] Always 0 (a.k.a. HypervisorPresent)
static void VmmpHandleVmx(_Inout_ GuestContext *guest_context)
static void VmmpHandleIoPort(_Inout_ GuestContext *guest_context)
ULONG_PTR af
[4] Borrow flag
EPT misconfiguration VM-exit occurred.
ULONG_PTR cf
[0] Carry flag
struct SegmentDescriptor::@8 fields
static void VmmpHandleMsrReadAccess(_Inout_ GuestContext *guest_context)
ULONG_PTR tf
[8] Trap flag
unsigned reserved1
[4:11] Always 1
See: Guest Register State.
ULONG_PTR string_instruction
[4]
unsigned bd
[13] Debug Register Access Detected
ULONG_PTR rep_prefixed
[5]
static void VmmpHandleRdtscp(_Inout_ GuestContext *guest_context)
ULONG64 UtilVmRead64(VmcsField field)
unsigned de
[3] Debugging Extensions
Declares interfaces to EPT functions.
static void VmmpHandleInvalidateTlbEntry(_Inout_ GuestContext *guest_context)
VmxStatus UtilInvvpidIndividualAddress(USHORT vpid, void *address)
VmxStatus UtilInveptGlobal()
Executes the INVEPT instruction and invalidates EPT entry cache.
ULONG_PTR df
[10] Direction flag
VmxStatus UtilInvvpidSingleContextExceptGlobal(USHORT vpid)
bool __stdcall VmmVmExitHandler(_Inout_ VmmInitialStack *stack)
SharedProcessorData * shared_data
Shared data.
struct VmxRegmentDescriptorAccessRight::@26 fields
struct FlagRegister::@1 fields
unsigned b0
[0] Breakpoint Condition Detected 0
static const bool kVmmpEnableRecordVmExit
unsigned gd
[13] General Detect Enable
#define HYPERPLATFORM_LOG_INFO_SAFE(format,...)
See: Page-Fault Error Code.
Declares interfaces to VMM functions.
ULONG_PTR control_register
[0:3]
void EptHandleEptViolation(EptData *ept_data)
See: Exit Qualification for Control-Register Accesses.
void UtilWriteMsr64(Msr msr, ULONG64 value)
struct VmEntryInterruptionInformationField::@49 fields
See: Exit Qualification for MOV DR.
unsigned reserved3
[17:31] Always 1
An unspecified bug occurred.
Declares interfaces to assembly functions.
unsigned reserved2
[12] Always 0
See: Feature Information Returned in the ECX Register.
Msr
See: MODEL-SPECIFIC REGISTERS (MSRS)
static void VmmpInjectInterruption(_In_ InterruptionType interruption_type, _In_ InterruptionVector vector, _In_ bool deliver_error_code, _In_ ULONG32 error_code)
ULONG_PTR gp_register
[8:11]
static void VmmpIoWrapper(_In_ bool to_memory, _In_ bool is_string, _In_ SIZE_T size_of_access, _In_ unsigned short port, _Inout_ void *address, _In_ unsigned long count)
See: SYSTEM FLAGS AND FIELDS IN THE EFLAGS REGISTER.
static void VmmpAdjustGuestInstructionPointer(_In_ GuestContext *guest_context)
unsigned reserved3
[14:15] Always 0
Declares interfaces to utility functions.
See: Format of the VM-Entry Interruption-Information Field.
ULONG_PTR zf
[6] Zero flag
ULONG_PTR UtilReadMsr(Msr msr)
unsigned b3
[3] Breakpoint Condition Detected 3
See: Debug Control Register (DR7)
VmcsField
See: FIELD ENCODING IN VMCS.
static void VmmpHandleRdtsc(_Inout_ GuestContext *guest_context)
static void VmmpHandleEptViolation(_Inout_ GuestContext *guest_context)
DECLSPEC_NORETURN void __stdcall VmmVmxFailureHandler(_Inout_ AllRegisters *all_regs)
ULONG_PTR debugl_register
[0:2]
VMRESUME or VMXOFF has failed.
InterruptionVector
See: Format of the VM-Entry Interruption-Information Field.
ProcessorData * processor_data
void UtilLoadPdptes(ULONG_PTR cr3_value)
ULONG_PTR sf
[7] Sign flag
static void VmmpHandleCpuid(_Inout_ GuestContext *guest_context)
unsigned b1
[1] Breakpoint Condition Detected 1
static void VmmpHandleGdtrOrIdtrAccess(_Inout_ GuestContext *guest_context)
static void VmmpHandleEptMisconfig(_Inout_ GuestContext *guest_context)
static const long kVmmpNumberOfRecords
static void VmmpHandleVmCall(_Inout_ GuestContext *guest_context)
static void VmmpHandleInvalidateInternalCaches(_Inout_ GuestContext *guest_context)
#define HYPERPLATFORM_LOG_DEBUG_SAFE(format,...)
Buffers a message as respective severity.
See: Exit Qualification for I/O Instructions.
unsigned b2
[2] Breakpoint Condition Detected 2
static ULONG_PTR VmmpGetKernelCr3()
VmxStatus UtilInvvpidAllContext()
Executes the INVVPID instruction (type 2)
ULONG_PTR UtilVmRead(VmcsField field)
bool UtilIsX86Pae()
Checks if the system is a PAE-enabled x86 system.
static const long kVmmpNumberOfProcessors
static const ULONG32 kHyperVCpuidInterface
A majority of modern hypervisors expose their signatures through CPUID with this CPUID function code ...
static void VmmpHandleMsrAccess(_Inout_ GuestContext *guest_context, _In_ bool read_access)
struct MovDrQualification::@37 fields
MovDrDirection
See: Exit Qualification for MOV DR.
ULONG32 deliver_error_code
[11]
void __stdcall AsmWriteCR2(_In_ ULONG_PTR cr2_value)
Writes to CR2.
static void VmmpHandleDrAccess(_Inout_ GuestContext *guest_context)
struct CpuFeaturesEcx::@9 fields
See: Debug Status Register (DR6)
void __lgdt(_In_ void *gdtr)
Reads SGDT.
Declares and implements common things across the project.
static void VmmpHandleMonitorTrap(_Inout_ GuestContext *guest_context)
static void VmmpHandleCrAccess(_Inout_ GuestContext *guest_context)
VmExitInformation exit_reason
ULONG64 UtilReadMsr64(Msr msr)
See: MEMORY-MANAGEMENT REGISTERS.
ULONG_PTR exit_qualification
static VmExitHistory g_vmmp_vm_exit_history[kVmmpNumberOfProcessors][kVmmpNumberOfRecords]
ULONG_PTR instruction_info
Declares interfaces to logging functions.
static void VmmpDumpGuestState()
ULONG_PTR size_of_access
[0:2]
static UCHAR VmmpGetGuestCpl()
void __stdcall AsmInvalidateInternalCaches()
Invalidates internal caches.
unsigned reserved1
[10] Always 1
static ULONG_PTR * VmmpSelectRegister(_In_ ULONG index, _In_ GuestContext *guest_context)
constexpr bool UtilIsInBounds(_In_ const T &value, _In_ const T &min, _In_ const T &max)
Tests if value is in between min and max.
An unexpected VM-exit occurred.
static void VmmpHandleMsrWriteAccess(_Inout_ GuestContext *guest_context)
ULONG_PTR of
[11] Overflow flag
Represents VMM related data associated with each processor.
Represents a stack layout after a sequence of PUSHFx, PUSHAx.
static void VmmpHandleException(_Inout_ GuestContext *guest_context)
unsigned reserved2
[12] Always 0
VmxStatus UtilVmWrite64(VmcsField field, ULONG64 field_value)