HyperDbg/hyperdbg/libhyperdbg/code/debugger/communication/forwarding.cpp
2024-06-24 18:05:54 +09:00

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;
}