HyperDbg/hyperdbg/hyperkd/code/debugger/commands/BreakpointCommands.c

1284 lines
40 KiB
C
Raw Permalink Normal View History

2021-03-12 22:31:55 +03:30
/**
* @file BreakpointCommands.c
2022-01-18 22:38:56 +03:30
* @author Sina Karvandi (sina@hyperdbg.org)
2021-03-12 22:31:55 +03:30
* @brief Routines for breakpoint commands
* @details
2021-03-12 22:31:55 +03:30
* @version 0.1
* @date 2021-03-12
*
2021-03-12 22:31:55 +03:30
* @copyright This project is released under the GNU Public License v3.
*
2021-03-12 22:31:55 +03:30
*/
#include "pch.h"
2021-03-12 22:31:55 +03:30
2023-07-14 15:39:02 +09:00
/**
* @brief Check and perform actions on RFLAGS.TF
2023-07-21 21:51:56 +09:00
* @param ProcessId
* @param ThreadId
* @param TrapSetByDebugger
2023-07-14 15:39:02 +09:00
*
* @return BOOLEAN Shows whether the #DB should be handled by the debugger or re-injected
2023-07-14 15:39:02 +09:00
*/
BOOLEAN
BreakpointCheckAndPerformActionsOnTrapFlags(UINT32 ProcessId, UINT32 ThreadId, BOOLEAN * TrapSetByDebugger)
2023-07-14 15:39:02 +09:00
{
UINT32 Index;
DEBUGGER_PROCESS_THREAD_INFORMATION ProcThrdInfo = {0};
BOOLEAN Result;
BOOLEAN ResultToReturn;
RFLAGS Rflags = {0};
2023-07-21 21:51:56 +09:00
//
// Read the RFLAGS
//
Rflags.AsUInt = VmFuncGetRflags();
2023-07-14 15:39:02 +09:00
//
// Form the process id and thread id into a 64-bit value
2023-07-14 15:39:02 +09:00
//
ProcThrdInfo.Fields.ProcessId = ProcessId;
ProcThrdInfo.Fields.ThreadId = ThreadId;
2023-07-14 15:39:02 +09:00
//
// Make sure, nobody is in the middle of modifying the list
//
SpinlockLock(&BreakpointCommandTrapListLock);
2023-07-14 15:39:02 +09:00
//
2023-07-21 21:51:56 +09:00
// *** Search the list of processes/threads for the current process's trap flag state ***
//
2024-03-01 21:02:52 +09:00
Result = BinarySearchPerformSearchItem((UINT64 *)&g_TrapFlagState.ThreadInformation[0],
2023-08-21 20:06:34 +09:00
g_TrapFlagState.NumberOfItems,
&Index,
ProcThrdInfo.AsUInt);
2023-07-21 21:51:56 +09:00
//
// Indicate whether the trap flag is set by the debugger or not
//
*TrapSetByDebugger = Result;
2023-07-14 15:39:02 +09:00
//
// We check the trap flag after the results because we might set the trap flag
// for the thread but the thread might run 'popfq' removing our trap flag
// so, we both check whether thread is expected to have trap flag, if not
// we check whether the trap flag is available or not
2023-07-21 21:51:56 +09:00
//
if (!Result && !Rflags.TrapFlag)
2023-07-14 15:39:02 +09:00
{
//
// It's not related to a TRAP FLAG, and we didn't previously set trap flag for this thread
// So, probably other events like setting hardware debug breakpoints caused this #DB
// which means that it should be handled by the debugger
2023-07-14 15:39:02 +09:00
//
ResultToReturn = TRUE;
goto Return;
2023-07-14 15:39:02 +09:00
}
2023-08-03 18:46:51 +09:00
else if (!Result && Rflags.TrapFlag)
2023-07-21 21:51:56 +09:00
{
2023-08-03 18:46:51 +09:00
//
// As it's not set by the debugger (not found in our list), it means the program or
2023-11-15 18:27:54 +09:00
// a debugger already set the trap flag, we'll return FALSE
2023-08-03 18:46:51 +09:00
//
// LogInfo("Caution: The process (pid:%x, tid:%x, name:%s) is utilizing a trap flag, "
// "which was not previously adjusted by HyperDbg. This occurrence could indicate "
// "the employment of an anti-debugging technique by the process or the involvement "
// "of another debugger. By default, HyperDbg automatically manages these #DB events "
// "and halt the debugger; however, if you wish to redirect them to the debugger, "
// "you can utilize 'test trap off'. Alternatively, you can use the transparent-mode "
// "to mitigate these situations",
// PsGetCurrentProcessId(),
// PsGetCurrentThreadId(),
// CommonGetProcessNameFromProcessControlBlock(PsGetCurrentProcess()));
//
// Returning false means that it should be re-injected into the debuggee
//
ResultToReturn = FALSE;
goto Return;
2023-07-21 21:51:56 +09:00
}
2023-08-03 18:46:51 +09:00
else
{
//
// *** being here means the thread is found in the list of threads that we set TRAP FLAG on it ***
//
2023-07-14 15:39:02 +09:00
2023-08-03 18:46:51 +09:00
//
// Uset or set the TRAP flag
//
VmFuncSetRflagTrapFlag(FALSE);
2023-08-03 18:46:51 +09:00
//
// Remove the thread/process from the list
// We're sure the Result is TRUE
//
2024-03-01 21:02:52 +09:00
InsertionSortDeleteItem((UINT64 *)&g_TrapFlagState.ThreadInformation[0],
2023-08-21 20:06:34 +09:00
&g_TrapFlagState.NumberOfItems,
Index);
2023-08-03 18:46:51 +09:00
//
// Handled #DB by debugger
//
ResultToReturn = TRUE;
goto Return;
}
Return:
//
// Unlock the list modification lock
//
SpinlockUnlock(&BreakpointCommandTrapListLock);
2023-07-14 15:39:02 +09:00
//
2023-07-21 21:51:56 +09:00
// By default, #DBs are managed by HyperDbg
2023-07-14 15:39:02 +09:00
//
return ResultToReturn;
2023-07-14 15:39:02 +09:00
}
/**
* @brief Trigger callback for breakpoint hit
*
* @param DbgState The state of the debugger on the current core
* @param ProcessId
* @param ThreadId
*
* @return BOOLEAN If true, it won't halt the debugger, but if false will halt the debugger
*/
BOOLEAN
BreakpointTriggerCallbacks(PROCESSOR_DEBUGGING_STATE * DbgState, UINT32 ProcessId, UINT32 ThreadId)
{
2024-03-01 18:11:24 +09:00
UNREFERENCED_PARAMETER(DbgState);
UNREFERENCED_PARAMETER(ProcessId);
UNREFERENCED_PARAMETER(ThreadId);
//
// Add the process/thread to the watching list
//
2023-09-14 21:17:44 +09:00
// LogInfo("Adding to watch list: Process Id: %x, Thread Id: %x", ProcessId, ThreadId);
//
// By default return FALSE to set handling the breakpoint to the user to the debugger
//
return FALSE;
}
2023-07-14 15:39:02 +09:00
/**
* @brief This function makes sure to unset the RFLAGS.TF on next trigger of #DB
2023-07-21 21:51:56 +09:00
* on the target process/thread
* @param ProcessId
* @param ThreadId
2023-07-14 15:39:02 +09:00
*
* @return BOOLEAN
*/
BOOLEAN
2023-07-21 21:51:56 +09:00
BreakpointRestoreTheTrapFlagOnceTriggered(UINT32 ProcessId, UINT32 ThreadId)
2023-07-14 15:39:02 +09:00
{
UINT32 Index;
BOOLEAN Result;
BOOLEAN SuccessfullyStored;
DEBUGGER_PROCESS_THREAD_INFORMATION ProcThrdInfo = {0};
//
// Form the process id and thread id into a 64-bit value
//
ProcThrdInfo.Fields.ProcessId = ProcessId;
ProcThrdInfo.Fields.ThreadId = ThreadId;
2023-07-21 21:51:56 +09:00
//
// Make sure, nobody is in the middle of modifying the list
//
SpinlockLock(&BreakpointCommandTrapListLock);
2023-07-21 21:51:56 +09:00
//
// *** Search the list of processes/threads for the current process's trap flag state ***
//
2024-03-01 21:02:52 +09:00
Result = BinarySearchPerformSearchItem((UINT64 *)&g_TrapFlagState.ThreadInformation[0],
2023-08-21 20:06:34 +09:00
g_TrapFlagState.NumberOfItems,
&Index,
ProcThrdInfo.AsUInt);
if (Result)
{
//
// It means that we already find this entry in the stored list
// so, just imply that the addition was successful (no need for extra addition)
//
SuccessfullyStored = TRUE;
goto Return;
}
2023-08-03 18:46:51 +09:00
else
{
//
// Insert the thread into the list as the item is not already present
//
2024-03-01 21:02:52 +09:00
SuccessfullyStored = InsertionSortInsertItem((UINT64 *)&g_TrapFlagState.ThreadInformation[0],
2023-08-21 20:06:34 +09:00
&g_TrapFlagState.NumberOfItems,
MAXIMUM_NUMBER_OF_THREAD_INFORMATION_FOR_TRAPS,
2025-04-30 20:52:04 +02:00
&Index, // not used
ProcThrdInfo.AsUInt);
2023-08-03 18:46:51 +09:00
goto Return;
}
2023-07-21 21:51:56 +09:00
Return:
2023-07-21 21:51:56 +09:00
//
// Unlock the list modification lock
//
SpinlockUnlock(&BreakpointCommandTrapListLock);
return SuccessfullyStored;
2023-07-14 15:39:02 +09:00
}
/**
* @brief Check and handle debug breakpoint exceptions
*
* @param CoreId
*
* @return BOOLEAN
*/
BOOLEAN
BreakpointCheckAndHandleDebugBreakpoint(UINT32 CoreId)
{
BOOLEAN TrapSetByDebugger;
2023-11-15 18:27:54 +09:00
PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId];
BOOLEAN HandledByDebuggerRoutines = TRUE;
2023-07-14 15:39:02 +09:00
//
2023-11-15 18:27:54 +09:00
// *** Check whether anything should be changed with trap-flags
// and also it indicates whether the debugger itself set this trap
// flag or it's not supposed to be set by the debugger ***
2023-07-14 15:39:02 +09:00
//
2024-03-01 23:02:03 +09:00
if (BreakpointCheckAndPerformActionsOnTrapFlags(HANDLE_TO_UINT32(PsGetCurrentProcessId()),
HANDLE_TO_UINT32(PsGetCurrentThreadId()),
2023-11-15 18:27:54 +09:00
&TrapSetByDebugger))
2023-07-21 21:51:56 +09:00
{
2023-11-15 18:27:54 +09:00
if (DbgState->ThreadOrProcessTracingDetails.DebugRegisterInterceptionState)
{
//
// This check was to show whether it is because of thread change detection or not
//
2023-07-21 21:51:56 +09:00
2023-11-15 18:27:54 +09:00
// This way of handling has a problem, if the user set to change
// the thread and instead of using 'g', it pressed the 'p' to
// set or a trap happens somewhere then will be ignored
// it because we don't know the origin of this debug breakpoint
// and it only happens on '.thread2' command, the correct way
// to handle it is to find the exact hw debug register that caused
// this vm-exit, but it's a really rare case, so we left it without
// handling this case
//
ThreadHandleThreadChange(DbgState);
}
else if (g_UserDebuggerState == TRUE &&
(g_IsWaitingForUserModeProcessEntryToBeCalled || g_IsWaitingForReturnAndRunFromPageFault))
{
//
// Handle for user-mode attaching mechanism
//
AttachingHandleEntrypointInterception(DbgState);
}
else if (g_KernelDebuggerState == TRUE)
{
//
// Here we added the handler for the kernel because we want
// stepping routines to work, even if the debugger masks the
// traps by using 'test trap off', so stepping still works
//
//
// Handle debug events (breakpoint, traps, hardware debug register when kernel
// debugger is attached)
//
KdHandleDebugEventsWhenKernelDebuggerIsAttached(DbgState, TrapSetByDebugger);
}
else if (g_UserDebuggerState == TRUE &&
UdHandleDebugEventsWhenUserDebuggerIsAttached(DbgState, TrapSetByDebugger))
2023-11-15 18:27:54 +09:00
{
//
// if the above function returns true, no need for further action
// it's handled in the user debugger
//
}
else
{
//
// Here it means that the trap is supposed to be handled by
// HyperDbg but, we couldn't find any routines that gonna
// handle it (it's probably an error)
//
HandledByDebuggerRoutines = FALSE;
LogError("Err, trap is supposed to be handled by the debugger, but none of routines handled it");
}
}
2023-11-15 18:27:54 +09:00
else
{
//
2023-11-15 18:27:54 +09:00
// *** it's not supposed to be handled by the debugger routines, the guest
// or the target debuggee throws a debug break (#DB) ***
//
2023-11-15 18:27:54 +09:00
//
2023-11-15 18:27:54 +09:00
// It means that it's not handled by the debugger routines
// By default HyperDbg intercepts all #DBs and break the debugger if
// it's attached to the debugger, otherwise injects to the guest VM
//
2023-11-15 18:27:54 +09:00
if (g_InterceptDebugBreaks)
{
//
// The user explicitly told the debugger not to intercept any
// traps (e.g., by using 'test trap off')
//
HandledByDebuggerRoutines = FALSE;
}
else if (g_KernelDebuggerState == TRUE)
{
//
// Handle debug events (breakpoint, traps, hardware debug register when kernel
// debugger is attached)
//
KdHandleDebugEventsWhenKernelDebuggerIsAttached(DbgState, TrapSetByDebugger);
}
else if (g_UserDebuggerState == TRUE &&
UdHandleDebugEventsWhenUserDebuggerIsAttached(DbgState, TrapSetByDebugger))
2023-11-15 18:27:54 +09:00
{
//
// if the above function returns true, no need for further action
// it's handled in the user debugger
//
}
else
{
//
// Inject to back to the guest as it's not either handled by the kernel debugger
// routines or the user debugger
//
HandledByDebuggerRoutines = FALSE;
}
}
2023-11-15 18:27:54 +09:00
return HandledByDebuggerRoutines;
}
/**
* @brief clears the 0xcc and removes the breakpoint
* @detail this function won't remove the descriptor from the list
* @param BreakpointDescriptor
*
* @return BOOLEAN
*/
BOOLEAN
BreakpointClear(PDEBUGGEE_BP_DESCRIPTOR BreakpointDescriptor)
{
2024-03-01 22:27:09 +09:00
BYTE TargetMem = NULL_ZERO;
//
// Check if address is safe (only one byte for 0xcc)
//
if (!CheckAccessValidityAndSafety(BreakpointDescriptor->Address, sizeof(BYTE)))
{
//
// Double check if we can access it by physical address
//
2024-03-01 15:59:58 +09:00
MemoryMapperReadMemorySafeByPhysicalAddress(BreakpointDescriptor->PhysAddress,
(UINT64)&TargetMem,
sizeof(BYTE));
if (TargetMem != 0xcc)
{
return FALSE;
}
}
//
// Apply the previous byte
//
MemoryMapperWriteMemorySafeByPhysicalAddress(BreakpointDescriptor->PhysAddress,
2024-03-01 15:59:58 +09:00
(UINT64)&BreakpointDescriptor->PreviousByte,
sizeof(BYTE));
//
// Set breakpoint to disabled
//
BreakpointDescriptor->Enabled = FALSE;
BreakpointDescriptor->AvoidReApplyBreakpoint = TRUE;
return TRUE;
}
/**
* @brief Clears the breakpoint and remove the entry from the breakpoint list
* @param
*
* @return VOID
*/
VOID
BreakpointClearAndDeallocateMemory(PDEBUGGEE_BP_DESCRIPTOR BreakpointDesc)
{
//
// Clear the breakpoint
//
BreakpointClear(BreakpointDesc);
//
// Remove breakpoint from the list of breakpoints
//
RemoveEntryList(&BreakpointDesc->BreakpointsList);
//
// Uninitialize the breakpoint descriptor (safely)
//
2024-03-01 18:11:24 +09:00
PoolManagerFreePool((UINT64)BreakpointDesc);
}
2023-01-08 03:59:10 +09:00
/**
* @brief Check and reapply breakpoint
*
2026-06-09 01:37:48 +02:00
* @param DbgState The state of the debugger on the current core
2023-01-08 03:59:10 +09:00
*
* @return BOOLEAN
*/
BOOLEAN
2026-06-09 01:37:48 +02:00
BreakpointCheckAndHandleReApplyingBreakpoint(PROCESSOR_DEBUGGING_STATE * DbgState)
2023-01-08 03:59:10 +09:00
{
2026-06-09 01:37:48 +02:00
BOOLEAN Result = FALSE;
2023-01-08 03:59:10 +09:00
if (DbgState->SoftwareBreakpointState != NULL)
{
BYTE BreakpointByte = 0xcc;
//
// MTF is handled
//
Result = TRUE;
//
// Restore previous breakpoint byte
//
MemoryMapperWriteMemorySafeByPhysicalAddress(
DbgState->SoftwareBreakpointState->PhysAddress,
2024-03-01 15:59:58 +09:00
(UINT64)&BreakpointByte,
2023-01-08 03:59:10 +09:00
sizeof(BYTE));
DbgState->SoftwareBreakpointState = NULL;
}
return Result;
}
2021-03-13 20:46:37 +03:30
/**
* @brief Check if the breakpoint vm-exit relates to 'bp' command or not
*
* @param DbgState The state of the debugger on the current core
* @param GuestRip
2022-12-05 15:31:56 +09:00
* @param Reason
2023-01-08 01:49:16 +09:00
* @param ChangeMtfState
*
2021-03-13 20:46:37 +03:30
* @return BOOLEAN
*/
BOOLEAN
BreakpointCheckAndHandleDebuggerDefinedBreakpoints(PROCESSOR_DEBUGGING_STATE * DbgState,
UINT64 GuestRip,
DEBUGGEE_PAUSING_REASON Reason,
2023-01-08 01:49:16 +09:00
BOOLEAN ChangeMtfState)
2021-03-13 20:46:37 +03:30
{
2024-03-01 18:11:24 +09:00
CR3_TYPE GuestCr3 = {0};
2021-03-14 02:06:46 +03:30
BOOLEAN IsHandledByBpRoutines = FALSE;
PLIST_ENTRY TempList = 0;
2024-03-01 18:11:24 +09:00
UINT64 GuestRipPhysical = (UINT64)NULL;
DEBUGGER_TRIGGERED_EVENT_DETAILS TargetContext = {0};
2023-01-08 01:49:16 +09:00
BOOLEAN AvoidUnsetMtf = FALSE;
BOOLEAN IgnoreUserHandling = FALSE;
2021-03-13 20:46:37 +03:30
//
// ***** Check breakpoint for 'bp' command *****
//
//
// Find the current process cr3
//
2023-04-27 18:21:55 +09:00
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
2021-03-13 20:46:37 +03:30
//
// Convert breakpoint to physical address
//
2024-03-01 18:11:24 +09:00
GuestRipPhysical = VirtualAddressToPhysicalAddressByProcessCr3((PVOID)GuestRip, GuestCr3);
2021-03-13 20:46:37 +03:30
//
// Iterate through the list of breakpoints
//
TempList = &g_BreakpointsListHead;
while (&g_BreakpointsListHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
if (CurrentBreakpointDesc->PhysAddress == GuestRipPhysical)
{
//
// It's a breakpoint by 'bp' command
//
IsHandledByBpRoutines = TRUE;
2021-03-14 02:06:46 +03:30
//
// First, we remove the breakpoint
//
2021-03-14 18:17:17 +03:30
MemoryMapperWriteMemorySafeByPhysicalAddress(GuestRipPhysical,
2024-03-01 15:59:58 +09:00
(UINT64)&CurrentBreakpointDesc->PreviousByte,
2021-03-17 02:22:17 +03:30
sizeof(BYTE));
2021-03-14 02:06:46 +03:30
//
// Now, halt the debuggee
//
2024-03-01 15:59:58 +09:00
TargetContext.Context = (PVOID)VmFuncGetLastVmexitRip(DbgState->CoreId);
2021-03-14 02:06:46 +03:30
//
// In breakpoints tag is breakpoint id, not event tag
//
2021-03-17 01:23:53 +03:30
if (Reason == DEBUGGEE_PAUSING_REASON_DEBUGGEE_SOFTWARE_BREAKPOINT_HIT)
{
TargetContext.Tag = CurrentBreakpointDesc->BreakpointId;
2021-03-17 01:23:53 +03:30
}
2021-03-14 02:06:46 +03:30
//
2021-08-31 00:42:02 +04:30
// Hint the debuggee about the length
//
DbgState->InstructionLengthHint = CurrentBreakpointDesc->InstructionLength;
2021-03-14 02:06:46 +03:30
//
2021-03-17 02:22:17 +03:30
// Check constraints
2021-03-14 02:06:46 +03:30
//
2024-03-01 23:02:03 +09:00
if ((CurrentBreakpointDesc->Pid == DEBUGGEE_BP_APPLY_TO_ALL_PROCESSES || CurrentBreakpointDesc->Pid == HANDLE_TO_UINT32(PsGetCurrentProcessId())) &&
(CurrentBreakpointDesc->Tid == DEBUGGEE_BP_APPLY_TO_ALL_THREADS || CurrentBreakpointDesc->Tid == HANDLE_TO_UINT32(PsGetCurrentThreadId())) &&
(CurrentBreakpointDesc->Core == DEBUGGEE_BP_APPLY_TO_ALL_CORES || CurrentBreakpointDesc->Core == DbgState->CoreId))
2021-03-17 02:22:17 +03:30
{
//
// Check if breakpoint should be removed after this hit or not
//
if (CurrentBreakpointDesc->RemoveAfterHit)
{
//
// One hit, we have to remove it
//
BreakpointClearAndDeallocateMemory(CurrentBreakpointDesc);
}
2021-03-17 02:22:17 +03:30
//
// Check if it needs to check for callbacks or not
2021-03-17 02:22:17 +03:30
//
if (CurrentBreakpointDesc->CheckForCallbacks)
{
//
// check callbacks
//
2024-03-01 23:02:03 +09:00
IgnoreUserHandling = BreakpointTriggerCallbacks(DbgState, HANDLE_TO_UINT32(PsGetCurrentProcessId()), HANDLE_TO_UINT32(PsGetCurrentThreadId()));
}
//
// Check if we need to handle the breakpoint by user or just ignore handling it
//
if (!IgnoreUserHandling && !g_InterceptBreakpoints && !g_InterceptBreakpointsAndEventsForCommandsInRemoteComputer)
{
//
// *** It's not safe to access CurrentBreakpointDesc anymore as the
// breakpoint might be removed ***
//
if (g_KernelDebuggerState)
{
KdHandleBreakpointAndDebugBreakpoints(DbgState,
Reason,
&TargetContext);
}
else if (g_UserDebuggerState)
{
UdHandleInstantBreak(DbgState, Reason, NULL);
}
else
{
LogInfo("Err, no debugger is attached to handle the breakpoint");
}
}
2021-03-17 02:22:17 +03:30
}
2021-03-13 20:46:37 +03:30
//
// Reset hint to instruction length
//
DbgState->InstructionLengthHint = 0;
2021-03-14 02:39:13 +03:30
//
// Check if we should re-apply the breakpoint after this instruction
// or not (in other words, is breakpoint still valid)
//
if (!CurrentBreakpointDesc->AvoidReApplyBreakpoint)
{
//
// We should re-apply the breakpoint on next mtf
//
DbgState->SoftwareBreakpointState = CurrentBreakpointDesc;
2021-03-16 20:11:55 +03:30
//
// As we want to continue debuggee, the MTF might arrive when the
// host finish executing it's time slice; thus, a clock interrupt
// or an IPI might be arrived and the next instruction is not what
// we expect. The following codes are added because we realized if the execution takes long then
// the execution might be switched to another routines, thus, MTF might conclude on
// another routine and we might (and will) trigger the same instruction soon
2021-03-16 20:11:55 +03:30
//
VmFuncEnableMtfAndChangeExternalInterruptState(DbgState->CoreId);
2021-03-16 20:11:55 +03:30
//
// Avoid unsetting MTF
//
AvoidUnsetMtf = TRUE;
2021-03-14 02:39:13 +03:30
}
2021-03-13 20:46:37 +03:30
//
// Do not increment rip
//
VmFuncSuppressRipIncrement(DbgState->CoreId);
2021-03-13 20:46:37 +03:30
//
// No need to iterate anymore
//
break;
}
}
2023-01-08 01:49:16 +09:00
if (IsHandledByBpRoutines && ChangeMtfState)
{
VmFuncChangeMtfUnsettingState(DbgState->CoreId, AvoidUnsetMtf);
}
2021-03-13 20:46:37 +03:30
return IsHandledByBpRoutines;
}
/**
* @brief Handle breakpoint vm-exits (#BP)
*
* @param CoreId
*
* @return BOOLEAN
2021-03-13 20:46:37 +03:30
*/
BOOLEAN
2023-11-15 18:27:54 +09:00
BreakpointHandleBreakpoints(UINT32 CoreId)
2021-03-13 20:46:37 +03:30
{
DEBUGGER_TRIGGERED_EVENT_DETAILS TargetContext = {0};
UINT64 GuestRip = 0;
2023-01-08 01:49:16 +09:00
PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId];
2021-03-13 20:46:37 +03:30
GuestRip = VmFuncGetRip();
//
// A breakpoint triggered and two things might be happened,
// first, a breakpoint is triggered randomly in the computer and
// we shouldn't do anything on it (won't change the instruction)
// second, the breakpoint is because of the 'bp' command, we should
// replace it with exact byte
//
//
// Check if the breakpoint is handled by the debugger routines
//
if (BreakpointCheckAndHandleDebuggerDefinedBreakpoints(DbgState,
GuestRip,
DEBUGGEE_PAUSING_REASON_DEBUGGEE_SOFTWARE_BREAKPOINT_HIT,
FALSE))
{
//
// The breakpoint is handled by the debugger routines
// so, we don't need to do anything else
//
return TRUE;
}
2021-03-13 20:46:37 +03:30
//
// re-inject #BP back to the guest if not handled by the hidden breakpoint
//
if (g_KernelDebuggerState)
{
//
// *** Kernel debugger is attached, let's halt everything ***
//
//
// To avoid the computer crash situation from the HyperDbg's breakpoint hitting while the interception is on
// we should always call BreakpointCheckAndHandleDebuggerDefinedBreakpoints first to handle the breakpoint
//
if (g_InterceptBreakpoints || g_InterceptBreakpointsAndEventsForCommandsInRemoteComputer)
2021-03-13 20:46:37 +03:30
{
//
// re-inject back to the guest as not handled if the interception is on and the breakpoint is not from the Hyperdbg's breakpoints
//
return FALSE;
}
//
// It's a random breakpoint byte
//
TargetContext.Context = (PVOID)GuestRip;
KdHandleBreakpointAndDebugBreakpoints(DbgState,
DEBUGGEE_PAUSING_REASON_DEBUGGEE_SOFTWARE_BREAKPOINT_HIT,
&TargetContext);
//
// Increment rip
//
VmFuncPerformRipIncrement(DbgState->CoreId);
//
// By default, we handle the random breakpoints if the kernel debugger is attached
//
return TRUE;
}
else if (g_UserDebuggerState)
{
//
// *** User debugger is attached, let's halt the process ***
//
//
// Check if it's a random breakpoint byte
//
if (UdHandleInstantBreak(DbgState,
DEBUGGEE_PAUSING_REASON_DEBUGGEE_SOFTWARE_BREAKPOINT_HIT,
NULL))
{
2021-03-13 20:46:37 +03:30
//
// if the above function returns true, it's handled in the user debugger
2021-03-13 20:46:37 +03:30
//
//
// Increment rip
2021-03-13 20:46:37 +03:30
//
VmFuncPerformRipIncrement(DbgState->CoreId);
return TRUE;
2021-03-13 20:46:37 +03:30
}
//
// By default, we won't handle the random (unrelated) breakpoints in the user debugger
//
return FALSE;
}
//
// *** re-inject back to the guest as not handled here ***
//
return FALSE;
2021-03-13 20:46:37 +03:30
}
2021-03-12 22:31:55 +03:30
/**
* @brief writes the 0xcc and applies the breakpoint
2021-03-12 22:31:55 +03:30
* @detail this function won't remove the descriptor from the list
*
* @param BreakpointDescriptor
* @param SwitchToTargetMemoryLayout If TRUE, it will switch to the target memory layout
*
2021-03-12 22:31:55 +03:30
* @return BOOLEAN
*/
BOOLEAN
BreakpointWrite(PDEBUGGEE_BP_DESCRIPTOR BreakpointDescriptor, BOOLEAN SwitchToTargetMemoryLayout)
2021-03-12 22:31:55 +03:30
{
BYTE PreviousByte = NULL_ZERO;
BYTE BreakpointByte = 0xcc; // int 3
//
// Check if address is safe (only one byte for 0xcc)
//
if (SwitchToTargetMemoryLayout)
{
if (!CheckAccessValidityAndSafetyByProcessId(BreakpointDescriptor->Address, sizeof(BYTE), BreakpointDescriptor->Pid))
{
return FALSE;
}
2021-03-12 22:31:55 +03:30
}
else
{
if (!CheckAccessValidityAndSafety(BreakpointDescriptor->Address, sizeof(BYTE)))
{
return FALSE;
}
}
2021-03-12 22:31:55 +03:30
//
2021-03-14 02:06:46 +03:30
// Read and save previous byte and save it to the descriptor
2021-03-12 22:31:55 +03:30
//
if (SwitchToTargetMemoryLayout)
{
MemoryMapperReadMemoryUnsafe(
BreakpointDescriptor->Address,
&PreviousByte,
sizeof(BYTE),
BreakpointDescriptor->Pid);
}
else
{
MemoryMapperReadMemorySafeOnTargetProcess(
BreakpointDescriptor->Address,
&PreviousByte,
sizeof(BYTE));
}
//
// Store the previous byte
//
2021-03-14 02:06:46 +03:30
BreakpointDescriptor->PreviousByte = PreviousByte;
2021-03-12 22:31:55 +03:30
//
// Set breakpoint to enabled
//
2021-03-14 02:39:13 +03:30
BreakpointDescriptor->Enabled = TRUE;
BreakpointDescriptor->AvoidReApplyBreakpoint = FALSE;
2021-03-12 22:31:55 +03:30
//
// Apply the breakpoint
//
if (SwitchToTargetMemoryLayout)
{
MemoryMapperWriteMemorySafeFromVmxNonRootyPhysicalAddress(BreakpointDescriptor->PhysAddress,
(PVOID)&BreakpointByte,
sizeof(BYTE));
}
else
{
MemoryMapperWriteMemorySafeByPhysicalAddress(BreakpointDescriptor->PhysAddress,
(UINT64)&BreakpointByte,
sizeof(BYTE));
}
2021-03-12 22:31:55 +03:30
return TRUE;
}
2021-03-17 02:22:17 +03:30
/**
* @brief Remove all the breakpoints if possible
*
2021-03-17 02:22:17 +03:30
* @return VOID
*/
VOID
BreakpointRemoveAllBreakpoints()
{
PLIST_ENTRY TempList = 0;
//
// Iterate through the list of breakpoints
//
TempList = &g_BreakpointsListHead;
while (&g_BreakpointsListHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
//
// Clear and deallocate the breakpoint
2021-03-17 02:22:17 +03:30
//
BreakpointClearAndDeallocateMemory(CurrentBreakpointDesc);
2021-03-17 02:22:17 +03:30
}
}
2021-03-13 00:40:16 +03:30
/**
* @brief Find entry of breakpoint descriptor from list
2021-03-13 00:40:16 +03:30
* of breakpoints by breakpoint id
* @param BreakpointId
*
2021-03-13 00:40:16 +03:30
* @return PDEBUGGEE_BP_DESCRIPTOR
*/
PDEBUGGEE_BP_DESCRIPTOR
BreakpointGetEntryByBreakpointId(UINT64 BreakpointId)
{
PLIST_ENTRY TempList = 0;
TempList = &g_BreakpointsListHead;
while (&g_BreakpointsListHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
if (CurrentBreakpointDesc->BreakpointId == BreakpointId)
{
return CurrentBreakpointDesc;
}
}
//
// We didn't find anything, so return null
//
return NULL;
}
/**
* @brief Find entry of breakpoint descriptor from list
2021-03-13 00:40:16 +03:30
* of breakpoints by address
* @param Address
*
2021-03-13 00:40:16 +03:30
* @return PDEBUGGEE_BP_DESCRIPTOR
*/
PDEBUGGEE_BP_DESCRIPTOR
BreakpointGetEntryByAddress(UINT64 Address)
{
PLIST_ENTRY TempList = 0;
TempList = &g_BreakpointsListHead;
while (&g_BreakpointsListHead != TempList->Flink)
{
TempList = TempList->Flink;
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
if (CurrentBreakpointDesc->Address == Address)
{
return CurrentBreakpointDesc;
}
}
//
// We didn't find anything, so return null
//
return NULL;
}
2021-03-12 22:31:55 +03:30
/**
* @brief Add new breakpoints
* @param BpDescriptor
* @param SwitchToTargetMemoryLayout
*
2021-03-12 22:31:55 +03:30
* @return BOOLEAN
*/
BOOLEAN
BreakpointAddNew(PDEBUGGEE_BP_PACKET BpDescriptorArg, BOOLEAN SwitchToTargetMemoryLayout)
2021-03-12 22:31:55 +03:30
{
2024-03-01 18:11:24 +09:00
CR3_TYPE GuestCr3 = {0};
PDEBUGGEE_BP_DESCRIPTOR BreakpointDescriptor = NULL;
2024-03-01 18:11:24 +09:00
BOOLEAN IsAddress32Bit = FALSE;
2021-03-13 00:40:16 +03:30
//
// Find the current process cr3
//
if (SwitchToTargetMemoryLayout)
{
//
// Check if the process id is valid or not
//
if (BpDescriptorArg->Pid != DEBUGGEE_BP_APPLY_TO_ALL_PROCESSES &&
!CommonIsProcessExist(BpDescriptorArg->Pid))
{
//
// Process id is invalid (Set the error)
//
BpDescriptorArg->Result = DEBUGGER_ERROR_INVALID_PROCESS_ID;
return FALSE;
}
2021-03-12 22:31:55 +03:30
GuestCr3.Flags = LayoutGetCr3ByProcessId(BpDescriptorArg->Pid).Flags;
}
else
{
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
}
2021-03-12 22:31:55 +03:30
//
// *** Validate arguments ***
2021-03-12 22:31:55 +03:30
//
//
// Check if the core number is not invalid
//
2021-03-13 00:40:16 +03:30
if (BpDescriptorArg->Core != DEBUGGEE_BP_APPLY_TO_ALL_CORES &&
!CommonValidateCoreNumber(BpDescriptorArg->Core))
2021-03-12 22:31:55 +03:30
{
//
// Core is invalid (Set the error)
//
BpDescriptorArg->Result = DEBUGGER_ERROR_INVALID_CORE_ID;
2021-03-12 22:31:55 +03:30
return FALSE;
}
2021-03-13 00:40:16 +03:30
//
// Check if breakpoint already exists on list or not
//
if (BreakpointGetEntryByAddress(BpDescriptorArg->Address) != NULL)
{
//
// Address is already on the list (Set the error)
//
BpDescriptorArg->Result = DEBUGGER_ERROR_BREAKPOINT_ALREADY_EXISTS_ON_THE_ADDRESS;
return FALSE;
}
//
// Check if address is safe (only one byte for 0xcc)
//
if (SwitchToTargetMemoryLayout)
{
if (!CheckAccessValidityAndSafetyByProcessId(BpDescriptorArg->Address, sizeof(BYTE), BpDescriptorArg->Pid))
{
BpDescriptorArg->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_CURRENT_PROCESS;
return FALSE;
}
}
else
{
if (!CheckAccessValidityAndSafety(BpDescriptorArg->Address, sizeof(BYTE)))
{
BpDescriptorArg->Result = DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_CURRENT_PROCESS;
return FALSE;
}
}
//
// On the debugger mode, we won't check for process id and thread id, if these arguments are invalid
2021-03-12 22:31:55 +03:30
// then the HyperDbg simply ignores the breakpoints but it makes the computer slow
// it just won't be triggered
//
//
// When we reach here means that the arguments are valid and address is
// safe to access (put 0xcc)
//
//
// Get the pre-allocated buffer
//
BreakpointDescriptor = (DEBUGGEE_BP_DESCRIPTOR *)
PoolManagerRequestPool(BREAKPOINT_DEFINITION_STRUCTURE, TRUE, sizeof(DEBUGGEE_BP_DESCRIPTOR));
2021-03-12 22:31:55 +03:30
if (BreakpointDescriptor == NULL)
{
//
// No pool ! Probably the user set more than MAXIMUM_BREAKPOINTS_WITHOUT_CONTINUE
// pools without IOCTL (continue)
//
BpDescriptorArg->Result = DEBUGGER_ERROR_MAXIMUM_BREAKPOINT_WITHOUT_CONTINUE;
return FALSE;
}
//
// Copy details of breakpoint to the descriptor structure
//
g_MaximumBreakpointId++;
BreakpointDescriptor->BreakpointId = g_MaximumBreakpointId;
BreakpointDescriptor->Address = BpDescriptorArg->Address;
2024-03-01 18:11:24 +09:00
BreakpointDescriptor->PhysAddress = VirtualAddressToPhysicalAddressByProcessCr3((PVOID)BpDescriptorArg->Address,
2021-03-13 00:40:16 +03:30
GuestCr3);
BreakpointDescriptor->Core = BpDescriptorArg->Core;
BreakpointDescriptor->Pid = BpDescriptorArg->Pid;
BreakpointDescriptor->Tid = BpDescriptorArg->Tid;
BreakpointDescriptor->RemoveAfterHit = BpDescriptorArg->RemoveAfterHit;
BreakpointDescriptor->CheckForCallbacks = BpDescriptorArg->CheckForCallbacks;
2021-03-12 22:31:55 +03:30
//
// Check whether address is 32-bit or 64-bit
//
if (BpDescriptorArg->Address & 0xff00000000000000)
{
//
// This is a kernel-base address and as the kernel is 64-bit, we assume it's a 64-bit address
//
IsAddress32Bit = FALSE;
}
else
{
//
// The address is not a kernel address, thus, we check whether the debuggee is running on user-mode
// or not
//
if (SwitchToTargetMemoryLayout)
{
UserAccessIsWow64Process((HANDLE)BpDescriptorArg->Pid, &IsAddress32Bit);
}
else
{
IsAddress32Bit = KdIsGuestOnUsermode32Bit();
}
}
//
// Use length disassembler engine to get the instruction length
//
if (SwitchToTargetMemoryLayout)
{
BreakpointDescriptor->InstructionLength = (UINT16)DisassemblerLengthDisassembleEngineByProcessId(
(PVOID)BpDescriptorArg->Address,
IsAddress32Bit,
BpDescriptorArg->Pid);
}
else
{
BreakpointDescriptor->InstructionLength = (UINT16)DisassemblerLengthDisassembleEngineInVmxRootOnTargetProcess(
(PVOID)BpDescriptorArg->Address,
IsAddress32Bit);
}
2021-03-12 22:31:55 +03:30
//
// Breakpoints are enabled by default
//
BreakpointDescriptor->Enabled = TRUE;
//
// Now we should add the breakpoint to the list of breakpoints (LIST_ENTRY)
//
InsertHeadList(&g_BreakpointsListHead, &(BreakpointDescriptor->BreakpointsList));
//
// Apply the breakpoint
//
BreakpointWrite(BreakpointDescriptor, SwitchToTargetMemoryLayout);
2021-03-12 22:31:55 +03:30
//
// Show that operation was successful
//
2022-10-06 17:45:07 +09:00
BpDescriptorArg->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
2021-03-12 22:31:55 +03:30
return TRUE;
}
2021-03-13 18:26:43 +03:30
/**
* @brief List all breakpoints
*
2021-03-13 18:26:43 +03:30
* @return VOID
*/
VOID
BreakpointListAllBreakpoint()
{
BOOLEAN IsListEmpty = TRUE;
PLIST_ENTRY TempList = 0;
TempList = &g_BreakpointsListHead;
while (&g_BreakpointsListHead != TempList->Blink)
{
TempList = TempList->Blink;
PDEBUGGEE_BP_DESCRIPTOR CurrentBreakpointDesc = CONTAINING_RECORD(TempList, DEBUGGEE_BP_DESCRIPTOR, BreakpointsList);
if (IsListEmpty)
{
2021-08-30 17:23:17 +04:30
Log("Id Address Status\n");
2021-03-14 18:38:33 +03:30
Log("-- --------------- --------");
2021-03-13 18:26:43 +03:30
IsListEmpty = FALSE;
}
2021-03-14 18:38:33 +03:30
Log("\n%02x %016llx %s", CurrentBreakpointDesc->BreakpointId, CurrentBreakpointDesc->Address, CurrentBreakpointDesc->Enabled ? "enabled" : "disabled");
2021-03-13 18:26:43 +03:30
if (CurrentBreakpointDesc->Core != DEBUGGEE_BP_APPLY_TO_ALL_CORES)
{
Log(" core = %x ", CurrentBreakpointDesc->Core);
}
if (CurrentBreakpointDesc->Pid != DEBUGGEE_BP_APPLY_TO_ALL_PROCESSES)
{
Log(" pid = %x ", CurrentBreakpointDesc->Pid);
}
if (CurrentBreakpointDesc->Tid != DEBUGGEE_BP_APPLY_TO_ALL_THREADS)
{
Log(" tid = %x ", CurrentBreakpointDesc->Tid);
}
}
//
// Check if the list is empty or not
//
if (IsListEmpty)
{
Log("Breakpoints list is empty");
}
}
2021-03-12 22:31:55 +03:30
/**
* @brief List of modify breakpoints
2021-03-12 22:31:55 +03:30
* @param ListOrModifyBreakpoints
* @param SwitchToTargetMemoryLayout
*
2021-03-13 00:40:16 +03:30
* @return BOOLEAN
2021-03-12 22:31:55 +03:30
*/
2021-03-13 00:40:16 +03:30
BOOLEAN
BreakpointListOrModify(PDEBUGGEE_BP_LIST_OR_MODIFY_PACKET ListOrModifyBreakpoints, BOOLEAN SwitchToTargetMemoryLayout)
2021-03-12 22:31:55 +03:30
{
2021-03-13 00:40:16 +03:30
PDEBUGGEE_BP_DESCRIPTOR BreakpointDescriptor = NULL;
2021-03-12 22:31:55 +03:30
if (ListOrModifyBreakpoints->Request == DEBUGGEE_BREAKPOINT_MODIFICATION_REQUEST_LIST_BREAKPOINTS)
{
2021-03-13 18:26:43 +03:30
BreakpointListAllBreakpoint();
2021-03-12 22:31:55 +03:30
}
else if (ListOrModifyBreakpoints->Request == DEBUGGEE_BREAKPOINT_MODIFICATION_REQUEST_ENABLE)
{
2021-03-13 00:40:16 +03:30
BreakpointDescriptor = BreakpointGetEntryByBreakpointId(ListOrModifyBreakpoints->BreakpointId);
if (BreakpointDescriptor == NULL)
{
//
// Breakpoint id is invalid
//
ListOrModifyBreakpoints->Result = DEBUGGER_ERROR_BREAKPOINT_ID_NOT_FOUND;
return FALSE;
}
//
// Check to make sure that breakpoint is not already enabled
//
if (BreakpointDescriptor->Enabled)
{
ListOrModifyBreakpoints->Result = DEBUGGER_ERROR_BREAKPOINT_ALREADY_ENABLED;
return FALSE;
}
//
// Set the breakpoint (without removing from list)
//
BreakpointWrite(BreakpointDescriptor, SwitchToTargetMemoryLayout);
2021-03-12 22:31:55 +03:30
}
else if (ListOrModifyBreakpoints->Request == DEBUGGEE_BREAKPOINT_MODIFICATION_REQUEST_DISABLE)
{
2021-03-13 00:40:16 +03:30
BreakpointDescriptor = BreakpointGetEntryByBreakpointId(ListOrModifyBreakpoints->BreakpointId);
if (BreakpointDescriptor == NULL)
{
//
// Breakpoint id is invalid
//
ListOrModifyBreakpoints->Result = DEBUGGER_ERROR_BREAKPOINT_ID_NOT_FOUND;
return FALSE;
}
//
// Check to make sure that breakpoint is not already disabled
//
if (!BreakpointDescriptor->Enabled)
{
ListOrModifyBreakpoints->Result = DEBUGGER_ERROR_BREAKPOINT_ALREADY_DISABLED;
return FALSE;
}
//
// Unset the breakpoint (without removing from list)
//
BreakpointClear(BreakpointDescriptor);
2021-03-12 22:31:55 +03:30
}
else if (ListOrModifyBreakpoints->Request == DEBUGGEE_BREAKPOINT_MODIFICATION_REQUEST_CLEAR)
{
2021-03-13 00:40:16 +03:30
BreakpointDescriptor = BreakpointGetEntryByBreakpointId(ListOrModifyBreakpoints->BreakpointId);
if (BreakpointDescriptor == NULL)
{
//
// Breakpoint id is invalid
//
ListOrModifyBreakpoints->Result = DEBUGGER_ERROR_BREAKPOINT_ID_NOT_FOUND;
return FALSE;
}
//
// Clear and deallocate the breakpoint
2021-03-13 00:40:16 +03:30
//
BreakpointClearAndDeallocateMemory(BreakpointDescriptor);
2021-03-12 22:31:55 +03:30
}
2021-03-13 00:40:16 +03:30
//
// Operation was successful
//
2022-10-06 17:45:07 +09:00
ListOrModifyBreakpoints->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
2021-03-13 00:40:16 +03:30
return TRUE;
2021-03-12 22:31:55 +03:30
}