mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-19 05:54:42 +00:00
609 lines
18 KiB
C++
609 lines
18 KiB
C++
/**
|
|
* @file forwarding.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Event source forwarding
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2020-11-16
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern UINT64 g_OutputSourceTag;
|
|
extern LIST_ENTRY g_OutputSources;
|
|
extern LIST_ENTRY g_EventTrace;
|
|
|
|
/**
|
|
* @brief Get the output source tag and increase the
|
|
* global variable for tag
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
ForwardingGetNewOutputSourceTag()
|
|
{
|
|
return g_OutputSourceTag++;
|
|
}
|
|
|
|
/**
|
|
* @brief Opens the output source
|
|
* @param SourceDescriptor Descriptor of the source
|
|
*
|
|
* @return DEBUGGER_OUTPUT_SOURCE_STATUS return status of the opening function
|
|
*/
|
|
DEBUGGER_OUTPUT_SOURCE_STATUS
|
|
ForwardingOpenOutputSource(PDEBUGGER_EVENT_FORWARDING SourceDescriptor)
|
|
{
|
|
//
|
|
// Check if already closed
|
|
//
|
|
if (SourceDescriptor->State == EVENT_FORWARDING_CLOSED)
|
|
{
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_ALREADY_CLOSED;
|
|
}
|
|
|
|
//
|
|
// check if already opened
|
|
//
|
|
if (SourceDescriptor->State == EVENT_FORWARDING_STATE_OPENED)
|
|
{
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_ALREADY_OPENED;
|
|
}
|
|
|
|
//
|
|
// Set the status to opened
|
|
//
|
|
SourceDescriptor->State = EVENT_FORWARDING_STATE_OPENED;
|
|
|
|
//
|
|
// Now, it's time to open the source based on its type
|
|
//
|
|
if (SourceDescriptor->Type == EVENT_FORWARDING_FILE)
|
|
{
|
|
//
|
|
// Nothing special to do here, file is opened with CreateFile
|
|
// and nothing should be called to open the handle
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_OPENED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_NAMEDPIPE)
|
|
{
|
|
//
|
|
// Nothing special to do here, namedpipe is opened with CreateFile
|
|
// and nothing should be called to open the handle
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_OPENED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_TCP)
|
|
{
|
|
//
|
|
// Nothing special to do here, tcp socket is opened with
|
|
// CommunicationClientConnectToServer and nothing should be
|
|
// called to open the socket
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_OPENED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_MODULE)
|
|
{
|
|
//
|
|
// Nothing special to do here, function is found previously
|
|
// and nothing should be called to open the module
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_OPENED;
|
|
}
|
|
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Closes the output source
|
|
* @param SourceDescriptor Descriptor of the source
|
|
*
|
|
* @return DEBUGGER_OUTPUT_SOURCE_STATUS return status of the closing function
|
|
*/
|
|
DEBUGGER_OUTPUT_SOURCE_STATUS
|
|
ForwardingCloseOutputSource(PDEBUGGER_EVENT_FORWARDING SourceDescriptor)
|
|
{
|
|
//
|
|
// Check if already closed
|
|
//
|
|
if (SourceDescriptor->State == EVENT_FORWARDING_CLOSED)
|
|
{
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_ALREADY_CLOSED;
|
|
}
|
|
|
|
//
|
|
// Check if not opened
|
|
//
|
|
if (SourceDescriptor->State == EVENT_FORWARDING_STATE_NOT_OPENED ||
|
|
SourceDescriptor->State != EVENT_FORWARDING_STATE_OPENED)
|
|
{
|
|
//
|
|
// Not opennd ? or state other than opened ?
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
//
|
|
// Set the state
|
|
//
|
|
SourceDescriptor->State = EVENT_FORWARDING_CLOSED;
|
|
|
|
//
|
|
// Now, it's time to close the source based on its type
|
|
//
|
|
if (SourceDescriptor->Type == EVENT_FORWARDING_FILE)
|
|
{
|
|
//
|
|
// Close the handle
|
|
//
|
|
CloseHandle(SourceDescriptor->Handle);
|
|
|
|
//
|
|
// Return the status
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_CLOSED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_TCP)
|
|
{
|
|
//
|
|
// Shutdown connection
|
|
//
|
|
CommunicationClientShutdownConnection(SourceDescriptor->Socket);
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
CommunicationClientCleanup(SourceDescriptor->Socket);
|
|
|
|
//
|
|
// Return the status
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_CLOSED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_NAMEDPIPE)
|
|
{
|
|
//
|
|
// Close the file
|
|
//
|
|
NamedPipeClientClosePipe(SourceDescriptor->Handle);
|
|
|
|
//
|
|
// Return the status
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_CLOSED;
|
|
}
|
|
else if (SourceDescriptor->Type == EVENT_FORWARDING_MODULE)
|
|
{
|
|
//
|
|
// Free the library
|
|
//
|
|
FreeLibrary(SourceDescriptor->Module);
|
|
|
|
//
|
|
// Return the status
|
|
//
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_SUCCESSFULLY_CLOSED;
|
|
}
|
|
|
|
return DEBUGGER_OUTPUT_SOURCE_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a new source (create handle from the source)
|
|
* @param SourceType Type of the source
|
|
* @param Description Description of the source
|
|
* @param Socket Socket object in the case of TCP connection
|
|
* @param Module Module object in the case of loading modules
|
|
*
|
|
* @details If the target connection is a tcp connection then there
|
|
* is no handle and instead there is a socket, this way we pass a
|
|
* valid value for handle (TRUE) which is not a valid handle but it
|
|
* indicates that the operation was successful and the caller can use
|
|
* the pointer that it passed as the socket.
|
|
* On anything other than tcp sockets, the socket pointer in not
|
|
* modified; thus, it's not value
|
|
*
|
|
* @return HANDLE returns handle of the source
|
|
*/
|
|
VOID *
|
|
ForwardingCreateOutputSource(DEBUGGER_EVENT_FORWARDING_TYPE SourceType,
|
|
const string & Description,
|
|
SOCKET * Socket,
|
|
HMODULE * Module)
|
|
{
|
|
string IpPortDelimiter;
|
|
string Ip;
|
|
string Port;
|
|
|
|
if (SourceType == EVENT_FORWARDING_FILE)
|
|
{
|
|
//
|
|
// Create a new file
|
|
//
|
|
HANDLE FileHandle = CreateFileA(Description.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
//
|
|
// The handle might be INVALID_HANDLE_VALUE which will be
|
|
// checked by the caller
|
|
//
|
|
return (void *)FileHandle;
|
|
}
|
|
else if (SourceType == EVENT_FORWARDING_MODULE)
|
|
{
|
|
HMODULE ModuleHandle = LoadLibraryA(Description.c_str());
|
|
|
|
if (ModuleHandle == NULL)
|
|
{
|
|
ShowMessages("err, unable to load the module\n");
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
hyperdbg_event_forwarding_t hyperdbg_event_forwarding = (hyperdbg_event_forwarding_t)GetProcAddress(ModuleHandle, "hyperdbg_event_forwarding");
|
|
|
|
if (hyperdbg_event_forwarding == NULL)
|
|
{
|
|
ShowMessages("err, unable to find the 'hyperdbg_event_forwarding' function\n");
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Set the module handle
|
|
//
|
|
*Module = ModuleHandle;
|
|
|
|
//
|
|
// The handle is the location of the hyperdbg_event_forwarding function
|
|
//
|
|
return (void *)hyperdbg_event_forwarding;
|
|
}
|
|
else if (SourceType == EVENT_FORWARDING_NAMEDPIPE)
|
|
{
|
|
HANDLE PipeHandle = NamedPipeClientCreatePipe(Description.c_str());
|
|
|
|
if (!PipeHandle)
|
|
{
|
|
//
|
|
// Unable to create handle
|
|
//
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return (void *)PipeHandle;
|
|
}
|
|
else if (SourceType == EVENT_FORWARDING_TCP)
|
|
{
|
|
//
|
|
// Check if the port is included in the description string or not
|
|
//
|
|
if (Description.find(':') != std::string::npos)
|
|
{
|
|
//
|
|
// Split the ip and port by : delimiter
|
|
//
|
|
IpPortDelimiter = ':';
|
|
size_t find = Description.find(IpPortDelimiter);
|
|
Ip = Description.substr(0, find);
|
|
Port = Description.substr(find + 1, find + Description.size());
|
|
|
|
//
|
|
// Connect to server, in this function 0 means there was no error
|
|
// and 1 means there was an error
|
|
//
|
|
if (CommunicationClientConnectToServer(Ip.c_str(), Port.c_str(), Socket) == 0)
|
|
{
|
|
//
|
|
// Send a fake handle just to avoid sending INVALID_HANDLE_VALUE
|
|
// because this functionality doesn't work with handlers; however,
|
|
// send 1 or TRUE is a valid handle
|
|
//
|
|
return (void *)TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There was an error in connecting to the server
|
|
// so return an invalid handle
|
|
//
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Invalid address format
|
|
//
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// By default, handle is invalid
|
|
//
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send the event result to the corresponding sources
|
|
* @param EventDetail Description saved about the event in the
|
|
* user-mode
|
|
* @param MessageLength Length of the message
|
|
* @details This function will not check whether the event has an
|
|
* output source or not, the caller if this function should make
|
|
* sure that the following event has valid output sources or not
|
|
*
|
|
* @return BOOLEAN whether sending results was successful or not
|
|
*/
|
|
BOOLEAN
|
|
ForwardingPerformEventForwarding(PDEBUGGER_GENERAL_EVENT_DETAIL EventDetail,
|
|
CHAR * Message,
|
|
UINT32 MessageLength)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
PLIST_ENTRY TempList = 0;
|
|
|
|
for (size_t i = 0; i < DebuggerOutputSourceMaximumRemoteSourceForSingleEvent; i++)
|
|
{
|
|
//
|
|
// Check whether we reached to the end of the events
|
|
//
|
|
if (EventDetail->OutputSourceTags[i] == NULL)
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
//
|
|
// If we reach here then the output tag is not null
|
|
// means that we should find the event tag from list
|
|
// of tags
|
|
//
|
|
TempList = &g_OutputSources;
|
|
|
|
while (&g_OutputSources != TempList->Flink)
|
|
{
|
|
TempList = TempList->Flink;
|
|
|
|
PDEBUGGER_EVENT_FORWARDING CurrentOutputSourceDetails = CONTAINING_RECORD(
|
|
TempList,
|
|
DEBUGGER_EVENT_FORWARDING,
|
|
OutputSourcesList);
|
|
|
|
if (EventDetail->OutputSourceTags[i] ==
|
|
CurrentOutputSourceDetails->OutputUniqueTag)
|
|
{
|
|
//
|
|
// Indicate that we found a tag that matches this item
|
|
// Now, we should check whether the output is opened or
|
|
// not closed
|
|
//
|
|
if (CurrentOutputSourceDetails->State ==
|
|
EVENT_FORWARDING_STATE_OPENED)
|
|
{
|
|
switch (CurrentOutputSourceDetails->Type)
|
|
{
|
|
case EVENT_FORWARDING_NAMEDPIPE:
|
|
Result = ForwardingSendToNamedPipe(
|
|
CurrentOutputSourceDetails->Handle,
|
|
Message,
|
|
MessageLength);
|
|
break;
|
|
case EVENT_FORWARDING_FILE:
|
|
Result = ForwardingWriteToFile(CurrentOutputSourceDetails->Handle,
|
|
Message,
|
|
MessageLength);
|
|
break;
|
|
case EVENT_FORWARDING_TCP:
|
|
Result = ForwardingSendToTcpSocket(
|
|
CurrentOutputSourceDetails->Socket,
|
|
Message,
|
|
MessageLength);
|
|
break;
|
|
case EVENT_FORWARDING_MODULE:
|
|
((hyperdbg_event_forwarding_t)CurrentOutputSourceDetails->Handle)(
|
|
Message,
|
|
MessageLength);
|
|
Result = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No need to search through the list anymore
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Check and send the event result to the corresponding sources
|
|
* @param OperationCode The target operation code or tag
|
|
* @param MessageLength Length of the message
|
|
* @details This function will not check whether the event has an
|
|
* output source or not, the caller if this function should make
|
|
* sure that the following event has valid output sources or not
|
|
*
|
|
* @return BOOLEAN whether sending results was successful or not
|
|
*/
|
|
BOOLEAN
|
|
ForwardingCheckAndPerformEventForwarding(UINT32 OperationCode,
|
|
CHAR * Message,
|
|
UINT32 MessageLength)
|
|
{
|
|
PLIST_ENTRY TempList;
|
|
BOOLEAN OutputSourceFound = FALSE;
|
|
|
|
//
|
|
// We should check whether the following flag matches
|
|
// with an output or not, also this is not where we want to
|
|
// check output resources
|
|
//
|
|
TempList = &g_EventTrace;
|
|
while (&g_EventTrace != TempList->Blink)
|
|
{
|
|
TempList = TempList->Blink;
|
|
|
|
PDEBUGGER_GENERAL_EVENT_DETAIL EventDetail = CONTAINING_RECORD(
|
|
TempList,
|
|
DEBUGGER_GENERAL_EVENT_DETAIL,
|
|
CommandsEventList);
|
|
|
|
if (EventDetail->HasCustomOutput && (UINT32)EventDetail->Tag == OperationCode)
|
|
{
|
|
//
|
|
// Output source found
|
|
//
|
|
OutputSourceFound = TRUE;
|
|
|
|
//
|
|
// Send the event to output sources
|
|
// Minus one (-1) is because we want to
|
|
// remove the null character at end of the message
|
|
//
|
|
if (!ForwardingPerformEventForwarding(
|
|
EventDetail,
|
|
Message,
|
|
MessageLength))
|
|
{
|
|
ShowMessages("err, there was an error transferring the "
|
|
"message to the remote sources\n");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return OutputSourceFound;
|
|
}
|
|
|
|
/**
|
|
* @brief Write the output results to the file
|
|
* @param FileHandle Handle of the target file
|
|
* @param Message The message that should be written to file
|
|
* @param MessageLength Length of the message
|
|
* @details This function will not check whether the event has an
|
|
* output source or not, the caller if this function should make
|
|
* sure that the following event has valid output sources or not
|
|
*
|
|
* @return BOOLEAN whether the writing to the file was successful or not
|
|
*/
|
|
BOOLEAN
|
|
ForwardingWriteToFile(HANDLE FileHandle, CHAR * Message, UINT32 MessageLength)
|
|
{
|
|
DWORD BytesWritten = 0;
|
|
BOOL ErrorFlag = FALSE;
|
|
|
|
ErrorFlag = WriteFile(FileHandle, // open file handle
|
|
Message, // start of data to write
|
|
MessageLength, // number of bytes to write
|
|
&BytesWritten, // number of bytes that were written
|
|
NULL); // no overlapped structure
|
|
|
|
return TRUE;
|
|
|
|
if (ErrorFlag == FALSE)
|
|
{
|
|
//
|
|
// Err, terminal failure: Unable to write to file
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (BytesWritten != MessageLength)
|
|
{
|
|
//
|
|
// This is an error because a synchronous write that results in
|
|
// success (WriteFile returns TRUE) should write all data as
|
|
// requested. This would not necessarily be the case for
|
|
// asynchronous writes.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Successfully wrote
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// by default we assume there was an error
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send the output results to the namedpipe
|
|
* @param NamedPipeHandle Handle of the target namedpipe
|
|
* @param Message The message that should be sent to namedpipe
|
|
* @param MessageLength Length of the message
|
|
* @details This function will not check whether the event has an
|
|
* output source or not, the caller if this function should make
|
|
* sure that the following event has valid output sources or not
|
|
*
|
|
* @return BOOLEAN whether the sending to the namedpipe was successful or not
|
|
*/
|
|
BOOLEAN
|
|
ForwardingSendToNamedPipe(HANDLE NamedPipeHandle, CHAR * Message, UINT32 MessageLength)
|
|
{
|
|
BOOLEAN SentMessageResult;
|
|
|
|
SentMessageResult =
|
|
NamedPipeClientSendMessage(NamedPipeHandle, Message, MessageLength);
|
|
|
|
if (!SentMessageResult)
|
|
{
|
|
//
|
|
// Sending error
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Successfully sent
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Send the output results to the tcp socket
|
|
* @param TcpSocket Socket object of the target tcp socket
|
|
* @param Message The message that should be sent to the tcp socket
|
|
* @param MessageLength Length of the message
|
|
* @details This function will not check whether the event has an
|
|
* output source or not, the caller if this function should make
|
|
* sure that the following event has valid output sources or not
|
|
*
|
|
* @return BOOLEAN whether the sending to the tcp socket was successful or not
|
|
*/
|
|
BOOLEAN
|
|
ForwardingSendToTcpSocket(SOCKET TcpSocket, CHAR * Message, UINT32 MessageLength)
|
|
{
|
|
if (CommunicationClientSendMessage(TcpSocket, Message, MessageLength) != 0)
|
|
{
|
|
//
|
|
// Failed to send
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Successfully sent
|
|
//
|
|
return TRUE;
|
|
}
|