mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-10 01:29:59 +00:00
2777 lines
81 KiB
C++
2777 lines
81 KiB
C++
/**
|
|
* @file kd.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief routines to kernel debugging
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2020-12-22
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "..\hprdbgctrl\pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern HANDLE g_SerialListeningThreadHandle;
|
|
extern HANDLE g_SerialRemoteComPortHandle;
|
|
extern HANDLE g_DebuggeeStopCommandEventHandle;
|
|
extern DEBUGGER_SYNCRONIZATION_EVENTS_STATE
|
|
g_KernelSyncronizationObjectsHandleTable[DEBUGGER_MAXIMUM_SYNCRONIZATION_KERNEL_DEBUGGER_OBJECTS];
|
|
extern BYTE g_CurrentRunningInstruction[MAXIMUM_INSTR_SIZE];
|
|
extern BOOLEAN g_IsConnectedToHyperDbgLocally;
|
|
extern OVERLAPPED g_OverlappedIoStructureForReadDebugger;
|
|
extern OVERLAPPED g_OverlappedIoStructureForWriteDebugger;
|
|
extern DEBUGGER_EVENT_AND_ACTION_REG_BUFFER g_DebuggeeResultOfRegisteringEvent;
|
|
extern DEBUGGER_EVENT_AND_ACTION_REG_BUFFER
|
|
g_DebuggeeResultOfAddingActionsToEvent;
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee;
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebugger;
|
|
extern BOOLEAN g_IsDebuggerConntectedToNamedPipe;
|
|
extern BOOLEAN g_IsDebuggeeRunning;
|
|
extern BOOLEAN g_IsDebuggerModulesLoaded;
|
|
extern BOOLEAN g_SerialConnectionAlreadyClosed;
|
|
extern BOOLEAN g_IgnoreNewLoggingMessages;
|
|
extern BOOLEAN g_SharedEventStatus;
|
|
extern BOOLEAN g_IsRunningInstruction32Bit;
|
|
extern BOOLEAN g_IgnorePauseRequests;
|
|
extern BYTE g_EndOfBufferCheckSerial[4];
|
|
extern ULONG g_CurrentRemoteCore;
|
|
|
|
/**
|
|
* @brief compares the buffer with a string
|
|
*
|
|
* @param CurrentLoopIndex Number of previously read bytes
|
|
* @param Buffer
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdCheckForTheEndOfTheBuffer(PUINT32 CurrentLoopIndex, BYTE * Buffer)
|
|
{
|
|
UINT32 ActualBufferLength;
|
|
|
|
ActualBufferLength = *CurrentLoopIndex;
|
|
|
|
//
|
|
// End of buffer is 4 character long
|
|
//
|
|
if (*CurrentLoopIndex <= 3)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Buffer[ActualBufferLength] == SERIAL_END_OF_BUFFER_CHAR_4 &&
|
|
Buffer[ActualBufferLength - 1] == SERIAL_END_OF_BUFFER_CHAR_3 &&
|
|
Buffer[ActualBufferLength - 2] == SERIAL_END_OF_BUFFER_CHAR_2 &&
|
|
Buffer[ActualBufferLength - 3] == SERIAL_END_OF_BUFFER_CHAR_1)
|
|
{
|
|
//
|
|
// Clear the end character
|
|
//
|
|
Buffer[ActualBufferLength - 3] = NULL;
|
|
Buffer[ActualBufferLength - 2] = NULL;
|
|
Buffer[ActualBufferLength - 1] = NULL;
|
|
Buffer[ActualBufferLength] = NULL;
|
|
|
|
//
|
|
// Set the new length
|
|
//
|
|
*CurrentLoopIndex = ActualBufferLength - 3;
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief compares the buffer with a string
|
|
*
|
|
* @param Buffer
|
|
* @param CompareBuffer
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdCompareBufferWithString(CHAR * Buffer, const CHAR * CompareBuffer)
|
|
{
|
|
int Result;
|
|
|
|
Result = strcmp(Buffer, CompareBuffer);
|
|
|
|
if (Result == 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief calculate the checksum of recived buffer from debugger
|
|
*
|
|
* @param Buffer
|
|
* @param LengthReceived
|
|
* @return BYTE
|
|
*/
|
|
BYTE
|
|
KdComputeDataChecksum(PVOID Buffer, UINT32 Length)
|
|
{
|
|
BYTE CalculatedCheckSum = 0;
|
|
BYTE Temp = 0;
|
|
while (Length--)
|
|
{
|
|
Temp = *(BYTE *)Buffer;
|
|
CalculatedCheckSum = CalculatedCheckSum + Temp;
|
|
Buffer = (PVOID)((UINT64)Buffer + 1);
|
|
}
|
|
return CalculatedCheckSum;
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret the packets from debuggee in the case of paused
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdInterpretPausedDebuggee()
|
|
{
|
|
//
|
|
// Wait for handshake to complete or in other words
|
|
// get the receive packet
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_PAUSED_DEBUGGEE_DETAILS]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_PAUSED_DEBUGGEE_DETAILS]
|
|
.EventHandle,
|
|
INFINITE);
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a continue or 'g' command packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendContinuePacketToDebuggee()
|
|
{
|
|
//
|
|
// No core
|
|
//
|
|
g_CurrentRemoteCore = DEBUGGER_DEBUGGEE_IS_RUNNING_NO_CORE;
|
|
|
|
//
|
|
// Send 'g' as continue packet
|
|
//
|
|
if (!KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CONTINUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a change core or '~ x' command packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendSwitchCorePacketToDebuggee(UINT32 NewCore)
|
|
{
|
|
DEBUGGEE_CHANGE_CORE_PACKET CoreChangePacket = {0};
|
|
|
|
CoreChangePacket.NewCore = NewCore;
|
|
|
|
if (CoreChangePacket.NewCore == g_CurrentRemoteCore)
|
|
{
|
|
//
|
|
// We show an error message here when there is no core change (because the
|
|
// target core and current operating core is the same)
|
|
//
|
|
ShowMessages("the current operating core is %x (not changed)\n",
|
|
CoreChangePacket.NewCore);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Send '~' as switch packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_CORE,
|
|
(CHAR *)&CoreChangePacket,
|
|
sizeof(DEBUGGEE_CHANGE_CORE_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of core change received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_CORE_SWITCHING_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_CORE_SWITCHING_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a query or request to enable/disable/clear for event
|
|
* @details if IsQueryState is TRUE then TypeOfAction is ignored
|
|
* @param Tag
|
|
* @param TypeOfAction
|
|
* @param IsEnabled If it's a query state then this argument can be used
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendEventQueryAndModifyPacketToDebuggee(
|
|
UINT64 Tag,
|
|
DEBUGGER_MODIFY_EVENTS_TYPE TypeOfAction,
|
|
BOOLEAN * IsEnabled)
|
|
{
|
|
DEBUGGER_MODIFY_EVENTS ModifyAndQueryEventPacket = {0};
|
|
|
|
g_SharedEventStatus = FALSE;
|
|
|
|
//
|
|
// Fill the structure of packet
|
|
//
|
|
ModifyAndQueryEventPacket.Tag = Tag;
|
|
ModifyAndQueryEventPacket.TypeOfAction = TypeOfAction;
|
|
|
|
//
|
|
// Send modify and query event packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_QUERY_AND_MODIFY_EVENT,
|
|
(CHAR *)&ModifyAndQueryEventPacket,
|
|
sizeof(DEBUGGER_MODIFY_EVENTS)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of query and modify event is received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_MODIFY_AND_QUERY_EVENT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_MODIFY_AND_QUERY_EVENT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
if (TypeOfAction == DEBUGGER_MODIFY_EVENTS_QUERY_STATE)
|
|
{
|
|
//
|
|
// We should read the results to set IsEnabled variable
|
|
//
|
|
*IsEnabled = g_SharedEventStatus;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a flush request to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendFlushPacketToDebuggee()
|
|
{
|
|
DEBUGGER_FLUSH_LOGGING_BUFFERS FlushPacket = {0};
|
|
|
|
//
|
|
// Send 'flush' command as flush packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_FLUSH_BUFFERS,
|
|
(CHAR *)&FlushPacket,
|
|
sizeof(DEBUGGER_FLUSH_LOGGING_BUFFERS)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of flushing received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_FLUSH_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_FLUSH_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a test query request to the debuggee
|
|
*
|
|
* @param Option
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendTestQueryPacketToDebuggee(UINT32 RequestIndex)
|
|
{
|
|
DEBUGGER_DEBUGGER_TEST_QUERY_BUFFER TestQueryPacket = {0};
|
|
|
|
TestQueryPacket.RequestIndex = RequestIndex;
|
|
|
|
//
|
|
// Send 'test query' command as query packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_TEST_QUERY,
|
|
(CHAR *)&TestQueryPacket,
|
|
sizeof(DEBUGGER_DEBUGGER_TEST_QUERY_BUFFER)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of test query is received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_TEST_QUERY]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_TEST_QUERY]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send symbol reload packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendSymbolReloadPacketToDebuggee()
|
|
{
|
|
//
|
|
// Send '.sym reload' as symbol reload packet
|
|
//
|
|
if (!KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_SYMBOL_RELOAD))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until all of the symbol packets are received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SYMBOL_RELOAD]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SYMBOL_RELOAD]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a Read register packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendReadRegisterPacketToDebuggee(PDEBUGGEE_REGISTER_READ_DESCRIPTION RegDes)
|
|
{
|
|
//
|
|
// Send r command as read register packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_READ_REGISTERS,
|
|
(CHAR *)RegDes,
|
|
sizeof(DEBUGGEE_REGISTER_READ_DESCRIPTION)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of read registers received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_READ_REGISTERS]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_READ_REGISTERS]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a Read memory packet to the debuggee
|
|
* @param ReadMem
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendReadMemoryPacketToDebuggee(PDEBUGGER_READ_MEMORY ReadMem)
|
|
{
|
|
//
|
|
// Send d command as read memory packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_READ_MEMORY,
|
|
(CHAR *)ReadMem,
|
|
sizeof(DEBUGGER_READ_MEMORY)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of read registers received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_READ_MEMORY]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_READ_MEMORY]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send an Edit memory packet to the debuggee
|
|
* @param EditMem
|
|
* @param Size
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendEditMemoryPacketToDebuggee(PDEBUGGER_EDIT_MEMORY EditMem, UINT32 Size)
|
|
{
|
|
//
|
|
// Send d command as read memory packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_EDIT_MEMORY,
|
|
(CHAR *)EditMem,
|
|
Size))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of read registers received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_EDIT_MEMORY]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_EDIT_MEMORY]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send a register event request to the debuggee
|
|
* @details as this command uses one global variable to transfer the buffers
|
|
* so should not be called simultaneously
|
|
* @param Event
|
|
* @param EventBufferLength
|
|
*
|
|
* @return PDEBUGGER_EVENT_AND_ACTION_REG_BUFFER
|
|
*/
|
|
PDEBUGGER_EVENT_AND_ACTION_REG_BUFFER
|
|
KdSendRegisterEventPacketToDebuggee(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
|
|
UINT32 EventBufferLength)
|
|
{
|
|
PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET Header;
|
|
UINT32 Len;
|
|
|
|
Len = EventBufferLength +
|
|
sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET);
|
|
|
|
Header = (PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)malloc(Len);
|
|
|
|
if (Header == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(Header, Len);
|
|
|
|
//
|
|
// Set length in header
|
|
//
|
|
Header->Length = EventBufferLength;
|
|
|
|
//
|
|
// Move buffer
|
|
//
|
|
memcpy((PVOID)((UINT64)Header +
|
|
sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)),
|
|
(PVOID)Event,
|
|
EventBufferLength);
|
|
|
|
RtlZeroMemory(&g_DebuggeeResultOfRegisteringEvent,
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER));
|
|
|
|
//
|
|
// Send register event packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_REGISTER_EVENT,
|
|
(CHAR *)Header,
|
|
Len))
|
|
{
|
|
free(Header);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of registering received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_REGISTER_EVENT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_REGISTER_EVENT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
free(Header);
|
|
|
|
return &g_DebuggeeResultOfRegisteringEvent;
|
|
}
|
|
|
|
/**
|
|
* @brief Send an add action to event request to the debuggee
|
|
* @details as this command uses one global variable to transfer the buffers
|
|
* so should not be called simultaneously
|
|
* @param GeneralAction
|
|
* @param GeneralActionLength
|
|
*
|
|
* @return PDEBUGGER_EVENT_AND_ACTION_REG_BUFFER
|
|
*/
|
|
PDEBUGGER_EVENT_AND_ACTION_REG_BUFFER
|
|
KdSendAddActionToEventPacketToDebuggee(PDEBUGGER_GENERAL_ACTION GeneralAction,
|
|
UINT32 GeneralActionLength)
|
|
{
|
|
PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET Header;
|
|
UINT32 Len;
|
|
|
|
Len = GeneralActionLength +
|
|
sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET);
|
|
|
|
Header = (PDEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)malloc(Len);
|
|
|
|
if (Header == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(Header, Len);
|
|
|
|
//
|
|
// Set length in header
|
|
//
|
|
Header->Length = GeneralActionLength;
|
|
|
|
//
|
|
// Move buffer
|
|
//
|
|
memcpy((PVOID)((UINT64)Header +
|
|
sizeof(DEBUGGEE_EVENT_AND_ACTION_HEADER_FOR_REMOTE_PACKET)),
|
|
(PVOID)GeneralAction,
|
|
GeneralActionLength);
|
|
|
|
RtlZeroMemory(&g_DebuggeeResultOfAddingActionsToEvent,
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER));
|
|
|
|
//
|
|
// Send add action to event packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_ADD_ACTION_TO_EVENT,
|
|
(CHAR *)Header,
|
|
Len))
|
|
{
|
|
free(Header);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of adding action to event received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_ADD_ACTION_TO_EVENT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_ADD_ACTION_TO_EVENT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
free(Header);
|
|
|
|
return &g_DebuggeeResultOfAddingActionsToEvent;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a change process or show process details packet to the debuggee
|
|
* @param ActionType
|
|
* @param NewPid
|
|
* @param NewProcess
|
|
* @param SetChangeByClockInterrupt
|
|
* @param SymDetailsForProcessList
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendSwitchProcessPacketToDebuggee(DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_TYPE ActionType,
|
|
UINT32 NewPid,
|
|
UINT64 NewProcess,
|
|
BOOLEAN SetChangeByClockInterrupt,
|
|
PDEBUGGEE_PROCESS_LIST_NEEDED_DETAILS SymDetailsForProcessList)
|
|
{
|
|
DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET ProcessChangePacket = {0};
|
|
|
|
ProcessChangePacket.ActionType = ActionType;
|
|
ProcessChangePacket.ProcessId = NewPid;
|
|
ProcessChangePacket.Process = NewProcess;
|
|
ProcessChangePacket.IsSwitchByClkIntr = SetChangeByClockInterrupt;
|
|
|
|
//
|
|
// Check if the command really needs these information or not
|
|
// it's because some of the command don't need symbol offset informations
|
|
//
|
|
if (SymDetailsForProcessList != NULL)
|
|
{
|
|
memcpy(&ProcessChangePacket.ProcessListSymDetails, SymDetailsForProcessList, sizeof(DEBUGGEE_PROCESS_LIST_NEEDED_DETAILS));
|
|
}
|
|
|
|
//
|
|
// Send '.process' as switch packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_PROCESS,
|
|
(CHAR *)&ProcessChangePacket,
|
|
sizeof(DEBUGGEE_DETAILS_AND_SWITCH_PROCESS_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of process change received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_PROCESS_SWITCHING_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_PROCESS_SWITCHING_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a change thread or show threads detail packet to the debuggee
|
|
* @param ActionType
|
|
* @param NewTid
|
|
* @param NewThread
|
|
* @param CheckByClockInterrupt
|
|
* @param SymDetailsForThreadList
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendSwitchThreadPacketToDebuggee(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_TYPE ActionType,
|
|
UINT32 NewTid,
|
|
UINT64 NewThread,
|
|
BOOLEAN CheckByClockInterrupt,
|
|
PDEBUGGEE_THREAD_LIST_NEEDED_DETAILS SymDetailsForThreadList)
|
|
{
|
|
DEBUGGEE_DETAILS_AND_SWITCH_THREAD_PACKET ThreadChangePacket = {0};
|
|
|
|
ThreadChangePacket.ActionType = ActionType;
|
|
ThreadChangePacket.ThreadId = NewTid;
|
|
ThreadChangePacket.Thread = NewThread;
|
|
ThreadChangePacket.CheckByClockInterrupt = CheckByClockInterrupt;
|
|
|
|
//
|
|
// Check if the command really needs these information or not
|
|
// it's because some of the command don't need symbol offset informations
|
|
//
|
|
if (SymDetailsForThreadList != NULL)
|
|
{
|
|
memcpy(&ThreadChangePacket.ThreadListSymDetails, SymDetailsForThreadList, sizeof(DEBUGGEE_THREAD_LIST_NEEDED_DETAILS));
|
|
}
|
|
|
|
//
|
|
// Send '.thread' as switch packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CHANGE_THREAD,
|
|
(CHAR *)&ThreadChangePacket,
|
|
sizeof(DEBUGGEE_DETAILS_AND_SWITCH_THREAD_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of thread change received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_THREAD_SWITCHING_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_THREAD_SWITCHING_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a breakpoint set or 'bp' command packet to the debuggee
|
|
* @param BpPacket
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendBpPacketToDebuggee(PDEBUGGEE_BP_PACKET BpPacket)
|
|
{
|
|
//
|
|
// Send 'bp' as a breakpoint packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_BP,
|
|
(CHAR *)BpPacket,
|
|
sizeof(DEBUGGEE_BP_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of putting breakpoint is received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_BP]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_BP]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a breakpoint list or modification packet to the debuggee
|
|
* @param ListOrModifyPacket
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendListOrModifyPacketToDebuggee(
|
|
PDEBUGGEE_BP_LIST_OR_MODIFY_PACKET ListOrModifyPacket)
|
|
{
|
|
//
|
|
// Send list or modify breakpoint packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_LIST_OR_MODIFY_BREAKPOINTS,
|
|
(CHAR *)ListOrModifyPacket,
|
|
sizeof(DEBUGGEE_BP_LIST_OR_MODIFY_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of listing or modifying breakpoints is received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_LIST_OR_MODIFY_BREAKPOINTS]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_LIST_OR_MODIFY_BREAKPOINTS]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a script packet to the debuggee
|
|
* @param BufferAddress
|
|
* @param BufferLength
|
|
* @param Pointer
|
|
* @param IsFormat
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendScriptPacketToDebuggee(UINT64 BufferAddress, UINT32 BufferLength, UINT32 Pointer, BOOLEAN IsFormat)
|
|
{
|
|
PDEBUGGEE_SCRIPT_PACKET ScriptPacket;
|
|
UINT32 SizeOfStruct = 0;
|
|
|
|
SizeOfStruct = sizeof(DEBUGGEE_SCRIPT_PACKET) + BufferLength;
|
|
|
|
ScriptPacket = (DEBUGGEE_SCRIPT_PACKET *)malloc(SizeOfStruct);
|
|
|
|
RtlZeroMemory(ScriptPacket, SizeOfStruct);
|
|
|
|
//
|
|
// Fill the script packet buffer
|
|
//
|
|
ScriptPacket->ScriptBufferSize = BufferLength;
|
|
ScriptPacket->ScriptBufferPointer = Pointer;
|
|
ScriptPacket->IsFormat = IsFormat;
|
|
|
|
//
|
|
// Move the buffer at the bottom of the script packet
|
|
//
|
|
memcpy((PVOID)((UINT64)ScriptPacket + sizeof(DEBUGGEE_SCRIPT_PACKET)),
|
|
(PVOID)BufferAddress,
|
|
BufferLength);
|
|
|
|
//
|
|
// Send script packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_RUN_SCRIPT,
|
|
(CHAR *)ScriptPacket,
|
|
SizeOfStruct))
|
|
{
|
|
free(ScriptPacket);
|
|
return FALSE;
|
|
}
|
|
|
|
if (IsFormat)
|
|
{
|
|
//
|
|
// Wait for formats results too
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SCRIPT_FORMATS_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SCRIPT_FORMATS_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
}
|
|
|
|
//
|
|
// Wait until the result of script engine received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SCRIPT_RUNNING_RESULT]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SCRIPT_RUNNING_RESULT]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
free(ScriptPacket);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends user input packet to the debuggee
|
|
* @param Sendbuf
|
|
* @param Len
|
|
* @param IgnoreBreakingAgain
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendUserInputPacketToDebuggee(const char * Sendbuf, int Len, BOOLEAN IgnoreBreakingAgain)
|
|
{
|
|
PDEBUGGEE_USER_INPUT_PACKET UserInputPacket;
|
|
UINT32 SizeOfStruct = 0;
|
|
|
|
SizeOfStruct = sizeof(DEBUGGEE_USER_INPUT_PACKET) + Len;
|
|
|
|
UserInputPacket = (DEBUGGEE_USER_INPUT_PACKET *)malloc(SizeOfStruct);
|
|
|
|
RtlZeroMemory(UserInputPacket, SizeOfStruct);
|
|
|
|
//
|
|
// Fill the script packet buffer descriptors
|
|
//
|
|
UserInputPacket->CommandLen = Len;
|
|
UserInputPacket->IgnoreFinishedSignal = IgnoreBreakingAgain;
|
|
|
|
//
|
|
// Move the user input buffer at the bottom of the structure packet
|
|
//
|
|
memcpy((PVOID)((UINT64)UserInputPacket + sizeof(DEBUGGEE_USER_INPUT_PACKET)),
|
|
(PVOID)Sendbuf,
|
|
Len);
|
|
|
|
//
|
|
// Send user-input packet
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_USER_INPUT_BUFFER,
|
|
(CHAR *)UserInputPacket,
|
|
SizeOfStruct))
|
|
{
|
|
free(UserInputPacket);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of user-input received
|
|
//
|
|
if (!IgnoreBreakingAgain)
|
|
{
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_DEBUGGEE_FINISHED_COMMAND_EXECUTION]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_DEBUGGEE_FINISHED_COMMAND_EXECUTION]
|
|
.EventHandle,
|
|
INFINITE);
|
|
}
|
|
|
|
free(UserInputPacket);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends p (step out) and t (step in) packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendStepPacketToDebuggee(DEBUGGER_REMOTE_STEPPING_REQUEST StepRequestType)
|
|
{
|
|
DEBUGGEE_STEP_PACKET StepPacket = {0};
|
|
UINT32 CallInstructionSize;
|
|
|
|
//
|
|
// Set the type of step packet
|
|
//
|
|
StepPacket.StepType = StepRequestType;
|
|
|
|
//
|
|
// Check if it's a step-over
|
|
//
|
|
if (StepRequestType == DEBUGGER_REMOTE_STEPPING_REQUEST_STEP_OVER)
|
|
{
|
|
//
|
|
// We should check whether the current instruction is a 'call'
|
|
// instruction or not, if yes we have to compute the length of call
|
|
//
|
|
if (HyperDbgCheckWhetherTheCurrentInstructionIsCall(
|
|
g_CurrentRunningInstruction,
|
|
MAXIMUM_INSTR_SIZE,
|
|
g_IsRunningInstruction32Bit ? FALSE : TRUE, // equals to !g_IsRunningInstruction32Bit
|
|
&CallInstructionSize))
|
|
{
|
|
//
|
|
// It's a call in step-over
|
|
//
|
|
StepPacket.IsCurrentInstructionACall = TRUE;
|
|
StepPacket.CallLength = CallInstructionSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send step packet to the serial
|
|
//
|
|
if (!KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_STEP,
|
|
(CHAR *)&StepPacket,
|
|
sizeof(DEBUGGEE_STEP_PACKET)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until the result of user-input received
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a PAUSE packet to the debuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendPausePacketToDebuggee()
|
|
{
|
|
//
|
|
// Send pause packet to debuggee
|
|
//
|
|
if (!KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_USER_MODE,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_USER_MODE_PAUSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Handle the paused state (show rip, instructions, etc.)
|
|
//
|
|
KdInterpretPausedDebuggee();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Windows name, version and build to send to debuggger
|
|
*
|
|
* @param BufferToSave
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdGetWindowVersion(CHAR * BufferToSave)
|
|
{
|
|
HKeyHolder currentVersion;
|
|
DWORD valueType;
|
|
CHAR bufferResult[MAXIMUM_CHARACTER_FOR_OS_NAME] = {0};
|
|
BYTE bufferProductName[MAXIMUM_CHARACTER_FOR_OS_NAME] = {0};
|
|
BYTE bufferCurrentBuild[MAXIMUM_CHARACTER_FOR_OS_NAME] = {0};
|
|
BYTE bufferDisplayVersion[MAXIMUM_CHARACTER_FOR_OS_NAME] = {0};
|
|
DWORD bufferSize = MAXIMUM_CHARACTER_FOR_OS_NAME;
|
|
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
¤tVersion) != ERROR_SUCCESS)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
if (RegQueryValueExA(currentVersion, "ProductName", nullptr, &valueType, bufferProductName, &bufferSize) != ERROR_SUCCESS)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
if (valueType != REG_SZ)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
bufferSize = MAXIMUM_CHARACTER_FOR_OS_NAME;
|
|
|
|
if (RegQueryValueExA(currentVersion, "DisplayVersion", nullptr, &valueType, bufferDisplayVersion, &bufferSize) != ERROR_SUCCESS)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
if (valueType != REG_SZ)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
bufferSize = MAXIMUM_CHARACTER_FOR_OS_NAME;
|
|
|
|
if (RegQueryValueExA(currentVersion, "CurrentBuild", nullptr, &valueType, bufferCurrentBuild, &bufferSize) != ERROR_SUCCESS)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
if (valueType != REG_SZ)
|
|
{
|
|
// return FALSE;
|
|
}
|
|
|
|
sprintf_s(bufferResult, "%s %s %s (OS Build %s)", bufferProductName, IsWindowsServer() ? "- Server" : "- Client", bufferDisplayVersion, bufferCurrentBuild);
|
|
|
|
memcpy(BufferToSave, bufferResult, MAXIMUM_CHARACTER_FOR_OS_NAME);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Receive packet from the debugger
|
|
*
|
|
* @param BufferToSave
|
|
* @param LengthReceived
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdReceivePacketFromDebuggee(CHAR * BufferToSave,
|
|
UINT32 * LengthReceived)
|
|
{
|
|
BOOL Status; /* Status */
|
|
char ReadData = NULL; /* temperory Character */
|
|
DWORD NoBytesRead = 0; /* Bytes read by ReadFile() */
|
|
UINT32 Loop = 0;
|
|
|
|
//
|
|
// Read data and store in a buffer
|
|
//
|
|
do
|
|
{
|
|
if (g_IsSerialConnectedToRemoteDebugger)
|
|
{
|
|
//
|
|
// It's a debuggee
|
|
//
|
|
Status = ReadFile(g_SerialRemoteComPortHandle, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a debugger
|
|
//
|
|
|
|
//
|
|
// Try to read one byte in overlapped I/O (in debugger)
|
|
//
|
|
if (!ReadFile(g_SerialRemoteComPortHandle, &ReadData, sizeof(ReadData), NULL, &g_OverlappedIoStructureForReadDebugger))
|
|
{
|
|
DWORD e = GetLastError();
|
|
|
|
if (e != ERROR_IO_PENDING)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Wait till one packet becomes available
|
|
//
|
|
WaitForSingleObject(g_OverlappedIoStructureForReadDebugger.hEvent,
|
|
INFINITE);
|
|
|
|
//
|
|
// Get the result
|
|
//
|
|
GetOverlappedResult(g_SerialRemoteComPortHandle,
|
|
&g_OverlappedIoStructureForReadDebugger,
|
|
&NoBytesRead,
|
|
FALSE);
|
|
|
|
//
|
|
// Reset event for next try
|
|
//
|
|
ResetEvent(g_OverlappedIoStructureForReadDebugger.hEvent);
|
|
}
|
|
|
|
//
|
|
// We already now that the maximum packet size is MaxSerialPacketSize
|
|
// Check to make sure that we don't pass the boundaries
|
|
//
|
|
if (!(MaxSerialPacketSize > Loop))
|
|
{
|
|
//
|
|
// Invalid buffer
|
|
//
|
|
ShowMessages("err, a buffer received in which exceeds the "
|
|
"buffer limitation\n");
|
|
return FALSE;
|
|
}
|
|
|
|
BufferToSave[Loop] = ReadData;
|
|
|
|
if (KdCheckForTheEndOfTheBuffer(&Loop, (BYTE *)BufferToSave))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Loop++;
|
|
|
|
} while (NoBytesRead > 0);
|
|
|
|
//
|
|
// Set the length
|
|
//
|
|
*LengthReceived = Loop;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a special packet to the debuggee
|
|
*
|
|
* @param Buffer
|
|
* @param Length
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendPacketToDebuggee(const CHAR * Buffer, UINT32 Length, BOOLEAN SendEndOfBuffer)
|
|
{
|
|
BOOL Status;
|
|
DWORD BytesWritten = 0;
|
|
DWORD LastErrorCode = 0;
|
|
|
|
//
|
|
// Start getting debuggee messages again
|
|
//
|
|
g_IgnoreNewLoggingMessages = FALSE;
|
|
|
|
//
|
|
// Check if buffer not pass the boundary
|
|
//
|
|
if (Length + SERIAL_END_OF_BUFFER_CHARS_COUNT > MaxSerialPacketSize)
|
|
{
|
|
ShowMessages(
|
|
"err, buffer is above the maximum buffer size that can be sent to "
|
|
"debuggee\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if the remote code's handle found or not
|
|
//
|
|
if (g_SerialRemoteComPortHandle == NULL)
|
|
{
|
|
ShowMessages("err, handle to remote debuggee's com port is not found\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_IsSerialConnectedToRemoteDebugger)
|
|
{
|
|
//
|
|
// It's for a debuggee
|
|
//
|
|
Status = WriteFile(g_SerialRemoteComPortHandle, // Handle to the Serialport
|
|
Buffer, // Data to be written to the port
|
|
Length, // No of bytes to write into the port
|
|
&BytesWritten, // No of bytes written to the port
|
|
NULL);
|
|
|
|
if (Status == FALSE)
|
|
{
|
|
ShowMessages("err, fail to write to com port or named pipe (error %x).\n",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if message delivered successfully
|
|
//
|
|
if (BytesWritten != Length)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a debugger
|
|
//
|
|
|
|
if (WriteFile(g_SerialRemoteComPortHandle, Buffer, Length, NULL, &g_OverlappedIoStructureForWriteDebugger))
|
|
{
|
|
//
|
|
// Write Completed
|
|
//
|
|
goto Out;
|
|
}
|
|
|
|
LastErrorCode = GetLastError();
|
|
if (LastErrorCode != ERROR_IO_PENDING)
|
|
{
|
|
//
|
|
// Error
|
|
//
|
|
// ShowMessages("err, on sending serial packets (0x%x)", LastErrorCode);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait until write completed
|
|
//
|
|
if (WaitForSingleObject(g_OverlappedIoStructureForWriteDebugger.hEvent,
|
|
INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
// ShowMessages("err, on sending serial packets (signal error)");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reset event
|
|
//
|
|
ResetEvent(g_OverlappedIoStructureForWriteDebugger.hEvent);
|
|
}
|
|
|
|
Out:
|
|
if (SendEndOfBuffer)
|
|
{
|
|
//
|
|
// Send End of Buffer Packet
|
|
//
|
|
if (!KdSendPacketToDebuggee((const CHAR *)g_EndOfBufferCheckSerial,
|
|
sizeof(g_EndOfBufferCheckSerial),
|
|
FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All the bytes are sent
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a HyperDbg packet to the debuggee
|
|
*
|
|
* @param RequestedAction
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE PacketType,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION RequestedAction)
|
|
{
|
|
DEBUGGER_REMOTE_PACKET Packet = {0};
|
|
|
|
//
|
|
// Make the packet's structure
|
|
//
|
|
Packet.Indicator = INDICATOR_OF_HYPERDBG_PACKER;
|
|
Packet.TypeOfThePacket = PacketType;
|
|
|
|
//
|
|
// Set the requested action
|
|
//
|
|
Packet.RequestedActionOfThePacket = RequestedAction;
|
|
|
|
//
|
|
// calculate checksum of the packet
|
|
//
|
|
Packet.Checksum =
|
|
KdComputeDataChecksum((PVOID)((UINT64)&Packet + 1),
|
|
sizeof(DEBUGGER_REMOTE_PACKET) - sizeof(BYTE));
|
|
|
|
if (!KdSendPacketToDebuggee((const CHAR *)&Packet,
|
|
sizeof(DEBUGGER_REMOTE_PACKET),
|
|
TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sends a HyperDbg packet + a buffer to the debuggee
|
|
*
|
|
* @param RequestedAction
|
|
* @param Buffer
|
|
* @param BufferLength
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdCommandPacketAndBufferToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE PacketType,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION RequestedAction,
|
|
CHAR * Buffer,
|
|
UINT32 BufferLength)
|
|
{
|
|
DEBUGGER_REMOTE_PACKET Packet = {0};
|
|
|
|
//
|
|
// Make the packet's structure
|
|
//
|
|
Packet.Indicator = INDICATOR_OF_HYPERDBG_PACKER;
|
|
Packet.TypeOfThePacket = PacketType;
|
|
|
|
//
|
|
// Set the requested action
|
|
//
|
|
Packet.RequestedActionOfThePacket = RequestedAction;
|
|
|
|
//
|
|
// calculate checksum of the packet
|
|
//
|
|
Packet.Checksum =
|
|
KdComputeDataChecksum((PVOID)((UINT64)&Packet + 1),
|
|
sizeof(DEBUGGER_REMOTE_PACKET) - sizeof(BYTE));
|
|
|
|
Packet.Checksum += KdComputeDataChecksum((PVOID)Buffer, BufferLength);
|
|
|
|
//
|
|
// Send the first buffer (without ending buffer indication)
|
|
//
|
|
if (!KdSendPacketToDebuggee((const CHAR *)&Packet,
|
|
sizeof(DEBUGGER_REMOTE_PACKET),
|
|
FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Send the second buffer (with ending buffer indication)
|
|
//
|
|
if (!KdSendPacketToDebuggee((const CHAR *)Buffer, BufferLength, TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief check if the debuggee needs to be paused
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdBreakControlCheckAndPauseDebugger()
|
|
{
|
|
//
|
|
// Check if debuggee is running, otherwise the user
|
|
// pressed ctrl+c multiple times
|
|
//
|
|
if (g_IsDebuggeeRunning)
|
|
{
|
|
//
|
|
// Send the pause request to the remote computer
|
|
//
|
|
if (!KdSendPausePacketToDebuggee())
|
|
{
|
|
ShowMessages("err, unable to pause the debuggee\n");
|
|
}
|
|
|
|
//
|
|
// Signal the event
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.IsOnWaitingState = FALSE;
|
|
SetEvent(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.EventHandle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Set the status of the debuggee to wait for the pause
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdSetStatusAndWaitForPause()
|
|
{
|
|
//
|
|
// Set the debuggee to show that it's running
|
|
//
|
|
g_IsDebuggeeRunning = TRUE;
|
|
|
|
//
|
|
// Halt the UI
|
|
//
|
|
KdTheRemoteSystemIsRunning();
|
|
}
|
|
|
|
/**
|
|
* @brief check if the debuggee needs to be continued
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdBreakControlCheckAndContinueDebugger()
|
|
{
|
|
//
|
|
// Check if debuggee is paused or not
|
|
//
|
|
if (!g_IsDebuggeeRunning)
|
|
{
|
|
//
|
|
// Send the continue request to the remote computer
|
|
//
|
|
if (KdSendContinuePacketToDebuggee())
|
|
{
|
|
//
|
|
// Set the debuggee to show that it's running
|
|
//
|
|
KdSetStatusAndWaitForPause();
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, unable to continue the debuggee\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief wait for a event to be triggered and if the debuggee
|
|
* is running it just halts the system
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdTheRemoteSystemIsRunning()
|
|
{
|
|
//
|
|
// Indicate that the debuggee is running
|
|
//
|
|
ShowMessages("debuggee is running...\n");
|
|
|
|
//
|
|
// Wait until the users press CTRL+C
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.EventHandle,
|
|
INFINITE);
|
|
}
|
|
|
|
/**
|
|
* @brief Prepare serial to connect to the remote server
|
|
* @details wait to connect to debuggee (this is debugger)
|
|
*
|
|
* @param SerialHandle
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdPrepareSerialConnectionToRemoteSystem(HANDLE SerialHandle,
|
|
BOOLEAN IsNamedPipe)
|
|
{
|
|
StartAgain:
|
|
|
|
BOOL Status; /* Status */
|
|
DWORD EventMask = 0; /* Event mask to trigger */
|
|
|
|
//
|
|
// Show an indication to connect the debugger
|
|
//
|
|
ShowMessages("waiting for debuggee to connect...\n");
|
|
|
|
if (!IsNamedPipe)
|
|
{
|
|
//
|
|
// Setting Receive Mask
|
|
//
|
|
Status = SetCommMask(SerialHandle, EV_RXCHAR);
|
|
if (Status == FALSE)
|
|
{
|
|
//
|
|
// Can be ignored
|
|
//
|
|
// ShowMessages("err, in setting CommMask\n");
|
|
// return FALSE;
|
|
}
|
|
|
|
//
|
|
// Setting WaitComm() Event
|
|
//
|
|
Status = WaitCommEvent(SerialHandle, &EventMask, NULL); /* Wait for the character to be received */
|
|
|
|
if (Status == FALSE)
|
|
{
|
|
//
|
|
// Can be ignored
|
|
//
|
|
// ShowMessages("err, in setting WaitCommEvent\n");
|
|
// return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the handle table
|
|
//
|
|
for (size_t i = 0; i < DEBUGGER_MAXIMUM_SYNCRONIZATION_KERNEL_DEBUGGER_OBJECTS; i++)
|
|
{
|
|
g_KernelSyncronizationObjectsHandleTable[i].IsOnWaitingState = FALSE;
|
|
g_KernelSyncronizationObjectsHandleTable[i].EventHandle =
|
|
CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// the debuggee is not already closed the connection
|
|
//
|
|
g_SerialConnectionAlreadyClosed = FALSE;
|
|
|
|
//
|
|
// Create the listening thread in debugger
|
|
//
|
|
|
|
g_SerialListeningThreadHandle =
|
|
CreateThread(NULL, 0, ListeningSerialPauseDebuggerThread, NULL, 0, NULL);
|
|
|
|
//
|
|
// Wait for the 'Start' packet on the listener side
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_STARTED_PACKET_RECEIVED]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_STARTED_PACKET_RECEIVED]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
//
|
|
// Check to make sure the connection not close before starting a session
|
|
//
|
|
if (!g_SerialConnectionAlreadyClosed)
|
|
{
|
|
//
|
|
// Ignore handling breaks
|
|
//
|
|
g_IgnorePauseRequests = TRUE;
|
|
|
|
//
|
|
// The next step is getting symbol details
|
|
//
|
|
ShowMessages("getting symbol details...\n");
|
|
|
|
//
|
|
// Wait to receive symbol details
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SYMBOL_RELOAD]
|
|
.IsOnWaitingState = TRUE;
|
|
WaitForSingleObject(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_SYMBOL_RELOAD]
|
|
.EventHandle,
|
|
INFINITE);
|
|
|
|
//
|
|
// Connected to the debuggee
|
|
//
|
|
g_IsSerialConnectedToRemoteDebuggee = TRUE;
|
|
|
|
//
|
|
// And debuggee is running
|
|
//
|
|
g_IsDebuggeeRunning = TRUE;
|
|
|
|
//
|
|
// Save the handler
|
|
//
|
|
g_SerialRemoteComPortHandle = SerialHandle;
|
|
|
|
//
|
|
// Is serial handle for a named pipe
|
|
//
|
|
g_IsDebuggerConntectedToNamedPipe = IsNamedPipe;
|
|
|
|
//
|
|
// Now, the user can press ctrl+c to pause the debuggee
|
|
//
|
|
ShowMessages("press CTRL+C to pause the debuggee\n");
|
|
|
|
//
|
|
// Process CTRL+C breaks again
|
|
//
|
|
g_IgnorePauseRequests = FALSE;
|
|
|
|
//
|
|
// Wait for event on this thread
|
|
//
|
|
KdTheRemoteSystemIsRunning();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Prepare and initialize COM port
|
|
*
|
|
* @param PortName
|
|
* @param Baudrate
|
|
* @param Port
|
|
* @param IsPreparing
|
|
* @param IsNamedPipe
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdPrepareAndConnectDebugPort(const char * PortName, DWORD Baudrate, UINT32 Port, BOOLEAN IsPreparing, BOOLEAN IsNamedPipe)
|
|
{
|
|
HANDLE Comm; /* Handle to the Serial port */
|
|
BOOL Status; /* Status */
|
|
DCB SerialParams = {0}; /* Initializing DCB structure */
|
|
COMMTIMEOUTS Timeouts = {0}; /* Initializing timeouts structure */
|
|
char PortNo[20] = {0}; /* contain friendly name */
|
|
BOOLEAN StatusIoctl;
|
|
ULONG ReturnedLength;
|
|
PDEBUGGER_PREPARE_DEBUGGEE DebuggeeRequest;
|
|
|
|
//
|
|
// Check if the debugger or debuggee is already active
|
|
//
|
|
if (IsConnectedToAnyInstanceOfDebuggerOrDebuggee())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (IsPreparing && IsNamedPipe)
|
|
{
|
|
ShowMessages("err, cannot used named pipe for debuggee");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsNamedPipe)
|
|
{
|
|
//
|
|
// It's a serial
|
|
//
|
|
|
|
//
|
|
// Append name to make a Windows understandable format
|
|
//
|
|
sprintf_s(PortNo, 20, "\\\\.\\%s", PortName);
|
|
|
|
//
|
|
// Open the serial com port (if it's the debugger (not debuggee)) then
|
|
// open with Overlapped I/O
|
|
//
|
|
|
|
if (IsPreparing)
|
|
{
|
|
//
|
|
// It's debuggee (Non-overlapped I/O)
|
|
//
|
|
Comm = CreateFile(PortNo, // Friendly name
|
|
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
|
|
0, // No Sharing, ports cant be shared
|
|
NULL, // No Security
|
|
OPEN_EXISTING, // Open existing port only
|
|
0, // Non Overlapped I/O
|
|
NULL); // Null for Comm Devices
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's debugger (Overlapped I/O)
|
|
//
|
|
Comm = CreateFile(PortNo, // Friendly name
|
|
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
|
|
0, // No Sharing, ports cant be shared
|
|
NULL, // No Security
|
|
OPEN_EXISTING, // Open existing port only
|
|
FILE_FLAG_OVERLAPPED, // Overlapped I/O
|
|
NULL); // Null for Comm Devices
|
|
|
|
//
|
|
// Create event for overlapped I/O (Read)
|
|
//
|
|
g_OverlappedIoStructureForReadDebugger.hEvent =
|
|
CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
//
|
|
// Create event for overlapped I/O (Write)
|
|
//
|
|
g_OverlappedIoStructureForWriteDebugger.hEvent =
|
|
CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
}
|
|
|
|
if (Comm == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowMessages("err, port can't be opened\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Purge the serial port
|
|
//
|
|
PurgeComm(Comm,
|
|
PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
|
|
|
|
//
|
|
// Setting the Parameters for the SerialPort
|
|
//
|
|
SerialParams.DCBlength = sizeof(SerialParams);
|
|
|
|
//
|
|
// retreives the current settings
|
|
//
|
|
Status = GetCommState(Comm, &SerialParams);
|
|
|
|
if (Status == FALSE)
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages("err, to Get the COM state\n");
|
|
return FALSE;
|
|
}
|
|
|
|
SerialParams.BaudRate =
|
|
Baudrate; // BaudRate = 9600 (Based on user selection)
|
|
SerialParams.ByteSize = 8; // ByteSize = 8
|
|
SerialParams.StopBits = ONESTOPBIT; // StopBits = 1
|
|
SerialParams.Parity = NOPARITY; // Parity = None
|
|
Status = SetCommState(Comm, &SerialParams);
|
|
|
|
if (Status == FALSE)
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages("err, to Setting DCB Structure\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Setting Timeouts (not use it anymore as we use special signature for
|
|
// ending buffer)
|
|
// (no need anymore as we use end buffer detection mechanism)
|
|
//
|
|
|
|
/*
|
|
Timeouts.ReadIntervalTimeout = 50;
|
|
Timeouts.ReadTotalTimeoutConstant = 50;
|
|
Timeouts.ReadTotalTimeoutMultiplier = 10;
|
|
Timeouts.WriteTotalTimeoutConstant = 50;
|
|
Timeouts.WriteTotalTimeoutMultiplier = 10;
|
|
|
|
if (SetCommTimeouts(Comm, &Timeouts) == FALSE) {
|
|
ShowMessages("err, to Setting Time outs (%x).\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a namedpipe
|
|
//
|
|
Comm = NamedPipeClientCreatePipeOverlappedIo(PortName);
|
|
|
|
if (!Comm)
|
|
{
|
|
//
|
|
// Unable to create handle
|
|
//
|
|
ShowMessages("is virtual machine running?\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (IsPreparing)
|
|
{
|
|
//
|
|
// It's a debuggee request
|
|
//
|
|
|
|
//
|
|
// First, connect to local machine and we load the VMM module as it's a
|
|
// module that is responsible for working on debugger
|
|
//
|
|
g_IsConnectedToHyperDbgLocally = TRUE;
|
|
|
|
//
|
|
// Load the VMM
|
|
//
|
|
if (!CommandLoadVmmModule())
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages("failed to install or load the driver\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if driver is loaded or not, in the case
|
|
// of connecting to a remote machine as debuggee
|
|
//
|
|
if (!g_DeviceHandle)
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages(
|
|
"handle not found, probably the driver is not loaded. Did you "
|
|
"use 'load' command?\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer
|
|
//
|
|
DebuggeeRequest =
|
|
(PDEBUGGER_PREPARE_DEBUGGEE)malloc(SIZEOF_DEBUGGER_PREPARE_DEBUGGEE);
|
|
|
|
if (DebuggeeRequest == NULL)
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages("err, unable to allocate memory for request packet");
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(DebuggeeRequest, SIZEOF_DEBUGGER_PREPARE_DEBUGGEE);
|
|
|
|
//
|
|
// Prepare the details structure
|
|
//
|
|
DebuggeeRequest->PortAddress = Port;
|
|
DebuggeeRequest->Baudrate = Baudrate;
|
|
|
|
//
|
|
// Get base address of ntoskrnl
|
|
//
|
|
DebuggeeRequest->NtoskrnlBaseAddress = DebuggerGetNtoskrnlBase();
|
|
|
|
//
|
|
// Set the debuggee name, version, and build number
|
|
//
|
|
if (!KdGetWindowVersion(DebuggeeRequest->OsName))
|
|
{
|
|
//
|
|
// It's not an error if it returned null
|
|
//
|
|
// return FALSE;
|
|
}
|
|
|
|
//
|
|
// Send the request to the kernel
|
|
//
|
|
StatusIoctl =
|
|
DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_PREPARE_DEBUGGEE, // IO Control code
|
|
DebuggeeRequest, // Input Buffer to driver.
|
|
SIZEOF_DEBUGGER_PREPARE_DEBUGGEE, // Input buffer
|
|
// length
|
|
DebuggeeRequest, // Output Buffer from driver.
|
|
SIZEOF_DEBUGGER_PREPARE_DEBUGGEE, // Length of output
|
|
// buffer in bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!StatusIoctl)
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
free(DebuggeeRequest);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (DebuggeeRequest->Result == DEBUGGER_OPERATION_WAS_SUCCESSFULL)
|
|
{
|
|
//
|
|
// Ignore handling CTRL+C breaks
|
|
//
|
|
g_IgnorePauseRequests = TRUE;
|
|
|
|
//
|
|
// initialize and load symbols (pdb) and send the details to the debugger
|
|
//
|
|
ShowMessages("synchronizing modules' symbol details\n");
|
|
|
|
//
|
|
// Do not pause debugger after finish
|
|
//
|
|
KdReloadSymbolsInDebuggee(FALSE);
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(Comm);
|
|
ShowErrorMessage(DebuggeeRequest->Result);
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
free(DebuggeeRequest);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// show the message that the operation was successful,
|
|
// we show it here because if we set the next variables
|
|
// to true, then it will be sent to the debugger instead
|
|
// of showing the message in debuggee
|
|
//
|
|
ShowMessages("the operation was successful\n");
|
|
|
|
//
|
|
// Serial connection is not already closed
|
|
//
|
|
g_SerialConnectionAlreadyClosed = FALSE;
|
|
|
|
//
|
|
// Indicate that we connected to the debugger
|
|
//
|
|
g_IsSerialConnectedToRemoteDebugger = TRUE;
|
|
|
|
//
|
|
// Set handle to serial device
|
|
//
|
|
g_SerialRemoteComPortHandle = Comm;
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
free(DebuggeeRequest);
|
|
|
|
//
|
|
// Wait here so the user can't give new commands
|
|
// Create an event (manually. no signal)
|
|
//
|
|
g_DebuggeeStopCommandEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
//
|
|
// Create a thread to listen for pauses from the remote debugger
|
|
//
|
|
|
|
g_SerialListeningThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
ListeningSerialPauseDebuggeeThread,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
//
|
|
// Test should be removed
|
|
//
|
|
// HyperdbgInterpreter("!syscall script { print(@rax); }");
|
|
// HyperdbgInterpreter("!epthook fffff801`639b1030 script { print(@rax);
|
|
// }"); HyperdbgInterpreter("!msrwrite script { print(@rax); }");
|
|
//
|
|
|
|
//
|
|
// Now we should wait on this state until the user closes the connection to
|
|
// debuggee from debugger
|
|
//
|
|
WaitForSingleObject(g_DebuggeeStopCommandEventHandle, INFINITE);
|
|
|
|
//
|
|
// Close the event's handle
|
|
//
|
|
CloseHandle(g_DebuggeeStopCommandEventHandle);
|
|
g_DebuggeeStopCommandEventHandle = NULL;
|
|
|
|
//
|
|
// Handle CTRL+C breaks again
|
|
//
|
|
g_IgnorePauseRequests = FALSE;
|
|
|
|
//
|
|
// Finish it here
|
|
//
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Save the handler
|
|
//
|
|
g_SerialRemoteComPortHandle = Comm;
|
|
|
|
//
|
|
// If we are here, then it's a debugger (not debuggee)
|
|
// let's prepare the debuggee
|
|
//
|
|
KdPrepareSerialConnectionToRemoteSystem(Comm, IsNamedPipe);
|
|
}
|
|
|
|
//
|
|
// everything was done
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send general buffer from debuggee to debugger
|
|
* @param RequestedAction
|
|
* @param Buffer
|
|
* @param Length
|
|
* @param PauseDebuggeeWhenSent
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION RequestedAction,
|
|
PVOID Buffer,
|
|
UINT32 BufferLength,
|
|
BOOLEAN PauseDebuggeeWhenSent)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
PDEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER
|
|
GeneralPacketFromDebuggeeToDebuggerRequest;
|
|
UINT32 Length;
|
|
|
|
//
|
|
// Compute the length of the packet (add to header )
|
|
//
|
|
Length = sizeof(DEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER) +
|
|
BufferLength;
|
|
|
|
//
|
|
// Allocate the target buffer
|
|
//
|
|
GeneralPacketFromDebuggeeToDebuggerRequest =
|
|
(PDEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER)malloc(Length);
|
|
|
|
if (GeneralPacketFromDebuggeeToDebuggerRequest == NULL)
|
|
{
|
|
//
|
|
// err, allocating buffer
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(GeneralPacketFromDebuggeeToDebuggerRequest, Length);
|
|
|
|
//
|
|
// Fill the header structure
|
|
//
|
|
GeneralPacketFromDebuggeeToDebuggerRequest->RequestedAction = RequestedAction;
|
|
GeneralPacketFromDebuggeeToDebuggerRequest->LengthOfBuffer = BufferLength;
|
|
GeneralPacketFromDebuggeeToDebuggerRequest->PauseDebuggeeWhenSent =
|
|
PauseDebuggeeWhenSent;
|
|
|
|
//
|
|
// Move the memory to the bottom of the structure
|
|
//
|
|
memcpy(
|
|
(PVOID)((UINT64)GeneralPacketFromDebuggeeToDebuggerRequest +
|
|
sizeof(DEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER)),
|
|
(PVOID)Buffer,
|
|
BufferLength);
|
|
|
|
//
|
|
// Send Ioctl to the kernel
|
|
//
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_SEND_GENERAL_BUFFER_FROM_DEBUGGEE_TO_DEBUGGER, // IO Control code
|
|
GeneralPacketFromDebuggeeToDebuggerRequest, // Input Buffer to driver.
|
|
Length, // Input buffer
|
|
// length
|
|
GeneralPacketFromDebuggeeToDebuggerRequest, // Output Buffer from driver.
|
|
SIZEOF_DEBUGGEE_SEND_GENERAL_PACKET_FROM_DEBUGGEE_TO_DEBUGGER, // 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());
|
|
free(GeneralPacketFromDebuggeeToDebuggerRequest);
|
|
return FALSE;
|
|
}
|
|
|
|
if (GeneralPacketFromDebuggeeToDebuggerRequest->KernelResult !=
|
|
DEBUGGER_OPERATION_WAS_SUCCESSFULL)
|
|
{
|
|
ShowErrorMessage(GeneralPacketFromDebuggeeToDebuggerRequest->KernelResult);
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
free(GeneralPacketFromDebuggeeToDebuggerRequest);
|
|
return FALSE;
|
|
}
|
|
|
|
free(GeneralPacketFromDebuggeeToDebuggerRequest);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send the packets of reloading symbols to build a new
|
|
* symbol table to the debugger and send the finished packet to
|
|
* the debugger
|
|
* @param PauseDebuggee
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdReloadSymbolsInDebuggee(BOOLEAN PauseDebuggee)
|
|
{
|
|
DEBUGGEE_SYMBOL_UPDATE_RESULT SymReload = {0};
|
|
|
|
//
|
|
// Request debuggee to send new symbol packets
|
|
//
|
|
SymbolPrepareDebuggerWithSymbolInfo();
|
|
|
|
//
|
|
// Set the status
|
|
//
|
|
SymReload.KernelStatus = DEBUGGER_OPERATION_WAS_SUCCESSFULL;
|
|
|
|
//
|
|
// Send the finished request packet
|
|
//
|
|
return KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RELOAD_SYMBOL_FINISHED,
|
|
&SymReload,
|
|
sizeof(DEBUGGEE_SYMBOL_UPDATE_RESULT),
|
|
PauseDebuggee);
|
|
}
|
|
|
|
/**
|
|
* @brief Send close packet to the debuggee and debugger
|
|
* @details This function close the connection in both debuggee and debugger
|
|
* both of the debuggee and debugger can use this function
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdCloseConnection()
|
|
{
|
|
//
|
|
// The process of closing connection already started
|
|
//
|
|
if (g_SerialConnectionAlreadyClosed)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_SerialConnectionAlreadyClosed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Unload the VMM driver if it's debuggee
|
|
//
|
|
if (g_IsSerialConnectedToRemoteDebugger)
|
|
{
|
|
if (g_IsConnectedToHyperDbgLocally && g_IsDebuggerModulesLoaded)
|
|
{
|
|
HyperdbgUnload();
|
|
}
|
|
}
|
|
else if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
//
|
|
// Send close & unload packet to debuggee (vmx-root)
|
|
//
|
|
if (KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_VMX_ROOT,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_VMX_ROOT_MODE_CLOSE_AND_UNLOAD_DEBUGGEE))
|
|
{
|
|
//
|
|
// It's a debugger, we should send the close packet to debuggee
|
|
//
|
|
ShowMessages("unloading debugger vmm module on debuggee...\n");
|
|
|
|
//
|
|
// Send another packet so the user-mode is not waiting for new packet
|
|
//
|
|
!KdCommandPacketToDebuggee(
|
|
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_USER_MODE,
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_USER_MODE_DO_NOT_READ_ANY_PACKET);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we're here, probably the connection started but the debuggee is closed
|
|
// without sending the start packet; thus, the debugger has no idea about
|
|
// connection but we should uninitialize everything
|
|
//
|
|
ShowMessages("err, start packet not received but the target machine closed the "
|
|
"connection\n");
|
|
|
|
//
|
|
// Not waiting for start packet
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_STARTED_PACKET_RECEIVED]
|
|
.IsOnWaitingState = FALSE;
|
|
SetEvent(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_STARTED_PACKET_RECEIVED]
|
|
.EventHandle);
|
|
}
|
|
|
|
//
|
|
// Close handle and uninitialize everything
|
|
//
|
|
KdUninitializeConnection();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Register an event in the debuggee
|
|
* @param EventRegBuffer
|
|
* @param Length
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdRegisterEventInDebuggee(PDEBUGGER_GENERAL_EVENT_DETAIL EventRegBuffer,
|
|
UINT32 Length)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
DEBUGGER_EVENT_AND_ACTION_REG_BUFFER ReturnedBuffer = {0};
|
|
|
|
if (!g_DeviceHandle)
|
|
{
|
|
ShowMessages("handle of the driver not found, probably the driver is not loaded. Did you "
|
|
"use 'load' command?\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Send IOCTL
|
|
//
|
|
Status =
|
|
DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control code
|
|
EventRegBuffer,
|
|
Length // Input Buffer to driver.
|
|
, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER), // 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;
|
|
}
|
|
|
|
//
|
|
// Now that we registered the event (with or without error),
|
|
// we should send the results back to the debugger
|
|
//
|
|
return KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_REGISTERING_EVENT,
|
|
&ReturnedBuffer,
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER),
|
|
TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Add action to an event in the debuggee
|
|
* @param ActionAddingBuffer
|
|
* @param Length
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdAddActionToEventInDebuggee(PDEBUGGER_GENERAL_ACTION ActionAddingBuffer,
|
|
UINT32 Length)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
DEBUGGER_EVENT_AND_ACTION_REG_BUFFER ReturnedBuffer = {0};
|
|
|
|
if (!g_DeviceHandle)
|
|
{
|
|
ShowMessages("handle of the driver not found, probably the driver is not loaded. Did you "
|
|
"use 'load' command?\n");
|
|
return FALSE;
|
|
}
|
|
|
|
Status =
|
|
DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control code
|
|
ActionAddingBuffer, // Input Buffer to driver.
|
|
Length, // Input buffer length
|
|
&ReturnedBuffer, // Output Buffer from driver.
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER), // 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;
|
|
}
|
|
|
|
//
|
|
// Now that we added the action to the event, we should send the
|
|
// results to the debugger
|
|
//
|
|
return KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_ADDING_ACTION_TO_EVENT,
|
|
&ReturnedBuffer,
|
|
sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER),
|
|
TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Modify event ioctl in the debuggee
|
|
* @param ModifyEvent
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
KdSendModifyEventInDebuggee(PDEBUGGER_MODIFY_EVENTS ModifyEvent)
|
|
{
|
|
BOOLEAN Status;
|
|
ULONG ReturnedLength;
|
|
|
|
//
|
|
// This mechanism is only used for clear event,
|
|
// it is because we apply enable/disable in kernel
|
|
// directly
|
|
//
|
|
|
|
//
|
|
// Check if debugger is loaded or not
|
|
//
|
|
if (!g_DeviceHandle)
|
|
{
|
|
ShowMessages("handle of the driver not found, probably the driver is not loaded. Did you "
|
|
"use 'load' command?\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Send the request to the kernel
|
|
//
|
|
|
|
Status = DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_MODIFY_EVENTS, // IO Control code
|
|
ModifyEvent, // Input Buffer to driver.
|
|
SIZEOF_DEBUGGER_MODIFY_EVENTS, // Input buffer length
|
|
ModifyEvent, // Output Buffer from driver.
|
|
SIZEOF_DEBUGGER_MODIFY_EVENTS, // 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 the buffer back to debugger
|
|
//
|
|
return KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_RESULT_OF_QUERY_AND_MODIFY_EVENT,
|
|
ModifyEvent,
|
|
sizeof(DEBUGGER_MODIFY_EVENTS),
|
|
TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Handle user-input in debuggee
|
|
* @param Descriptor
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdHandleUserInputInDebuggee(DEBUGGEE_USER_INPUT_PACKET * Descriptor)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
CHAR * Input;
|
|
DEBUGGER_SEND_COMMAND_EXECUTION_FINISHED_SIGNAL FinishExecutionRequest = {0};
|
|
|
|
Input = (CHAR *)Descriptor + sizeof(DEBUGGEE_USER_INPUT_PACKET);
|
|
|
|
//
|
|
// Run the command
|
|
//
|
|
HyperdbgInterpreter(Input);
|
|
|
|
//
|
|
// Check if it needs to send a signal to indicate that the execution of
|
|
// command finished
|
|
//
|
|
|
|
if (!Descriptor->IgnoreFinishedSignal)
|
|
{
|
|
//
|
|
// By the way, we don't need to send an input buffer
|
|
// to the kernel, but let's keep it like this, if we
|
|
// want to pass some other aguments to the kernel in
|
|
// the future
|
|
//
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_SEND_SIGNAL_EXECUTION_IN_DEBUGGEE_FINISHED, // IO Control code
|
|
&FinishExecutionRequest, // Input Buffer to driver.
|
|
SIZEOF_DEBUGGER_SEND_COMMAND_EXECUTION_FINISHED_SIGNAL, // Input buffer
|
|
// length
|
|
&FinishExecutionRequest, // Output Buffer from driver.
|
|
SIZEOF_DEBUGGER_SEND_COMMAND_EXECUTION_FINISHED_SIGNAL, // 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Send result of user-mode ShowMessages to debuggee
|
|
* @param Input
|
|
* @param Length
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdSendUsermodePrints(CHAR * Input, UINT32 Length)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
PDEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER UsermodeMessageRequest;
|
|
UINT32 SizeToSend;
|
|
|
|
SizeToSend = sizeof(DEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER) + Length;
|
|
|
|
UsermodeMessageRequest =
|
|
(DEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER *)malloc(SizeToSend);
|
|
|
|
RtlZeroMemory(UsermodeMessageRequest, SizeToSend);
|
|
|
|
//
|
|
// Set the length
|
|
//
|
|
UsermodeMessageRequest->Length = Length;
|
|
|
|
//
|
|
// Move the user message buffer at the bottom of the structure packet
|
|
//
|
|
memcpy((PVOID)((UINT64)UsermodeMessageRequest +
|
|
sizeof(DEBUGGER_SEND_USERMODE_MESSAGES_TO_DEBUGGER)),
|
|
(PVOID)Input,
|
|
Length);
|
|
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_SEND_USERMODE_MESSAGES_TO_DEBUGGER, // IO Control code
|
|
UsermodeMessageRequest, // Input Buffer to driver.
|
|
SizeToSend, // Input buffer
|
|
// length
|
|
UsermodeMessageRequest, // Output Buffer from driver.
|
|
SizeToSend, // 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());
|
|
free(UsermodeMessageRequest);
|
|
return;
|
|
}
|
|
|
|
free(UsermodeMessageRequest);
|
|
}
|
|
|
|
/**
|
|
* @brief Send each packet of debugging information (PDB) to the debugger
|
|
* @param SymbolDetailPacket
|
|
* @param CurrentSymbolInfoIndex
|
|
* @param TotalSymbols
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdSendSymbolDetailPacket(PMODULE_SYMBOL_DETAIL SymbolDetailPacket, UINT32 CurrentSymbolInfoIndex, UINT32 TotalSymbols)
|
|
{
|
|
BOOLEAN Result;
|
|
ULONG ReturnedLength;
|
|
PDEBUGGER_UPDATE_SYMBOL_TABLE UsermodeSymDetailRequest;
|
|
|
|
UsermodeSymDetailRequest =
|
|
(DEBUGGER_UPDATE_SYMBOL_TABLE *)malloc(sizeof(DEBUGGER_UPDATE_SYMBOL_TABLE));
|
|
|
|
RtlZeroMemory(UsermodeSymDetailRequest, sizeof(DEBUGGER_UPDATE_SYMBOL_TABLE));
|
|
|
|
//
|
|
// Set other parameters for the symbol details
|
|
//
|
|
UsermodeSymDetailRequest->CurrentSymbolIndex = CurrentSymbolInfoIndex;
|
|
UsermodeSymDetailRequest->TotalSymbols = TotalSymbols;
|
|
|
|
//
|
|
// Move the user message buffer at the bottom of the structure packet
|
|
//
|
|
memcpy((PVOID)((UINT64)&UsermodeSymDetailRequest->SymbolDetailPacket),
|
|
(PVOID)SymbolDetailPacket,
|
|
sizeof(MODULE_SYMBOL_DETAIL));
|
|
|
|
//
|
|
// Send the symbol update buffer to the debugger
|
|
//
|
|
Result = KdSendGeneralBuffersFromDebuggeeToDebugger(
|
|
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_DEBUGGEE_UPDATE_SYMBOL_INFO,
|
|
UsermodeSymDetailRequest,
|
|
sizeof(DEBUGGER_UPDATE_SYMBOL_TABLE),
|
|
FALSE);
|
|
|
|
if (!Result)
|
|
{
|
|
ShowMessages("err, sending symbol packets failed in debuggee");
|
|
free(UsermodeSymDetailRequest);
|
|
return;
|
|
}
|
|
|
|
free(UsermodeSymDetailRequest);
|
|
}
|
|
|
|
/**
|
|
* @brief Uninitialize everything in both debuggee and debugger
|
|
* @details This function close the connection in both debuggee and debugger
|
|
* both of the debuggee and debugger can use this function
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
KdUninitializeConnection()
|
|
{
|
|
//
|
|
// Close the handles of the listening threads
|
|
//
|
|
if (g_SerialListeningThreadHandle != NULL)
|
|
{
|
|
CloseHandle(g_SerialListeningThreadHandle);
|
|
g_SerialListeningThreadHandle = NULL;
|
|
}
|
|
|
|
if (g_OverlappedIoStructureForReadDebugger.hEvent != NULL)
|
|
{
|
|
CloseHandle(g_OverlappedIoStructureForReadDebugger.hEvent);
|
|
}
|
|
|
|
if (g_OverlappedIoStructureForWriteDebugger.hEvent != NULL)
|
|
{
|
|
CloseHandle(g_OverlappedIoStructureForWriteDebugger.hEvent);
|
|
}
|
|
|
|
if (g_DebuggeeStopCommandEventHandle != NULL)
|
|
{
|
|
//
|
|
// Signal the debuggee to get new commands
|
|
//
|
|
SetEvent(g_DebuggeeStopCommandEventHandle);
|
|
}
|
|
|
|
//
|
|
// Unpause the debugger to get commands
|
|
//
|
|
g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.IsOnWaitingState = FALSE;
|
|
SetEvent(g_KernelSyncronizationObjectsHandleTable
|
|
[DEBUGGER_SYNCRONIZATION_OBJECT_KERNEL_DEBUGGER_IS_DEBUGGER_RUNNING]
|
|
.EventHandle);
|
|
|
|
//
|
|
// Close synchronization objects
|
|
//
|
|
for (size_t i = 0; i < DEBUGGER_MAXIMUM_SYNCRONIZATION_KERNEL_DEBUGGER_OBJECTS; i++)
|
|
{
|
|
if (g_KernelSyncronizationObjectsHandleTable[i].EventHandle != NULL)
|
|
{
|
|
if (g_KernelSyncronizationObjectsHandleTable[i].IsOnWaitingState)
|
|
{
|
|
g_KernelSyncronizationObjectsHandleTable[i].IsOnWaitingState = FALSE;
|
|
SetEvent(g_KernelSyncronizationObjectsHandleTable[i].EventHandle);
|
|
}
|
|
|
|
CloseHandle(g_KernelSyncronizationObjectsHandleTable[i].EventHandle);
|
|
g_KernelSyncronizationObjectsHandleTable[i].EventHandle = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No current core
|
|
//
|
|
g_CurrentRemoteCore = DEBUGGER_DEBUGGEE_IS_RUNNING_NO_CORE;
|
|
|
|
//
|
|
// If connected to debugger
|
|
//
|
|
g_IsSerialConnectedToRemoteDebugger = FALSE;
|
|
|
|
//
|
|
// No longer connected to the debuggee
|
|
//
|
|
g_IsSerialConnectedToRemoteDebuggee = FALSE;
|
|
|
|
//
|
|
// And debuggee is not running
|
|
//
|
|
g_IsDebuggeeRunning = FALSE;
|
|
|
|
//
|
|
// Clear the handler
|
|
//
|
|
if (g_SerialRemoteComPortHandle != NULL)
|
|
{
|
|
CloseHandle(g_SerialRemoteComPortHandle);
|
|
g_SerialRemoteComPortHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// Start getting debuggee messages on next try
|
|
//
|
|
g_IgnoreNewLoggingMessages = FALSE;
|
|
|
|
//
|
|
// Is serial handle for a named pipe
|
|
//
|
|
g_IsDebuggerConntectedToNamedPipe = FALSE;
|
|
}
|