HyperDbg/hyperdbg/hyperhv/code/hooks/ept-hook/ModeBasedExecHook.c

286 lines
6.8 KiB
C

/**
* @file ModeBasedExecHook.c
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Implementation of hooks based on Mode-based execution
* @details
*
* @version 0.2
* @date 2023-03-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief Adjust (unset) user-mode execution bit of target page-table
* @details should be called from vmx non-root mode
* @param EptTable
*
* @return BOOLEAN
*/
BOOLEAN
ModeBasedExecHookDisableUserModeExecution(PVMM_EPT_PAGE_TABLE EptTable)
{
//
// Set execute access for PML4s
//
for (SIZE_T i = 0; i < VMM_EPT_PML4E_COUNT; i++)
{
//
// We only set the top-level PML4 for intercepting user-mode execution
//
EptTable->PML4[i].UserModeExecute = FALSE;
}
//
// Set execute access for PML3s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
EptTable->PML3[i].UserModeExecute = TRUE;
}
//
// Set execute access for PML2s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
for (SIZE_T j = 0; j < VMM_EPT_PML2E_COUNT; j++)
{
EptTable->PML2[i][j].UserModeExecute = TRUE;
}
}
return TRUE;
}
/**
* @brief Adjust (unset) kernel-mode execution bit of target page-table
* but allow user-mode execution
* @details should be called from vmx non-root mode
* @param EptTable
*
* @return BOOLEAN
*/
BOOLEAN
ModeBasedExecHookDisableKernelModeExecution(PVMM_EPT_PAGE_TABLE EptTable)
{
//
// 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++)
{
EptTable->PML4[i].UserModeExecute = TRUE;
//
// We only set the top-level PML4 for intercepting kernel-mode execution
//
EptTable->PML4[i].ExecuteAccess = FALSE;
}
//
// Set execute access for PML3s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
EptTable->PML3[i].UserModeExecute = TRUE;
}
//
// Set execute access for PML2s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
for (SIZE_T j = 0; j < VMM_EPT_PML2E_COUNT; j++)
{
EptTable->PML2[i][j].UserModeExecute = TRUE;
}
}
return TRUE;
}
/**
* @brief Enables user-mode execution bit of target page-table
* @param EptTable
*
* @return BOOLEAN
*/
BOOLEAN
ModeBasedExecHookEnableUsermodeExecution(PVMM_EPT_PAGE_TABLE EptTable)
{
EPT_PML1_ENTRY Pml1Entries[VMM_EPT_PML1E_COUNT];
//
// Set execute access for PML4s
//
for (SIZE_T i = 0; i < VMM_EPT_PML4E_COUNT; i++)
{
//
// We only set the top-level PML4 for intercepting user-mode execution
//
EptTable->PML4[i].UserModeExecute = TRUE;
}
//
// Set execute access for PML3s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
EptTable->PML3[i].UserModeExecute = TRUE;
}
//
// Set execute access for PML2s
//
for (SIZE_T i = 0; i < VMM_EPT_PML3E_COUNT; i++)
{
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);
}
}
}
return TRUE;
}
/**
* @brief Enable/disable MBEC
* @param VCpu The virtual processor's state
*
* @return VOID
*/
VOID
ModeBasedExecHookEnableOrDisable(VIRTUAL_MACHINE_STATE * VCpu, UINT32 State)
{
if (State == 0x0)
{
HvSetModeBasedExecutionEnableFlag(FALSE);
//
// MBEC is not enabled anymore!
//
VCpu->MbecEnabled = FALSE;
}
else
{
HvSetModeBasedExecutionEnableFlag(TRUE);
}
}
/**
* @brief Initialize the needed structure for hooking mode execution
* @details should be called from vmx non-root mode
*
* @return BOOLEAN
*/
BOOLEAN
ModeBasedExecHookInitialize()
{
ULONG ProcessorsCount;
//
// Get number of processors
//
ProcessorsCount = KeQueryActiveProcessorCount(0);
//
// Check if MBEC supported by this processors
//
if (!g_CompatibilityCheck.ModeBasedExecutionSupport)
{
return FALSE;
}
//
// Check if execute-only feature is supported on this processor or not
//
if (!g_CompatibilityCheck.ExecuteOnlySupport)
{
return FALSE;
}
//
// Check if it's already initialized or not
//
if (g_ModeBasedExecutionControlState)
{
return FALSE;
}
//
// Enable EPT user-mode execution bit for the target EPTP
//
for (SIZE_T i = 0; i < ProcessorsCount; i++)
{
ModeBasedExecHookEnableUsermodeExecution(g_GuestState[i].EptPageTable);
}
//
// Invalidate ALL context on all cores (not necessary here because we will change
// it later but let's respect memory primitives)
//
BroadcastNotifyAllToInvalidateEptAllCores();
//
// Indicate that MBEC is initialized
//
g_ModeBasedExecutionControlState = TRUE;
return TRUE;
}
/**
* @brief Uinitialize the needed structure for hooking mode execution
* @details should be called from vmx non-root mode
*
* @return VOID
*/
VOID
ModeBasedExecHookUninitialize()
{
//
// Broadcast to disable MBEC on all cores
//
BroadcasDisableMbecOnAllProcessors();
//
// Indicate that MBEC is disabled
//
g_ModeBasedExecutionControlState = FALSE;
}