mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-10 09:39:36 +00:00
451 lines
14 KiB
C
451 lines
14 KiB
C
/**
|
|
* @file Loader.c
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief The functions used in loading the debugger and VMM
|
|
* @version 0.2
|
|
* @date 2023-01-15
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Initialize the hyper trace module
|
|
*
|
|
* @param RunningOnHypervisorEnvironment Whether the initialization is being done for hypervisor environment or not
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
LoaderInitHyperTrace(PDEBUGGER_INIT_HYPERTRACE_PACKET InitHyperTracePacket, BOOLEAN RunningOnHypervisorEnvironment)
|
|
{
|
|
HYPERTRACE_CALLBACKS HyperTraceCallbacks = {0};
|
|
|
|
//
|
|
// *** Fill the callbacks for using hypertrace ***
|
|
//
|
|
|
|
//
|
|
// Fill the callbacks for using hyperlog in hypertrace
|
|
// We use the callbacks directly to avoid two calls to the same function
|
|
//
|
|
HyperTraceCallbacks.LogCallbackPrepareAndSendMessageToQueueWrapper = LogCallbackPrepareAndSendMessageToQueueWrapper;
|
|
HyperTraceCallbacks.LogCallbackSendMessageToQueue = LogCallbackSendMessageToQueue;
|
|
HyperTraceCallbacks.LogCallbackSendBuffer = LogCallbackSendBuffer;
|
|
HyperTraceCallbacks.LogCallbackCheckIfBufferIsFull = LogCallbackCheckIfBufferIsFull;
|
|
|
|
//
|
|
// Fill the callbacks for using hyperhv in hypertrace
|
|
//
|
|
HyperTraceCallbacks.VmFuncVmxGetCurrentExecutionMode = VmFuncVmxGetCurrentExecutionMode;
|
|
|
|
//
|
|
// *** Legacy LBR callbacks ***
|
|
//
|
|
|
|
HyperTraceCallbacks.VmFuncCheckCpuSupportForSaveAndLoadDebugControls = VmFuncCheckCpuSupportForSaveAndLoadDebugControls;
|
|
|
|
HyperTraceCallbacks.VmFuncGetDebugctl = VmFuncGetDebugctl;
|
|
HyperTraceCallbacks.VmFuncGetDebugctlVmcallOnTargetCore = VmFuncGetDebugctlVmcallOnTargetCore;
|
|
HyperTraceCallbacks.VmFuncSetDebugctl = VmFuncSetDebugctl;
|
|
HyperTraceCallbacks.VmFuncSetDebugctlVmcallOnTargetCore = VmFuncSetDebugctlVmcallOnTargetCore;
|
|
|
|
HyperTraceCallbacks.VmFuncSetLoadDebugControls = VmFuncSetLoadDebugControls;
|
|
HyperTraceCallbacks.VmFuncSetLoadDebugControlsVmcallOnTargetCore = VmFuncSetLoadDebugControlsVmcallOnTargetCore;
|
|
HyperTraceCallbacks.VmFuncSetSaveDebugControls = VmFuncSetSaveDebugControls;
|
|
HyperTraceCallbacks.VmFuncSetSaveDebugControlsVmcallOnTargetCore = VmFuncSetSaveDebugControlsVmcallOnTargetCore;
|
|
|
|
HyperTraceCallbacks.VmFuncSetLbrSelect = VmFuncSetLbrSelect;
|
|
HyperTraceCallbacks.VmFuncSetLbrSelectVmcallOnTargetCore = VmFuncSetLbrSelectVmcallOnTargetCore;
|
|
|
|
//
|
|
// *** Architectural LBR callbacks ***
|
|
//
|
|
|
|
HyperTraceCallbacks.VmFuncCheckCpuSupportForLoadAndClearGuestIa32LbrCtlControls = VmFuncCheckCpuSupportForLoadAndClearGuestIa32LbrCtlControls;
|
|
|
|
HyperTraceCallbacks.VmFuncGetGuestIa32LbrCtl = VmFuncGetGuestIa32LbrCtl;
|
|
HyperTraceCallbacks.VmFuncGetGuestIa32LbrCtlVmcallOnTargetCore = VmFuncGetGuestIa32LbrCtlVmcallOnTargetCore;
|
|
HyperTraceCallbacks.VmFuncSetGuestIa32LbrCtl = VmFuncSetGuestIa32LbrCtl;
|
|
HyperTraceCallbacks.VmFuncSetGuestIa32LbrCtlVmcallOnTargetCore = VmFuncSetGuestIa32LbrCtlVmcallOnTargetCore;
|
|
|
|
HyperTraceCallbacks.VmFuncSetLoadGuestIa32LbrCtl = VmFuncSetLoadGuestIa32LbrCtl;
|
|
HyperTraceCallbacks.VmFuncSetLoadGuestIa32LbrCtlVmcallOnTargetCore = VmFuncSetLoadGuestIa32LbrCtlVmcallOnTargetCore;
|
|
HyperTraceCallbacks.VmFuncSetClearGuestIa32LbrCtl = VmFuncSetClearGuestIa32LbrCtl;
|
|
HyperTraceCallbacks.VmFuncSetClearGuestIa32LbrCtlVmcallOnTargetCore = VmFuncSetClearGuestIa32LbrCtlVmcallOnTargetCore;
|
|
|
|
//
|
|
// Initialize hypertrace module
|
|
//
|
|
if (HyperTraceInitCallback(&HyperTraceCallbacks, RunningOnHypervisorEnvironment))
|
|
{
|
|
LogDebugInfo("HyperDbg's hypertrace loaded successfully");
|
|
|
|
//
|
|
// Mark hypertrace as initialized
|
|
//
|
|
g_HyperTraceInitialized = TRUE;
|
|
|
|
//
|
|
// Set the kernel status to success
|
|
//
|
|
InitHyperTracePacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We won't fail the loading just because of hypertrace, so we just log the error and continue without loading hypertrace
|
|
//
|
|
LogDebugInfo("Err, HyperDbg's hypertrace was not loaded");
|
|
|
|
//
|
|
// Set the kernel status to indicate failure
|
|
//
|
|
InitHyperTracePacket->KernelStatus = DEBUGGER_ERROR_HYPERTRACE_NOT_INITIALIZED;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the hyper log module
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
LoaderInitHyperLog()
|
|
{
|
|
MESSAGE_TRACING_CALLBACKS MsgTracingCallbacks = {0};
|
|
|
|
//
|
|
// *** Fill the callbacks for the message tracer ***
|
|
//
|
|
MsgTracingCallbacks.VmxOperationCheck = VmFuncVmxGetCurrentExecutionMode;
|
|
MsgTracingCallbacks.CheckImmediateMessageSending = KdCheckImmediateMessagingMechanism;
|
|
MsgTracingCallbacks.SendImmediateMessage = KdLoggingResponsePacketToDebugger;
|
|
|
|
//
|
|
// Initialize message tracer (if not already initialized)
|
|
//
|
|
if (g_HyperLogInitialized == FALSE && LogInitialize(&MsgTracingCallbacks))
|
|
{
|
|
g_HyperLogInitialized = TRUE;
|
|
|
|
LogDebugInfo("HyperDbg's hyperlog loaded successfully");
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We use DbgPrint here because if the hyperlog is not loaded we can't use it to log the error
|
|
// so we just log the error with DbgPrint and continue without loading hyperlog
|
|
//
|
|
DbgPrint("Err, HyperDbg's hyperlog was not loaded or already loaded");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the VMM
|
|
*
|
|
* @param InitVmmPacket The packet to fill the result of the initialization
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
LoaderInitVmm(PDEBUGGER_INIT_VMM_PACKET InitVmmPacket)
|
|
{
|
|
VMM_CALLBACKS VmmCallbacks = {0};
|
|
|
|
//
|
|
// Check if KD is not already initialized, if so we cannot initialize VMM
|
|
//
|
|
if (!g_KdInitialized)
|
|
{
|
|
InitVmmPacket->KernelStatus = DEBUGGER_ERROR_VMM_CANNOT_BE_INITIALIZED_IF_DEBUGGER_IS_NOT_LOADED;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if HyperTrace is already initialized, if so we cannot initialize VMM
|
|
//
|
|
if (g_HyperTraceInitialized)
|
|
{
|
|
InitVmmPacket->KernelStatus = DEBUGGER_ERROR_VMM_CANNOT_BE_INITIALIZED_IF_HYPERTRACE_IS_LOADED;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// *** Fill the callbacks for using hyperlog in VMM ***
|
|
//
|
|
VmmCallbacks.LogCallbackPrepareAndSendMessageToQueueWrapper = LogCallbackPrepareAndSendMessageToQueueWrapper;
|
|
VmmCallbacks.LogCallbackSendMessageToQueue = LogCallbackSendMessageToQueue;
|
|
VmmCallbacks.LogCallbackSendBuffer = LogCallbackSendBuffer;
|
|
VmmCallbacks.LogCallbackCheckIfBufferIsFull = LogCallbackCheckIfBufferIsFull;
|
|
|
|
//
|
|
// Fill the HyperTrace callback(s)
|
|
//
|
|
VmmCallbacks.HyperTraceCallbackLbrIsSupported = HyperTraceLbrIsSupported;
|
|
|
|
//
|
|
// Fill the VMM callbacks
|
|
//
|
|
VmmCallbacks.VmmCallbackTriggerEvents = DebuggerTriggerEvents;
|
|
VmmCallbacks.VmmCallbackSetLastError = DebuggerSetLastError;
|
|
VmmCallbacks.VmmCallbackVmcallHandler = DebuggerVmcallHandler;
|
|
VmmCallbacks.VmmCallbackNmiBroadcastRequestHandler = KdHandleNmiBroadcastDebugBreaks;
|
|
VmmCallbacks.VmmCallbackQueryTerminateProtectedResource = TerminateQueryDebuggerResource;
|
|
VmmCallbacks.VmmCallbackRestoreEptState = UserAccessCheckForLoadedModuleDetails;
|
|
VmmCallbacks.VmmCallbackCheckUnhandledEptViolations = AttachingCheckUnhandledEptViolation;
|
|
VmmCallbacks.VmmCallbackHandleMtfCallback = KdHandleMtfCallback;
|
|
|
|
//
|
|
// Fill the debugging callbacks
|
|
//
|
|
VmmCallbacks.DebuggingCallbackHandleBreakpointException = BreakpointHandleBreakpoints;
|
|
VmmCallbacks.DebuggingCallbackHandleDebugBreakpointException = BreakpointCheckAndHandleDebugBreakpoint;
|
|
VmmCallbacks.DebuggingCallbackCheckThreadInterception = AttachingCheckThreadInterceptionWithUserDebugger;
|
|
VmmCallbacks.DebuggingCallbackTriggerOnClockAndIpiEvents = DebuggerCheckProcessOrThreadChange;
|
|
VmmCallbacks.DebuggingCallbackIgnoreHandlingMov2DebugRegs = KdQueryIgnoreHandlingMov2DebugRegs;
|
|
|
|
//
|
|
// Fill the pool manager callbacks
|
|
//
|
|
VmmCallbacks.PoolManagerCallbackRequestAllocation = PoolManagerRequestAllocation;
|
|
VmmCallbacks.PoolManagerCallbackRequestPool = PoolManagerRequestPool;
|
|
VmmCallbacks.PoolManagerCallbackFreePool = PoolManagerFreePool;
|
|
|
|
//
|
|
// Fill the interception callbacks
|
|
//
|
|
VmmCallbacks.InterceptionCallbackTriggerCr3ProcessChange = ProcessTriggerCr3ProcessChange;
|
|
|
|
//
|
|
// Initialize VMX
|
|
//
|
|
if (VmFuncInitVmm(&VmmCallbacks))
|
|
{
|
|
LogDebugInfo("HyperDbg's hypervisor loaded successfully");
|
|
|
|
//
|
|
// Initialize VMM opeartions (event related state from the debugger)
|
|
//
|
|
if (!DebuggerInitializeVmmOperations())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// VMM module initialized
|
|
//
|
|
g_VmmInitialized = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
LogError("Err, HyperDbg's hypervisor was not loaded");
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the debugger
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
LoaderInitKd()
|
|
{
|
|
//
|
|
// If the debugger is already initialized, we don't need to initialize it again
|
|
// and simply return true
|
|
//
|
|
if (g_KdInitialized)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The debugger is not initialized, so we try to initialize it
|
|
//
|
|
if (DebuggerInitialize())
|
|
{
|
|
LogDebugInfo("HyperDbg's debugger loaded successfully");
|
|
|
|
//
|
|
// KD module initialized
|
|
//
|
|
g_KdInitialized = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LogError("Err, HyperDbg's debugger was not loaded");
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the debugger and the vmm
|
|
*
|
|
* @param InitVmmPacket The packet to fill the result of the initialization
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
LoaderInitDebuggerAndVmm(PDEBUGGER_INIT_VMM_PACKET InitVmmPacket)
|
|
{
|
|
//
|
|
// First we need to initialize the debugger
|
|
// because the VMM relies on the debugger for some of its functionalities,
|
|
// so if we cannot initialize the debugger we cannot initialize the VMM
|
|
//
|
|
if (!LoaderInitKd())
|
|
{
|
|
//
|
|
// Unable to initialize the debugger, so we cannot initialize the VMM, and we return false
|
|
//
|
|
InitVmmPacket->KernelStatus = DEBUGGER_ERROR_CANNOT_INITIALIZE_DEBUGGER;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now we can initialize the VMM
|
|
//
|
|
if (!LoaderInitVmm(InitVmmPacket))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set the kernel status to success
|
|
//
|
|
InitVmmPacket->KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize the hyper trace module
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
LoaderUninitHyperTrace()
|
|
{
|
|
//
|
|
// Mark hypertrace as uninitialized before uninitializing it to avoid any potential reentrancy issues during the uninitialization process
|
|
//
|
|
g_HyperTraceInitialized = FALSE;
|
|
|
|
//
|
|
// Uninitialize the hypertrace
|
|
//
|
|
HyperTraceUninit();
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize the VMM
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
LoaderUninitVmm()
|
|
{
|
|
//
|
|
// Mark VMM as uninitialized before uninitializing it to avoid any potential reentrancy issues during the uninitialization process
|
|
//
|
|
g_VmmInitialized = FALSE;
|
|
|
|
//
|
|
// Uninitialize the HyperTrace (if it was initialized)
|
|
//
|
|
// If the trace module is currently loaded, it must be unloaded before the VMM module can be unloaded
|
|
// HyperTrace can operate both with and without the VMM module. When loaded after the VMM module, HyperTrace can make
|
|
// use of hypervisor-specific features. Otherwise, it will operate normally, but those features will not be available
|
|
// The trace module will be unloaded automatically and may be reloaded later if needed
|
|
//
|
|
// Note: The user mode should automatically request to unload the 'trace' module if it is already loaded
|
|
// however, here we also unload it just in case if this function is directly called or the user mode
|
|
// code did not unload it
|
|
//
|
|
LoaderUninitHyperTrace();
|
|
|
|
//
|
|
// First remove all VMM related state from the debugger
|
|
//
|
|
DebuggerUninitializeVmmOperations();
|
|
|
|
//
|
|
// Terminate VMM and its sub-mechanisms
|
|
//
|
|
VmFuncUninitVmm();
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize the debugger
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
LoaderUninitKd()
|
|
{
|
|
//
|
|
// Mark KD as uninitialized before uninitializing it to avoid any potential reentrancy issues during the uninitialization process
|
|
//
|
|
g_KdInitialized = FALSE;
|
|
|
|
//
|
|
// Uninitialize the debugger and its sub-mechanisms
|
|
//
|
|
DebuggerUninitialize();
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize the VMM and the debugger
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
LoaderUninitVmmAndDebugger()
|
|
{
|
|
//
|
|
// Uninitialize the VMM first because it relies on the debugger for some
|
|
//
|
|
LoaderUninitVmm();
|
|
|
|
//
|
|
// Uninitialize the debugger
|
|
//
|
|
LoaderUninitKd();
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize the log tracer
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
LoaderUninitLogTracer()
|
|
{
|
|
#if !UseDbgPrintInsteadOfUsermodeMessageTracking
|
|
|
|
LogDebugInfo("Unloading hyperlog...\n");
|
|
|
|
//
|
|
// Uinitialize log buffer if it was initialized
|
|
//
|
|
if (g_HyperLogInitialized)
|
|
{
|
|
g_HyperLogInitialized = FALSE;
|
|
LogUnInitialize();
|
|
}
|
|
#endif
|
|
}
|