mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-11 18:19:36 +00:00
1350 lines
43 KiB
C++
1350 lines
43 KiB
C++
/**
|
|
* @file symbol.cpp
|
|
* @author Alee Amini (alee@hyperdbg.org)
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief symbol parser
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2021-05-20
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
extern PMODULE_SYMBOL_DETAIL g_SymbolTable;
|
|
extern UINT32 g_SymbolTableSize;
|
|
extern UINT32 g_SymbolTableCurrentIndex;
|
|
extern BOOLEAN g_IsExecutingSymbolLoadingRoutines;
|
|
extern BOOLEAN g_AddressConversion;
|
|
extern std::map<UINT64, LOCAL_FUNCTION_DESCRIPTION> g_DisassemblerSymbolMap;
|
|
|
|
using namespace std;
|
|
|
|
/**
|
|
* @brief Initial load of symbols (for previously download symbols)
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymbolInitialReload()
|
|
{
|
|
//
|
|
// Load already downloaded symbol (won't download at this point)
|
|
//
|
|
ShowMessages("interpreting symbols and creating symbol maps\n");
|
|
SymbolLoadOrDownloadSymbols(FALSE, TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Locally reload the symbol table
|
|
* @param UserProcessId
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolLocalReload(UINT32 UserProcessId)
|
|
{
|
|
ShowMessages("interpreting symbols and creating symbol maps\n");
|
|
|
|
SymbolBuildSymbolTable(&g_SymbolTable, &g_SymbolTableSize, UserProcessId, FALSE);
|
|
|
|
//
|
|
// And also load the symbols
|
|
//
|
|
return SymbolLoadOrDownloadSymbols(FALSE, TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Initial and send the results of serial for the debugger
|
|
* in the case of debugger mode
|
|
* @param UserProcessId
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymbolPrepareDebuggerWithSymbolInfo(UINT32 UserProcessId)
|
|
{
|
|
//
|
|
// Load already downloaded symbol (won't download at this point)
|
|
//
|
|
SymbolBuildSymbolTable(&g_SymbolTable, &g_SymbolTableSize, UserProcessId, TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Callback for creating symbol map for disassembler
|
|
*
|
|
* @param Address
|
|
* @param ModuleName
|
|
* @param ObjectName
|
|
* @param ObjectSize
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymbolCreateDisassemblerMapCallback(UINT64 Address,
|
|
CHAR * ModuleName,
|
|
CHAR * ObjectName,
|
|
UINT32 ObjectSize)
|
|
{
|
|
//
|
|
// It has a string, should not be initialized with zero
|
|
//
|
|
LOCAL_FUNCTION_DESCRIPTION LocalFunctionDescription = {};
|
|
string FinalModuleName = "";
|
|
|
|
if (ObjectSize == 0)
|
|
{
|
|
ObjectSize = DISASSEMBLY_MAXIMUM_DISTANCE_FROM_OBJECT_NAME;
|
|
}
|
|
|
|
//
|
|
// Convert module name to string
|
|
//
|
|
if (ModuleName != NULL)
|
|
{
|
|
FinalModuleName += std::string(ModuleName) + "!";
|
|
}
|
|
|
|
//
|
|
// Convert object name to string
|
|
//
|
|
if (ObjectName != NULL)
|
|
{
|
|
FinalModuleName += std::string(ObjectName);
|
|
}
|
|
|
|
//
|
|
// Create the structure
|
|
//
|
|
LocalFunctionDescription.ObjectName = std::move(FinalModuleName);
|
|
LocalFunctionDescription.ObjectSize = ObjectSize;
|
|
|
|
//
|
|
// Add to disassembler map
|
|
//
|
|
g_DisassemblerSymbolMap[Address] = LocalFunctionDescription;
|
|
}
|
|
|
|
/**
|
|
* @brief Update (or create) symbol map for the disassembler
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolCreateDisassemblerSymbolMap()
|
|
{
|
|
//
|
|
// Clear the map table
|
|
//
|
|
g_DisassemblerSymbolMap.clear();
|
|
|
|
//
|
|
// Get all the symbols in the callback
|
|
//
|
|
ScriptEngineCreateSymbolTableForDisassemblerWrapper(SymbolCreateDisassemblerMapCallback);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief shows the functions' name for the disassembler
|
|
* @param Address
|
|
* @param UsedBaseAddress
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress)
|
|
{
|
|
std::map<UINT64, LOCAL_FUNCTION_DESCRIPTION>::iterator Low, Prev;
|
|
UINT64 Pos = Address;
|
|
|
|
//
|
|
// Check if showing function (object) names is not prohibited
|
|
// form settings command
|
|
//
|
|
if (!g_AddressConversion)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if we already built the symbol map for disassembler or not
|
|
//
|
|
if (!g_DisassemblerSymbolMap.empty())
|
|
{
|
|
Low = g_DisassemblerSymbolMap.lower_bound(Pos);
|
|
|
|
if (Low == g_DisassemblerSymbolMap.end())
|
|
{
|
|
//
|
|
// Nothing found, maybe use rbegin()
|
|
//
|
|
return FALSE;
|
|
}
|
|
else if (Low == g_DisassemblerSymbolMap.begin() && Low->first > Address)
|
|
{
|
|
//
|
|
// Nothing to do, address is below the lowest entry in symbol table
|
|
//
|
|
return FALSE;
|
|
}
|
|
else if (Low->first == Address)
|
|
{
|
|
if (*UsedBaseAddress != Address)
|
|
{
|
|
ShowMessages("%s", Low->second.ObjectName.c_str());
|
|
*UsedBaseAddress = Address;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
Prev = std::prev(Low);
|
|
UINT64 Diff = Address - Prev->first;
|
|
|
|
//
|
|
// Check, so we have a threshold boundary to add +xx to the
|
|
// symbols function name, in otherwords, the maximum number of
|
|
// bytes that a function could contain (it's definitely not the
|
|
// best option to find start and end of function, it's an approximate
|
|
// and not always might be true)
|
|
//
|
|
if (Prev->second.ObjectSize >= Diff)
|
|
{
|
|
if (*UsedBaseAddress != Prev->first)
|
|
{
|
|
ShowMessages("%s+0x%x", Prev->second.ObjectName.c_str(), Diff);
|
|
*UsedBaseAddress = Prev->first;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else if (DISASSEMBLY_MAXIMUM_DISTANCE_FROM_OBJECT_NAME >= Diff)
|
|
{
|
|
//
|
|
// We add the logic of adding Name+X+X to show that a address is x bytes
|
|
// after the Object Name and not within the size of the function but x
|
|
// bytes from the above of the function
|
|
//
|
|
if (*UsedBaseAddress != Prev->first)
|
|
{
|
|
ShowMessages("%s+0x%x+0x%x", Prev->second.ObjectName.c_str(), Diff, Diff - Prev->second.ObjectSize);
|
|
*UsedBaseAddress = Prev->first;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Nothing is showed
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Build and show symbol table details
|
|
* @param BuildLocalSymTable Should this function call to build local symbol
|
|
* or the symbols are from a remote debuggee in debugger mode
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymbolBuildAndShowSymbolTable()
|
|
{
|
|
if (g_SymbolTable == NULL || g_SymbolTableSize == NULL)
|
|
{
|
|
ShowMessages("err, symbol table is empty. please use '.sym reload' "
|
|
"to build the symbol table\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// show packet details
|
|
//
|
|
for (SIZE_T i = 0; i < g_SymbolTableSize / sizeof(MODULE_SYMBOL_DETAIL); i++)
|
|
{
|
|
ShowMessages("is pdb details available? : %s\n", g_SymbolTable[i].IsSymbolDetailsFound ? "true" : "false");
|
|
ShowMessages("is pdb a path instead of module name? : %s\n", g_SymbolTable[i].IsLocalSymbolPath ? "true" : "false");
|
|
ShowMessages("base address : %llx\n", g_SymbolTable[i].BaseAddress);
|
|
ShowMessages("file path : %s\n", g_SymbolTable[i].FilePath);
|
|
ShowMessages("guid and age : %s\n", g_SymbolTable[i].ModuleSymbolGuidAndAge);
|
|
ShowMessages("module symbol path/name : %s\n", g_SymbolTable[i].ModuleSymbolPath);
|
|
ShowMessages("is user-mode? : %s - is 32-bit? %s\n",
|
|
g_SymbolTable[i].IsUserMode ? "true" : "false",
|
|
g_SymbolTable[i].Is32Bit ? "true" : "false");
|
|
ShowMessages("========================================================================\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Load or download symbols
|
|
* @param IsDownload Download from remote server if not available locally
|
|
* @param SilentLoad Load without any message
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolLoadOrDownloadSymbols(BOOLEAN IsDownload, BOOLEAN SilentLoad)
|
|
{
|
|
string SymbolServer;
|
|
BOOLEAN Result = FALSE;
|
|
|
|
//
|
|
// *** Read symbol path/server from config file ***
|
|
//
|
|
|
|
if (!CommandSettingsGetValueFromConfigFile("SymbolServer", SymbolServer))
|
|
{
|
|
ShowMessages("please configure the symbol path (use '.help .sympath' for more information)\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if symbol table is empty
|
|
//
|
|
if (g_SymbolTable == NULL || g_SymbolTableSize == NULL)
|
|
{
|
|
ShowMessages("symbol table is empty, please use '.sym reload' to build a symbol table\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// *** Load or download available symbols ***
|
|
//
|
|
|
|
//
|
|
// Indicate that we're in loading routines
|
|
//
|
|
g_IsExecutingSymbolLoadingRoutines = TRUE;
|
|
Result = ScriptEngineSymbolInitLoadWrapper(g_SymbolTable,
|
|
g_SymbolTableSize,
|
|
IsDownload,
|
|
SymbolServer.c_str(),
|
|
SilentLoad);
|
|
|
|
//
|
|
// Build symbol table for disassembler
|
|
//
|
|
SymbolCreateDisassemblerSymbolMap();
|
|
|
|
//
|
|
// Not in loading routines anymore
|
|
//
|
|
g_IsExecutingSymbolLoadingRoutines = FALSE;
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief check and convert string to a 64 bit unsigned integer and also
|
|
* check for symbol object names and evaluate expressions
|
|
*
|
|
* @param TextToConvert the target string
|
|
* @param Result result will be save to the pointer
|
|
*
|
|
* @return BOOLEAN shows whether the conversion was successful or not
|
|
*/
|
|
BOOLEAN
|
|
SymbolConvertNameOrExprToAddress(const string & TextToConvert, PUINT64 Result)
|
|
{
|
|
BOOLEAN IsFound = FALSE;
|
|
BOOLEAN HasError = NULL;
|
|
UINT64 Address = NULL;
|
|
|
|
if (!ConvertStringToUInt64(TextToConvert, &Address))
|
|
{
|
|
//
|
|
// Check for symbol object names
|
|
//
|
|
string ConstTextToConvert = TextToConvert;
|
|
Address = ScriptEngineConvertNameToAddressWrapper(ConstTextToConvert.c_str(), &IsFound);
|
|
|
|
if (!IsFound)
|
|
{
|
|
//
|
|
// It's neither a number, nor a founded object name,
|
|
// as the last resort, we have to test whether it's an expression or not
|
|
// if we're in the Debugger Mode then we have to send it the kernel to get
|
|
// the evaluation, if we're in VMI mode, then we evaluate it here with all
|
|
// registers set to Zero
|
|
//
|
|
Address = ScriptEngineEvalSingleExpression(TextToConvert, &HasError);
|
|
|
|
if (HasError)
|
|
{
|
|
//
|
|
// Not found or has error
|
|
//
|
|
IsFound = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Expression evaluated successfully
|
|
//
|
|
IsFound = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Object name is found
|
|
//
|
|
IsFound = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a hex number
|
|
//
|
|
IsFound = TRUE;
|
|
}
|
|
|
|
//
|
|
// Set the number if the address is founded
|
|
//
|
|
if (IsFound)
|
|
{
|
|
*Result = Address;
|
|
}
|
|
|
|
return IsFound;
|
|
}
|
|
|
|
/**
|
|
* @brief Read user virtual memory of the debuggee process
|
|
*
|
|
* @param BaseAddress the base address to read from
|
|
* @param UserProcessId the target process id to read its memory
|
|
* @param ReadSize the size of bytes to read
|
|
* @param Bytes the output vector to receive the read bytes
|
|
*
|
|
* @return BOOLEAN shows whether the reading was successful or not
|
|
*/
|
|
static BOOLEAN
|
|
SymbolReadUserVirtualMemoryExact(UINT64 BaseAddress, UINT32 UserProcessId, UINT32 ReadSize, std::vector<BYTE> & Bytes)
|
|
{
|
|
BOOL Status = FALSE;
|
|
ULONG ReturnedLength = 0;
|
|
if (ReadSize > 0xffffffffu - SIZEOF_DEBUGGER_READ_MEMORY)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
UINT32 SizeOfTargetBuffer = SIZEOF_DEBUGGER_READ_MEMORY + ReadSize;
|
|
DEBUGGER_READ_MEMORY ReadMemoryRequest = {0};
|
|
DEBUGGER_READ_MEMORY * MemReadRequest = NULL;
|
|
|
|
Bytes.clear();
|
|
|
|
if (g_DeviceHandle == NULL || BaseAddress == 0 || UserProcessId == 0 || ReadSize == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MemReadRequest = (DEBUGGER_READ_MEMORY *)malloc(SizeOfTargetBuffer);
|
|
if (MemReadRequest == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ReadMemoryRequest.Address = BaseAddress;
|
|
ReadMemoryRequest.Pid = UserProcessId;
|
|
ReadMemoryRequest.Size = ReadSize;
|
|
ReadMemoryRequest.MemoryType = DEBUGGER_READ_VIRTUAL_ADDRESS;
|
|
ReadMemoryRequest.ReadingType = READ_FROM_KERNEL;
|
|
|
|
RtlZeroMemory(MemReadRequest, SizeOfTargetBuffer);
|
|
memcpy(MemReadRequest, &ReadMemoryRequest, sizeof(DEBUGGER_READ_MEMORY));
|
|
|
|
Status = DeviceIoControl(g_DeviceHandle,
|
|
IOCTL_DEBUGGER_READ_MEMORY,
|
|
MemReadRequest,
|
|
SIZEOF_DEBUGGER_READ_MEMORY,
|
|
MemReadRequest,
|
|
SizeOfTargetBuffer,
|
|
&ReturnedLength,
|
|
NULL);
|
|
|
|
if (!Status || MemReadRequest->KernelStatus != DEBUGGER_OPERATION_WAS_SUCCESSFUL || ReturnedLength != SizeOfTargetBuffer)
|
|
{
|
|
free(MemReadRequest);
|
|
return FALSE;
|
|
}
|
|
|
|
Bytes.resize(ReadSize);
|
|
memcpy(Bytes.data(), ((UCHAR *)MemReadRequest) + SIZEOF_DEBUGGER_READ_MEMORY, ReadSize);
|
|
|
|
free(MemReadRequest);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the provided range is valid and update the required size to read if needed
|
|
*
|
|
* @param Offset the offset of the range to check
|
|
* @param Length the length of the range to check
|
|
* @param RequiredSize a pointer to the variable containing the current required size, which will be updated if the end of the range exceeds it
|
|
*
|
|
* @return BOOLEAN TRUE if the range is valid and RequiredSize is updated if needed, FALSE if the range is invalid (e.g., due to overflow)
|
|
*/
|
|
static BOOLEAN
|
|
SymbolAddLoadedImageReadRange(UINT32 Offset, UINT32 Length, UINT32 * RequiredSize)
|
|
{
|
|
UINT32 EndOffset = 0;
|
|
|
|
if (RequiredSize == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Length == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (Offset > 0xffffffffu - Length)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
EndOffset = Offset + Length;
|
|
if (EndOffset > *RequiredSize)
|
|
{
|
|
*RequiredSize = EndOffset;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the provided range is within the bounds of the loaded image bytes
|
|
*
|
|
* @param LoadedImageBytes the vector containing the bytes of the loaded image
|
|
* @param Offset the offset of the range to check
|
|
* @param Length the length of the range to check
|
|
*
|
|
* @return BOOLEAN TRUE if the range is within bounds, FALSE otherwise
|
|
*/
|
|
static BOOLEAN
|
|
SymbolLoadedImageHasRange(const std::vector<BYTE> & LoadedImageBytes, UINT32 Offset, UINT32 Length)
|
|
{
|
|
return Length <= LoadedImageBytes.size() && Offset <= LoadedImageBytes.size() - Length;
|
|
}
|
|
|
|
/**
|
|
* @brief Parse the headers of a loaded PE image from the provided bytes and extract the SizeOfImage and DebugDirectory details
|
|
*
|
|
* @param LoadedImagePrefix a vector containing the initial bytes of the loaded image, which should include at least the DOS header and NT headers
|
|
* @param SizeOfImage an output pointer to receive the SizeOfImage extracted from the optional header
|
|
* @param DebugDirectory an optional output pointer to receive the IMAGE_DATA_DIRECTORY entry for the debug directory if available (can be NULL if not needed)
|
|
*
|
|
* @return BOOLEAN TRUE if the headers were successfully parsed and SizeOfImage was extracted, FALSE otherwise (e.g., if the headers are invalid or incomplete)
|
|
*/
|
|
static BOOLEAN
|
|
SymbolGetLoadedImageHeaderDetails(const std::vector<BYTE> & LoadedImagePrefix, UINT32 * SizeOfImage, IMAGE_DATA_DIRECTORY * DebugDirectory)
|
|
{
|
|
if (LoadedImagePrefix.size() < sizeof(IMAGE_DOS_HEADER) || SizeOfImage == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (DebugDirectory != NULL)
|
|
{
|
|
RtlZeroMemory(DebugDirectory, sizeof(*DebugDirectory));
|
|
}
|
|
|
|
const IMAGE_DOS_HEADER * DosHeader = (const IMAGE_DOS_HEADER *)LoadedImagePrefix.data();
|
|
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE || DosHeader->e_lfanew < 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SIZE_T NtHeaderOffset = (SIZE_T)DosHeader->e_lfanew;
|
|
if (NtHeaderOffset > LoadedImagePrefix.size() || sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) > LoadedImagePrefix.size() - NtHeaderOffset)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
const BYTE * NtHeaders = LoadedImagePrefix.data() + NtHeaderOffset;
|
|
if (*(const DWORD *)NtHeaders != IMAGE_NT_SIGNATURE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
const IMAGE_FILE_HEADER * FileHeader = (const IMAGE_FILE_HEADER *)(NtHeaders + sizeof(DWORD));
|
|
SIZE_T OptionalHeaderOffset = NtHeaderOffset + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER);
|
|
|
|
if (OptionalHeaderOffset > LoadedImagePrefix.size() || FileHeader->SizeOfOptionalHeader < sizeof(WORD) ||
|
|
FileHeader->SizeOfOptionalHeader > LoadedImagePrefix.size() - OptionalHeaderOffset)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
const BYTE * OptionalHeader = LoadedImagePrefix.data() + OptionalHeaderOffset;
|
|
WORD Magic = *(const WORD *)OptionalHeader;
|
|
|
|
if (Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC && FileHeader->SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER32))
|
|
{
|
|
const IMAGE_OPTIONAL_HEADER32 * OptionalHeader32 = (const IMAGE_OPTIONAL_HEADER32 *)OptionalHeader;
|
|
|
|
*SizeOfImage = OptionalHeader32->SizeOfImage;
|
|
if (DebugDirectory != NULL && OptionalHeader32->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_DEBUG)
|
|
{
|
|
*DebugDirectory = OptionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC && FileHeader->SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER64))
|
|
{
|
|
const IMAGE_OPTIONAL_HEADER64 * OptionalHeader64 = (const IMAGE_OPTIONAL_HEADER64 *)OptionalHeader;
|
|
|
|
*SizeOfImage = OptionalHeader64->SizeOfImage;
|
|
if (DebugDirectory != NULL && OptionalHeader64->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_DEBUG)
|
|
{
|
|
*DebugDirectory = OptionalHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the necessary bytes of a loaded user-mode module to extract the debug directory and code view information for symbol loading
|
|
*
|
|
* @param BaseAddress the base address of the loaded module in the user process
|
|
* @param UserProcessId the target process id to read its memory
|
|
* @param LoadedImageBytes an output vector to receive the bytes of the loaded image that are necessary for symbol extraction (should be cleared and filled by this function)
|
|
*
|
|
* @return BOOLEAN TRUE if the necessary bytes were successfully read, FALSE otherwise (e.g., if memory reading failed or headers are invalid)
|
|
*/
|
|
static BOOLEAN
|
|
SymbolReadLoadedUserModuleForCodeView(UINT64 BaseAddress, UINT32 UserProcessId, std::vector<BYTE> & LoadedImageBytes)
|
|
{
|
|
static constexpr UINT32 InitialLoadedImagePrefixSize = 4 * 1024;
|
|
static constexpr UINT32 MaximumLoadedImageCodeViewReadSize = 4 * 1024 * 1024;
|
|
|
|
UINT32 SizeOfImage = 0;
|
|
UINT32 RequiredSize = InitialLoadedImagePrefixSize;
|
|
IMAGE_DATA_DIRECTORY DebugDirectory = {0};
|
|
|
|
if (!SymbolReadUserVirtualMemoryExact(BaseAddress, UserProcessId, InitialLoadedImagePrefixSize, LoadedImageBytes))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SymbolGetLoadedImageHeaderDetails(LoadedImageBytes, &SizeOfImage, &DebugDirectory) || SizeOfImage == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (DebugDirectory.VirtualAddress == 0 || DebugDirectory.Size < sizeof(IMAGE_DEBUG_DIRECTORY) ||
|
|
DebugDirectory.Size % sizeof(IMAGE_DEBUG_DIRECTORY) != 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (!SymbolAddLoadedImageReadRange(DebugDirectory.VirtualAddress, DebugDirectory.Size, &RequiredSize) ||
|
|
RequiredSize > SizeOfImage ||
|
|
RequiredSize > MaximumLoadedImageCodeViewReadSize)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (RequiredSize > LoadedImageBytes.size() && !SymbolReadUserVirtualMemoryExact(BaseAddress, UserProcessId, RequiredSize, LoadedImageBytes))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (SymbolLoadedImageHasRange(LoadedImageBytes, DebugDirectory.VirtualAddress, DebugDirectory.Size))
|
|
{
|
|
const IMAGE_DEBUG_DIRECTORY * DebugEntries = (const IMAGE_DEBUG_DIRECTORY *)(LoadedImageBytes.data() + DebugDirectory.VirtualAddress);
|
|
DWORD DebugEntryCount = DebugDirectory.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
|
|
for (DWORD Index = 0; Index < DebugEntryCount; Index++)
|
|
{
|
|
const IMAGE_DEBUG_DIRECTORY * DebugEntry = &DebugEntries[Index];
|
|
|
|
if (DebugEntry->Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
|
|
DebugEntry->SizeOfData < sizeof(DWORD) + sizeof(GUID) + sizeof(DWORD))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UINT32 NextRequiredSize = RequiredSize;
|
|
if (!SymbolAddLoadedImageReadRange(DebugEntry->AddressOfRawData, DebugEntry->SizeOfData, &NextRequiredSize) ||
|
|
NextRequiredSize > SizeOfImage ||
|
|
NextRequiredSize > MaximumLoadedImageCodeViewReadSize)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RequiredSize = NextRequiredSize;
|
|
}
|
|
}
|
|
|
|
if (RequiredSize <= LoadedImageBytes.size())
|
|
{
|
|
LoadedImageBytes.resize(RequiredSize);
|
|
return TRUE;
|
|
}
|
|
|
|
return SymbolReadUserVirtualMemoryExact(BaseAddress, UserProcessId, RequiredSize, LoadedImageBytes);
|
|
}
|
|
|
|
/**
|
|
* @brief Delete and free structures and variables related to the symbols
|
|
*
|
|
* @return BOOLEAN shows whether the operation was successful or not
|
|
*/
|
|
BOOLEAN
|
|
SymbolDeleteSymTable()
|
|
{
|
|
//
|
|
// Unload all symbols
|
|
//
|
|
ScriptEngineUnloadAllSymbolsWrapper();
|
|
|
|
//
|
|
// Delete symbols
|
|
//
|
|
if (g_SymbolTable != NULL)
|
|
{
|
|
free(g_SymbolTable);
|
|
|
|
g_SymbolTable = NULL;
|
|
g_SymbolTableSize = NULL;
|
|
g_SymbolTableCurrentIndex = 0;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief make the initial packet required for symbol server
|
|
* or reload packet
|
|
*
|
|
* @param BufferToStoreDetails Pointer to a buffer to store the symbols details
|
|
* this buffer will be allocated by this function and needs to be freed by caller
|
|
* @param StoredLength The length that stored on the BufferToStoreDetails
|
|
* @param UserProcessId Which user mode process to get its modules
|
|
* @param SendOverSerial Shows whether the packet should be sent to the debugger
|
|
* over the serial or not
|
|
*
|
|
* @return BOOLEAN shows whether the operation was successful or not
|
|
*/
|
|
BOOLEAN
|
|
SymbolBuildSymbolTable(PMODULE_SYMBOL_DETAIL * BufferToStoreDetails,
|
|
PUINT32 StoredLength,
|
|
UINT32 UserProcessId,
|
|
BOOLEAN SendOverSerial)
|
|
{
|
|
BOOLEAN Status;
|
|
ULONG ReturnedLength;
|
|
PRTL_PROCESS_MODULES ModuleInfo = NULL;
|
|
PMODULE_SYMBOL_DETAIL ModuleSymDetailArray = NULL;
|
|
CHAR SystemRoot[MAX_PATH] = {0};
|
|
CHAR ModuleSymbolPath[MAX_PATH] = {0};
|
|
CHAR TempPath[MAX_PATH] = {0};
|
|
CHAR ModuleSymbolGuidAndAge[MAXIMUM_GUID_AND_AGE_SIZE] = {0};
|
|
BOOLEAN IsSymbolPdbDetailAvailable = FALSE;
|
|
BOOLEAN IsFreeUsermodeModulesBuffer = FALSE;
|
|
UINT32 ModuleDetailsSize = 0;
|
|
UINT32 ModulesCount = 0;
|
|
PUSERMODE_LOADED_MODULE_DETAILS ModuleDetailsRequest = NULL;
|
|
PUSERMODE_LOADED_MODULE_SYMBOLS Modules = NULL;
|
|
USERMODE_LOADED_MODULE_DETAILS ModuleCountRequest = {0};
|
|
|
|
//
|
|
// Check if we found an already built symbol table
|
|
//
|
|
SymbolDeleteSymTable();
|
|
|
|
//
|
|
// Get system root
|
|
//
|
|
if (GetSystemDirectoryA(SystemRoot, MAX_PATH) == NULL)
|
|
{
|
|
ShowMessages("err, unable to get system directory (%x)\n",
|
|
GetLastError());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
string SystemRootString(SystemRoot);
|
|
|
|
//
|
|
// Convert root path to lower-case
|
|
//
|
|
transform(SystemRootString.begin(),
|
|
SystemRootString.end(),
|
|
SystemRootString.begin(),
|
|
[](UCHAR c) { return std::tolower(c); });
|
|
|
|
//
|
|
// Remove system32 from the root
|
|
//
|
|
Replace(SystemRootString, "\\system32", "");
|
|
|
|
//
|
|
// *****************************************************************
|
|
// Get kernel-mode modules information
|
|
// *****************************************************************
|
|
//
|
|
if (SymbolCheckAndAllocateModuleInformation(&ModuleInfo) == FALSE)
|
|
{
|
|
ShowMessages("err, unable to get module information\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// *****************************************************************
|
|
// Get user-mode modules information
|
|
// *****************************************************************
|
|
//
|
|
|
|
//
|
|
// We won't fail the entire process if the user-mode modules is failed
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// Check if debugger is loaded or not
|
|
//
|
|
if (!g_DeviceHandle)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the module details to get the details
|
|
//
|
|
ModuleCountRequest.ProcessId = UserProcessId;
|
|
ModuleCountRequest.OnlyCountModules = TRUE;
|
|
|
|
//
|
|
// Send the request to the kernel
|
|
//
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_GET_USER_MODE_MODULE_DETAILS, // IO Control
|
|
// code
|
|
&ModuleCountRequest, // Input Buffer to driver.
|
|
sizeof(USERMODE_LOADED_MODULE_DETAILS), // Input buffer length
|
|
&ModuleCountRequest, // Output Buffer from driver.
|
|
sizeof(USERMODE_LOADED_MODULE_DETAILS), // Length of output
|
|
// buffer in bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if counting modules was successful or not
|
|
//
|
|
if (ModuleCountRequest.Result == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
ModulesCount = ModuleCountRequest.ModulesCount;
|
|
|
|
// ShowMessages("Count of modules : 0x%x\n", ModuleCountRequest.ModulesCount);
|
|
|
|
ModuleDetailsSize = sizeof(USERMODE_LOADED_MODULE_DETAILS) +
|
|
(ModuleCountRequest.ModulesCount * sizeof(USERMODE_LOADED_MODULE_SYMBOLS));
|
|
|
|
ModuleDetailsRequest = (PUSERMODE_LOADED_MODULE_DETAILS)malloc(ModuleDetailsSize);
|
|
|
|
if (ModuleDetailsRequest == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(ModuleDetailsRequest, ModuleDetailsSize);
|
|
|
|
//
|
|
// Set the module details to get the modules (not count)
|
|
//
|
|
ModuleDetailsRequest->ProcessId = UserProcessId;
|
|
ModuleDetailsRequest->OnlyCountModules = FALSE;
|
|
|
|
//
|
|
// Send the request to the kernel
|
|
//
|
|
Status = DeviceIoControl(
|
|
g_DeviceHandle, // Handle to device
|
|
IOCTL_GET_USER_MODE_MODULE_DETAILS, // IO Control
|
|
// code
|
|
ModuleDetailsRequest, // Input Buffer to driver.
|
|
sizeof(USERMODE_LOADED_MODULE_DETAILS), // Input buffer length
|
|
ModuleDetailsRequest, // Output Buffer from driver.
|
|
ModuleDetailsSize, // Length of output
|
|
// buffer in bytes.
|
|
&ReturnedLength, // Bytes placed in buffer.
|
|
NULL // synchronous call
|
|
);
|
|
|
|
if (!Status)
|
|
{
|
|
free(ModuleDetailsRequest);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// check the module list
|
|
//
|
|
if (ModuleDetailsRequest->Result == DEBUGGER_OPERATION_WAS_SUCCESSFUL)
|
|
{
|
|
//
|
|
// Se the modules buffer
|
|
//
|
|
Modules = (PUSERMODE_LOADED_MODULE_SYMBOLS)((UINT64)ModuleDetailsRequest +
|
|
sizeof(USERMODE_LOADED_MODULE_DETAILS));
|
|
|
|
//
|
|
// Check if modules buffer is valid
|
|
//
|
|
if (Modules == NULL)
|
|
{
|
|
ModulesCount = 0;
|
|
free(ModuleDetailsRequest);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(ModuleDetailsRequest->Result);
|
|
free(ModuleDetailsRequest);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Everything is okay when we reached here
|
|
//
|
|
IsFreeUsermodeModulesBuffer = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ShowErrorMessage(ModuleCountRequest.Result);
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// *****************************************************************
|
|
//
|
|
|
|
//
|
|
// Allocate Details buffer
|
|
//
|
|
ModuleSymDetailArray = (PMODULE_SYMBOL_DETAIL)malloc(
|
|
(ModuleInfo->NumberOfModules + ModulesCount) * sizeof(MODULE_SYMBOL_DETAIL));
|
|
|
|
if (ModuleSymDetailArray == NULL)
|
|
{
|
|
ShowMessages("err, unable to allocate memory for module list (%x)\n",
|
|
GetLastError());
|
|
|
|
if (IsFreeUsermodeModulesBuffer)
|
|
{
|
|
free(ModuleDetailsRequest);
|
|
}
|
|
|
|
free(ModuleInfo);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure buffer is zero
|
|
//
|
|
RtlZeroMemory(ModuleSymDetailArray, (ModuleInfo->NumberOfModules + ModulesCount) * sizeof(MODULE_SYMBOL_DETAIL));
|
|
|
|
//
|
|
// ----------------------------------------------------------------------------------
|
|
// Add user-modules
|
|
// ----------------------------------------------------------------------------------
|
|
//
|
|
|
|
if (ModulesCount != 0)
|
|
{
|
|
for (UINT32 i = 0; i < ModulesCount; i++)
|
|
{
|
|
//
|
|
// For logging purpose
|
|
//
|
|
|
|
/*
|
|
ShowMessages("%016llx\t%016llx\t%ws\n",
|
|
Modules[i].BaseAddress,
|
|
Modules[i].Entrypoint,
|
|
Modules[i].FilePath);
|
|
*/
|
|
|
|
//
|
|
// Read symbol signature details
|
|
//
|
|
RtlZeroMemory(ModuleSymbolPath, sizeof(ModuleSymbolPath));
|
|
RtlZeroMemory(TempPath, sizeof(TempPath));
|
|
RtlZeroMemory(ModuleSymbolGuidAndAge, sizeof(ModuleSymbolGuidAndAge));
|
|
std::vector<BYTE> LoadedImageBytes;
|
|
|
|
//
|
|
// Convert symbol path from unicode to ascii
|
|
//
|
|
wcstombs(TempPath, Modules[i].FilePath, MAX_PATH);
|
|
|
|
if (SendOverSerial && Modules[i].BaseAddress != 0 &&
|
|
SymbolReadLoadedUserModuleForCodeView(Modules[i].BaseAddress, UserProcessId, LoadedImageBytes) &&
|
|
ScriptEngineConvertLoadedModuleToPdbFileAndGuidAndAgeDetailsWrapper(LoadedImageBytes.data(),
|
|
LoadedImageBytes.size(),
|
|
TempPath,
|
|
ModuleSymbolPath,
|
|
ModuleSymbolGuidAndAge,
|
|
ModuleDetailsRequest->Is32Bit))
|
|
{
|
|
IsSymbolPdbDetailAvailable = TRUE;
|
|
|
|
// ShowMessages("Hash : %s , Symbol path : %s\n", ModuleSymbolGuidAndAge, ModuleSymbolPath);
|
|
}
|
|
else if (ScriptEngineConvertFileToPdbFileAndGuidAndAgeDetailsWrapper(
|
|
TempPath,
|
|
ModuleSymbolPath,
|
|
ModuleSymbolGuidAndAge,
|
|
ModuleDetailsRequest->Is32Bit))
|
|
{
|
|
IsSymbolPdbDetailAvailable = TRUE;
|
|
|
|
// ShowMessages("Hash : %s , Symbol path : %s\n", ModuleSymbolGuidAndAge, ModuleSymbolPath);
|
|
}
|
|
else
|
|
{
|
|
IsSymbolPdbDetailAvailable = FALSE;
|
|
|
|
// ShowMessages("err, unable to get module pdb details\n");
|
|
}
|
|
|
|
//
|
|
// Build the structure for this module
|
|
//
|
|
ModuleSymDetailArray[i].BaseAddress = Modules[i].BaseAddress;
|
|
ModuleSymDetailArray[i].IsUserMode = TRUE;
|
|
ModuleSymDetailArray[i].Is32Bit = ModuleDetailsRequest->Is32Bit;
|
|
|
|
memcpy(ModuleSymDetailArray[i].FilePath, TempPath, strlen(TempPath));
|
|
|
|
if (IsSymbolPdbDetailAvailable)
|
|
{
|
|
ModuleSymDetailArray[i].IsSymbolDetailsFound = TRUE;
|
|
memcpy(ModuleSymDetailArray[i].ModuleSymbolGuidAndAge, ModuleSymbolGuidAndAge, MAXIMUM_GUID_AND_AGE_SIZE);
|
|
memcpy(ModuleSymDetailArray[i].ModuleSymbolPath, ModuleSymbolPath, MAX_PATH);
|
|
|
|
//
|
|
// Check if pdb file name is a real path or a module name
|
|
//
|
|
string ModuleSymbolPathString(ModuleSymbolPath);
|
|
if (ModuleSymbolPathString.find(":\\") != std::string::npos)
|
|
ModuleSymDetailArray[i].IsLocalSymbolPath = TRUE;
|
|
else
|
|
ModuleSymDetailArray[i].IsLocalSymbolPath = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ModuleSymDetailArray[i].IsSymbolDetailsFound = FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if it should be send to the remote debugger over serial
|
|
// and also make sure that we're connected to the remote debugger
|
|
// and this is a debuggee
|
|
//
|
|
if (SendOverSerial)
|
|
{
|
|
KdSendSymbolDetailPacket(&ModuleSymDetailArray[i], i, ModuleInfo->NumberOfModules + ModulesCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ----------------------------------------------------------------------------------
|
|
// Add kernel-modules
|
|
// ----------------------------------------------------------------------------------
|
|
//
|
|
|
|
for (UINT32 i = 0; i < ModuleInfo->NumberOfModules; i++)
|
|
{
|
|
UINT32 IndexInSymbolBuffer = ModulesCount + i;
|
|
|
|
auto PathName = ModuleInfo->Modules[i].FullPathName + ModuleInfo->Modules[i].OffsetToFileName;
|
|
|
|
//
|
|
// Read symbol signature details
|
|
//
|
|
RtlZeroMemory(ModuleSymbolPath, sizeof(ModuleSymbolPath));
|
|
RtlZeroMemory(ModuleSymbolGuidAndAge, sizeof(ModuleSymbolGuidAndAge));
|
|
|
|
string ModuleFullPath((const CHAR *)ModuleInfo->Modules[i].FullPathName);
|
|
|
|
if (ModuleFullPath.rfind("\\SystemRoot\\", 0) == 0)
|
|
{
|
|
//
|
|
// Path starts with \SystemRoot\
|
|
// we should change it to the real system root
|
|
// path
|
|
//
|
|
Replace(ModuleFullPath, "\\SystemRoot", SystemRootString);
|
|
}
|
|
|
|
//
|
|
// Kernel modules are all 64-bit
|
|
//
|
|
if (ScriptEngineConvertFileToPdbFileAndGuidAndAgeDetailsWrapper(ModuleFullPath.c_str(),
|
|
ModuleSymbolPath,
|
|
ModuleSymbolGuidAndAge,
|
|
FALSE))
|
|
{
|
|
IsSymbolPdbDetailAvailable = TRUE;
|
|
|
|
//
|
|
// ShowMessages("Hash : %s , Symbol path : %s\n", ModuleSymbolGuidAndAge, ModuleSymbolPath);
|
|
//
|
|
}
|
|
else
|
|
{
|
|
IsSymbolPdbDetailAvailable = FALSE;
|
|
|
|
//
|
|
// ShowMessages("err, unable to get module pdb details\n");
|
|
//
|
|
}
|
|
|
|
//
|
|
// Build the structure for this module
|
|
//
|
|
ModuleSymDetailArray[IndexInSymbolBuffer].BaseAddress = (UINT64)ModuleInfo->Modules[i].ImageBase;
|
|
memcpy(ModuleSymDetailArray[IndexInSymbolBuffer].FilePath, ModuleFullPath.c_str(), ModuleFullPath.size());
|
|
|
|
if (IsSymbolPdbDetailAvailable)
|
|
{
|
|
ModuleSymDetailArray[IndexInSymbolBuffer].IsSymbolDetailsFound = TRUE;
|
|
memcpy(ModuleSymDetailArray[IndexInSymbolBuffer].ModuleSymbolGuidAndAge, ModuleSymbolGuidAndAge, MAXIMUM_GUID_AND_AGE_SIZE);
|
|
memcpy(ModuleSymDetailArray[IndexInSymbolBuffer].ModuleSymbolPath, ModuleSymbolPath, MAX_PATH);
|
|
|
|
//
|
|
// Check if pdb file name is a real path or a module name
|
|
//
|
|
string ModuleSymbolPathString(ModuleSymbolPath);
|
|
if (ModuleSymbolPathString.find(":\\") != std::string::npos)
|
|
ModuleSymDetailArray[IndexInSymbolBuffer].IsLocalSymbolPath = TRUE;
|
|
else
|
|
ModuleSymDetailArray[IndexInSymbolBuffer].IsLocalSymbolPath = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ModuleSymDetailArray[IndexInSymbolBuffer].IsSymbolDetailsFound = FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if it should be send to the remote debugger over serial
|
|
// and also make sure that we're connected to the remote debugger
|
|
// and this is a debuggee
|
|
//
|
|
if (SendOverSerial)
|
|
{
|
|
KdSendSymbolDetailPacket(&ModuleSymDetailArray[IndexInSymbolBuffer], IndexInSymbolBuffer, ModuleInfo->NumberOfModules + ModulesCount);
|
|
}
|
|
}
|
|
|
|
//
|
|
// ----------------------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
// Store the buffer and length of module symbols details
|
|
//
|
|
*BufferToStoreDetails = ModuleSymDetailArray;
|
|
*StoredLength = (ModuleInfo->NumberOfModules + ModulesCount) * sizeof(MODULE_SYMBOL_DETAIL);
|
|
|
|
free(ModuleInfo);
|
|
|
|
if (IsFreeUsermodeModulesBuffer)
|
|
{
|
|
free(ModuleDetailsRequest);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate (build) and update the symbol table whenever a debuggee is attached
|
|
* on the debugger mode
|
|
*
|
|
* @param SymbolDetail Pointer to a buffer that was received as the single
|
|
* symbol info
|
|
*
|
|
* @return BOOLEAN shows whether the operation was successful or not
|
|
*/
|
|
BOOLEAN
|
|
SymbolBuildAndUpdateSymbolTable(PMODULE_SYMBOL_DETAIL SymbolDetail)
|
|
{
|
|
//
|
|
// Check to avoid overflow in symbol table
|
|
//
|
|
if (g_SymbolTableCurrentIndex >= MAXIMUM_SUPPORTED_SYMBOLS)
|
|
{
|
|
ShowMessages("err, the symbol table buffer is full, unable to add new symbol\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if we found an already built symbol table
|
|
//
|
|
if (g_SymbolTable == NULL)
|
|
{
|
|
//
|
|
// Allocate Details buffer
|
|
//
|
|
g_SymbolTable = (PMODULE_SYMBOL_DETAIL)malloc(MAXIMUM_SUPPORTED_SYMBOLS * sizeof(MODULE_SYMBOL_DETAIL));
|
|
|
|
if (g_SymbolTable == NULL)
|
|
{
|
|
ShowMessages("err, unable to allocate memory for module list (%x)\n",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reset the index
|
|
//
|
|
g_SymbolTableCurrentIndex = 0;
|
|
|
|
//
|
|
// Make sure buffer is zero
|
|
//
|
|
RtlZeroMemory(g_SymbolTable, MAXIMUM_SUPPORTED_SYMBOLS * sizeof(MODULE_SYMBOL_DETAIL));
|
|
}
|
|
|
|
//
|
|
// Move it to the new buffer
|
|
//
|
|
memcpy(&g_SymbolTable[g_SymbolTableCurrentIndex], SymbolDetail, sizeof(MODULE_SYMBOL_DETAIL));
|
|
|
|
//
|
|
// Add to index for future symbols
|
|
//
|
|
g_SymbolTableCurrentIndex++;
|
|
|
|
//
|
|
// Compute the (new) current size
|
|
//
|
|
g_SymbolTableSize = g_SymbolTableCurrentIndex * sizeof(MODULE_SYMBOL_DETAIL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Update the symbol table from remote debuggee in debugger mode
|
|
* @param ProcessId
|
|
*
|
|
* @return BOOLEAN shows whether the operation was successful or not
|
|
*/
|
|
BOOLEAN
|
|
SymbolReloadSymbolTableInDebuggerMode(UINT32 ProcessId)
|
|
{
|
|
//
|
|
// Check if we found an already built symbol table
|
|
//
|
|
SymbolDeleteSymTable();
|
|
|
|
//
|
|
// Request to send new symbol details
|
|
//
|
|
if (KdSendSymbolReloadPacketToDebuggee(ProcessId))
|
|
{
|
|
ShowMessages("symbol table updated successfully\n");
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Check and allocate module information
|
|
* @details The caller should free the buffer
|
|
* @param Modules
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolCheckAndAllocateModuleInformation(PRTL_PROCESS_MODULES * Modules)
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
ULONG SysModuleInfoBufferSize = 0;
|
|
|
|
//
|
|
// Enable Debug privilege to the current token
|
|
//
|
|
if (!WindowsSetDebugPrivilege())
|
|
{
|
|
ShowMessages("err, couldn't set debug privilege\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get required size of "RTL_PROCESS_MODULES" buffer
|
|
//
|
|
Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, NULL, NULL, &SysModuleInfoBufferSize);
|
|
|
|
//
|
|
// print the size of the buffer
|
|
//
|
|
// ShowMessages("size of the buffer : %x\n", SysModuleInfoBufferSize);
|
|
|
|
*Modules = (PRTL_PROCESS_MODULES)malloc(SysModuleInfoBufferSize);
|
|
|
|
if (*Modules == NULL)
|
|
{
|
|
ShowMessages("err, unable to allocate memory for module list (%x)\n",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the module list
|
|
//
|
|
Status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, *Modules, SysModuleInfoBufferSize, NULL);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ShowMessages("err, unable to query module list (%x)\n", Status);
|
|
free(*Modules);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|