From 0e5ff45c30f2b38c41acaa6e03c3d23bd0c2cfb5 Mon Sep 17 00:00:00 2001 From: sina Date: Tue, 8 Jul 2025 00:10:43 +0200 Subject: [PATCH] set user mode execute bit for different core EPTPs --- .../hyperhv/code/hooks/ept-hook/EptHook.c | 6 +-- .../hyperhv/code/hooks/ept-hook/ExecTrap.c | 43 +++++++++++++++++++ .../code/hooks/ept-hook/ModeBasedExecHook.c | 28 ++++++++++++ hyperdbg/hyperhv/code/vmm/ept/Ept.c | 33 ++++++++++++-- hyperdbg/hyperhv/header/vmm/ept/Ept.h | 1 - .../hyperkd/code/debugger/user-level/Ud.c | 2 +- 6 files changed, 104 insertions(+), 9 deletions(-) diff --git a/hyperdbg/hyperhv/code/hooks/ept-hook/EptHook.c b/hyperdbg/hyperhv/code/hooks/ept-hook/EptHook.c index 4e8585a6..00031109 100644 --- a/hyperdbg/hyperhv/code/hooks/ept-hook/EptHook.c +++ b/hyperdbg/hyperhv/code/hooks/ept-hook/EptHook.c @@ -2570,7 +2570,7 @@ EptHookModifyInstructionFetchState(VIRTUAL_MACHINE_STATE * VCpu, PVOID PmlEntry = NULL; BOOLEAN IsLargePage = FALSE; - PmlEntry = EptGetPml1OrPml2Entry(g_EptState->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); + PmlEntry = EptGetPml1OrPml2Entry(VCpu->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); if (PmlEntry) { @@ -2628,7 +2628,7 @@ EptHookModifyPageReadState(VIRTUAL_MACHINE_STATE * VCpu, PVOID PmlEntry = NULL; BOOLEAN IsLargePage = FALSE; - PmlEntry = EptGetPml1OrPml2Entry(g_EptState->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); + PmlEntry = EptGetPml1OrPml2Entry(VCpu->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); if (PmlEntry) { @@ -2686,7 +2686,7 @@ EptHookModifyPageWriteState(VIRTUAL_MACHINE_STATE * VCpu, PVOID PmlEntry = NULL; BOOLEAN IsLargePage = FALSE; - PmlEntry = EptGetPml1OrPml2Entry(g_EptState->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); + PmlEntry = EptGetPml1OrPml2Entry(VCpu->EptPageTable, (SIZE_T)PhysicalAddress, &IsLargePage); if (PmlEntry) { diff --git a/hyperdbg/hyperhv/code/hooks/ept-hook/ExecTrap.c b/hyperdbg/hyperhv/code/hooks/ept-hook/ExecTrap.c index 9b501e2a..435a73c9 100644 --- a/hyperdbg/hyperhv/code/hooks/ept-hook/ExecTrap.c +++ b/hyperdbg/hyperhv/code/hooks/ept-hook/ExecTrap.c @@ -572,6 +572,47 @@ ExecTrapChangeToKernelDisabledMbecEptp(VIRTUAL_MACHINE_STATE * VCpu) VCpu->NotNormalEptp = TRUE; } +/** + * @brief change to normal MBEC EPTP + * @param VCpu The virtual processor's state + * + * @return VOID + */ +VOID +ExecTrapChangeToNormalMbecEptp(VIRTUAL_MACHINE_STATE * VCpu) +{ + // + // From Intel Manual: + // [Bit 2] If the "mode-based execute control for EPT" VM - execution control is 0, execute access; + // indicates whether instruction fetches are allowed from the 2-MByte page controlled by this entry. + // If that control is 1, execute access for supervisor-mode linear addresses; indicates whether instruction + // fetches are allowed from supervisor - mode linear addresses in the 2 - MByte page controlled by this entry + // + + // + // Set execute access for PML4s + // + // for (size_t i = 0; i < VMM_EPT_PML4E_COUNT; i++) + // { + VCpu->EptPageTable->PML4[0].UserModeExecute = TRUE; + + // + // We only set the top-level PML4 for intercepting kernel-mode execution + // + VCpu->EptPageTable->PML4[0].ExecuteAccess = TRUE; + // } + + // + // Invalidate the EPT cache + // + EptInveptSingleContext(VCpu->EptPointer.AsUInt); + + // + // It's not on normal EPTP + // + VCpu->NotNormalEptp = FALSE; +} + /** * @brief Restore the execution of the trap to adjusted trap state * @param VCpu The virtual processor's state @@ -711,6 +752,7 @@ ExecTrapApplyMbecConfiguratinFromKernelSide(VIRTUAL_MACHINE_STATE * VCpu) // // Enable MBEC to detect execution in user-mode // + ExecTrapChangeToNormalMbecEptp(VCpu); HvSetModeBasedExecutionEnableFlag(TRUE); VCpu->MbecEnabled = TRUE; @@ -724,6 +766,7 @@ ExecTrapApplyMbecConfiguratinFromKernelSide(VIRTUAL_MACHINE_STATE * VCpu) // // In case, the process is changed, we've disable the MBEC // + ExecTrapChangeToNormalMbecEptp(VCpu); HvSetModeBasedExecutionEnableFlag(FALSE); VCpu->MbecEnabled = FALSE; } diff --git a/hyperdbg/hyperhv/code/hooks/ept-hook/ModeBasedExecHook.c b/hyperdbg/hyperhv/code/hooks/ept-hook/ModeBasedExecHook.c index 179c04dd..b3265539 100644 --- a/hyperdbg/hyperhv/code/hooks/ept-hook/ModeBasedExecHook.c +++ b/hyperdbg/hyperhv/code/hooks/ept-hook/ModeBasedExecHook.c @@ -118,6 +118,8 @@ ModeBasedExecHookDisableKernelModeExecution(PVMM_EPT_PAGE_TABLE EptTable) BOOLEAN ModeBasedExecHookEnableUsermodeExecution(PVMM_EPT_PAGE_TABLE EptTable) { + EPT_PML1_ENTRY Pml1Entries[VMM_EPT_PML1E_COUNT]; + // // Set execute access for PML4s // @@ -145,6 +147,32 @@ ModeBasedExecHookEnableUsermodeExecution(PVMM_EPT_PAGE_TABLE EptTable) for (size_t j = 0; j < VMM_EPT_PML2E_COUNT; j++) { EptTable->PML2[i][j].UserModeExecute = TRUE; + + // + // If the PML2 entry is not a large page, we should set execute access for PML1s + // It usually happens when the PML2 entry is not a large page and is previously + // used for an EPT hook, so, it has PML1 entries + // + if (!EptTable->PML2[i][j].LargePage) + { + // + // Shift to the left to get the PFN + // + MemoryMapperReadMemorySafeByPhysicalAddress(EptTable->PML2[i][j].PageFrameNumber << 12, (UINT64)Pml1Entries, PAGE_SIZE); + + // + // Set execute access for PML1s + // + for (size_t k = 0; k < VMM_EPT_PML1E_COUNT; k++) + { + Pml1Entries[k].UserModeExecute = TRUE; + } + + // + // Write back the PML1 entries to the EPT page table + // + MemoryMapperWriteMemorySafeByPhysicalAddress(EptTable->PML2[i][j].PageFrameNumber << 12, (UINT64)Pml1Entries, PAGE_SIZE); + } } } diff --git a/hyperdbg/hyperhv/code/vmm/ept/Ept.c b/hyperdbg/hyperhv/code/vmm/ept/Ept.c index 3100f6af..7114302b 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Ept.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Ept.c @@ -520,6 +520,18 @@ EptSplitLargePage(PVMM_EPT_PAGE_TABLE EptPageTable, EntryTemplate.WriteAccess = 1; EntryTemplate.ExecuteAccess = 1; + // + // Set the UserModeExecute bit based on the global state of MBEC + // + if (g_ModeBasedExecutionControlState) + { + EntryTemplate.UserModeExecute = 1; + } + else + { + EntryTemplate.UserModeExecute = 0; + } + // // copy other bits from target entry // @@ -547,10 +559,23 @@ EptSplitLargePage(PVMM_EPT_PAGE_TABLE EptPageTable, // // Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries // - NewPointer.AsUInt = 0; - NewPointer.WriteAccess = 1; - NewPointer.ReadAccess = 1; - NewPointer.ExecuteAccess = 1; + NewPointer.AsUInt = 0; + NewPointer.WriteAccess = 1; + NewPointer.ReadAccess = 1; + NewPointer.ExecuteAccess = 1; + + // + // Set the UserModeExecute bit based on the global state of MBEC + // + if (g_ModeBasedExecutionControlState) + { + NewPointer.UserModeExecute = 1; + } + else + { + NewPointer.UserModeExecute = 0; + } + NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&NewSplit->PML1[0]) / PAGE_SIZE; // diff --git a/hyperdbg/hyperhv/header/vmm/ept/Ept.h b/hyperdbg/hyperhv/header/vmm/ept/Ept.h index 3a38f82d..1ae84fbf 100644 --- a/hyperdbg/hyperhv/header/vmm/ept/Ept.h +++ b/hyperdbg/hyperhv/header/vmm/ept/Ept.h @@ -118,7 +118,6 @@ typedef struct _EPT_STATE LIST_ENTRY HookedPagesList; // A list of the details about hooked pages MTRR_RANGE_DESCRIPTOR MemoryRanges[NUM_MTRR_ENTRIES]; // Physical memory ranges described by the BIOS in the MTRRs. Used to build the EPT identity mapping. UINT32 NumberOfEnabledMemoryRanges; // Number of memory ranges specified in MemoryRanges - PVMM_EPT_PAGE_TABLE EptPageTable; // Page table entries for EPT operation UINT8 DefaultMemoryType; } EPT_STATE, *PEPT_STATE; diff --git a/hyperdbg/hyperkd/code/debugger/user-level/Ud.c b/hyperdbg/hyperkd/code/debugger/user-level/Ud.c index e5f1f140..a32dd7f8 100644 --- a/hyperdbg/hyperkd/code/debugger/user-level/Ud.c +++ b/hyperdbg/hyperkd/code/debugger/user-level/Ud.c @@ -148,7 +148,7 @@ UdHandleInstantBreak(PROCESSOR_DEBUGGING_STATE * DbgState, // Since the adding it to the watching list will take effect from the next // CR3 vm-exit, we should change the state of the core to prevent further execution // - // ConfigureExecTrapApplyMbecConfiguratinFromKernelSide(DbgState->CoreId); + ConfigureExecTrapApplyMbecConfiguratinFromKernelSide(DbgState->CoreId); // // Handling state through the user-mode debugger