mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-12 18:49:32 +00:00
726 lines
20 KiB
C++
726 lines
20 KiB
C++
/**
|
|
* @file readmem.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @author Alee Amini (aleeamini@gmail.com)
|
|
* @brief HyperDbg command for u and d*
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2020-05-27
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern BOOLEAN g_IsKdModuleLoaded;
|
|
extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee;
|
|
|
|
/**
|
|
* @brief Read memory and disassembler
|
|
*
|
|
* @param TargetAddress location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param ReadingType read from kernel or vmx-root
|
|
* @param Pid The target process id
|
|
* @param Size size of memory to read
|
|
* @param GetAddressMode check for address mode
|
|
* @param AddressMode Address mode (32 or 64)
|
|
* @param TargetBufferToStore The buffer to store the read memory
|
|
* @param ReturnLength The length of the read memory
|
|
*
|
|
* @return BOOLEAN TRUE if the operation was successful, otherwise FALSE
|
|
*/
|
|
BOOLEAN
|
|
HyperDbgReadMemory(UINT64 TargetAddress,
|
|
DEBUGGER_READ_MEMORY_TYPE MemoryType,
|
|
DEBUGGER_READ_READING_TYPE ReadingType,
|
|
UINT32 Pid,
|
|
UINT32 Size,
|
|
BOOLEAN GetAddressMode,
|
|
DEBUGGER_READ_MEMORY_ADDRESS_MODE * AddressMode,
|
|
BYTE * TargetBufferToStore,
|
|
UINT32 * ReturnLength)
|
|
{
|
|
BOOL Status;
|
|
ULONG ReturnedLength;
|
|
DEBUGGER_READ_MEMORY ReadMem = {0};
|
|
UINT32 SizeOfTargetBuffer;
|
|
|
|
//
|
|
// Check if driver is loaded if it's in local debugging mode
|
|
//
|
|
if (!g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
AssertShowMessageReturnStmt(g_IsKdModuleLoaded, g_DeviceHandle, ASSERT_MESSAGE_KD_NOT_LOADED, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse);
|
|
}
|
|
|
|
//
|
|
// Fill the read memory structure
|
|
//
|
|
ReadMem.Address = TargetAddress;
|
|
ReadMem.Pid = Pid;
|
|
ReadMem.Size = Size;
|
|
ReadMem.MemoryType = MemoryType;
|
|
ReadMem.ReadingType = ReadingType;
|
|
ReadMem.GetAddressMode = GetAddressMode;
|
|
|
|
//
|
|
// allocate buffer for transferring messages
|
|
//
|
|
SizeOfTargetBuffer = sizeof(DEBUGGER_READ_MEMORY) + (Size * sizeof(CHAR));
|
|
DEBUGGER_READ_MEMORY * MemReadRequest = (DEBUGGER_READ_MEMORY *)malloc(SizeOfTargetBuffer);
|
|
|
|
//
|
|
// Check if the buffer is allocated successfully
|
|
//
|
|
if (MemReadRequest == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(MemReadRequest, SizeOfTargetBuffer);
|
|
|
|
//
|
|
// Copy the buffer to send
|
|
//
|
|
memcpy(MemReadRequest, &ReadMem, sizeof(DEBUGGER_READ_MEMORY));
|
|
|
|
//
|
|
// Check if this is used for Debugger Mode or VMI mode
|
|
//
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
//
|
|
// It's on Debugger mode
|
|
//
|
|
if (!KdSendReadMemoryPacketToDebuggee(MemReadRequest, SizeOfTargetBuffer))
|
|
{
|
|
std::free(MemReadRequest);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's on local debugging mode
|
|
//
|
|
Status = DeviceIoControl(g_DeviceHandle, // Handle to device
|
|
IOCTL_DEBUGGER_READ_MEMORY, // IO Control Code (IOCTL)
|
|
MemReadRequest, // Input Buffer to driver.
|
|
SIZEOF_DEBUGGER_READ_MEMORY, // Input buffer length
|
|
MemReadRequest, // Output Buffer from driver.
|
|
SizeOfTargetBuffer, // 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());
|
|
std::free(MemReadRequest);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if reading memory was successful or not
|
|
//
|
|
if (MemReadRequest->KernelStatus != DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
ShowErrorMessage(MemReadRequest->KernelStatus);
|
|
std::free(MemReadRequest);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (g_IsSerialConnectedToRemoteDebuggee)
|
|
{
|
|
//
|
|
// Change the ReturnedLength as it contains the headers
|
|
//
|
|
*ReturnLength = MemReadRequest->ReturnLength;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Change the ReturnedLength as it contains the headers
|
|
//
|
|
ReturnedLength -= SIZEOF_DEBUGGER_READ_MEMORY;
|
|
*ReturnLength = ReturnedLength;
|
|
}
|
|
|
|
//
|
|
// Set address mode (if requested)
|
|
//
|
|
if (GetAddressMode)
|
|
{
|
|
*AddressMode = MemReadRequest->AddressMode;
|
|
}
|
|
|
|
//
|
|
// Copy the buffer
|
|
//
|
|
memcpy(TargetBufferToStore,
|
|
((UCHAR *)MemReadRequest) + sizeof(DEBUGGER_READ_MEMORY),
|
|
*ReturnLength);
|
|
|
|
//
|
|
// free the buffer
|
|
//
|
|
std::free(MemReadRequest);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Show memory or disassembler
|
|
*
|
|
* @param Style style of show memory (as byte, dwrod, qword)
|
|
* @param Address location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param ReadingType read from kernel or vmx-root
|
|
* @param Pid The target process id
|
|
* @param Size size of memory to read
|
|
* @param DtDetails Options for dt structure show details
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
HyperDbgShowMemoryOrDisassemble(DEBUGGER_SHOW_MEMORY_STYLE Style,
|
|
UINT64 Address,
|
|
DEBUGGER_READ_MEMORY_TYPE MemoryType,
|
|
DEBUGGER_READ_READING_TYPE ReadingType,
|
|
UINT32 Pid,
|
|
UINT32 Size,
|
|
PDEBUGGER_DT_COMMAND_OPTIONS DtDetails)
|
|
{
|
|
UINT32 ReturnedLength;
|
|
UCHAR * Buffer;
|
|
DEBUGGER_READ_MEMORY_ADDRESS_MODE AddressMode;
|
|
BOOLEAN CheckForAddressMode = FALSE;
|
|
BOOLEAN Status = FALSE;
|
|
|
|
//
|
|
// Check if this is used for disassembler or not
|
|
//
|
|
if (Style == DEBUGGER_SHOW_COMMAND_DISASSEMBLE64 ||
|
|
Style == DEBUGGER_SHOW_COMMAND_DISASSEMBLE32)
|
|
{
|
|
CheckForAddressMode = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CheckForAddressMode = FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer for output
|
|
//
|
|
Buffer = (UCHAR *)malloc(Size);
|
|
|
|
//
|
|
// Perform reading memory
|
|
//
|
|
Status = HyperDbgReadMemory(Address,
|
|
MemoryType,
|
|
ReadingType,
|
|
Pid,
|
|
Size,
|
|
CheckForAddressMode,
|
|
&AddressMode,
|
|
(BYTE *)Buffer,
|
|
&ReturnedLength);
|
|
|
|
//
|
|
// Check if reading memory was successful or not
|
|
//
|
|
if (!Status)
|
|
{
|
|
//
|
|
// Check for extra message for the dump command
|
|
//
|
|
if (Style == DEBUGGER_SHOW_COMMAND_DUMP)
|
|
{
|
|
ShowMessages("HyperDbg attempted to access an invalid target address: 0x%llx\n"
|
|
"if you are confident that the address is valid, it may be paged out "
|
|
"or not yet available in the current CR3 page table\n"
|
|
"you can use the '.pagein' command to load this page table into memory and "
|
|
"trigger a page fault (#PF), please refer to the documentation for further details\n\n",
|
|
Address);
|
|
}
|
|
|
|
//
|
|
// free the buffer
|
|
//
|
|
std::free(Buffer);
|
|
return;
|
|
}
|
|
|
|
switch (Style)
|
|
{
|
|
case DEBUGGER_SHOW_COMMAND_DT:
|
|
|
|
//
|
|
// Show the 'dt' command view
|
|
//
|
|
if (Size == ReturnedLength)
|
|
{
|
|
ScriptEngineShowDataBasedOnSymbolTypesWrapper(DtDetails->TypeName,
|
|
Address,
|
|
FALSE,
|
|
Buffer,
|
|
DtDetails->AdditionalParameters);
|
|
}
|
|
else if (ReturnedLength == 0)
|
|
{
|
|
ShowMessages("err, invalid address");
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, invalid address or memory is smaller than the structure size");
|
|
}
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DB:
|
|
|
|
ShowMemoryCommandDB(
|
|
Buffer,
|
|
Size,
|
|
Address,
|
|
MemoryType,
|
|
ReturnedLength);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DC:
|
|
|
|
ShowMemoryCommandDC(
|
|
Buffer,
|
|
Size,
|
|
Address,
|
|
MemoryType,
|
|
ReturnedLength);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DD:
|
|
|
|
ShowMemoryCommandDD(
|
|
Buffer,
|
|
Size,
|
|
Address,
|
|
MemoryType,
|
|
ReturnedLength);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DQ:
|
|
|
|
ShowMemoryCommandDQ(
|
|
Buffer,
|
|
Size,
|
|
Address,
|
|
MemoryType,
|
|
ReturnedLength);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DUMP:
|
|
|
|
CommandDumpSaveIntoFile(Buffer, Size);
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DISASSEMBLE64:
|
|
|
|
//
|
|
// Check if assembly mismatch occurred with the target address
|
|
//
|
|
if (AddressMode == DEBUGGER_READ_ADDRESS_MODE_32_BIT && MemoryType == DEBUGGER_READ_VIRTUAL_ADDRESS)
|
|
{
|
|
ShowMessages("the target address seems to be located in a 32-bit program, if so, "
|
|
"please consider using the 'u32' instead to utilize the 32-bit disassembler\n");
|
|
}
|
|
|
|
//
|
|
// Show diassembles
|
|
//
|
|
if (ReturnedLength != 0)
|
|
{
|
|
HyperDbgDisassembler64(
|
|
Buffer,
|
|
Address,
|
|
ReturnedLength,
|
|
0,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, invalid address\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case DEBUGGER_SHOW_COMMAND_DISASSEMBLE32:
|
|
|
|
//
|
|
// Check if assembly mismatch occurred with the target address
|
|
//
|
|
if (AddressMode == DEBUGGER_READ_ADDRESS_MODE_64_BIT && MemoryType == DEBUGGER_READ_VIRTUAL_ADDRESS)
|
|
{
|
|
ShowMessages("the target address seems to be located in a 64-bit program, if so, "
|
|
"please consider using the 'u' instead to utilize the 64-bit disassembler\n");
|
|
}
|
|
|
|
//
|
|
// Show diassembles
|
|
//
|
|
if (ReturnedLength != 0)
|
|
{
|
|
HyperDbgDisassembler32(
|
|
Buffer,
|
|
Address,
|
|
ReturnedLength,
|
|
0,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, invalid address\n");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// free the buffer
|
|
//
|
|
std::free(Buffer);
|
|
}
|
|
|
|
/**
|
|
* @brief Show memory in bytes (DB)
|
|
*
|
|
* @param OutputBuffer the buffer to show
|
|
* @param Size size of memory to read
|
|
* @param Address location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param Length Length of memory to show
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ShowMemoryCommandDB(UCHAR * OutputBuffer, UINT32 Size, UINT64 Address, DEBUGGER_READ_MEMORY_TYPE MemoryType, UINT64 Length)
|
|
{
|
|
UINT32 Character;
|
|
|
|
for (UINT32 i = 0; i < Size; i += 16)
|
|
{
|
|
if (MemoryType == DEBUGGER_READ_PHYSICAL_ADDRESS)
|
|
{
|
|
ShowMessages("#\t");
|
|
}
|
|
|
|
//
|
|
// Print address
|
|
//
|
|
ShowMessages("%s ", SeparateTo64BitValue((UINT64)(Address + i)).c_str());
|
|
|
|
//
|
|
// Print the hex code
|
|
//
|
|
for (SIZE_T j = 0; j < 16; j++)
|
|
{
|
|
//
|
|
// check to see if the address is valid or not
|
|
//
|
|
if (i + j >= Length)
|
|
{
|
|
ShowMessages("?? ");
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("%02X ", OutputBuffer[i + j]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Print the character
|
|
//
|
|
ShowMessages(" ");
|
|
for (SIZE_T j = 0; j < 16; j++)
|
|
{
|
|
Character = (OutputBuffer[i + j]);
|
|
if (isprint(Character))
|
|
{
|
|
ShowMessages("%c", Character);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages(".");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to new line
|
|
//
|
|
ShowMessages("\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Show memory in dword format (DC)
|
|
*
|
|
* @param OutputBuffer the buffer to show
|
|
* @param Size size of memory to read
|
|
* @param Address location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param Length Length of memory to show
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ShowMemoryCommandDC(UCHAR * OutputBuffer, UINT32 Size, UINT64 Address, DEBUGGER_READ_MEMORY_TYPE MemoryType, UINT64 Length)
|
|
{
|
|
UINT32 Character;
|
|
for (UINT32 i = 0; i < Size; i += 16)
|
|
{
|
|
if (MemoryType == DEBUGGER_READ_PHYSICAL_ADDRESS)
|
|
{
|
|
ShowMessages("#\t");
|
|
}
|
|
|
|
//
|
|
// Print address
|
|
//
|
|
ShowMessages("%s ", SeparateTo64BitValue((UINT64)(Address + i)).c_str());
|
|
|
|
//
|
|
// Print the hex code
|
|
//
|
|
for (SIZE_T j = 0; j < 16; j += 4)
|
|
{
|
|
//
|
|
// check to see if the address is valid or not
|
|
//
|
|
if (i + j >= Length)
|
|
{
|
|
ShowMessages("???????? ");
|
|
}
|
|
else
|
|
{
|
|
UINT32 OutputBufferVar = *((UINT32 *)&OutputBuffer[i + j]);
|
|
ShowMessages("%08X ", OutputBufferVar);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Print the character
|
|
//
|
|
|
|
ShowMessages(" ");
|
|
for (SIZE_T j = 0; j < 16; j++)
|
|
{
|
|
Character = (OutputBuffer[i + j]);
|
|
if (isprint(Character))
|
|
{
|
|
ShowMessages("%c", Character);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages(".");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to new line
|
|
//
|
|
ShowMessages("\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Show memory in dword format (DD)
|
|
*
|
|
* @param OutputBuffer the buffer to show
|
|
* @param Size size of memory to read
|
|
* @param Address location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param Length Length of memory to show
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ShowMemoryCommandDD(UCHAR * OutputBuffer, UINT32 Size, UINT64 Address, DEBUGGER_READ_MEMORY_TYPE MemoryType, UINT64 Length)
|
|
{
|
|
for (UINT32 i = 0; i < Size; i += 16)
|
|
{
|
|
if (MemoryType == DEBUGGER_READ_PHYSICAL_ADDRESS)
|
|
{
|
|
ShowMessages("#\t");
|
|
}
|
|
|
|
//
|
|
// Print address
|
|
//
|
|
ShowMessages("%s ", SeparateTo64BitValue((UINT64)(Address + i)).c_str());
|
|
|
|
//
|
|
// Print the hex code
|
|
//
|
|
for (SIZE_T j = 0; j < 16; j += 4)
|
|
{
|
|
//
|
|
// check to see if the address is valid or not
|
|
//
|
|
if (i + j >= Length)
|
|
{
|
|
ShowMessages("???????? ");
|
|
}
|
|
else
|
|
{
|
|
UINT32 OutputBufferVar = *((UINT32 *)&OutputBuffer[i + j]);
|
|
ShowMessages("%08X ", OutputBufferVar);
|
|
}
|
|
}
|
|
//
|
|
// Go to new line
|
|
//
|
|
ShowMessages("\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Show memory in qword format (DQ)
|
|
*
|
|
* @param OutputBuffer the buffer to show
|
|
* @param Size size of memory to read
|
|
* @param Address location of where to read the memory
|
|
* @param MemoryType type of memory (phyical or virtual)
|
|
* @param Length Length of memory to show
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ShowMemoryCommandDQ(UCHAR * OutputBuffer, UINT32 Size, UINT64 Address, DEBUGGER_READ_MEMORY_TYPE MemoryType, UINT64 Length)
|
|
{
|
|
for (UINT32 i = 0; i < Size; i += 16)
|
|
{
|
|
if (MemoryType == DEBUGGER_READ_PHYSICAL_ADDRESS)
|
|
{
|
|
ShowMessages("#\t");
|
|
}
|
|
|
|
//
|
|
// Print address
|
|
//
|
|
ShowMessages("%s ", SeparateTo64BitValue((UINT64)(Address + i)).c_str());
|
|
|
|
//
|
|
// Print the hex code
|
|
//
|
|
for (SIZE_T j = 0; j < 16; j += 8)
|
|
{
|
|
//
|
|
// check to see if the address is valid or not
|
|
//
|
|
if (i + j >= Length)
|
|
{
|
|
ShowMessages("???????? ");
|
|
}
|
|
else
|
|
{
|
|
UINT32 OutputBufferVar = *((UINT32 *)&OutputBuffer[i + j + 4]);
|
|
ShowMessages("%08X`", OutputBufferVar);
|
|
|
|
OutputBufferVar = *((UINT32 *)&OutputBuffer[i + j]);
|
|
ShowMessages("%08X ", OutputBufferVar);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go to new line
|
|
//
|
|
ShowMessages("\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Walk a linked list and show the nodes
|
|
*
|
|
* @param TargetAddress The address of the head of the linked list
|
|
* @param MemoryType The type of memory (physical or virtual)
|
|
* @param Pid The process ID to read from
|
|
* @param Offset The offset to the next pointer in the structure
|
|
* @param MaxNodes The maximum number of nodes to walk
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
HyperDbgShowMemoryLinkedList(UINT64 TargetAddress,
|
|
DEBUGGER_READ_MEMORY_TYPE MemoryType,
|
|
UINT32 Pid,
|
|
UINT64 Offset,
|
|
UINT64 MaxNodes)
|
|
{
|
|
ShowMessages("walking linked list (%s address) from %llx (offset = %llx)\n\n",
|
|
MemoryType == DEBUGGER_READ_VIRTUAL_ADDRESS ? "virtual" : "physical",
|
|
TargetAddress,
|
|
Offset);
|
|
|
|
UINT64 CurrentAddress = TargetAddress;
|
|
UINT64 Index = 0;
|
|
|
|
while (CurrentAddress != 0 && Index < MaxNodes)
|
|
{
|
|
ShowMessages("%02llx: %016llx\n", Index, CurrentAddress);
|
|
|
|
UINT64 NextPointer = 0;
|
|
UINT32 ReturnedLength = 0;
|
|
DEBUGGER_READ_MEMORY_ADDRESS_MODE AddressMode;
|
|
BOOLEAN Status;
|
|
|
|
Status = HyperDbgReadMemory(CurrentAddress + Offset,
|
|
MemoryType,
|
|
READ_FROM_KERNEL,
|
|
Pid,
|
|
sizeof(UINT64),
|
|
FALSE,
|
|
&AddressMode,
|
|
(BYTE *)&NextPointer,
|
|
&ReturnedLength);
|
|
|
|
if (!Status || ReturnedLength != sizeof(UINT64))
|
|
{
|
|
ShowMessages("err, unable to read memory at %llx\n",
|
|
CurrentAddress + Offset);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Cycle detection: stop if we looped back to the head
|
|
// (common for doubly linked circular lists like LIST_ENTRY)
|
|
//
|
|
if (NextPointer == TargetAddress && Index > 0)
|
|
{
|
|
ShowMessages("\n(list is circular, returned to head)\n");
|
|
break;
|
|
}
|
|
|
|
CurrentAddress = NextPointer;
|
|
Index++;
|
|
}
|
|
|
|
if (Index >= MaxNodes)
|
|
{
|
|
ShowMessages("\n(stopped after %llx nodes; use 'l Count' to see more)\n",
|
|
MaxNodes);
|
|
}
|
|
}
|