mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-09 17:19:26 +00:00
2727 lines
88 KiB
C++
2727 lines
88 KiB
C++
/**
|
|
* @file debugger.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Interpret general fields
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2020-05-27
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern UINT64 g_EventTag;
|
|
extern UINT64 g_KernelBaseAddress;
|
|
extern LIST_ENTRY g_EventTrace;
|
|
extern BOOLEAN g_EventTraceInitialized;
|
|
extern BOOLEAN g_BreakPrintingOutput;
|
|
extern BOOLEAN g_AutoUnpause;
|
|
extern BOOLEAN g_OutputSourcesInitialized;
|
|
extern LIST_ENTRY g_OutputSources;
|
|
extern BOOLEAN g_IsConnectedToRemoteDebuggee;
|
|
extern BOOLEAN g_IsConnectedToRemoteDebugger;
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee;
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebugger;
|
|
extern ACTIVE_DEBUGGING_PROCESS g_ActiveProcessDebuggingState;
|
|
|
|
/**
|
|
* @brief shows the error message
|
|
*
|
|
* @param Error
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
ShowErrorMessage(UINT32 Error)
|
|
{
|
|
switch (Error)
|
|
{
|
|
case DEBUGGER_ERROR_TAG_NOT_EXISTS:
|
|
ShowMessages("err, tag not found (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_ACTION_TYPE:
|
|
ShowMessages("err, invalid action type (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_ACTION_BUFFER_SIZE_IS_ZERO:
|
|
ShowMessages("err, action buffer size is zero (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EVENT_TYPE_IS_INVALID:
|
|
ShowMessages("err, the event type is invalid (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_CREATE_EVENT:
|
|
ShowMessages("err, unable to create event (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_ADDRESS:
|
|
|
|
ShowMessages("err, invalid address (%x)\n"
|
|
"address may be paged-out or unavailable on the page table due to 'demand paging'\n"
|
|
"please refer to https://docs.hyperdbg.org/tips-and-tricks/considerations/accessing-invalid-address for further information\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_CORE_ID:
|
|
ShowMessages("err, invalid core id (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EXCEPTION_INDEX_EXCEED_FIRST_32_ENTRIES:
|
|
ShowMessages("err, exception index exceed first 32 entries (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INTERRUPT_INDEX_IS_NOT_VALID:
|
|
ShowMessages("err, interrupt index is not valid (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_HIDE_OR_UNHIDE_DEBUGGER:
|
|
ShowMessages("err, unable to hide or unhide debugger (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_DEBUGGER_ALREADY_UHIDE:
|
|
ShowMessages("err, debugger already unhide (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER:
|
|
ShowMessages("err, edit memory request has invalid parameters (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_CURRENT_PROCESS:
|
|
ShowMessages("err, edit memory request has invalid address based on "
|
|
"current process layout, the address might be valid but not "
|
|
"present in the ram (%x)\n"
|
|
"address may be paged-out or unavailable on the page table due to 'demand paging'\n"
|
|
"please refer to https://docs.hyperdbg.org/tips-and-tricks/considerations/accessing-invalid-address for further information\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_OTHER_PROCESS:
|
|
ShowMessages("err, edit memory request has invalid address based on other "
|
|
"process layout (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TAG:
|
|
ShowMessages("err, modify event with invalid tag (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TYPE_OF_ACTION:
|
|
ShowMessages("err, modify event with invalid type of action (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_STEPPING_INVALID_PARAMETER:
|
|
ShowMessages("err, invalid parameter passed to stepping core. (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_STEPPINGS_EITHER_THREAD_NOT_FOUND_OR_DISABLED:
|
|
ShowMessages(
|
|
"err, the target thread not found or the thread is disabled (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PREPARING_DEBUGGEE_INVALID_BAUDRATE:
|
|
ShowMessages("err, invalid baud rate (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PREPARING_DEBUGGEE_INVALID_SERIAL_PORT:
|
|
ShowMessages("err, invalid serial port (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PREPARING_DEBUGGEE_INVALID_CORE_IN_REMOTE_DEBUGGE:
|
|
ShowMessages("err, invalid core selected in switching cores (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PREPARING_DEBUGGEE_UNABLE_TO_SWITCH_TO_NEW_PROCESS:
|
|
ShowMessages("err, unable to switch to the new process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PREPARING_DEBUGGEE_TO_RUN_SCRIPT:
|
|
ShowMessages("err, unable to run the script on remote debuggee (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_REGISTER_NUMBER:
|
|
ShowMessages("err, invalid register number (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MAXIMUM_BREAKPOINT_WITHOUT_CONTINUE:
|
|
ShowMessages("err, maximum number of breakpoints are used, you need to "
|
|
"send an ioctl to re-allocate new pre-allocated buffers or "
|
|
"configure HyperDbg to pre-allocated more buffers by "
|
|
"configuring MAXIMUM_BREAKPOINTS_WITHOUT_CONTINUE (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_BREAKPOINT_ALREADY_EXISTS_ON_THE_ADDRESS:
|
|
ShowMessages("err, breakpoint already exists on target address, you can "
|
|
"use 'bl' command to view a list of breakpoints (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_BREAKPOINT_ID_NOT_FOUND:
|
|
ShowMessages("err, breakpoint id is invalid (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_BREAKPOINT_ALREADY_DISABLED:
|
|
ShowMessages("err, breakpoint already disabled (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_BREAKPOINT_ALREADY_ENABLED:
|
|
ShowMessages("err, breakpoint already enabled (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MEMORY_TYPE_INVALID:
|
|
ShowMessages("err, the memory type is invalid (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_PROCESS_ID:
|
|
ShowMessages("err, the process id is invalid, make sure to enter the "
|
|
"process id in hex format, or if you want to use it in decimal "
|
|
"format, add '0n' prefix to the number (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EVENT_IS_NOT_APPLIED:
|
|
ShowMessages("err, the event is not applied (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_DETAILS_OR_SWITCH_PROCESS_INVALID_PARAMETER:
|
|
ShowMessages("err, either the process id or the _EPROCESS is invalid or "
|
|
"cannot get the details based on the provided parameters (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_DETAILS_OR_SWITCH_THREAD_INVALID_PARAMETER:
|
|
ShowMessages("err, either the thread id, _ETHREAD, or _EPROCESS is invalid or "
|
|
"cannot get the details based on the provided parameters (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MAXIMUM_BREAKPOINT_FOR_A_SINGLE_PAGE_IS_HIT:
|
|
ShowMessages("err, the maximum breakpoint for a single page is hit, "
|
|
"you cannot apply more breakpoints in this page (%x)\n"
|
|
"please refer to https://docs.hyperdbg.org/tips-and-tricks/misc/customize-build/number-of-ept-hooks-in-one-page"
|
|
" for further information\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PRE_ALLOCATED_BUFFER_IS_EMPTY:
|
|
ShowMessages("err, the pre-allocated buffer is empty, usually this buffer will be "
|
|
"filled at the next IOCTL when the debugger is continued (%x)\n"
|
|
"please visit the documentation for the 'prealloc' command or use "
|
|
"'.help prealloc' to to reserve more pre-allocated pools\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EPT_COULD_NOT_SPLIT_THE_LARGE_PAGE_TO_4KB_PAGES:
|
|
ShowMessages("err, could not convert 2MB large page to 4KB pages (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EPT_FAILED_TO_GET_PML1_ENTRY_OF_TARGET_ADDRESS:
|
|
ShowMessages("err, failed to get the PML1 entry of the target address (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_EPT_MULTIPLE_HOOKS_IN_A_SINGLE_PAGE:
|
|
ShowMessages("err, the page modification is not applied, make sure that you don't "
|
|
"put multiple EPT Hooks or Monitors on a single page (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_COULD_NOT_BUILD_THE_EPT_HOOK:
|
|
ShowMessages("err, could not build the EPT hook (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_COULD_NOT_FIND_ALLOCATION_TYPE:
|
|
ShowMessages("err, could not find the allocation type (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_TEST_QUERY_INDEX:
|
|
ShowMessages("err, invalid index specified for test query command (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_ATTACH_TO_TARGET_USER_MODE_PROCESS:
|
|
ShowMessages("err, unable to attach to the target process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_REMOVE_HOOKS_ENTRYPOINT_NOT_REACHED:
|
|
ShowMessages("err, unable to remove hooks as the entrypoint of user-mode "
|
|
"process is not reached yet (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_REMOVE_HOOKS:
|
|
ShowMessages("err, unable to remove hooks (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_FUNCTIONS_FOR_INITIALIZING_PEB_ADDRESSES_ARE_NOT_INITIALIZED:
|
|
ShowMessages("err, the routines for getting the PEB is not correctly "
|
|
"initialized (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_DETECT_32_BIT_OR_64_BIT_PROCESS:
|
|
ShowMessages("err, unable to detect whether the process was 32-bit "
|
|
"or 64-bit (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_KILL_THE_PROCESS:
|
|
ShowMessages("err, unable to kill the process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_THREAD_DEBUGGING_TOKEN:
|
|
ShowMessages("err, invalid thread debugging token (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_PAUSE_THE_PROCESS_THREADS:
|
|
ShowMessages("err, unable to pause the threads of the process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_ATTACH_TO_AN_ALREADY_ATTACHED_PROCESS:
|
|
ShowMessages("err, the user debugger is already attached to this "
|
|
"process, please use the '.switch' command to switch "
|
|
"to this process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_THE_USER_DEBUGGER_NOT_ATTACHED_TO_THE_PROCESS:
|
|
ShowMessages("err, the user debugger is not already attached to "
|
|
"the process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_DETACH_AS_THERE_ARE_PAUSED_THREADS:
|
|
ShowMessages("err, the user debugger is not able to detach from "
|
|
"this process as there are paused threads in the "
|
|
"target process, please make sure to remove all "
|
|
"the events and continue the target process, then "
|
|
"perform the detach again (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_SWITCH_PROCESS_ID_OR_THREAD_ID_IS_INVALID:
|
|
ShowMessages("err, unable to switch to the process id or thread id "
|
|
"as the target process id or thread id is not found in "
|
|
"the attached threads list, please view the list of "
|
|
"processes and threads by using the '.switch list' command (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_SWITCH_THERE_IS_NO_THREAD_ON_THE_PROCESS:
|
|
ShowMessages("err, unable to switch to the process as the process doesn't "
|
|
"contain an active intercepted thread (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_GET_MODULES_OF_THE_PROCESS:
|
|
ShowMessages("err, unable to get user-mode modules of the process (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_GET_CALLSTACK:
|
|
ShowMessages("err, unable to get the callstack (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_QUERY_COUNT_OF_PROCESSES_OR_THREADS:
|
|
ShowMessages("err, unable to query count of processes or threads (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_USING_SHORT_CIRCUITING_EVENT_WITH_POST_EVENT_MODE_IS_FORBIDDEDN:
|
|
ShowMessages("err, using short-circuiting event with post events is not possible (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNKNOWN_TEST_QUERY_RECEIVED:
|
|
ShowMessages("err, unknown test query is received to the debugger (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_READING_MEMORY_INVALID_PARAMETER:
|
|
ShowMessages("err, invalid process or memory address (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_THE_TRAP_FLAG_LIST_IS_FULL:
|
|
ShowMessages("err, unable to add the current thread/process to the list of trap flags. "
|
|
"Are you debugging multiple threads or stepping through different processes "
|
|
"simultaneously? (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_KILL_THE_PROCESS_DOES_NOT_EXISTS:
|
|
ShowMessages("err, process does not exists (already terminated?) (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_MODE_EXECUTION_IS_INVALID:
|
|
ShowMessages("err, the specified execution mode is invalid (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_PROCESS_ID_CANNOT_BE_SPECIFIED_WHILE_APPLYING_EVENT_FROM_VMX_ROOT_MODE:
|
|
ShowMessages("err, you cannot specify process id while the debugger is paused in the debugger mode. "
|
|
"You can use the '.process' or the '.thread' command to switch to the target process's "
|
|
"memory layout (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_PREALLOCATED_BUFFER_IS_NOT_ENOUGH_FOR_EVENT_AND_CONDITIONALS:
|
|
ShowMessages("err, the requested buffer for storing event and conditions is larger than the pre-allocated "
|
|
"buffer size (%x)\nfor more information on how to resolve this issue, "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_REGULAR_PREALLOCATED_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, not enough pre-allocated buffer exists for storing the event. You can use the 'prealloc' "
|
|
"command to fix this issue by pre-allocating more buffers (%x)\nfor more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_BIG_PREALLOCATED_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, the requested event is considered as a \"big instant event\" and right now, there is no "
|
|
"pre-allocated buffer for storing it. You can use the 'prealloc' command to fix this issue by "
|
|
"pre-allocating big instant event buffers (%x)\nfor more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_CREATE_ACTION_CANNOT_ALLOCATE_BUFFER:
|
|
ShowMessages("err, unable to allocate buffer for the target action (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_ACTION_REGULAR_PREALLOCATED_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, not enough pre-allocated buffer exists for storing the event's action. You can use the 'prealloc' "
|
|
"command to fix this issue by pre-allocating more buffers (%x)\nfor more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_ACTION_BIG_PREALLOCATED_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, the requested action is considered as a \"big instant event (action)\" and right now, there is no "
|
|
"pre-allocated buffer for storing it. You can use the 'prealloc' command to fix this issue by "
|
|
"pre-allocating big instant event's action buffers (%x)\nfor more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_PREALLOCATED_BUFFER_IS_NOT_ENOUGH_FOR_ACTION_BUFFER:
|
|
ShowMessages("err, the requested buffer for storing action is larger than the pre-allocated "
|
|
"buffer size (%x)\nfor more information on how to resolve this issue, "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_REQUESTED_OPTIONAL_BUFFER_IS_BIGGER_THAN_DEBUGGERS_SEND_RECEIVE_STACK:
|
|
ShowMessages("err, the requested optional buffer is bigger than the debuggers send/receive stack, "
|
|
"please select a smaller requested buffer for the target event (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_REGULAR_REQUESTED_SAFE_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, by default HyperDbg won't allocate requested safe buffers for instant events in "
|
|
"the debugger mode. You can use the 'prealloc' command to allocate (regular) requested safe "
|
|
"buffers before running this command (%x)\nfor more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_BIG_REQUESTED_SAFE_BUFFER_NOT_FOUND:
|
|
ShowMessages("err, the requested safe buffer is bigger than regular buffers. You can use the 'prealloc' "
|
|
"command to allocate (big) requested safe buffers before running this command (%x)\n"
|
|
"for more information "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INSTANT_EVENT_PREALLOCATED_BUFFER_IS_NOT_ENOUGH_FOR_REQUESTED_SAFE_BUFFER:
|
|
ShowMessages("err, the requested buffer for storing safe buffers of the action is larger than the pre-allocated "
|
|
"buffer size (%x)\nfor more information on how to resolve this issue, "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_UNABLE_TO_ALLOCATE_REQUESTED_SAFE_BUFFER:
|
|
ShowMessages("err, unable to allocate buffer for the target requested safe buffer (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_COULD_NOT_FIND_PREACTIVATION_TYPE:
|
|
ShowMessages("err, invalid type is specified for preactivation (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_THE_MODE_EXEC_TRAP_IS_NOT_INITIALIZED:
|
|
ShowMessages("err, the '!mode' event command cannot be directly initialized in the Debugger Mode. "
|
|
"To avoid wasting system resources and performance issues we decided to use another "
|
|
"command to initialize it first then use it. You can use the 'preactivate mode' command "
|
|
"to preactivate this mechanism after that, you can use the '!mode' event (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_THE_TARGET_EVENT_IS_DISABLED_BUT_CANNOT_BE_CLEARED_PRIRITY_BUFFER_IS_FULL:
|
|
ShowMessages("err, the event(s) that you've requested are disabled, yet HyperDbg cannot remove (clear) them in "
|
|
"the subsequent run due to the user-mode priority buffers being at full capacity. "
|
|
"This typically occurs when you attempt to clear numerous events without resuming the debuggee. "
|
|
"Since these unserviced events remain in the queue, HyperDbg is unable to clear them. "
|
|
"To address this issue, you can resume the debuggee, allowing all queued events to be cleared (usually 2 to 10 seconds). "
|
|
"Afterward, you can pause the debuggee again and request the removal of new events (%x)\n"
|
|
"for more information on how to resolve this issue "
|
|
"please visit: https://docs.hyperdbg.org/tips-and-tricks/misc/instant-events\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_NOT_ALL_CORES_ARE_LOCKED_FOR_APPLYING_INSTANT_EVENT:
|
|
ShowMessages("err, the event cannot be applied since not all cores are locked! "
|
|
"If you are using the instant-event mechanism or switching between cores, this will likely halt the system. "
|
|
"This may be caused by a race condition, but continuing and halting the debugger might resolve it. "
|
|
"If the problem persists, please open an issue and provide steps to reproduce it (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_TARGET_SWITCHING_CORE_IS_NOT_LOCKED:
|
|
ShowMessages("err, target core is not locked! "
|
|
"This may be caused by a race condition, continuing and halting the debugger might resolve it. "
|
|
"If the problem persists, please open an issue and provide steps to reproduce it (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_INVALID_PHYSICAL_ADDRESS:
|
|
ShowMessages("err, invalid physical address (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
case DEBUGGER_ERROR_APIC_ACTIONS_ERROR:
|
|
ShowMessages("err, could not perform APIC actions (%x)\n",
|
|
Error);
|
|
break;
|
|
|
|
default:
|
|
ShowMessages("err, error not found (%x)\n",
|
|
Error);
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get ntoskrnl.exe base in the kernel
|
|
*
|
|
* @return UINT64 Base address of ntoskrnl.exe
|
|
*/
|
|
UINT64
|
|
DebuggerGetNtoskrnlBase()
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
UINT64 NtoskrnlBase = NULL;
|
|
PRTL_PROCESS_MODULES Modules = NULL;
|
|
ULONG SysModuleInfoBufferSize = 0;
|
|
|
|
//
|
|
// Get required size of "RTL_PROCESS_MODULES" buffer
|
|
//
|
|
Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, NULL, NULL, &SysModuleInfoBufferSize);
|
|
|
|
Modules = (PRTL_PROCESS_MODULES)malloc(SysModuleInfoBufferSize);
|
|
|
|
if (Modules == NULL)
|
|
{
|
|
return NULL64_ZERO;
|
|
}
|
|
|
|
NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, Modules, SysModuleInfoBufferSize, NULL);
|
|
|
|
for (UINT32 i = 0; i < Modules->NumberOfModules; i++)
|
|
{
|
|
if (!strcmp((const char *)Modules->Modules[i].FullPathName + Modules->Modules[i].OffsetToFileName,
|
|
"ntoskrnl.exe"))
|
|
{
|
|
NtoskrnlBase = (UINT64)Modules->Modules[i].ImageBase;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(Modules);
|
|
|
|
return NtoskrnlBase;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the base address of the kernel module
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
DebuggerGetKernelBase()
|
|
{
|
|
UINT64 KernelBase = NULL;
|
|
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
KernelBase = g_KernelBaseAddress;
|
|
}
|
|
else
|
|
{
|
|
KernelBase = DebuggerGetNtoskrnlBase();
|
|
g_KernelBaseAddress = KernelBase;
|
|
}
|
|
|
|
return KernelBase;
|
|
}
|
|
|
|
/**
|
|
* @brief pauses the debuggee
|
|
*
|
|
* @return BOOLEAN shows whether the pause was successful or not, if successful
|
|
* then when it returns true the debuggee is not paused anymore (continued)
|
|
*/
|
|
BOOLEAN
|
|
DebuggerPauseDebuggee()
|
|
{
|
|
BOOLEAN StatusIoctl = 0;
|
|
ULONG ReturnedLength = 0;
|
|
DEBUGGER_PAUSE_PACKET_RECEIVED PauseRequest = {0};
|
|
|
|
//
|
|
// Send a pause IOCTL
|
|
//
|
|
StatusIoctl = DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_PAUSE_PACKET_RECEIVED, // IO Control Code (IOCTL)
|
|
&PauseRequest, // Input Buffer to driver.
|
|
SIZEOF_DEBUGGER_PAUSE_PACKET_RECEIVED, // Input buffer
|
|
// length
|
|
&PauseRequest, // Output Buffer from driver.
|
|
SIZEOF_DEBUGGER_PAUSE_PACKET_RECEIVED, // Length of output
|
|
// buffer in bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!StatusIoctl)
|
|
{
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
if (PauseRequest.Result == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
//
|
|
// Nothing to show, the request was successfully processed
|
|
//
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(PauseRequest.Result);
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Shows whether the debugger is connected to a debugger
|
|
* or debuggee connected to a debugger
|
|
* @details we use this function to avoid connecting to a remote machine when
|
|
* debuggee or debugger is already connected to an instance
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
IsConnectedToAnyInstanceOfDebuggerOrDebuggee()
|
|
{
|
|
if (g_DeviceHandle)
|
|
{
|
|
ShowMessages("err, the current system is already connected to the local "
|
|
"debugging, use '.disconnect' to disconnect\n");
|
|
return TRUE;
|
|
}
|
|
else if (g_IsConnectedToRemoteDebuggee)
|
|
{
|
|
ShowMessages("err, the current system is already connected to remote "
|
|
"machine (debuggee), use '.disconnect' to disconnect from the "
|
|
"remote machine\n");
|
|
return TRUE;
|
|
}
|
|
else if (g_IsConnectedToRemoteDebugger)
|
|
{
|
|
ShowMessages("err, the current system is already connected to remote "
|
|
"machine (debugger), use '.disconnect' to disconnect from the "
|
|
"remote machine from debugger\n");
|
|
return TRUE;
|
|
}
|
|
else if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
ShowMessages(
|
|
"err, the current system is already connected to remote "
|
|
"machine (debuggee), use '.debug close' to disconnect from the "
|
|
"remote machine\n");
|
|
return TRUE;
|
|
}
|
|
else if (g_IsSerialConnectedToRemoteDebugger)
|
|
{
|
|
ShowMessages(
|
|
"err, the current system is already connected to remote "
|
|
"machine (debugger), use '.debug close' to disconnect from the "
|
|
"remote machine from debugger\n");
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Check whether the tag exists or not, if the tag is
|
|
* DEBUGGER_MODIFY_EVENTS_APPLY_TO_ALL_TAG then if we find just one event, it
|
|
* also means that the tag is found
|
|
*
|
|
* @param Tag
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
IsTagExist(UINT64 Tag)
|
|
{
|
|
PLIST_ENTRY TempList = 0;
|
|
PDEBUGGER_GENERAL_EVENT_DETAIL CommandDetail = {0};
|
|
|
|
if (!g_EventTraceInitialized)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TempList = &g_EventTrace;
|
|
while (&g_EventTrace != TempList->Blink)
|
|
{
|
|
TempList = TempList->Blink;
|
|
|
|
CommandDetail = CONTAINING_RECORD(TempList, DEBUGGER_GENERAL_EVENT_DETAIL, CommandsEventList);
|
|
|
|
if (CommandDetail->Tag == Tag || Tag == DEBUGGER_MODIFY_EVENTS_APPLY_TO_ALL_TAG)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not found
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret script (if an event has script)
|
|
* @details If this function returns true then it means that there is a
|
|
* script in this command split and the details are returned in the
|
|
* input structure
|
|
*
|
|
* @param CommandTokens command tokens
|
|
* @param BufferAddress the address that the allocated buffer will be saved on
|
|
* it
|
|
* @param BufferLength the length of the buffer
|
|
* @return BOOLEAN shows whether the interpret was successful (true) or not
|
|
* successful (false)
|
|
*/
|
|
BOOLEAN
|
|
InterpretScript(vector<CommandToken> * CommandTokens,
|
|
PBOOLEAN ScriptSyntaxErrors,
|
|
PUINT64 BufferAddress,
|
|
PUINT32 BufferLength,
|
|
PUINT32 Pointer,
|
|
PUINT64 ScriptCodeBuffer)
|
|
{
|
|
BOOLEAN IsTextVisited = FALSE;
|
|
string TargetBracketString = "";
|
|
|
|
vector<int> IndexesToRemove;
|
|
UINT32 Index = 0;
|
|
UINT32 NewIndexToRemove = 0;
|
|
|
|
//
|
|
// Indicate that there is an error at first
|
|
//
|
|
*ScriptSyntaxErrors = TRUE;
|
|
|
|
for (auto Section : *CommandTokens)
|
|
{
|
|
Index++;
|
|
|
|
if (IsTextVisited && IsTokenBracketString(Section))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
//
|
|
// Fill the bracket string
|
|
//
|
|
TargetBracketString = GetCaseSensitiveStringFromCommandToken(Section);
|
|
|
|
IsTextVisited = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "script"))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
IsTextVisited = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have everything in buffer
|
|
// Check to see if it is empty or not
|
|
//
|
|
if (TargetBracketString.length() == 0)
|
|
{
|
|
//
|
|
// Nothing in output buffer, return zero
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (TargetBracketString.rfind("file:", 0) == 0)
|
|
{
|
|
//
|
|
// It's a file script
|
|
//
|
|
std::ifstream t(TargetBracketString.erase(0, 5).c_str());
|
|
std::stringstream buffer;
|
|
buffer << t.rdbuf();
|
|
TargetBracketString = buffer.str();
|
|
if (TargetBracketString.empty())
|
|
{
|
|
ShowMessages("err, either script file is not found or it's empty\n");
|
|
|
|
//
|
|
// There was an error
|
|
//
|
|
*ScriptSyntaxErrors = TRUE;
|
|
|
|
//
|
|
// return TRUE to show that this item contains an script
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// ShowMessages("script : %s\n", TargetBracketString.c_str());
|
|
|
|
//
|
|
// Run script engine handler
|
|
//
|
|
PVOID CodeBuffer = ScriptEngineParseWrapper((char *)TargetBracketString.c_str(), TRUE);
|
|
|
|
if (CodeBuffer == NULL)
|
|
{
|
|
//
|
|
// There was an error
|
|
//
|
|
*ScriptSyntaxErrors = TRUE;
|
|
|
|
//
|
|
// return TRUE to show that this item contains an script
|
|
//
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There is no syntax error
|
|
//
|
|
*ScriptSyntaxErrors = FALSE;
|
|
}
|
|
|
|
//
|
|
// Print symbols (test)
|
|
//
|
|
// PrintSymbolBufferWrapper(CodeBuffer);
|
|
|
|
//
|
|
// Set the buffer and length
|
|
//
|
|
*BufferAddress = ScriptEngineWrapperGetHead(CodeBuffer);
|
|
*BufferLength = ScriptEngineWrapperGetSize(CodeBuffer);
|
|
*Pointer = ScriptEngineWrapperGetPointer(CodeBuffer);
|
|
*ScriptCodeBuffer = (UINT64)CodeBuffer;
|
|
|
|
//
|
|
// Removing the script indexes from the command
|
|
//
|
|
NewIndexToRemove = 0;
|
|
|
|
for (auto IndexToRemove : IndexesToRemove)
|
|
{
|
|
NewIndexToRemove++;
|
|
|
|
CommandTokens->erase(CommandTokens->begin() + (IndexToRemove - NewIndexToRemove));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret conditions (if an event has condition) and custom code
|
|
* @details If this function returns true then it means that there is a condition
|
|
* or code buffer in this command split and the details are returned in the
|
|
* input structure
|
|
*
|
|
* @param CommandTokens command tokens
|
|
* @param IsConditionBuffer is it a condition buffer or a custom code buffer
|
|
* @param BufferAddress the address that the allocated buffer will be saved on
|
|
* it
|
|
* @param BufferLength the length of the buffer
|
|
* @return BOOLEAN shows whether the interpret was successful (true) or not
|
|
* successful (false)
|
|
*/
|
|
BOOLEAN
|
|
InterpretConditionsAndCodes(vector<CommandToken> * CommandTokens,
|
|
BOOLEAN IsConditionBuffer,
|
|
PUINT64 BufferAddress,
|
|
PUINT32 BufferLength)
|
|
{
|
|
BOOLEAN IsAsm = FALSE;
|
|
BOOLEAN IsTextVisited = FALSE;
|
|
string TargetBracketString = "";
|
|
|
|
string Temp;
|
|
vector<CHAR> ParsedBytes;
|
|
vector<int> IndexesToRemove;
|
|
UCHAR * FinalBuffer;
|
|
UINT32 AssembledByteCount;
|
|
int NewIndexToRemove = 0;
|
|
int Index = 0;
|
|
|
|
for (auto Section : *CommandTokens)
|
|
{
|
|
Index++;
|
|
|
|
if (IsTextVisited && IsTokenBracketString(Section))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
//
|
|
// Fill the bracket string
|
|
//
|
|
TargetBracketString = GetCaseSensitiveStringFromCommandToken(Section);
|
|
|
|
IsTextVisited = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (!IsConditionBuffer && CompareLowerCaseStrings(Section, "code"))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
IsTextVisited = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (IsConditionBuffer && CompareLowerCaseStrings(Section, "condition"))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
IsTextVisited = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "asm"))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
IsAsm = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have everything in condition/code buffer
|
|
// Check to see if it is empty or not
|
|
//
|
|
if (TargetBracketString.length() == 0)
|
|
{
|
|
//
|
|
// Nothing in condition buffer, return zero
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if asm code was provided instead of hex
|
|
//
|
|
if (IsAsm)
|
|
{
|
|
AssembleData AssembleData;
|
|
AssembleData.AsmRaw = TargetBracketString; // by now, it should only have one element
|
|
AssembleData.ParseAssemblyData();
|
|
|
|
//
|
|
// Append a 'ret' at the end of asm code
|
|
//
|
|
AssembleData.AsmFixed += "; ret";
|
|
|
|
if (AssembleData.Assemble(0)) // we just want the hex bytes, so NULL instead of Start_Address
|
|
{
|
|
ShowMessages("err, assemble error code: '%u'\n\n", AssembleData.KsErr);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the BytesCount; for readability.
|
|
//
|
|
AssembledByteCount = (UINT32)AssembleData.BytesCount;
|
|
|
|
//
|
|
// * FinalBuffer *
|
|
//
|
|
FinalBuffer = (unsigned char *)malloc(AssembledByteCount);
|
|
|
|
if (FinalBuffer == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(FinalBuffer, AssembleData.EncodedBytes, AssembledByteCount);
|
|
|
|
//
|
|
// Set the buffer and length
|
|
//
|
|
*BufferAddress = (UINT64)FinalBuffer;
|
|
*BufferLength = AssembledByteCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Append a 'ret' at the end of the buffer
|
|
//
|
|
TargetBracketString += "c3";
|
|
|
|
//
|
|
// If we reach here then there is sth in condition buffer
|
|
//
|
|
|
|
//
|
|
// Check if the TargetBracketString is started with '0x'
|
|
//
|
|
if (TargetBracketString.rfind("0x", 0) == 0 || TargetBracketString.rfind("0X", 0) == 0 || TargetBracketString.rfind("\\x", 0) == 0 || TargetBracketString.rfind("\\X", 0) == 0)
|
|
{
|
|
Temp = TargetBracketString.erase(0, 2);
|
|
}
|
|
else if (TargetBracketString.rfind('x', 0) == 0 || TargetBracketString.rfind('X', 0) == 0)
|
|
{
|
|
Temp = TargetBracketString.erase(0, 1);
|
|
}
|
|
else
|
|
{
|
|
Temp = std::move(TargetBracketString);
|
|
}
|
|
|
|
//
|
|
// replace \x s
|
|
//
|
|
ReplaceAll(Temp, "\\x", "");
|
|
|
|
//
|
|
// check if the buffer is aligned to 2
|
|
//
|
|
if (Temp.size() % 2 != 0)
|
|
{
|
|
//
|
|
// Add a zero to the start of the buffer
|
|
//
|
|
Temp.insert(0, 1, '0');
|
|
}
|
|
|
|
if (!IsHexNotation(Temp))
|
|
{
|
|
ShowMessages("please enter condition code in a hex notation\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert it to vectored bytes
|
|
//
|
|
ParsedBytes = HexToBytes(Temp);
|
|
|
|
//
|
|
// Convert to a contigues buffer
|
|
//
|
|
FinalBuffer = (UCHAR *)malloc(ParsedBytes.size());
|
|
|
|
if (FinalBuffer == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
std::copy(ParsedBytes.begin(), ParsedBytes.end(), FinalBuffer);
|
|
|
|
//
|
|
// Set the buffer and length
|
|
//
|
|
*BufferAddress = (UINT64)FinalBuffer;
|
|
*BufferLength = (UINT32)ParsedBytes.size();
|
|
}
|
|
|
|
//
|
|
// Removing the code or condition indexes from the command
|
|
//
|
|
NewIndexToRemove = 0;
|
|
for (auto IndexToRemove : IndexesToRemove)
|
|
{
|
|
NewIndexToRemove++;
|
|
|
|
CommandTokens->erase(CommandTokens->begin() + (IndexToRemove - NewIndexToRemove));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret output (if an event has special output)
|
|
* @details If this function returns true then it means that there is a special
|
|
* input that needs to be considered for this event (other than just printing)
|
|
* like sending over network, save to file, and send over a namedpipe
|
|
*
|
|
* @param CommandTokens command tokens
|
|
* @param BufferAddress the address that the allocated buffer will be saved on
|
|
* it
|
|
* @param BufferLength the length of the buffer
|
|
* @return BOOLEAN shows whether the interpret was successful (true) or not
|
|
* successful (false)
|
|
*/
|
|
BOOLEAN
|
|
InterpretOutput(vector<CommandToken> * CommandTokens,
|
|
vector<string> & InputSources)
|
|
{
|
|
BOOLEAN IsTextVisited = FALSE;
|
|
string TargetBracketString = "";
|
|
|
|
vector<int> IndexesToRemove;
|
|
string Token;
|
|
int NewIndexToRemove = 0;
|
|
int Index = 0;
|
|
char Delimiter = ',';
|
|
size_t Pos = 0;
|
|
|
|
for (auto Section : *CommandTokens)
|
|
{
|
|
Index++;
|
|
|
|
if (IsTextVisited && IsTokenBracketString(Section))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
//
|
|
// Fill the bracket string
|
|
//
|
|
TargetBracketString = GetCaseSensitiveStringFromCommandToken(Section);
|
|
|
|
IsTextVisited = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "output"))
|
|
{
|
|
//
|
|
// Save to remove this string from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
IsTextVisited = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have everything in buffer
|
|
// Check to see if it is empty or not
|
|
//
|
|
if (TargetBracketString.length() == 0)
|
|
{
|
|
//
|
|
// Nothing in output buffer, return zero
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if we see multiple sources or it's just one single output
|
|
//
|
|
if (TargetBracketString.find(Delimiter) != std::string::npos)
|
|
{
|
|
//
|
|
// Delimiter found !
|
|
//
|
|
while ((Pos = TargetBracketString.find(Delimiter)) != string::npos)
|
|
{
|
|
Token = TargetBracketString.substr(0, Pos);
|
|
Trim(Token);
|
|
|
|
if (!Token.empty())
|
|
{
|
|
InputSources.push_back(Token);
|
|
}
|
|
|
|
TargetBracketString.erase(0, Pos + sizeof(Delimiter) / sizeof(char));
|
|
}
|
|
|
|
if (!TargetBracketString.empty())
|
|
{
|
|
InputSources.push_back(TargetBracketString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Delimiter not found !
|
|
//
|
|
InputSources.push_back(TargetBracketString);
|
|
}
|
|
|
|
//
|
|
// Removing indexes from the command
|
|
//
|
|
NewIndexToRemove = 0;
|
|
for (auto IndexToRemove : IndexesToRemove)
|
|
{
|
|
NewIndexToRemove++;
|
|
CommandTokens->erase(CommandTokens->begin() + (IndexToRemove - NewIndexToRemove));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Register the event to the kernel
|
|
*
|
|
* @param Event the event structure to send
|
|
* @param EventBufferLength the buffer length of event
|
|
* @return BOOLEAN if the request was successful then true
|
|
* if the request was not successful then false
|
|
*/
|
|
BOOLEAN
|
|
SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
|
|
UINT32 EventBufferLength)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
DEBUGGER_EVENT_AND_ACTION_RESULT ReturnedBuffer = {0};
|
|
PDEBUGGER_EVENT_AND_ACTION_RESULT TempRegResult;
|
|
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
//
|
|
// It's a debugger, we should send the events buffer directly
|
|
// from here
|
|
//
|
|
|
|
//
|
|
// Send the buffer from here
|
|
//
|
|
TempRegResult = KdSendRegisterEventPacketToDebuggee(Event, EventBufferLength);
|
|
|
|
//
|
|
// Move the buffer to local buffer
|
|
//
|
|
memcpy(&ReturnedBuffer, TempRegResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's either a debuggee or a local debugging instance
|
|
//
|
|
|
|
AssertShowMessageReturnStmt(g_DeviceHandle, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse);
|
|
|
|
//
|
|
// Send IOCTL
|
|
//
|
|
|
|
Status = DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control Code (IOCTL)
|
|
Event, // Input Buffer to driver.
|
|
EventBufferLength, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT), // Length
|
|
// of
|
|
// output
|
|
// buffer
|
|
// in
|
|
// bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (ReturnedBuffer.IsSuccessful && ReturnedBuffer.Error == 0)
|
|
{
|
|
//
|
|
// Check for auto-unpause mode
|
|
//
|
|
if (!g_IsSerialConnectedToRemoteDebuggee && !g_IsSerialConnectedToRemoteDebugger && g_BreakPrintingOutput && g_AutoUnpause)
|
|
{
|
|
//
|
|
// Allow debugger to show its contents
|
|
//
|
|
|
|
//
|
|
// Set the g_BreakPrintingOutput to FALSE
|
|
//
|
|
g_BreakPrintingOutput = FALSE;
|
|
|
|
//
|
|
// If it's a remote debugger then we send the remote debuggee a 'g'
|
|
//
|
|
if (g_IsConnectedToRemoteDebuggee)
|
|
{
|
|
RemoteConnectionSendCommand("g", (UINT32)strlen("g") + 1);
|
|
}
|
|
|
|
ShowMessages("\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Show the error
|
|
//
|
|
if (ReturnedBuffer.Error != 0)
|
|
{
|
|
ShowErrorMessage(ReturnedBuffer.Error);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Register the action to the event
|
|
*
|
|
* @param Event the event instance buffer
|
|
* @param ActionBreakToDebugger the action of breaking into the debugger
|
|
* @param ActionBreakToDebuggerLength the action of breaking into the debugger (length)
|
|
* @param ActionCustomCode the action of custom code
|
|
* @param ActionCustomCodeLength the action of custom code (length)
|
|
* @param ActionScript the action of script buffer
|
|
* @param ActionScriptLength the action of script buffer (length)
|
|
*
|
|
* @return BOOLEAN BOOLEAN if the request was successful then true
|
|
* if the request was not successful then false
|
|
*/
|
|
BOOLEAN
|
|
RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
|
|
PDEBUGGER_GENERAL_ACTION ActionBreakToDebugger,
|
|
UINT32 ActionBreakToDebuggerLength,
|
|
PDEBUGGER_GENERAL_ACTION ActionCustomCode,
|
|
UINT32 ActionCustomCodeLength,
|
|
PDEBUGGER_GENERAL_ACTION ActionScript,
|
|
UINT32 ActionScriptLength)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
DEBUGGER_EVENT_AND_ACTION_RESULT ReturnedBuffer = {0};
|
|
PDEBUGGER_EVENT_AND_ACTION_RESULT TempAddingResult;
|
|
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
//
|
|
// It's action(s) in debugger mode
|
|
//
|
|
|
|
//
|
|
// Send Break to debugger packet to debuggee
|
|
//
|
|
if (ActionBreakToDebugger != NULL)
|
|
{
|
|
//
|
|
// Send the add action to event from here
|
|
//
|
|
TempAddingResult = KdSendAddActionToEventPacketToDebuggee(
|
|
ActionBreakToDebugger,
|
|
ActionBreakToDebuggerLength);
|
|
|
|
//
|
|
// Move the buffer to local buffer
|
|
//
|
|
memcpy(&ReturnedBuffer, TempAddingResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT));
|
|
}
|
|
|
|
//
|
|
// Send custom code packet to debuggee
|
|
//
|
|
if (ActionCustomCode != NULL)
|
|
{
|
|
//
|
|
// Send the add action to event from here
|
|
//
|
|
TempAddingResult = KdSendAddActionToEventPacketToDebuggee(
|
|
ActionCustomCode,
|
|
ActionCustomCodeLength);
|
|
|
|
//
|
|
// Move the buffer to local buffer
|
|
//
|
|
memcpy(&ReturnedBuffer, TempAddingResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT));
|
|
}
|
|
|
|
//
|
|
// Send custom code packet to debuggee
|
|
//
|
|
if (ActionScript != NULL)
|
|
{
|
|
//
|
|
// Send the add action to event from here
|
|
//
|
|
TempAddingResult = KdSendAddActionToEventPacketToDebuggee(
|
|
ActionScript,
|
|
ActionScriptLength);
|
|
|
|
//
|
|
// Move the buffer to local buffer
|
|
//
|
|
memcpy(&ReturnedBuffer, TempAddingResult, sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's either a local debugger to in vmi-mode remote conntection
|
|
//
|
|
|
|
AssertShowMessageReturnStmt(g_DeviceHandle, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse);
|
|
|
|
//
|
|
// Send IOCTLs
|
|
//
|
|
|
|
//
|
|
// Send Break to debugger ioctl
|
|
//
|
|
if (ActionBreakToDebugger != NULL)
|
|
{
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
|
|
ActionBreakToDebugger, // Input Buffer to driver.
|
|
ActionBreakToDebuggerLength, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT), // Length
|
|
// of
|
|
// output
|
|
// buffer
|
|
// in
|
|
// bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send custom code ioctl
|
|
//
|
|
if (ActionCustomCode != NULL)
|
|
{
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
|
|
ActionCustomCode, // Input Buffer to driver.
|
|
ActionCustomCodeLength, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT), // Length
|
|
// of
|
|
// output
|
|
// buffer
|
|
// in
|
|
// bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send custom code ioctl
|
|
//
|
|
if (ActionScript != NULL)
|
|
{
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
|
|
ActionScript, // Input Buffer to driver.
|
|
ActionScriptLength, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_RESULT), // Length
|
|
// of
|
|
// output
|
|
// buffer
|
|
// in
|
|
// bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// As we're not needing any of action buffer, we'll free all of the
|
|
// here in the case of a successful registration of action, however
|
|
// event detail is needed for the 'event' command's list
|
|
//
|
|
FreeEventsAndActionsMemory(NULL, ActionBreakToDebugger, ActionCustomCode, ActionScript);
|
|
|
|
//
|
|
// Now, we'll register the command as returned buffer shows that
|
|
// this event was successful and now we can add it to the list of
|
|
// events
|
|
//
|
|
InsertHeadList(&g_EventTrace, &(Event->CommandsEventList));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the New Debugger Event Tag object and increase the
|
|
* global variable for tag
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
GetNewDebuggerEventTag()
|
|
{
|
|
return g_EventTag++;
|
|
}
|
|
|
|
/**
|
|
* @brief Deallocate buffers relating to events and actions
|
|
* @param
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
FreeEventsAndActionsMemory(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
|
|
PDEBUGGER_GENERAL_ACTION ActionBreakToDebugger,
|
|
PDEBUGGER_GENERAL_ACTION ActionCustomCode,
|
|
PDEBUGGER_GENERAL_ACTION ActionScript)
|
|
{
|
|
if (Event != NULL)
|
|
{
|
|
if (Event->CommandStringBuffer != NULL)
|
|
{
|
|
free(Event->CommandStringBuffer);
|
|
}
|
|
|
|
free(Event);
|
|
}
|
|
|
|
if (ActionBreakToDebugger != NULL)
|
|
{
|
|
free(ActionBreakToDebugger);
|
|
}
|
|
if (ActionCustomCode != NULL)
|
|
{
|
|
free(ActionCustomCode);
|
|
}
|
|
if (ActionScript != NULL)
|
|
{
|
|
free(ActionScript);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret general event fields
|
|
*
|
|
* @param CommandTokens the command tokens
|
|
* @param EventType type of event
|
|
* @param EventDetailsToFill a pointer address that will be filled
|
|
* by event detail buffer
|
|
* @param EventBufferLength a pointer the receives the buffer length
|
|
* of the event
|
|
* @param ActionDetailsToFill a pointer address that will be filled
|
|
* by action detail buffer
|
|
* @param ActionBufferLength a pointer the receives the buffer length
|
|
* of the action
|
|
* @param ReasonForErrorInParsing reason that interpretation failed, null
|
|
* if the returns true
|
|
* @return BOOLEAN If this function returns true then it means that there
|
|
* was no error in parsing the general event details
|
|
*/
|
|
BOOLEAN
|
|
InterpretGeneralEventAndActionsFields(
|
|
vector<CommandToken> * CommandTokens,
|
|
VMM_EVENT_TYPE_ENUM EventType,
|
|
PDEBUGGER_GENERAL_EVENT_DETAIL * EventDetailsToFill,
|
|
PUINT32 EventBufferLength,
|
|
PDEBUGGER_GENERAL_ACTION * ActionDetailsToFillBreakToDebugger,
|
|
PUINT32 ActionBufferLengthBreakToDebugger,
|
|
PDEBUGGER_GENERAL_ACTION * ActionDetailsToFillCustomCode,
|
|
PUINT32 ActionBufferLengthCustomCode,
|
|
PDEBUGGER_GENERAL_ACTION * ActionDetailsToFillScript,
|
|
PUINT32 ActionBufferLengthScript,
|
|
PDEBUGGER_EVENT_PARSING_ERROR_CAUSE ReasonForErrorInParsing)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
PDEBUGGER_GENERAL_EVENT_DETAIL TempEvent = NULL;
|
|
PDEBUGGER_GENERAL_ACTION TempActionBreak = NULL;
|
|
PDEBUGGER_GENERAL_ACTION TempActionScript = NULL;
|
|
PDEBUGGER_GENERAL_ACTION TempActionCustomCode = NULL;
|
|
VMM_CALLBACK_EVENT_CALLING_STAGE_TYPE CallingStage = VMM_CALLBACK_CALLING_STAGE_PRE_EVENT_EMULATION; // by default 'pre' event
|
|
UINT32 LengthOfCustomCodeActionBuffer = 0;
|
|
UINT32 LengthOfScriptActionBuffer = 0;
|
|
UINT32 LengthOfBreakActionBuffer = 0;
|
|
UINT64 ConditionBufferAddress;
|
|
UINT32 ConditionBufferLength = 0;
|
|
vector<string> ListOfOutputSources;
|
|
UINT64 CodeBufferAddress;
|
|
UINT32 CodeBufferLength = 0;
|
|
UINT64 ScriptBufferAddress;
|
|
UINT64 ScriptCodeBuffer = 0;
|
|
BOOLEAN HasScriptSyntaxError = 0;
|
|
UINT32 ScriptBufferLength = 0;
|
|
UINT32 ScriptBufferPointer = 0;
|
|
UINT32 LengthOfEventBuffer = 0;
|
|
string CommandString;
|
|
BOOLEAN IsAShortCircuitingEventByDefault = FALSE;
|
|
BOOLEAN HasConditionBuffer = FALSE;
|
|
BOOLEAN HasOutputPath = FALSE;
|
|
BOOLEAN HasCodeBuffer = FALSE;
|
|
BOOLEAN HasScript = FALSE;
|
|
BOOLEAN IsNextCommandPid = FALSE;
|
|
BOOLEAN IsNextCommandCoreId = FALSE;
|
|
BOOLEAN IsNextCommandBufferSize = FALSE;
|
|
BOOLEAN IsNextCommandImmediateMessaging = FALSE;
|
|
BOOLEAN IsNextCommandExecutionStage = FALSE;
|
|
BOOLEAN IsNextCommandSc = FALSE;
|
|
BOOLEAN ImmediateMessagePassing = UseImmediateMessagingByDefaultOnEvents;
|
|
UINT32 CoreId;
|
|
UINT32 ProcessId;
|
|
UINT32 IndexOfValidSourceTags;
|
|
UINT32 RequestBuffer = 0;
|
|
PLIST_ENTRY TempList;
|
|
BOOLEAN OutputSourceFound;
|
|
vector<int> IndexesToRemove;
|
|
vector<UINT64> ListOfValidSourceTags;
|
|
int NewIndexToRemove = 0;
|
|
int Index = 0;
|
|
|
|
//
|
|
// Create a command string to show in the history
|
|
//
|
|
for (auto Section : *CommandTokens)
|
|
{
|
|
CommandString.append(GetCaseSensitiveStringFromCommandToken(Section));
|
|
CommandString.append(" ");
|
|
}
|
|
|
|
//
|
|
// Compute the size of buffer + 1 null for the end of buffer
|
|
//
|
|
UINT64 BufferOfCommandStringLength = CommandString.size() + 1;
|
|
|
|
//
|
|
// Allocate Buffer and zero for command to the buffer
|
|
//
|
|
PVOID BufferOfCommandString = malloc(BufferOfCommandStringLength);
|
|
|
|
RtlZeroMemory(BufferOfCommandString, BufferOfCommandStringLength);
|
|
|
|
//
|
|
// Copy the string to the buffer
|
|
//
|
|
memcpy(BufferOfCommandString, CommandString.c_str(), CommandString.size());
|
|
|
|
//
|
|
// Check if there is a condition buffer in the command
|
|
//
|
|
if (!InterpretConditionsAndCodes(CommandTokens, TRUE, &ConditionBufferAddress, &ConditionBufferLength))
|
|
{
|
|
//
|
|
// Indicate condition is not available
|
|
//
|
|
HasConditionBuffer = FALSE;
|
|
|
|
/* ShowMessages("\nNo condition!\n"); */
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Indicate condition is available
|
|
//
|
|
HasConditionBuffer = TRUE;
|
|
|
|
/*
|
|
ShowMessages(
|
|
"\n========================= Condition =========================\n");
|
|
|
|
ShowMessages(
|
|
"\nUINT64 DebuggerCheckForCondition(PGUEST_REGS Regs_RCX, PVOID "
|
|
"Context_RDX)\n{\n");
|
|
|
|
//
|
|
// Disassemble the buffer
|
|
//
|
|
HyperDbgDisassembler64((unsigned char *)ConditionBufferAddress, 0x0,
|
|
ConditionBufferLength);
|
|
|
|
ShowMessages("}\n\n");
|
|
|
|
ShowMessages(
|
|
"=============================================================\n");
|
|
*/
|
|
}
|
|
|
|
//
|
|
// Check if there is a code buffer in the command
|
|
//
|
|
if (!InterpretConditionsAndCodes(CommandTokens, FALSE, &CodeBufferAddress, &CodeBufferLength))
|
|
{
|
|
//
|
|
// Indicate code is not available
|
|
//
|
|
HasCodeBuffer = FALSE;
|
|
//
|
|
// ShowMessages("\nNo custom code!\n");
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Indicate code is available
|
|
//
|
|
HasCodeBuffer = TRUE;
|
|
|
|
/*
|
|
ShowMessages(
|
|
"\n========================= Code =========================\n");
|
|
ShowMessages("\nPVOID DebuggerRunCustomCodeFunc(PVOID "
|
|
"PreAllocatedBufferAddress_RCX, "
|
|
"PGUEST_REGS Regs_RDX, PVOID Context_R8)\n{\n");
|
|
|
|
//
|
|
// Disassemble the buffer
|
|
//
|
|
HyperDbgDisassembler64((unsigned char *)CodeBufferAddress, 0x0,
|
|
CodeBufferLength);
|
|
|
|
ShowMessages("}\n\n");
|
|
|
|
ShowMessages(
|
|
"=============================================================\n");
|
|
*/
|
|
}
|
|
|
|
//
|
|
// Check if there is a Script block in the command
|
|
//
|
|
if (!InterpretScript(CommandTokens,
|
|
&HasScriptSyntaxError,
|
|
&ScriptBufferAddress,
|
|
&ScriptBufferLength,
|
|
&ScriptBufferPointer,
|
|
&ScriptCodeBuffer))
|
|
{
|
|
//
|
|
// Indicate code is not available
|
|
//
|
|
HasScript = FALSE;
|
|
//
|
|
// ShowMessages("\nNo script!\n");
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if there is a syntax error
|
|
//
|
|
if (HasScriptSyntaxError)
|
|
{
|
|
free(BufferOfCommandString);
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_SCRIPT_SYNTAX_ERROR;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Indicate code is available
|
|
//
|
|
HasScript = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if there is a output path in the command
|
|
//
|
|
if (!InterpretOutput(CommandTokens, ListOfOutputSources))
|
|
{
|
|
//
|
|
// Indicate output is not available
|
|
//
|
|
HasOutputPath = FALSE;
|
|
|
|
//
|
|
// ShowMessages("\nNo condition!\n");
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check for empty input
|
|
//
|
|
if (ListOfOutputSources.size() == 0)
|
|
{
|
|
//
|
|
// No input !
|
|
//
|
|
free(BufferOfCommandString);
|
|
|
|
ShowMessages("err, no input found\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_NO_INPUT;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check whether the size exceed maximum size or not
|
|
//
|
|
if (ListOfOutputSources.size() > DebuggerOutputSourceMaximumRemoteSourceForSingleEvent)
|
|
{
|
|
free(BufferOfCommandString);
|
|
|
|
ShowMessages(
|
|
"err, based on this build of HyperDbg, the maximum input sources for "
|
|
"a single event is 0x%x sources but you entered 0x%x sources\n",
|
|
DebuggerOutputSourceMaximumRemoteSourceForSingleEvent,
|
|
ListOfOutputSources.size());
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_MAXIMUM_INPUT_REACHED;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check whether the output source initialized or not
|
|
//
|
|
if (!g_OutputSourcesInitialized)
|
|
{
|
|
free(BufferOfCommandString);
|
|
|
|
ShowMessages("err, the name you entered, not found. Did you use "
|
|
"'output' command to create it?\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_OUTPUT_NAME_NOT_FOUND;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert output sources to the corresponding tags
|
|
//
|
|
for (auto item : ListOfOutputSources)
|
|
{
|
|
TempList = 0;
|
|
OutputSourceFound = FALSE;
|
|
|
|
//
|
|
// Now we should find the corresponding object in the list
|
|
// of output sources and save its tags
|
|
//
|
|
TempList = &g_OutputSources;
|
|
|
|
while (&g_OutputSources != TempList->Flink)
|
|
{
|
|
TempList = TempList->Flink;
|
|
|
|
PDEBUGGER_EVENT_FORWARDING CurrentOutputSourceDetails = CONTAINING_RECORD(TempList, DEBUGGER_EVENT_FORWARDING, OutputSourcesList);
|
|
|
|
if (strcmp(CurrentOutputSourceDetails->Name,
|
|
RemoveSpaces(item).c_str()) == 0)
|
|
{
|
|
//
|
|
// Check to see the source state, whether it is closed or not
|
|
//
|
|
if (CurrentOutputSourceDetails->State == EVENT_FORWARDING_CLOSED)
|
|
{
|
|
free(BufferOfCommandString);
|
|
|
|
ShowMessages("err, output source already closed\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_OUTPUT_SOURCE_ALREADY_CLOSED;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check to see the source state, whether it is opened or not
|
|
//
|
|
if (CurrentOutputSourceDetails->State == EVENT_FORWARDING_STATE_NOT_OPENED)
|
|
{
|
|
//
|
|
// Just show a message
|
|
//
|
|
ShowMessages("some of the output(s) are not opened, it's not an error, but please ensure "
|
|
"to open the output using the 'output' command to forward the results to the "
|
|
"target resource\n");
|
|
}
|
|
|
|
//
|
|
// Indicate that we found this item
|
|
//
|
|
OutputSourceFound = TRUE;
|
|
|
|
//
|
|
// Save the tag into a list which will be used later
|
|
//
|
|
ListOfValidSourceTags.push_back(
|
|
CurrentOutputSourceDetails->OutputUniqueTag);
|
|
|
|
//
|
|
// No need to search through the list anymore
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!OutputSourceFound)
|
|
{
|
|
free(BufferOfCommandString);
|
|
|
|
ShowMessages("err, the name you entered, not found. Did you use "
|
|
"'output' command to create it?\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_OUTPUT_NAME_NOT_FOUND;
|
|
return FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Indicate output is available
|
|
//
|
|
HasOutputPath = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create action and event based on previously parsed buffers
|
|
// (DEBUGGER_GENERAL_ACTION)
|
|
//
|
|
// Allocate the buffer (with ConditionBufferLength and CodeBufferLength)
|
|
//
|
|
|
|
/*
|
|
|
|
Layout of Buffer :
|
|
|
|
________________________________
|
|
| |
|
|
| DEBUGGER_GENERAL_EVENT_DETAIL |
|
|
| |
|
|
|________________________________|
|
|
| |
|
|
| Condition Buffer |
|
|
| |
|
|
|________________________________|
|
|
|
|
*/
|
|
|
|
/*
|
|
________________________________
|
|
| |
|
|
| DEBUGGER_GENERAL_ACTION |
|
|
| |
|
|
|________________________________|
|
|
| |
|
|
| Condition Custom Code |
|
|
| or Script Buffer |
|
|
|________________________________|
|
|
|
|
*/
|
|
|
|
LengthOfEventBuffer = sizeof(DEBUGGER_GENERAL_EVENT_DETAIL) + ConditionBufferLength;
|
|
|
|
TempEvent = (PDEBUGGER_GENERAL_EVENT_DETAIL)malloc(LengthOfEventBuffer);
|
|
RtlZeroMemory(TempEvent, LengthOfEventBuffer);
|
|
|
|
//
|
|
// Check if buffer is available
|
|
//
|
|
if (TempEvent == NULL)
|
|
{
|
|
ShowMessages("err, allocation error\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_ALLOCATION_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
//
|
|
// Event is enabled by default when created
|
|
//
|
|
TempEvent->IsEnabled = TRUE;
|
|
|
|
//
|
|
// Get a new tag for it
|
|
//
|
|
TempEvent->Tag = GetNewDebuggerEventTag();
|
|
|
|
//
|
|
// Set the core Id and Process Id to all cores and all
|
|
// processes, next time we check whether the user needs
|
|
// a special core or a special process then we change it
|
|
//
|
|
TempEvent->CoreId = DEBUGGER_EVENT_APPLY_TO_ALL_CORES;
|
|
|
|
if (g_ActiveProcessDebuggingState.IsActive)
|
|
{
|
|
ShowMessages("notice: as you're debugging a user-mode application, "
|
|
"this event will only trigger on your current debugging process "
|
|
"(pid:%x). If you want the event from the entire system, "
|
|
"add 'pid all' to the event\n",
|
|
g_ActiveProcessDebuggingState.ProcessId);
|
|
|
|
TempEvent->ProcessId = g_ActiveProcessDebuggingState.ProcessId;
|
|
}
|
|
else
|
|
{
|
|
TempEvent->ProcessId = DEBUGGER_EVENT_APPLY_TO_ALL_PROCESSES;
|
|
}
|
|
|
|
//
|
|
// Set the event type
|
|
//
|
|
TempEvent->EventType = EventType;
|
|
|
|
//
|
|
// Get the current time
|
|
//
|
|
TempEvent->CreationTime = time(0);
|
|
|
|
//
|
|
// Set buffer string command
|
|
//
|
|
TempEvent->CommandStringBuffer = BufferOfCommandString;
|
|
|
|
//
|
|
// Fill the buffer of condition for event
|
|
//
|
|
if (HasConditionBuffer)
|
|
{
|
|
memcpy((PVOID)((UINT64)TempEvent + sizeof(DEBUGGER_GENERAL_EVENT_DETAIL)),
|
|
(PVOID)ConditionBufferAddress,
|
|
ConditionBufferLength);
|
|
|
|
//
|
|
// Set the size of the buffer for event condition
|
|
//
|
|
TempEvent->ConditionBufferSize = ConditionBufferLength;
|
|
}
|
|
|
|
//
|
|
// Fill the buffer of custom code for action
|
|
//
|
|
if (HasCodeBuffer)
|
|
{
|
|
//
|
|
// Allocate the Action (THIS ACTION BUFFER WILL BE FREED WHEN WE SENT IT TO
|
|
// THE KERNEL AND RETURNED FROM THE KERNEL AS WE DON'T NEED IT ANYMORE)
|
|
//
|
|
LengthOfCustomCodeActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + CodeBufferLength;
|
|
|
|
TempActionCustomCode = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfCustomCodeActionBuffer);
|
|
|
|
RtlZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer);
|
|
|
|
memcpy(
|
|
(PVOID)((UINT64)TempActionCustomCode + sizeof(DEBUGGER_GENERAL_ACTION)),
|
|
(PVOID)CodeBufferAddress,
|
|
CodeBufferLength);
|
|
//
|
|
// Set the action Tag
|
|
//
|
|
TempActionCustomCode->EventTag = TempEvent->Tag;
|
|
|
|
//
|
|
// Set the action type
|
|
//
|
|
TempActionCustomCode->ActionType = RUN_CUSTOM_CODE;
|
|
|
|
//
|
|
// Set the action buffer size
|
|
//
|
|
TempActionCustomCode->CustomCodeBufferSize = CodeBufferLength;
|
|
|
|
//
|
|
// Increase the count of actions
|
|
//
|
|
TempEvent->CountOfActions = TempEvent->CountOfActions + 1;
|
|
}
|
|
|
|
//
|
|
// Fill the buffer of script for action
|
|
//
|
|
if (HasScript)
|
|
{
|
|
//
|
|
// Allocate the Action (THIS ACTION BUFFER WILL BE FREED WHEN WE SENT IT TO
|
|
// THE KERNEL AND RETURNED FROM THE KERNEL AS WE DON'T NEED IT ANYMORE)
|
|
//
|
|
LengthOfScriptActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + ScriptBufferLength;
|
|
TempActionScript = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfScriptActionBuffer);
|
|
|
|
RtlZeroMemory(TempActionScript, LengthOfScriptActionBuffer);
|
|
|
|
memcpy((PVOID)((UINT64)TempActionScript + sizeof(DEBUGGER_GENERAL_ACTION)),
|
|
(PVOID)ScriptBufferAddress,
|
|
ScriptBufferLength);
|
|
//
|
|
// Set the action Tag
|
|
//
|
|
TempActionScript->EventTag = TempEvent->Tag;
|
|
|
|
//
|
|
// Set the action type
|
|
//
|
|
TempActionScript->ActionType = RUN_SCRIPT;
|
|
|
|
//
|
|
// Set the action buffer size and pointer
|
|
//
|
|
TempActionScript->ScriptBufferSize = ScriptBufferLength;
|
|
TempActionScript->ScriptBufferPointer = ScriptBufferPointer;
|
|
|
|
//
|
|
// Increase the count of actions
|
|
//
|
|
TempEvent->CountOfActions = TempEvent->CountOfActions + 1;
|
|
|
|
//
|
|
// Free the buffer of script related functions
|
|
//
|
|
ScriptEngineWrapperRemoveSymbolBuffer((PVOID)ScriptCodeBuffer);
|
|
}
|
|
|
|
//
|
|
// If this action didn't contain a buffer for custom code and
|
|
// a buffer for script then it's a break to debugger
|
|
//
|
|
if (!HasCodeBuffer && !HasScript)
|
|
{
|
|
//
|
|
// Allocate the Action (THIS ACTION BUFFER WILL BE FREED WHEN WE SENT IT TO
|
|
// THE KERNEL AND RETURNED FROM THE KERNEL AS WE DON'T NEED IT ANYMORE)
|
|
//
|
|
LengthOfBreakActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION);
|
|
|
|
TempActionBreak = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfBreakActionBuffer);
|
|
|
|
RtlZeroMemory(TempActionBreak, LengthOfBreakActionBuffer);
|
|
|
|
//
|
|
// Set the action Tag
|
|
//
|
|
TempActionBreak->EventTag = TempEvent->Tag;
|
|
|
|
//
|
|
// Set the action type
|
|
//
|
|
TempActionBreak->ActionType = BREAK_TO_DEBUGGER;
|
|
|
|
//
|
|
// Increase the count of actions
|
|
//
|
|
TempEvent->CountOfActions = TempEvent->CountOfActions + 1;
|
|
}
|
|
|
|
//
|
|
// Interpret rest of the command
|
|
//
|
|
for (auto Section : *CommandTokens)
|
|
{
|
|
Index++;
|
|
|
|
if (IsNextCommandBufferSize)
|
|
{
|
|
if (!ConvertTokenToUInt32(Section, &RequestBuffer))
|
|
{
|
|
ShowMessages("err, buffer size is invalid\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the specific requested buffer size
|
|
//
|
|
if (TempActionBreak != NULL)
|
|
{
|
|
TempActionBreak->PreAllocatedBuffer = RequestBuffer;
|
|
}
|
|
if (TempActionScript != NULL)
|
|
{
|
|
TempActionScript->PreAllocatedBuffer = RequestBuffer;
|
|
}
|
|
if (TempActionCustomCode != NULL)
|
|
{
|
|
TempActionCustomCode->PreAllocatedBuffer = RequestBuffer;
|
|
}
|
|
}
|
|
IsNextCommandBufferSize = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsNextCommandImmediateMessaging)
|
|
{
|
|
if (CompareLowerCaseStrings(Section, "yes"))
|
|
{
|
|
ImmediateMessagePassing = TRUE;
|
|
}
|
|
else if (CompareLowerCaseStrings(Section, "no"))
|
|
{
|
|
ImmediateMessagePassing = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// err, not token recognized error
|
|
//
|
|
|
|
ShowMessages("err, immediate messaging token is invalid\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
IsNextCommandImmediateMessaging = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsNextCommandExecutionStage)
|
|
{
|
|
if (CompareLowerCaseStrings(Section, "pre"))
|
|
{
|
|
CallingStage = VMM_CALLBACK_CALLING_STAGE_PRE_EVENT_EMULATION;
|
|
}
|
|
else if (CompareLowerCaseStrings(Section, "post"))
|
|
{
|
|
CallingStage = VMM_CALLBACK_CALLING_STAGE_POST_EVENT_EMULATION;
|
|
}
|
|
else if (CompareLowerCaseStrings(Section, "all"))
|
|
{
|
|
CallingStage = VMM_CALLBACK_CALLING_STAGE_ALL_EVENT_EMULATION;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// err, not token recognized error
|
|
//
|
|
|
|
ShowMessages("err, the specified execution mode is invalid; you can either choose 'pre' or 'post'\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
IsNextCommandExecutionStage = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsNextCommandSc)
|
|
{
|
|
if (CompareLowerCaseStrings(Section, "on"))
|
|
{
|
|
IsAShortCircuitingEventByDefault = TRUE;
|
|
}
|
|
else if (CompareLowerCaseStrings(Section, "off"))
|
|
{
|
|
IsAShortCircuitingEventByDefault = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// err, not token recognized error
|
|
//
|
|
|
|
ShowMessages("err, the specified short-circuiting state is invalid; you can either choose 'on' or 'off'\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
IsNextCommandSc = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsNextCommandPid)
|
|
{
|
|
if (CompareLowerCaseStrings(Section, "all"))
|
|
{
|
|
TempEvent->ProcessId = DEBUGGER_EVENT_APPLY_TO_ALL_PROCESSES;
|
|
}
|
|
else if (!ConvertTokenToUInt32(Section, &ProcessId))
|
|
{
|
|
ShowMessages("err, pid is invalid\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the specific process id
|
|
//
|
|
TempEvent->ProcessId = ProcessId;
|
|
}
|
|
|
|
IsNextCommandPid = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsNextCommandCoreId)
|
|
{
|
|
if (!ConvertTokenToUInt32(Section, &CoreId))
|
|
{
|
|
ShowMessages("err, core id is invalid\n");
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
goto ReturnWithError;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the specific core id
|
|
//
|
|
TempEvent->CoreId = CoreId;
|
|
}
|
|
IsNextCommandCoreId = FALSE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "pid"))
|
|
{
|
|
IsNextCommandPid = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
if (CompareLowerCaseStrings(Section, "core"))
|
|
{
|
|
IsNextCommandCoreId = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "imm"))
|
|
{
|
|
//
|
|
// the next command is immediate messaging indicator
|
|
//
|
|
IsNextCommandImmediateMessaging = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "stage"))
|
|
{
|
|
//
|
|
// the next command is execution mode (pre- and post-events)
|
|
//
|
|
IsNextCommandExecutionStage = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "sc"))
|
|
{
|
|
//
|
|
// the next command is the default short-circuiting state
|
|
//
|
|
IsNextCommandSc = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CompareLowerCaseStrings(Section, "buffer"))
|
|
{
|
|
IsNextCommandBufferSize = TRUE;
|
|
|
|
//
|
|
// Add index to remove it from the command
|
|
//
|
|
IndexesToRemove.push_back(Index);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Additional validation
|
|
//
|
|
if (IsNextCommandCoreId)
|
|
{
|
|
ShowMessages("err, please specify a value for 'core'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
if (IsNextCommandPid)
|
|
{
|
|
ShowMessages("err, please specify a value for 'pid'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
if (IsNextCommandBufferSize)
|
|
{
|
|
ShowMessages("err, please specify a value for 'buffer'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
if (IsNextCommandImmediateMessaging)
|
|
{
|
|
ShowMessages("err, please specify a value for 'imm'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
if (IsNextCommandExecutionStage)
|
|
{
|
|
ShowMessages("err, please specify a value for 'stage'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
if (IsNextCommandSc)
|
|
{
|
|
ShowMessages("err, please specify a value for 'sc'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_FORMAT_ERROR;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
//
|
|
// Check to make sure that short-circuiting is not used in post-events
|
|
//
|
|
if ((CallingStage == VMM_CALLBACK_CALLING_STAGE_POST_EVENT_EMULATION ||
|
|
CallingStage == VMM_CALLBACK_CALLING_STAGE_ALL_EVENT_EMULATION) &&
|
|
IsAShortCircuitingEventByDefault)
|
|
{
|
|
ShowMessages(
|
|
"err, using the short-circuiting mechanism with 'post' or 'all' stage events "
|
|
"doesn't make sense; it's not supported!\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_USING_SHORT_CIRCUITING_IN_POST_EVENTS;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
//
|
|
// It's not possible to break to debugger in VMI-mode
|
|
//
|
|
if (!g_IsSerialConnectedToRemoteDebuggee && TempActionBreak != NULL)
|
|
{
|
|
ShowMessages(
|
|
"err, the script or assembly code is either not found or invalid. "
|
|
"As a result, the default action is to break. "
|
|
"However, breaking to the debugger is not possible in the VMI Mode. "
|
|
"To achieve full control of the system, you can switch to the Debugger Mode. "
|
|
"In the VMI Mode, you can still use scripts and run custom code for local debugging."
|
|
"For more information, please check: https://docs.hyperdbg.org/using-hyperdbg/prerequisites/operation-modes\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_ATTEMPT_TO_BREAK_ON_VMI_MODE;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
//
|
|
// We do not support non-immediate message passing if the user
|
|
// specified a special output
|
|
//
|
|
if (!ImmediateMessagePassing && HasOutputPath)
|
|
{
|
|
ShowMessages("err, non-immediate message passing is not supported in "
|
|
"'output-forwarding mode'\n");
|
|
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_IMMEDIATE_MESSAGING_IN_EVENT_FORWARDING_MODE;
|
|
|
|
goto ReturnWithError;
|
|
}
|
|
|
|
//
|
|
// Set the specific requested immediate message passing
|
|
//
|
|
if (TempActionBreak != NULL)
|
|
{
|
|
TempActionBreak->ImmediateMessagePassing = ImmediateMessagePassing;
|
|
}
|
|
if (TempActionScript != NULL)
|
|
{
|
|
TempActionScript->ImmediateMessagePassing = ImmediateMessagePassing;
|
|
}
|
|
if (TempActionCustomCode != NULL)
|
|
{
|
|
TempActionCustomCode->ImmediateMessagePassing = ImmediateMessagePassing;
|
|
}
|
|
|
|
//
|
|
// Set the tags into the event list
|
|
//
|
|
IndexOfValidSourceTags = 0;
|
|
for (auto item : ListOfValidSourceTags)
|
|
{
|
|
TempEvent->OutputSourceTags[IndexOfValidSourceTags] = item;
|
|
|
|
//
|
|
// Increase index
|
|
//
|
|
IndexOfValidSourceTags++;
|
|
}
|
|
|
|
//
|
|
// If it has output then we should indicate it in event's object
|
|
//
|
|
if (HasOutputPath)
|
|
{
|
|
TempEvent->HasCustomOutput = TRUE;
|
|
}
|
|
|
|
//
|
|
// Set the specific requested short-circuiting state
|
|
//
|
|
if (IsAShortCircuitingEventByDefault)
|
|
{
|
|
TempEvent->EnableShortCircuiting = TRUE;
|
|
}
|
|
|
|
//
|
|
// Set the specific event mode (calling stage)
|
|
//
|
|
TempEvent->EventStage = CallingStage;
|
|
|
|
//
|
|
// Fill the address and length of event before release
|
|
//
|
|
*EventDetailsToFill = TempEvent;
|
|
*EventBufferLength = LengthOfEventBuffer;
|
|
|
|
//
|
|
// Fill the address and length of action before release
|
|
//
|
|
if (TempActionBreak != NULL)
|
|
{
|
|
*ActionDetailsToFillBreakToDebugger = TempActionBreak;
|
|
*ActionBufferLengthBreakToDebugger = LengthOfBreakActionBuffer;
|
|
}
|
|
if (TempActionScript != NULL)
|
|
{
|
|
*ActionDetailsToFillScript = TempActionScript;
|
|
*ActionBufferLengthScript = LengthOfScriptActionBuffer;
|
|
}
|
|
if (TempActionCustomCode != NULL)
|
|
{
|
|
*ActionDetailsToFillCustomCode = TempActionCustomCode;
|
|
*ActionBufferLengthCustomCode = LengthOfCustomCodeActionBuffer;
|
|
}
|
|
|
|
//
|
|
// Remove the command that we interpreted above from the command
|
|
//
|
|
for (auto IndexToRemove : IndexesToRemove)
|
|
{
|
|
NewIndexToRemove++;
|
|
CommandTokens->erase(CommandTokens->begin() + (IndexToRemove - NewIndexToRemove));
|
|
}
|
|
|
|
//
|
|
// Check if list is initialized or not
|
|
//
|
|
if (!g_EventTraceInitialized)
|
|
{
|
|
InitializeListHead(&g_EventTrace);
|
|
g_EventTraceInitialized = TRUE;
|
|
}
|
|
|
|
//
|
|
// Add the event to the trace list
|
|
// UPDATE : if we add it here, then if the event was not
|
|
// successful then still the event shows this event, so
|
|
// we have to add it lated
|
|
//
|
|
// InsertHeadList(&g_EventTrace, &(TempEvent->CommandsEventList));
|
|
|
|
//
|
|
// Everything is ok, let's return TRUE
|
|
//
|
|
*ReasonForErrorInParsing = DEBUGGER_EVENT_PARSING_ERROR_CAUSE_SUCCESSFUL_NO_ERROR;
|
|
return TRUE;
|
|
|
|
ReturnWithError:
|
|
|
|
if (BufferOfCommandString)
|
|
{
|
|
free(BufferOfCommandString);
|
|
}
|
|
|
|
if (TempEvent)
|
|
{
|
|
free(TempEvent);
|
|
}
|
|
|
|
if (TempActionBreak != NULL)
|
|
{
|
|
free(TempActionBreak);
|
|
}
|
|
if (TempActionScript != NULL)
|
|
{
|
|
free(TempActionScript);
|
|
}
|
|
if (TempActionCustomCode != NULL)
|
|
{
|
|
free(TempActionCustomCode);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|