// RDMSR and WRMSR _Use_decl_annotations_ static void VmmpHandleMsrAccess( GuestContext *guest_context, bool read_access) { // Apply it for VMCS instead of a real MSR if a speficied MSR is either of // them. const auto msr = static_cast<Msr>(guest_context->gp_regs->cx); bool transfer_to_vmcs = false; VmcsField vmcs_field = {}; switch (msr) { case Msr::kIa32SysenterCs: vmcs_field = VmcsField::kGuestSysenterCs; transfer_to_vmcs = true; break; case Msr::kIa32SysenterEsp: vmcs_field = VmcsField::kGuestSysenterEsp; transfer_to_vmcs = true; break; case Msr::kIa32SysenterEip: vmcs_field = VmcsField::kGuestSysenterEip; transfer_to_vmcs = true; break; case Msr::kIa32GsBase: vmcs_field = VmcsField::kGuestGsBase; transfer_to_vmcs = true; break; case Msr::kIa32FsBase: vmcs_field = VmcsField::kGuestFsBase; break; default: break; } // Do not shadow 64bit fields because the current implmentation for x86 is not // able to handle it due to a simple use of UtilVmWrite() below. NT_ASSERT(UtilIsInBounds(vmcs_field, VmcsField::kIoBitmapA, VmcsField::kHostIa32PerfGlobalCtrlHigh) == false); // This unconditional __readmsr and __writemsr may cause #GP resulting in // bug check. A proper way to solve this is check supported MSR values // beforehand and inject an exception when unsupported MSR values are given. LARGE_INTEGER msr_value = {}; if (read_access) { if (transfer_to_vmcs) { msr_value.QuadPart = UtilVmRead(vmcs_field); } else { msr_value.QuadPart = UtilReadMsr64(msr); } guest_context->gp_regs->ax = msr_value.LowPart; guest_context->gp_regs->dx = msr_value.HighPart; } else { msr_value.LowPart = static_cast<ULONG>(guest_context->gp_regs->ax); msr_value.HighPart = static_cast<ULONG>(guest_context->gp_regs->dx); if (transfer_to_vmcs) { UtilVmWrite(vmcs_field, static_cast<ULONG_PTR>(msr_value.QuadPart)); } else { UtilWriteMsr64(msr, msr_value.QuadPart); } } VmmpAdjustGuestInstructionPointer(guest_context->ip); }
// Returns a memory type based on MTRRs _Use_decl_annotations_ static memory_type EptpGetMemoryType( ULONG64 physical_address) { // Indicate that MTRR is not defined (as a default) UCHAR result_type = MAXUCHAR; // Looks for MTRR that includes the specified physical_address for (const auto mtrr_entry : g_eptp_mtrr_entries) { if (!mtrr_entry.enabled) { // Reached out the end of stored MTRRs break; } if (!UtilIsInBounds(physical_address, mtrr_entry.range_base, mtrr_entry.range_end)) { // This MTRR does not describe a memory type of the physical_address continue; } // See: MTRR Precedences if (mtrr_entry.fixedMtrr) { // If a fixed MTRR describes a memory type, it is priority result_type = mtrr_entry.type; break; } if (mtrr_entry.type == static_cast<UCHAR>(memory_type::kUncacheable)) { // If a memory type is UC, it is priority. Do not continue to search as // UC has the highest priority result_type = mtrr_entry.type; break; } if (result_type == static_cast<UCHAR>(memory_type::kWriteThrough) || mtrr_entry.type == static_cast<UCHAR>(memory_type::kWriteThrough)) { if (result_type == static_cast<UCHAR>(memory_type::kWriteBack)) { // If two or more MTRRs describes an over-wrapped memory region, and // one is WT and the other one is WB, use WT. However, look for other // MTRRs, as the other MTRR specifies the memory address as UC, which is // priority. result_type = static_cast<UCHAR>(memory_type::kWriteThrough); continue; } } // Otherwise, processor behavior is undefined. We just use the last MTRR // describes the memory address. result_type = mtrr_entry.type; } // Use the default MTRR if no MTRR entry is found if (result_type == MAXUCHAR) { result_type = g_eptp_mtrr_default_type; } return static_cast<memory_type>(result_type); }
// Checks if the export is listed as a hook target, and if so install a hook. _Use_decl_annotations_ EXTERN_C static bool DdimonpEnumExportedSymbolsCallback( ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory, ULONG_PTR directory_base, ULONG_PTR directory_end, void* context) { PAGED_CODE(); if (!context) { return false; } auto functions = reinterpret_cast<ULONG*>(base_address + directory->AddressOfFunctions); auto ordinals = reinterpret_cast<USHORT*>(base_address + directory->AddressOfNameOrdinals); auto names = reinterpret_cast<ULONG*>(base_address + directory->AddressOfNames); auto ord = ordinals[index]; auto export_address = base_address + functions[ord]; auto export_name = reinterpret_cast<const char*>(base_address + names[index]); // Check if an export is forwared one? If so, ignore it. if (UtilIsInBounds(export_address, directory_base, directory_end)) { return true; } // convert the name to UNICODE_STRING wchar_t name[100]; auto status = RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name); if (!NT_SUCCESS(status)) { return true; } UNICODE_STRING name_u = {}; RtlInitUnicodeString(&name_u, name); for (auto& target : g_ddimonp_hook_targets) { // Is this export listed as a target if (!FsRtlIsNameInExpression(&target.target_name, &name_u, TRUE, nullptr)) { continue; } // Yes, install a hook to the export if (!ShInstallHook(reinterpret_cast<SharedShadowHookData*>(context), reinterpret_cast<void*>(export_address), &target)) { // This is an error which should not happen DdimonpFreeAllocatedTrampolineRegions(); return false; } HYPERPLATFORM_LOG_INFO("Hook has been installed at %p %s.", export_address, export_name); } return true; }
// Returns if the physical_address is device memory (which could not have a // corresponding PFN entry) _Use_decl_annotations_ static bool EptpIsDeviceMemory( ULONG64 physical_address) { const auto pm_ranges = UtilGetPhysicalMemoryRanges(); for (auto i = 0ul; i < pm_ranges->number_of_runs; ++i) { const auto current_run = &pm_ranges->run[i]; const auto base_addr = static_cast<ULONG64>(current_run->base_page) * PAGE_SIZE; const auto endAddr = base_addr + current_run->page_count * PAGE_SIZE - 1; if (UtilIsInBounds(physical_address, base_addr, endAddr)) { return false; } } return true; }
// A fake RtlPcToFileHeader without accquireing PsLoadedModuleSpinLock. Thus, it // is unsafe and should be updated if we can locate PsLoadedModuleSpinLock. _Use_decl_annotations_ static PVOID NTAPI MmonpUnsafePcToFileHeader(PVOID pc_value, PVOID *base_of_image) { if (pc_value < MmSystemRangeStart) { return nullptr; } const auto head = g_mmonp_PsLoadedModuleList; for (auto current = head->Flink; current != head; current = current->Flink) { const auto module = CONTAINING_RECORD(current, LdrDataTableEntry, in_load_order_links); const auto driver_end = reinterpret_cast<void *>( reinterpret_cast<ULONG_PTR>(module->dll_base) + module->size_of_image); if (UtilIsInBounds(pc_value, module->dll_base, driver_end)) { *base_of_image = module->dll_base; return module->dll_base; } } return nullptr; }