HyperDbg/hyperdbg/hyperkd/code/debugger/objects/Process.c
2024-06-24 18:05:54 +09:00

686 lines
20 KiB
C

/**
* @file Process.c
* @author Sina Karvandi (sina@hyperdbg.org)
* @brief Implementation of kernel debugger functions for processes
* @details
*
* @version 0.1
* @date 2021-11-23
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
/**
* @brief handle process changes for cr3 registers
* @param CoreId
*
* @return VOID
*/
VOID
ProcessTriggerCr3ProcessChange(UINT32 CoreId)
{
PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId];
//
// Call kernel debugger handler for mov to cr3 in kernel debugger
//
if (DbgState->ThreadOrProcessTracingDetails.IsWatingForMovCr3VmExits)
{
ProcessHandleProcessChange(DbgState);
}
}
/**
* @brief handle process changes
* @param DbgState The state of the debugger on the current core
*
* @return VOID
*/
BOOLEAN
ProcessHandleProcessChange(PROCESSOR_DEBUGGING_STATE * DbgState)
{
//
// Check if we reached to the target process or not
//
if ((g_ProcessSwitch.ProcessId != NULL_ZERO && g_ProcessSwitch.ProcessId == HANDLE_TO_UINT32(PsGetCurrentProcessId())) ||
(g_ProcessSwitch.Process != (UINT64)NULL && g_ProcessSwitch.Process == PsGetCurrentProcess()))
{
KdHandleBreakpointAndDebugBreakpoints(DbgState, DEBUGGEE_PAUSING_REASON_DEBUGGEE_PROCESS_SWITCHED, NULL);
//
// Found
//
return TRUE;
}
//
// Not found
//
return FALSE;
}
/**
* @brief make evnvironment ready to change the process
*
* @param DbgState The state of the debugger on the current core
* @param ProcessId
* @param EProcess
* @param IsSwitchByClockIntrrupt
*
* @return BOOLEAN
*/
BOOLEAN
ProcessSwitch(PROCESSOR_DEBUGGING_STATE * DbgState,
UINT32 ProcessId,
PEPROCESS EProcess,
BOOLEAN IsSwitchByClockIntrrupt)
{
//
// Initialized with NULL
//
g_ProcessSwitch.Process = NULL;
g_ProcessSwitch.ProcessId = NULL_ZERO;
//
// Check to avoid invalid switch
//
if (ProcessId == NULL_ZERO && EProcess == (PEPROCESS)NULL)
{
return FALSE;
}
//
// Set the target process id, eprocess to switch
//
if (EProcess != NULL)
{
if (CheckAccessValidityAndSafety((UINT64)EProcess, sizeof(BYTE)))
{
g_ProcessSwitch.Process = (PVOID)EProcess;
}
else
{
//
// An invalid address is specified by user
//
return FALSE;
}
}
else if (ProcessId != NULL_ZERO)
{
g_ProcessSwitch.ProcessId = ProcessId;
}
//
// Send request for the target task to the halted cores (synchronized)
//
HaltedCoreBroadcastTaskAllCores(DbgState,
DEBUGGER_HALTED_CORE_TASK_SET_PROCESS_INTERCEPTION,
TRUE,
TRUE,
(PVOID)IsSwitchByClockIntrrupt);
return TRUE;
}
/**
* @brief Enable or disable the process change monitoring detection
* on the running core based on intercepting clock interrupts
* @details should be called on vmx root
*
* @param DbgState The state of the debugger on the current core
* @param Enable
* @return VOID
*/
VOID
ProcessDetectChangeByInterceptingClockInterrupts(PROCESSOR_DEBUGGING_STATE * DbgState,
BOOLEAN Enable)
{
if (Enable)
{
//
// Indicate that we're waiting for clock interrupt vm-exits
//
DbgState->ThreadOrProcessTracingDetails.InterceptClockInterruptsForProcessChange = TRUE;
//
// Set external-interrupt vm-exits
//
VmFuncSetExternalInterruptExiting(DbgState->CoreId, TRUE);
}
else
{
//
// Indicate that we're not waiting for clock interrupt vm-exits
//
DbgState->ThreadOrProcessTracingDetails.InterceptClockInterruptsForProcessChange = FALSE;
//
// Unset external-interrupt vm-exits
//
VmFuncSetExternalInterruptExiting(DbgState->CoreId, FALSE);
}
}
/**
* @brief Enable or disable the process change monitoring detection
* on the running core based on mov-to-cr3 vm-exits
* @details should be called on vmx root
*
* @param DbgState The state of the debugger on the current core
* @param Enable
* @return VOID
*/
VOID
ProcessDetectChangeByMov2Cr3Vmexits(PROCESSOR_DEBUGGING_STATE * DbgState,
BOOLEAN Enable)
{
if (Enable)
{
//
// Indicate that we're waiting for mov-to-cr3 vm-exits
//
DbgState->ThreadOrProcessTracingDetails.IsWatingForMovCr3VmExits = TRUE;
//
// Set mov to cr3 vm-exit, this flag is also use to remove the
// mov 2 cr3 on next halt
//
VmFuncSetMovToCr3Vmexit(DbgState->CoreId, TRUE);
}
else
{
//
// Indicate that we're not waiting for mov-to-cr3 vm-exits
//
DbgState->ThreadOrProcessTracingDetails.IsWatingForMovCr3VmExits = FALSE;
//
// Unset mov to cr3 vm-exit, this flag is also use to remove the
// mov 2 cr3 on next halt
//
VmFuncSetMovToCr3Vmexit(DbgState->CoreId, FALSE);
}
}
/**
* @brief Enable or disable the process change monitoring detection
* on the running core
* @details should be called on vmx root
*
* @param DbgState The state of the debugger on the current core
* @param Enable
* @param IsSwitchByClockIntrrupt
*
* @return VOID
*/
VOID
ProcessEnableOrDisableThreadChangeMonitor(PROCESSOR_DEBUGGING_STATE * DbgState,
BOOLEAN Enable,
BOOLEAN IsSwitchByClockIntrrupt)
{
if (Enable)
{
//
// Set indicator process interception on the target core
//
DbgState->ThreadOrProcessTracingDetails.InitialSetProcessChangeEvent = TRUE;
DbgState->ThreadOrProcessTracingDetails.InitialSetByClockInterrupt = IsSwitchByClockIntrrupt;
}
else
{
//
// Avoid future sets/unsets
//
DbgState->ThreadOrProcessTracingDetails.InitialSetProcessChangeEvent = FALSE;
DbgState->ThreadOrProcessTracingDetails.InitialSetByClockInterrupt = FALSE;
}
//
// Check whether we should intercept mov-to-cr3 vm-exits or intercept
// the clock interrupts
//
if (!IsSwitchByClockIntrrupt)
{
ProcessDetectChangeByMov2Cr3Vmexits(DbgState, Enable);
}
else
{
ProcessDetectChangeByInterceptingClockInterrupts(DbgState, Enable);
}
}
/**
* @brief checks whether the given nt!_EPROCESS is valid or not
* @param Eprocess target nt!_EPROCESS
* @param ActiveProcessHead nt!PsActiveProcessHead
* @param ActiveProcessLinksOffset nt!_EPROCESS.ActiveProcessLinks
*
* @return BOOLEAN
*/
BOOLEAN
ProcessCheckIfEprocessIsValid(UINT64 Eprocess, UINT64 ActiveProcessHead, ULONG ActiveProcessLinksOffset)
{
UINT64 Process;
LIST_ENTRY ActiveProcessLinks = {0};
//
// Dirty validation of parameters
//
if (ActiveProcessHead == NULL64_ZERO ||
ActiveProcessLinksOffset == NULL_ZERO)
{
return FALSE;
}
//
// Check if address is valid
//
if (CheckAccessValidityAndSafety(ActiveProcessHead, sizeof(BYTE)))
{
//
// Show processes list, we read everything from the view of system
// process
//
MemoryMapperReadMemorySafe(ActiveProcessHead, &ActiveProcessLinks, sizeof(ActiveProcessLinks));
//
// Find the top of EPROCESS from nt!_EPROCESS.ActiveProcessLinks
//
Process = (UINT64)ActiveProcessLinks.Flink - ActiveProcessLinksOffset;
do
{
//
// Read the next process
//
MemoryMapperReadMemorySafe(Process + ActiveProcessLinksOffset,
&ActiveProcessLinks,
sizeof(ActiveProcessLinks));
//
// Check if we find the process
//
if (Process == Eprocess)
{
return TRUE;
}
//
// Find the next process from the list of this process
//
Process = (UINT64)ActiveProcessLinks.Flink - ActiveProcessLinksOffset;
} while ((UINT64)ActiveProcessLinks.Flink != ActiveProcessHead);
}
else
{
//
// An invalid address is specified
//
return FALSE;
}
return FALSE;
}
/**
* @brief shows the processes list
* @param PorcessListSymbolInfo
* @param QueryAction
* @param CountOfProcesses
* @param ListSaveBuffer
* @param ListSaveBuffSize
*
* @return BOOLEAN
*/
BOOLEAN
ProcessShowList(PDEBUGGEE_PROCESS_LIST_NEEDED_DETAILS PorcessListSymbolInfo,
DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTIONS QueryAction,
UINT32 * CountOfProcesses,
PVOID ListSaveBuffer,
UINT64 ListSaveBuffSize)
{
UINT64 Process;
UINT64 UniquePid = 0;
LIST_ENTRY ActiveProcessLinks = {0};
UCHAR ImageFileName[15] = {0};
CR3_TYPE ProcessCr3 = {0};
UINT32 EnumerationCount = 0;
UINT32 MaximumBufferCount = 0;
PDEBUGGEE_PROCESS_LIST_DETAILS_ENTRY SavingEntries = ListSaveBuffer;
//
// validate parameters
//
if (QueryAction == DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_COUNT &&
CountOfProcesses == NULL)
{
return FALSE;
}
if (QueryAction == DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_SAVE_DETAILS &&
(ListSaveBuffer == NULL || ListSaveBuffSize == 0))
{
return FALSE;
}
//
// compute size to avoid overflow
//
if (QueryAction == DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_SAVE_DETAILS)
{
MaximumBufferCount = (UINT32)(ListSaveBuffSize / sizeof(DEBUGGEE_PROCESS_LIST_DETAILS_ENTRY));
}
//
// Set the details derived from the symbols
//
UINT64 ActiveProcessHead = PorcessListSymbolInfo->PsActiveProcessHead; // nt!PsActiveProcessHead
ULONG ImageFileNameOffset = PorcessListSymbolInfo->ImageFileNameOffset; // nt!_EPROCESS.ImageFileName
ULONG UniquePidOffset = PorcessListSymbolInfo->UniquePidOffset; // nt!_EPROCESS.UniqueProcessId
ULONG ActiveProcessLinksOffset = PorcessListSymbolInfo->ActiveProcessLinksOffset; // nt!_EPROCESS.ActiveProcessLinks
//
// Dirty validation of parameters
//
if (ActiveProcessHead == NULL64_ZERO ||
ImageFileNameOffset == NULL_ZERO ||
UniquePidOffset == NULL_ZERO ||
ActiveProcessLinksOffset == NULL_ZERO)
{
return FALSE;
}
//
// Check if address is valid
//
if (CheckAccessValidityAndSafety(ActiveProcessHead, sizeof(BYTE)))
{
//
// Show processes list, we read everything from the view of system
// process
//
MemoryMapperReadMemorySafe(ActiveProcessHead, &ActiveProcessLinks, sizeof(ActiveProcessLinks));
//
// Find the top of EPROCESS from nt!_EPROCESS.ActiveProcessLinks
//
Process = (UINT64)ActiveProcessLinks.Flink - ActiveProcessLinksOffset;
do
{
//
// Read Process name, Process ID, CR3 of the target process
//
MemoryMapperReadMemorySafe(Process + ImageFileNameOffset,
&ImageFileName,
sizeof(ImageFileName));
MemoryMapperReadMemorySafe(Process + UniquePidOffset,
&UniquePid,
sizeof(UniquePid));
MemoryMapperReadMemorySafe(Process + ActiveProcessLinksOffset,
&ActiveProcessLinks,
sizeof(ActiveProcessLinks));
//
// Get the kernel CR3 for the target process
//
NT_KPROCESS * CurrentProcess = (NT_KPROCESS *)(Process);
ProcessCr3.Flags = CurrentProcess->DirectoryTableBase;
//
// Show the list of process
//
switch (QueryAction)
{
case DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_SHOW_INSTANTLY:
Log("PROCESS\t%llx\n\tProcess Id: %04x\tDirBase (Kernel Cr3): %016llx\tImage: %s\n\n",
Process,
UniquePid,
ProcessCr3.Flags,
ImageFileName);
case DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_COUNT:
EnumerationCount++;
break;
case DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_SAVE_DETAILS:
EnumerationCount++;
//
// Check to avoid overflow
//
if (EnumerationCount == MaximumBufferCount - 1)
{
//
// buffer is full
//
goto ReturnEnd;
}
//
// Save the details
//
SavingEntries[EnumerationCount - 1].Eprocess = Process;
SavingEntries[EnumerationCount - 1].ProcessId = (UINT32)UniquePid;
SavingEntries[EnumerationCount - 1].Cr3 = ProcessCr3.Flags;
RtlCopyMemory(&SavingEntries[EnumerationCount - 1].ImageFileName, ImageFileName, 15);
break;
default:
LogError("Err, invalid action specified for process enumeration");
break;
}
//
// Find the next process from the list of this process
//
Process = (UINT64)ActiveProcessLinks.Flink - ActiveProcessLinksOffset;
} while ((UINT64)ActiveProcessLinks.Flink != ActiveProcessHead);
}
else
{
//
// An invalid address is specified by the debugger
//
return FALSE;
}
ReturnEnd:
//
// In case of query count of processes, we'll set this parameter
//
if (QueryAction == DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_COUNT)
{
*CountOfProcesses = EnumerationCount;
}
return TRUE;
}
/**
* @brief change the current process
* @detail ONLY TO BE USED IN KD STUFFS
*
* @param DbgState The state of the debugger on the current core
* @param PidRequest
*
* @return BOOLEAN
*/
BOOLEAN
ProcessInterpretProcess(PROCESSOR_DEBUGGING_STATE * DbgState, PDEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET PidRequest)
{
switch (PidRequest->ActionType)
{
case DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_GET_PROCESS_DETAILS:
//
// Debugger wants to know current pid, nt!_EPROCESS and process name
//
PidRequest->ProcessId = HANDLE_TO_UINT32(PsGetCurrentProcessId());
PidRequest->Process = (UINT64)PsGetCurrentProcess();
MemoryMapperReadMemorySafe((UINT64)CommonGetProcessNameFromProcessControlBlock(PsGetCurrentProcess()), &PidRequest->ProcessName, 16);
//
// Operation was successful
//
PidRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
break;
case DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PERFORM_SWITCH:
//
// Perform the process switch
//
if (!ProcessSwitch(DbgState, PidRequest->ProcessId, (PEPROCESS)PidRequest->Process, PidRequest->IsSwitchByClkIntr))
{
PidRequest->Result = DEBUGGER_ERROR_DETAILS_OR_SWITCH_PROCESS_INVALID_PARAMETER;
break;
}
//
// Operation was successful
//
PidRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
break;
case DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_GET_PROCESS_LIST:
//
// Show the process list
//
if (!ProcessShowList(&PidRequest->ProcessListSymDetails,
DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_SHOW_INSTANTLY,
NULL,
NULL,
(UINT64)NULL))
{
PidRequest->Result = DEBUGGER_ERROR_DETAILS_OR_SWITCH_PROCESS_INVALID_PARAMETER;
break;
}
//
// Operation was successful
//
PidRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
break;
default:
//
// Invalid type of action
//
PidRequest->Result = DEBUGGER_ERROR_DETAILS_OR_SWITCH_PROCESS_INVALID_PARAMETER;
break;
}
//
// Check if the above operation contains error
//
if (PidRequest->Result == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* @brief Query process details (count)
*
* @param DebuggerUsermodeProcessOrThreadQueryRequest
*
* @return BOOLEAN
*/
BOOLEAN
ProcessQueryCount(PDEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS DebuggerUsermodeProcessOrThreadQueryRequest)
{
BOOLEAN Result = FALSE;
//
// Getting the count results
//
Result = ProcessShowList(&DebuggerUsermodeProcessOrThreadQueryRequest->ProcessListNeededDetails,
DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_COUNT,
&DebuggerUsermodeProcessOrThreadQueryRequest->Count,
NULL,
(UINT64)NULL);
if (Result && DebuggerUsermodeProcessOrThreadQueryRequest->Count != 0)
{
DebuggerUsermodeProcessOrThreadQueryRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
return TRUE;
}
DebuggerUsermodeProcessOrThreadQueryRequest->Result = DEBUGGER_ERROR_UNABLE_TO_QUERY_COUNT_OF_PROCESSES_OR_THREADS;
return FALSE;
}
/**
* @brief Query process details (list)
*
* @param DebuggerUsermodeProcessOrThreadQueryRequest
* @param AddressToSaveDetail
* @param BufferSize
*
* @return BOOLEAN
*/
BOOLEAN
ProcessQueryList(PDEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS DebuggerUsermodeProcessOrThreadQueryRequest,
PVOID AddressToSaveDetail,
UINT32 BufferSize)
{
BOOLEAN Result = FALSE;
//
// Getting the count results
//
Result = ProcessShowList(&DebuggerUsermodeProcessOrThreadQueryRequest->ProcessListNeededDetails,
DEBUGGER_QUERY_ACTIVE_PROCESSES_OR_THREADS_ACTION_QUERY_SAVE_DETAILS,
NULL,
AddressToSaveDetail,
BufferSize);
return Result;
}
/**
* @brief Query process details
*
* @param GetInformationProcessRequest
*
* @return BOOLEAN
*/
BOOLEAN
ProcessQueryDetails(PDEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET GetInformationProcessRequest)
{
GetInformationProcessRequest->ProcessId = HANDLE_TO_UINT32(PsGetCurrentProcessId());
GetInformationProcessRequest->Process = (UINT64)PsGetCurrentProcess();
RtlCopyMemory(&GetInformationProcessRequest->ProcessName,
CommonGetProcessNameFromProcessControlBlock(PsGetCurrentProcess()),
15);
GetInformationProcessRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
return TRUE;
}