mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-12 02:29:28 +00:00
2334 lines
60 KiB
C++
2334 lines
60 KiB
C++
/**
|
|
* @file symbol-parser.cpp
|
|
* @author Alee Amini (alee@hyperdbg.org)
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief symbol parser
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2021-05-29
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
#include "header/pdb-identity.h"
|
|
|
|
//
|
|
// Global Variables
|
|
//
|
|
std::vector<PSYMBOL_LOADED_MODULE_DETAILS> g_LoadedModules;
|
|
BOOLEAN g_IsLoadedModulesInitialized = FALSE;
|
|
BOOLEAN g_AbortLoadingExecution = FALSE;
|
|
CHAR * g_CurrentModuleName = NULL;
|
|
PVOID g_MessageHandler = NULL;
|
|
SymbolMapCallback g_SymbolMapForDisassembler = NULL;
|
|
|
|
/**
|
|
* @brief Reads the contents of a file into a byte vector
|
|
*
|
|
* @param LocalFilePath The path to the local file to read
|
|
* @param FileBytes An output reference to a vector that will receive the file bytes on success; will be cleared on failure
|
|
*
|
|
* @return BOOLEAN TRUE if the file was successfully read, FALSE on failure (e.g. file not found, access denied, read error)
|
|
*/
|
|
static BOOLEAN
|
|
SymReadFileBytes(const char * LocalFilePath, std::vector<BYTE> & FileBytes)
|
|
{
|
|
FileBytes.clear();
|
|
|
|
if (LocalFilePath == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
HANDLE FileHandle = CreateFileA(LocalFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (FileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LARGE_INTEGER FileSize = {0};
|
|
if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart <= 0 || (ULONGLONG)FileSize.QuadPart > FileBytes.max_size())
|
|
{
|
|
CloseHandle(FileHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
FileBytes.resize((SIZE_T)FileSize.QuadPart);
|
|
|
|
BYTE * WriteCursor = FileBytes.data();
|
|
SIZE_T Remaining = FileBytes.size();
|
|
while (Remaining != 0)
|
|
{
|
|
DWORD ChunkSize = Remaining > MAXDWORD ? MAXDWORD : (DWORD)Remaining;
|
|
DWORD BytesRead = 0;
|
|
|
|
if (!ReadFile(FileHandle, WriteCursor, ChunkSize, &BytesRead, NULL) || BytesRead == 0)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
FileBytes.clear();
|
|
return FALSE;
|
|
}
|
|
|
|
WriteCursor += BytesRead;
|
|
Remaining -= BytesRead;
|
|
}
|
|
|
|
CloseHandle(FileHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Fallback callback function to retrieve PDB file name, GUID, and age information using SymSrvGetFileIndexInfo when the primary extractor fails
|
|
*
|
|
* @param Context A context pointer that is expected to be a string representing the file path to query with SymSrvGetFileIndexInfo
|
|
* @param PdbFile An output buffer to receive the base name of the PDB file extracted from SymSrvGetFileIndexInfo. Must be at least PdbFileSize bytes
|
|
* @param PdbFileSize The size of the PdbFile buffer in bytes
|
|
* @param Guid An output pointer to receive the GUID extracted from SymSrvGetFileIndexInfo
|
|
* @param Age An output pointer to receive the age extracted from SymSrvGetFileIndexInfo
|
|
*
|
|
* @return BOOLEAN TRUE if the information was successfully retrieved and output buffers were filled as requested, FALSE otherwise (e.g., if Context is invalid or SymSrvGetFileIndexInfo fails)
|
|
*/
|
|
static BOOLEAN
|
|
SymSrvGetFileIndexInfoFallback(PVOID Context, CHAR * PdbFile, SIZE_T PdbFileSize, GUID * Guid, DWORD * Age)
|
|
{
|
|
SYMSRV_INDEX_INFO SymInfo = {0};
|
|
SymInfo.sizeofstruct = sizeof(SYMSRV_INDEX_INFO);
|
|
|
|
if (Context == NULL || PdbFile == NULL || Guid == NULL || Age == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SymSrvGetFileIndexInfo((const char *)Context, &SymInfo, 0))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCchCopyA(PdbFile, PdbFileSize, SymInfo.pdbfile)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*Guid = SymInfo.guid;
|
|
*Age = SymInfo.age;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the function callback that will be called if any message
|
|
* needs to be shown
|
|
*
|
|
* @param Handler Function that handles the messages
|
|
*/
|
|
void
|
|
SymSetTextMessageCallback(PVOID Handler)
|
|
{
|
|
g_MessageHandler = Handler;
|
|
|
|
//
|
|
// Set the pdbex's message handler
|
|
//
|
|
pdbex_set_logging_method_export(Handler);
|
|
}
|
|
|
|
/**
|
|
* @brief Show messages
|
|
*
|
|
* @param Fmt format string message
|
|
*/
|
|
VOID
|
|
ShowMessages(const char * Fmt, ...)
|
|
{
|
|
va_list ArgList;
|
|
va_list Args;
|
|
|
|
if (g_MessageHandler == NULL)
|
|
{
|
|
va_start(Args, Fmt);
|
|
vprintf(Fmt, Args);
|
|
va_end(Args);
|
|
}
|
|
else
|
|
{
|
|
char TempMessage[COMMUNICATION_BUFFER_SIZE + TCP_END_OF_BUFFER_CHARS_COUNT] = {0};
|
|
va_start(ArgList, Fmt);
|
|
INT SprintfResult = vsprintf_s(TempMessage, Fmt, ArgList);
|
|
va_end(ArgList);
|
|
|
|
if (SprintfResult != -1)
|
|
{
|
|
//
|
|
// There is another handler
|
|
//
|
|
((SendMessageWithParamCallback)g_MessageHandler)(TempMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret and find module base, based on module name
|
|
* @param SearchMask the search mask to find module
|
|
* @param SetModuleNameGlobally whether to set module name globally
|
|
*
|
|
* @return PSYMBOL_LOADED_MODULE_DETAILS NULL means error or not found,
|
|
* otherwise it returns the instance of loaded module based on search mask
|
|
*/
|
|
PSYMBOL_LOADED_MODULE_DETAILS
|
|
SymGetModuleBaseFromSearchMask(const char * SearchMask, BOOLEAN SetModuleNameGlobally)
|
|
{
|
|
string Token;
|
|
char ModuleName[_MAX_FNAME] = {0};
|
|
int Index = 0;
|
|
char Ch = '\0';
|
|
|
|
if (!g_IsLoadedModulesInitialized || SearchMask == NULL)
|
|
{
|
|
//
|
|
// no module is loaded or search mask is invalid
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Convert search mask to string
|
|
//
|
|
string SearchMaskString(SearchMask);
|
|
|
|
//
|
|
// Check if the string contains '!'
|
|
//
|
|
char Delimiter = '!';
|
|
if (SearchMaskString.find(Delimiter) != std::string::npos)
|
|
{
|
|
//
|
|
// Found
|
|
//
|
|
Token = SearchMaskString.substr(0, SearchMaskString.find(Delimiter));
|
|
|
|
strcpy(ModuleName, Token.c_str());
|
|
|
|
if (ModuleName[0] == '\0')
|
|
{
|
|
//
|
|
// Invalid name
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Convert module name to lowercase
|
|
//
|
|
while (ModuleName[Index])
|
|
{
|
|
Ch = ModuleName[Index];
|
|
|
|
//
|
|
// convert ch to lowercase using toLower()
|
|
//
|
|
ModuleName[Index] = tolower(Ch);
|
|
|
|
Index++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There is no '!' in the middle of the search mask so,
|
|
// we assume that the module is nt
|
|
//
|
|
RtlZeroMemory(ModuleName, _MAX_FNAME);
|
|
|
|
ModuleName[0] = 'n';
|
|
ModuleName[1] = 't';
|
|
}
|
|
|
|
//
|
|
// ************* Interpret based on remarks type name *************
|
|
//
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
//
|
|
// Check for the module name
|
|
//
|
|
if (strcmp((const char *)item->ModuleName, ModuleName) == 0)
|
|
{
|
|
if (SetModuleNameGlobally)
|
|
{
|
|
g_CurrentModuleName = (char *)item->ModuleName;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
//
|
|
// Check for alternative module name
|
|
//
|
|
if (strcmp((const char *)item->ModuleAlternativeName, ModuleName) == 0)
|
|
{
|
|
if (SetModuleNameGlobally)
|
|
{
|
|
g_CurrentModuleName = (char *)item->ModuleAlternativeName;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the function continues until here then it means
|
|
// that the module not found
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the offset of a field from the top of a structure
|
|
* @param Base
|
|
* @param TypeName
|
|
* @param FieldName
|
|
* @param FieldOffset
|
|
* @details This function is derived from: https://github.com/0vercl0k/sic/blob/master/src/sic/sym.cc
|
|
*
|
|
* @return BOOLEAN Whether the module is found successfully or not
|
|
*/
|
|
BOOLEAN
|
|
SymGetFieldOffsetFromModule(UINT64 Base, WCHAR * TypeName, WCHAR * FieldName, UINT32 * FieldOffset)
|
|
{
|
|
BOOLEAN Found = FALSE;
|
|
|
|
//
|
|
// Allocate a buffer to back the SYMBOL_INFO structure
|
|
//
|
|
const DWORD SizeOfStruct =
|
|
sizeof(SYMBOL_INFOW) + ((MAX_SYM_NAME - 1) * sizeof(wchar_t));
|
|
UINT8 SymbolInfoBuffer[SizeOfStruct];
|
|
auto SymbolInfo = PSYMBOL_INFOW(SymbolInfoBuffer);
|
|
|
|
//
|
|
// Initialize the fields that need initialization
|
|
//
|
|
SymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
|
|
SymbolInfo->MaxNameLen = MAX_SYM_NAME;
|
|
|
|
//
|
|
// Retrieve a type index for the type we're after
|
|
//
|
|
if (!SymGetTypeFromNameW(GetCurrentProcess(), Base, TypeName, SymbolInfo))
|
|
{
|
|
// ShowMessages("err, SymGetTypeFromName failed (%x)\n",
|
|
// GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now that we have a type, we need to enumerate its children to find the
|
|
// field we're after. First step is to get the number of children
|
|
//
|
|
const ULONG TypeIndex = SymbolInfo->TypeIndex;
|
|
DWORD ChildrenCount = 0;
|
|
if (!SymGetTypeInfo(GetCurrentProcess(), Base, TypeIndex, TI_GET_CHILDRENCOUNT, &ChildrenCount))
|
|
{
|
|
// ShowMessages("err, SymGetTypeInfo failed (%x)\n",
|
|
// GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate enough memory to receive the children ids
|
|
//
|
|
auto FindChildrenParamsBacking = std::make_unique<UINT8[]>(
|
|
sizeof(_TI_FINDCHILDREN_PARAMS) + ((ChildrenCount - 1) * sizeof(ULONG)));
|
|
auto FindChildrenParams =
|
|
(_TI_FINDCHILDREN_PARAMS *)FindChildrenParamsBacking.get();
|
|
|
|
//
|
|
// Initialize the structure with the children count
|
|
//
|
|
FindChildrenParams->Count = ChildrenCount;
|
|
|
|
//
|
|
// Get all the children ids
|
|
//
|
|
if (!SymGetTypeInfo(GetCurrentProcess(), Base, TypeIndex, TI_FINDCHILDREN, FindChildrenParams))
|
|
{
|
|
// ShowMessages("err, SymGetTypeInfo failed (%x)\n",
|
|
// GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now that we have all the ids, we can walk them and find the one that
|
|
// matches the field we're looking for
|
|
//
|
|
|
|
for (DWORD ChildIdx = 0; ChildIdx < ChildrenCount; ChildIdx++)
|
|
{
|
|
//
|
|
// Grab the child name
|
|
//
|
|
const ULONG ChildId = FindChildrenParams->ChildId[ChildIdx];
|
|
WCHAR * ChildName = nullptr;
|
|
SymGetTypeInfo(GetCurrentProcess(), Base, ChildId, TI_GET_SYMNAME, &ChildName);
|
|
|
|
//
|
|
// Grab the child size - this is useful to know if a field is a bit or a
|
|
// normal field
|
|
//
|
|
UINT64 ChildSize = 0;
|
|
SymGetTypeInfo(GetCurrentProcess(), Base, ChildId, TI_GET_LENGTH, &ChildSize);
|
|
|
|
//
|
|
// Does this child's name match the field we're looking for?
|
|
//
|
|
Found = FALSE;
|
|
if (wcscmp(ChildName, FieldName) == 0)
|
|
{
|
|
//
|
|
// If we have found the field, now we need to find its offset if
|
|
// it's a normal field, or its bit position if it is a bit
|
|
//
|
|
const IMAGEHLP_SYMBOL_TYPE_INFO Info =
|
|
(ChildSize == 1) ? TI_GET_BITPOSITION : TI_GET_OFFSET;
|
|
SymGetTypeInfo(GetCurrentProcess(), Base, ChildId, Info, FieldOffset);
|
|
|
|
Found = TRUE;
|
|
}
|
|
|
|
//
|
|
// Even if we have found a match, we need to clean up the memory
|
|
//
|
|
LocalFree(ChildName);
|
|
ChildName = nullptr;
|
|
|
|
//
|
|
// We can now break out of the loop if we have what we came looking for
|
|
//
|
|
if (Found)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Found;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the size of a data type (structure)
|
|
* @param Base
|
|
* @param TypeName
|
|
* @param TypeSize
|
|
*
|
|
* @return BOOLEAN Whether the module is found successfully or not
|
|
*/
|
|
BOOLEAN
|
|
SymGetDataTypeSizeFromModule(UINT64 Base, WCHAR * TypeName, UINT64 * TypeSize)
|
|
{
|
|
//
|
|
// Allocate a buffer to back the SYMBOL_INFO structure
|
|
//
|
|
const DWORD SizeOfStruct =
|
|
sizeof(SYMBOL_INFOW) + ((MAX_SYM_NAME - 1) * sizeof(wchar_t));
|
|
UINT8 SymbolInfoBuffer[SizeOfStruct];
|
|
auto SymbolInfo = PSYMBOL_INFOW(SymbolInfoBuffer);
|
|
|
|
//
|
|
// Initialize the fields that need initialization
|
|
//
|
|
SymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
|
|
SymbolInfo->MaxNameLen = MAX_SYM_NAME;
|
|
|
|
//
|
|
// Retrieve a type index for the type we're after
|
|
//
|
|
if (!SymGetTypeFromNameW(GetCurrentProcess(), Base, TypeName, SymbolInfo))
|
|
{
|
|
// ShowMessages("err, SymGetTypeFromName failed (%x)\n",
|
|
// GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SymGetTypeInfo(GetCurrentProcess(), Base, SymbolInfo->TypeIndex, TI_GET_LENGTH, TypeSize))
|
|
{
|
|
// ShowMessages("err, SymGetTypeInfo failed (%x)\n",
|
|
// GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
// ShowMessages("type size : %llx\n", TypeSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief initialize the DbgHelp symbols
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymInit()
|
|
{
|
|
BOOL Ret = FALSE;
|
|
DWORD Options = 0;
|
|
|
|
//
|
|
// Get options
|
|
//
|
|
Options = SymGetOptions();
|
|
|
|
//
|
|
// SYMOPT_DEBUG option asks DbgHelp to print additional troubleshooting
|
|
// messages to debug output - use the debugger's Debug Output window
|
|
// to view the messages
|
|
//
|
|
Options |= SYMOPT_DEBUG;
|
|
Options |= SYMOPT_CASE_INSENSITIVE;
|
|
SymSetOptions(Options);
|
|
|
|
//
|
|
// Initialize DbgHelp and load symbols for all modules of the current process
|
|
//
|
|
Ret = SymInitialize(
|
|
GetCurrentProcess(), // Process handle of the current process
|
|
NULL, // No user-defined search path -> use default
|
|
FALSE // Do not load symbols for modules in the current process
|
|
);
|
|
|
|
if (!Ret)
|
|
{
|
|
ShowMessages("err, symbol init failed (%x)\n",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Indicate that at least one module is loaded
|
|
//
|
|
g_IsLoadedModulesInitialized = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove wow64 extension to the module names
|
|
*
|
|
* @param ModuleAddress
|
|
* @param PdbFileName
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymCheckAndRemoveWow64Prefix(const char * ModuleAddress, const char * PdbFileName, std::string & CustomModuleName)
|
|
{
|
|
char ModuleName[_MAX_FNAME] = {0};
|
|
char PdbModuleName[_MAX_FNAME] = {0};
|
|
|
|
//
|
|
// Determine the name of the file
|
|
//
|
|
_splitpath(ModuleAddress, NULL, NULL, ModuleName, NULL);
|
|
_splitpath(PdbFileName, NULL, NULL, PdbModuleName, NULL);
|
|
|
|
//
|
|
// Convert ModuleName to lowercase
|
|
//
|
|
std::transform(ModuleName, ModuleName + strlen(ModuleName), ModuleName, [](unsigned char c) { return std::tolower(c); });
|
|
|
|
//
|
|
// Convert PdbModuleName to lowercase
|
|
//
|
|
std::transform(PdbModuleName, PdbModuleName + strlen(PdbModuleName), PdbModuleName, [](unsigned char c) { return std::tolower(c); });
|
|
|
|
//
|
|
// Compare the lowercase strings
|
|
//
|
|
if (std::strcmp(ModuleName, PdbModuleName) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Convert ModuleAddress to lowercase
|
|
std::string LowerModuleAddress(ModuleAddress);
|
|
std::transform(LowerModuleAddress.begin(), LowerModuleAddress.end(), LowerModuleAddress.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
|
|
if (PdbModuleName[0] == 'w' && LowerModuleAddress.find(":\\windows\\system32") != std::string::npos)
|
|
{
|
|
//
|
|
// Set CustomModuleName to lowercase ModuleName
|
|
//
|
|
CustomModuleName = std::string(ModuleName);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Check for alternative name of ntoskrn;
|
|
*
|
|
* @param PdbFileName
|
|
* @param CustomModuleName
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymCheckNtoskrnlPrefix(const char * PdbFileName, std::string & CustomModuleName)
|
|
{
|
|
char PdbModuleName[_MAX_FNAME] = {0};
|
|
|
|
//
|
|
// Determine the name of the file
|
|
//
|
|
_splitpath(PdbFileName, NULL, NULL, PdbModuleName, NULL);
|
|
|
|
//
|
|
// Convert PdbModuleName to lowercase
|
|
//
|
|
std::transform(PdbModuleName, PdbModuleName + strlen(PdbModuleName), PdbModuleName, [](unsigned char c) { return std::tolower(c); });
|
|
|
|
//
|
|
// Is it "nt" module or not
|
|
//
|
|
// Names of kernel
|
|
// NTOSKRNL.EXE : 1 CPU
|
|
// NTKRNLMP.EXE : N CPU, SMP
|
|
// NTKRNLPA.EXE : 1 CPU, PAE
|
|
// NTKRPAMP.EXE : N CPU SMP, PAE
|
|
//
|
|
if (strcmp(PdbModuleName, ("ntkrnlmp")) == 0 || strcmp(PdbModuleName, ("ntoskrnl")) == 0 ||
|
|
strcmp(PdbModuleName, ("ntkrpamp")) == 0 || strcmp(PdbModuleName, ("ntkrnlpa")) == 0)
|
|
{
|
|
CustomModuleName = "nt";
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Not nt module
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief load symbol based on a file name and GUID
|
|
*
|
|
* @param BaseAddress
|
|
* @param PdbFileName
|
|
* @param CustomModuleName
|
|
*
|
|
* @return UINT32
|
|
*/
|
|
UINT32
|
|
SymLoadFileSymbol(UINT64 BaseAddress, const char * PdbFileName, const char * CustomModuleName)
|
|
{
|
|
DWORD FileSize = 0;
|
|
int Index = 0;
|
|
char Ch = '\0';
|
|
char ModuleName[_MAX_FNAME] = {0};
|
|
char AlternateModuleName[_MAX_FNAME] = {0};
|
|
PSYMBOL_LOADED_MODULE_DETAILS ModuleDetails = NULL;
|
|
|
|
//
|
|
// Check if the loaded modules are initialized or not
|
|
//
|
|
if (!g_IsLoadedModulesInitialized)
|
|
{
|
|
//
|
|
// Initialize the symbol table
|
|
//
|
|
SymInit();
|
|
}
|
|
|
|
//
|
|
// Determine the base address and the file size
|
|
//
|
|
if (!SymGetFileParams(PdbFileName, FileSize))
|
|
{
|
|
ShowMessages("err, cannot obtain file parameters (internal error)\n");
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Determine the name of the file
|
|
//
|
|
_splitpath(PdbFileName, NULL, NULL, ModuleName, NULL);
|
|
|
|
//
|
|
// Move to alternate list
|
|
//
|
|
strcpy(AlternateModuleName, ModuleName);
|
|
|
|
//
|
|
// Convert module name to lowercase
|
|
//
|
|
while (ModuleName[Index])
|
|
{
|
|
Ch = ModuleName[Index];
|
|
|
|
//
|
|
// convert ch to lowercase using toLower()
|
|
//
|
|
ModuleName[Index] = tolower(Ch);
|
|
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer to store the details
|
|
//
|
|
ModuleDetails = (SYMBOL_LOADED_MODULE_DETAILS *)malloc(sizeof(SYMBOL_LOADED_MODULE_DETAILS));
|
|
|
|
if (ModuleDetails == NULL)
|
|
{
|
|
ShowMessages("err, allocating buffer for storing symbol details (%x)\n",
|
|
GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
RtlZeroMemory(ModuleDetails, sizeof(SYMBOL_LOADED_MODULE_DETAILS));
|
|
|
|
ModuleDetails->ModuleBase = SymLoadModule64(
|
|
GetCurrentProcess(), // Process handle of the current process
|
|
NULL, // Handle to the module's image file (not needed)
|
|
PdbFileName, // Path/name of the file
|
|
NULL, // User-defined short name of the module (it can be NULL)
|
|
BaseAddress, // Base address of the module (cannot be NULL if .PDB file is
|
|
// used, otherwise it can be NULL)
|
|
FileSize // Size of the file (cannot be NULL if .PDB file is used,
|
|
// otherwise it can be NULL)
|
|
);
|
|
|
|
if (ModuleDetails->ModuleBase == NULL)
|
|
{
|
|
ShowMessages("err, loading symbols failed (%x)\n",
|
|
GetLastError());
|
|
|
|
free(ModuleDetails);
|
|
return -1;
|
|
}
|
|
|
|
#ifndef DoNotShowDetailedResult
|
|
|
|
//
|
|
// Load symbols for the module
|
|
//
|
|
ShowMessages("loading symbols for: %s\n", PdbFilePath);
|
|
|
|
ShowMessages("load address: %I64x\n", ModuleDetails.ModuleBase);
|
|
|
|
//
|
|
// Obtain and display information about loaded symbols
|
|
//
|
|
SymShowSymbolInfo(ModuleDetails.ModuleBase);
|
|
|
|
#endif // !DoNotShowDetailedResult
|
|
|
|
//
|
|
// Make the details (to save)
|
|
//
|
|
ModuleDetails->BaseAddress = BaseAddress;
|
|
strcpy((char *)ModuleDetails->ModuleName, ModuleName);
|
|
strcpy((char *)ModuleDetails->PdbFilePath, PdbFileName);
|
|
|
|
//
|
|
// Save the custom module name (if any)
|
|
//
|
|
if (CustomModuleName != NULL)
|
|
{
|
|
strcpy((char *)ModuleDetails->ModuleAlternativeName, CustomModuleName);
|
|
}
|
|
|
|
//
|
|
// Save it
|
|
//
|
|
g_LoadedModules.push_back(ModuleDetails);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unload one module symbol
|
|
*
|
|
* @param ModuleName
|
|
*
|
|
* @return UINT32
|
|
*/
|
|
UINT32
|
|
SymUnloadModuleSymbol(char * ModuleName)
|
|
{
|
|
BOOLEAN OneModuleFound = FALSE;
|
|
BOOL Ret = FALSE;
|
|
UINT32 Index = 0;
|
|
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
Index++;
|
|
|
|
if (strcmp(item->ModuleName, ModuleName) == 0 || strcmp(item->ModuleAlternativeName, ModuleName) == 0)
|
|
{
|
|
//
|
|
// Unload symbol for the module
|
|
//
|
|
Ret = SymUnloadModule64(GetCurrentProcess(), item->ModuleBase);
|
|
|
|
if (!Ret)
|
|
{
|
|
ShowMessages("err, unload symbol failed (%x)\n",
|
|
GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
OneModuleFound = TRUE;
|
|
|
|
free(item);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!OneModuleFound)
|
|
{
|
|
//
|
|
// Not found
|
|
//
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Remove it from the vector
|
|
//
|
|
std::vector<PSYMBOL_LOADED_MODULE_DETAILS>::iterator it = g_LoadedModules.begin();
|
|
std::advance(it, --Index);
|
|
g_LoadedModules.erase(it);
|
|
|
|
//
|
|
// Success
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unload all the symbols
|
|
*
|
|
* @return UINT32
|
|
*/
|
|
UINT32
|
|
SymUnloadAllSymbols()
|
|
{
|
|
BOOL Ret = FALSE;
|
|
BOOLEAN IsAnythingLoaded = FALSE;
|
|
|
|
//
|
|
// Check if it's already initialized
|
|
//
|
|
if (!g_IsLoadedModulesInitialized)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
//
|
|
// At least one module is loaded
|
|
//
|
|
IsAnythingLoaded = TRUE;
|
|
|
|
//
|
|
// Unload symbols for the module
|
|
//
|
|
Ret = SymUnloadModule64(GetCurrentProcess(), item->ModuleBase);
|
|
|
|
if (!Ret)
|
|
{
|
|
// ShowMessages("err, unload symbol failed (%x)\n",
|
|
// GetLastError());
|
|
}
|
|
|
|
free(item);
|
|
}
|
|
|
|
//
|
|
// Check if there is at least one module is loaded
|
|
//
|
|
if (!IsAnythingLoaded)
|
|
{
|
|
//
|
|
// Nothing is loaded yet!
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Clear the list
|
|
//
|
|
g_LoadedModules.clear();
|
|
|
|
//
|
|
// Uninitialize DbgHelp
|
|
//
|
|
Ret = SymCleanup(GetCurrentProcess());
|
|
|
|
if (!Ret)
|
|
{
|
|
ShowMessages("err, symbol cleanup failed (%x)\n", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// It's not initialized anymore
|
|
//
|
|
g_IsLoadedModulesInitialized = FALSE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert function name to address
|
|
*
|
|
* @param FunctionOrVariableName the name of the function or variable to convert
|
|
* @param WasFound
|
|
*
|
|
* @return UINT64
|
|
*/
|
|
UINT64
|
|
SymConvertNameToAddress(const char * FunctionOrVariableName, PBOOLEAN WasFound)
|
|
{
|
|
BOOLEAN Found = FALSE;
|
|
UINT64 Address = NULL;
|
|
UINT64 Buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR) + sizeof(UINT64) - 1) / sizeof(UINT64)];
|
|
PSYMBOL_INFO Symbol = (PSYMBOL_INFO)Buffer;
|
|
string FinalModuleName;
|
|
string TempName(FunctionOrVariableName);
|
|
string ExtractedModuleName;
|
|
string FunctionName;
|
|
|
|
//
|
|
// Not found by default
|
|
//
|
|
*WasFound = FALSE;
|
|
|
|
//
|
|
// Retrieve the address from name
|
|
//
|
|
Symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
Symbol->MaxNameLen = MAX_SYM_NAME;
|
|
|
|
//
|
|
// *** Check if the module has an alternative name, then replace the
|
|
// original name with alternative name ***
|
|
//
|
|
|
|
//
|
|
// Check if '!' is present in the function or variable name
|
|
//
|
|
SIZE_T FoundIndex = TempName.find('!');
|
|
if (FoundIndex != std::string::npos)
|
|
{
|
|
ExtractedModuleName = TempName.substr(0, FoundIndex);
|
|
FunctionName = TempName.substr(FoundIndex + 1);
|
|
|
|
//
|
|
// Convert module name to lower-case
|
|
//
|
|
std::transform(ExtractedModuleName.begin(), ExtractedModuleName.end(), ExtractedModuleName.begin(), [](unsigned char c) {
|
|
return std::tolower(c);
|
|
});
|
|
|
|
//
|
|
// Check if the we find it with an alternative module name or not
|
|
//
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
//
|
|
// Check for the module name
|
|
//
|
|
if (strcmp((const char *)item->ModuleName, ExtractedModuleName.c_str()) == 0)
|
|
{
|
|
string ModuleName(item->ModuleName);
|
|
FinalModuleName = ModuleName + "!" + FunctionName;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for the alternative module name
|
|
//
|
|
if (strcmp((const char *)item->ModuleAlternativeName, ExtractedModuleName.c_str()) == 0)
|
|
{
|
|
//
|
|
// Replace the alternative module name with original module name
|
|
//
|
|
string ModuleName(item->ModuleName);
|
|
FinalModuleName = ModuleName + "!" + FunctionName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It doesn't contain a module name, so we'll use 'nt' by default
|
|
//
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
//
|
|
// Check for the module name
|
|
//
|
|
if (strcmp((const char *)item->ModuleAlternativeName, "nt") == 0)
|
|
{
|
|
//
|
|
// Replace the alternative module name with original module name
|
|
//
|
|
string ModuleName(item->ModuleName);
|
|
FinalModuleName = ModuleName + "!" + TempName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the final module name is not empty
|
|
//
|
|
if (FinalModuleName.empty())
|
|
{
|
|
*WasFound = FALSE;
|
|
return NULL;
|
|
}
|
|
|
|
if (SymFromName(GetCurrentProcess(), FinalModuleName.c_str(), Symbol))
|
|
{
|
|
//
|
|
// SymFromName returned success
|
|
//
|
|
Found = TRUE;
|
|
Address = Symbol->Address;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// SymFromName failed
|
|
//
|
|
Found = FALSE;
|
|
|
|
//
|
|
// ShowMessages("symbol not found (%x)\n", GetLastError());
|
|
//
|
|
}
|
|
|
|
*WasFound = Found;
|
|
return Address;
|
|
}
|
|
|
|
/**
|
|
* @brief Search and show symbols
|
|
* @details mainly used by the 'x' command
|
|
*
|
|
* @param TypeName
|
|
* @param FieldName
|
|
* @param FieldOffset
|
|
*
|
|
* @return BOOLEAN Whether the module is found successfully or not
|
|
*/
|
|
BOOLEAN
|
|
SymGetFieldOffset(CHAR * TypeName, CHAR * FieldName, UINT32 * FieldOffset)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
UINT32 Index = 0;
|
|
PSYMBOL_LOADED_MODULE_DETAILS SymbolInfo = NULL;
|
|
BOOLEAN Result = FALSE;
|
|
|
|
//
|
|
// Find module info
|
|
//
|
|
SymbolInfo = SymGetModuleBaseFromSearchMask(TypeName, TRUE);
|
|
|
|
//
|
|
// Check if module is found
|
|
//
|
|
if (SymbolInfo == NULL)
|
|
{
|
|
//
|
|
// Module not found or there was an error
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove the *!Name from TypeName as it not supports module name
|
|
// at the beginning of a type name
|
|
//
|
|
while (TypeName[Index] != '\0')
|
|
{
|
|
if (TypeName[Index] == '!')
|
|
{
|
|
TypeName = (CHAR *)(TypeName + Index + 1);
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Convert TypeName to wide-char, it's because SymGetTypeInfo supports
|
|
// wide-char
|
|
//
|
|
const SIZE_T TypeNameSize = strlen(TypeName) + 1;
|
|
WCHAR * TypeNameW = (WCHAR *)malloc(sizeof(wchar_t) * TypeNameSize);
|
|
|
|
if (TypeNameW == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(TypeNameW, sizeof(wchar_t) * TypeNameSize);
|
|
mbstowcs(TypeNameW, TypeName, TypeNameSize);
|
|
|
|
//
|
|
// Convert FieldName to wide-char, it's because SymGetTypeInfo supports
|
|
// wide-char
|
|
//
|
|
const SIZE_T FieldNameSize = strlen(FieldName) + 1;
|
|
WCHAR * FieldNameW = (WCHAR *)malloc(sizeof(wchar_t) * FieldNameSize);
|
|
|
|
if (FieldNameW == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
free(TypeNameW);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(FieldNameW, sizeof(wchar_t) * FieldNameSize);
|
|
mbstowcs(FieldNameW, FieldName, FieldNameSize);
|
|
|
|
Result = SymGetFieldOffsetFromModule(SymbolInfo->ModuleBase, TypeNameW, FieldNameW, FieldOffset);
|
|
|
|
free(TypeNameW);
|
|
free(FieldNameW);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the size of structures from the symbols
|
|
*
|
|
* @param TypeName the type (structure) name to query
|
|
* @param TypeSize pointer to receive the size of the data type
|
|
*
|
|
* @return BOOLEAN Whether the module is found successfully or not
|
|
*/
|
|
BOOLEAN
|
|
SymGetDataTypeSize(CHAR * TypeName, UINT64 * TypeSize)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
UINT32 Index = 0;
|
|
PSYMBOL_LOADED_MODULE_DETAILS SymbolInfo = NULL;
|
|
BOOLEAN Result = FALSE;
|
|
|
|
//
|
|
// Find module info
|
|
//
|
|
SymbolInfo = SymGetModuleBaseFromSearchMask(TypeName, TRUE);
|
|
|
|
//
|
|
// Check if module is found
|
|
//
|
|
if (SymbolInfo == NULL)
|
|
{
|
|
//
|
|
// Module not found or there was an error
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove the *!Name from TypeName as it not supports module name
|
|
// at the beginning of a type name
|
|
//
|
|
while (TypeName[Index] != '\0')
|
|
{
|
|
if (TypeName[Index] == '!')
|
|
{
|
|
TypeName = (CHAR *)(TypeName + Index + 1);
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Convert FieldName to wide-char, it's because SymGetTypeInfo supports
|
|
// wide-char
|
|
//
|
|
const SIZE_T TypeNameSize = strlen(TypeName) + 1;
|
|
WCHAR * TypeNameW = (WCHAR *)malloc(sizeof(wchar_t) * TypeNameSize);
|
|
|
|
if (TypeNameW == NULL)
|
|
{
|
|
printf("err, could not allocate buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(TypeNameW, sizeof(wchar_t) * TypeNameSize);
|
|
mbstowcs(TypeNameW, TypeName, TypeNameSize);
|
|
|
|
Result = SymGetDataTypeSizeFromModule(SymbolInfo->ModuleBase, TypeNameW, TypeSize);
|
|
|
|
free(TypeNameW);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the offset from the symbol
|
|
*
|
|
* @param SearchMask
|
|
*
|
|
* @return UINT32
|
|
*/
|
|
UINT32
|
|
SymSearchSymbolForMask(const char * SearchMask)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
PSYMBOL_LOADED_MODULE_DETAILS SymbolInfo = NULL;
|
|
|
|
//
|
|
// Get the module info
|
|
//
|
|
SymbolInfo = SymGetModuleBaseFromSearchMask(SearchMask, TRUE);
|
|
|
|
//
|
|
// Check to see if module info is found
|
|
//
|
|
if (SymbolInfo == NULL)
|
|
{
|
|
//
|
|
// Module not found or there was an error
|
|
//
|
|
return -1;
|
|
}
|
|
|
|
Ret = SymEnumSymbols(
|
|
GetCurrentProcess(), // Process handle of the current process
|
|
SymbolInfo->ModuleBase, // Base address of the module
|
|
SearchMask, // Mask (NULL -> all symbols)
|
|
SymDisplayMaskSymbolsCallback, // The callback function
|
|
NULL // A used-defined context can be passed here, if necessary
|
|
);
|
|
|
|
if (!Ret)
|
|
{
|
|
ShowMessages("err, symbol enum failed (%x)\n",
|
|
GetLastError());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Create symbol table for disassembler
|
|
* @details mainly used by disassembler for 'u' command
|
|
*
|
|
* @param CallbackFunction
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymCreateSymbolTableForDisassembler(void * CallbackFunction)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
BOOLEAN Result = TRUE;
|
|
|
|
//
|
|
// Set the callback function to deliver the name of module!ObjectName
|
|
//
|
|
g_SymbolMapForDisassembler = (SymbolMapCallback)CallbackFunction;
|
|
|
|
//
|
|
// Create a symbol table from all modules
|
|
//
|
|
for (auto item : g_LoadedModules)
|
|
{
|
|
//
|
|
// Set module name
|
|
//
|
|
g_CurrentModuleName = (char *)item->ModuleName;
|
|
|
|
//
|
|
// Call the callback for the current module
|
|
//
|
|
Ret = SymEnumSymbols(
|
|
GetCurrentProcess(), // Process handle of the current process
|
|
item->BaseAddress, // Base address of the module
|
|
NULL, // Mask (NULL -> all symbols)
|
|
SymDeliverDisassemblerSymbolMapCallback, // The callback function
|
|
NULL // A used-defined context can be passed here, if necessary
|
|
);
|
|
|
|
if (!Ret)
|
|
{
|
|
//
|
|
// A module did not added correctly
|
|
//
|
|
// ShowMessages("err, symbol enum failed (%x)\n", GetLastError());
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief add ` between 64 bit values and convert them to string
|
|
*
|
|
* @param Value
|
|
* @return string
|
|
*/
|
|
string
|
|
SymSeparateTo64BitValue(UINT64 Value)
|
|
{
|
|
ostringstream OstringStream;
|
|
string Temp;
|
|
|
|
OstringStream << setw(16) << setfill('0') << hex << Value;
|
|
Temp = OstringStream.str();
|
|
|
|
Temp.insert(8, 1, '`');
|
|
return Temp;
|
|
}
|
|
|
|
/**
|
|
* @brief Get symbol file parameters
|
|
*
|
|
* @param FileName
|
|
* @param BaseAddr
|
|
* @param FileSize
|
|
*
|
|
* @return BOOL
|
|
*/
|
|
BOOL
|
|
SymGetFileParams(const char * FileName, DWORD & FileSize)
|
|
{
|
|
//
|
|
// Check parameters
|
|
//
|
|
if (FileName == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine the extension of the file
|
|
//
|
|
char FileExt[_MAX_EXT] = {0};
|
|
|
|
_splitpath(FileName, NULL, NULL, NULL, FileExt);
|
|
|
|
//
|
|
// Is it .PDB file?
|
|
//
|
|
if (strcmp(FileExt, (".pdb")) == 0 || strcmp(FileExt, (".PDB")) == 0)
|
|
{
|
|
//
|
|
// Yes, it is a .PDB file
|
|
// Determine its size, and use a dummy base address
|
|
//
|
|
|
|
if (!SymGetFileSize(FileName, FileSize))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It is not a .PDB file
|
|
// Base address and file size can be 0
|
|
//
|
|
FileSize = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get symbol file size
|
|
*
|
|
* @param FileName
|
|
* @param FileSize
|
|
*
|
|
* @return BOOL
|
|
*/
|
|
BOOL
|
|
SymGetFileSize(const char * FileName, DWORD & FileSize)
|
|
{
|
|
//
|
|
// Check parameters
|
|
//
|
|
if (FileName == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
HANDLE HFile = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (HFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowMessages("err, unable to open symbol file (%x)\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Obtain the size of the file
|
|
//
|
|
FileSize = GetFileSize(HFile, NULL);
|
|
|
|
if (FileSize == INVALID_FILE_SIZE)
|
|
{
|
|
ShowMessages("err, unable to get symbol file size (%x)\n", GetLastError());
|
|
|
|
//
|
|
// and continue ...
|
|
//
|
|
}
|
|
|
|
//
|
|
// Close the file
|
|
//
|
|
if (!CloseHandle(HFile))
|
|
{
|
|
ShowMessages("err, unable to close symbol file (%x)\n", GetLastError());
|
|
|
|
//
|
|
// and continue ...
|
|
//
|
|
}
|
|
|
|
return (FileSize != INVALID_FILE_SIZE);
|
|
}
|
|
|
|
/**
|
|
* @brief Show symbol info
|
|
*
|
|
* @param ModuleBase
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymShowSymbolInfo(UINT64 ModuleBase)
|
|
{
|
|
//
|
|
// Get module information
|
|
//
|
|
IMAGEHLP_MODULE64 ModuleInfo;
|
|
|
|
memset(&ModuleInfo, 0, sizeof(ModuleInfo));
|
|
|
|
ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
|
|
|
|
BOOL Ret = SymGetModuleInfo64(GetCurrentProcess(), ModuleBase, &ModuleInfo);
|
|
|
|
if (!Ret)
|
|
{
|
|
ShowMessages("err, unable to get symbol file information (%x)\n",
|
|
GetLastError());
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Display information about symbols
|
|
// Kind of symbols
|
|
//
|
|
switch (ModuleInfo.SymType)
|
|
{
|
|
case SymNone:
|
|
ShowMessages("no symbols available for the module\n");
|
|
break;
|
|
|
|
case SymExport:
|
|
ShowMessages("loaded symbols: Exports\n");
|
|
break;
|
|
|
|
case SymCoff:
|
|
ShowMessages("loaded symbols: COFF\n");
|
|
break;
|
|
|
|
case SymCv:
|
|
ShowMessages("loaded symbols: CodeView\n");
|
|
break;
|
|
|
|
case SymSym:
|
|
ShowMessages("loaded symbols: SYM\n");
|
|
break;
|
|
|
|
case SymVirtual:
|
|
ShowMessages("loaded symbols: Virtual\n");
|
|
break;
|
|
|
|
case SymPdb:
|
|
ShowMessages("loaded symbols: PDB\n");
|
|
break;
|
|
|
|
case SymDia:
|
|
ShowMessages("loaded symbols: DIA\n");
|
|
break;
|
|
|
|
case SymDeferred:
|
|
|
|
//
|
|
// not actually loaded
|
|
//
|
|
ShowMessages("loaded symbols: Deferred\n");
|
|
break;
|
|
|
|
default:
|
|
ShowMessages("loaded symbols: Unknown format\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Image name
|
|
//
|
|
if (ModuleInfo.ImageName[0] != '\0')
|
|
{
|
|
ShowMessages("image name: %s\n", ModuleInfo.ImageName);
|
|
}
|
|
|
|
//
|
|
// Loaded image name
|
|
//
|
|
if (ModuleInfo.LoadedImageName[0] != '\0')
|
|
{
|
|
ShowMessages("loaded image name: %s\n", ModuleInfo.LoadedImageName);
|
|
}
|
|
|
|
//
|
|
// Loaded PDB name
|
|
//
|
|
if (ModuleInfo.LoadedPdbName[0] != '\0')
|
|
{
|
|
ShowMessages("PDB file name: %s\n", ModuleInfo.LoadedPdbName);
|
|
}
|
|
|
|
//
|
|
// Is debug information unmatched?
|
|
// (It can only happen if the debug information is contained
|
|
// in a separate file (.DBG or .PDB)
|
|
//
|
|
if (ModuleInfo.PdbUnmatched || ModuleInfo.DbgUnmatched)
|
|
{
|
|
ShowMessages("warning, unmatched symbols\n");
|
|
}
|
|
|
|
//
|
|
// *** Contents ***
|
|
//
|
|
|
|
//
|
|
// Line numbers available?
|
|
//
|
|
ShowMessages("line numbers: %s\n",
|
|
ModuleInfo.LineNumbers ? "available" : "not available");
|
|
|
|
//
|
|
// Global symbols available?
|
|
//
|
|
ShowMessages("global symbols: %s\n",
|
|
ModuleInfo.GlobalSymbols ? "available" : "not available");
|
|
|
|
//
|
|
// Type information available?
|
|
//
|
|
ShowMessages("type information: %s\n",
|
|
ModuleInfo.TypeInfo ? ("Available") : ("Not available"));
|
|
|
|
//
|
|
// Source indexing available?
|
|
//
|
|
ShowMessages("source indexing: %s\n",
|
|
ModuleInfo.SourceIndexed ? "yes" : "no");
|
|
|
|
//
|
|
// Public symbols available?
|
|
//
|
|
ShowMessages("public symbols: %s\n",
|
|
ModuleInfo.Publics ? "available" : "not available");
|
|
}
|
|
|
|
/**
|
|
* @brief Callback for showing and enumerating symbols
|
|
*
|
|
* @param SymInfo
|
|
* @param SymbolSize
|
|
* @param UserContext
|
|
*
|
|
* @return BOOL
|
|
*/
|
|
BOOL CALLBACK
|
|
SymDisplayMaskSymbolsCallback(SYMBOL_INFO * SymInfo, ULONG SymbolSize, PVOID UserContext)
|
|
{
|
|
if (SymInfo != 0)
|
|
{
|
|
SymShowSymbolDetails(*SymInfo);
|
|
}
|
|
|
|
//
|
|
// Continue enumeration
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Callback for delivering module!ObjectName to disassembler symbol map
|
|
*
|
|
* @param SymInfo
|
|
* @param SymbolSize
|
|
* @param UserContext
|
|
*
|
|
* @return BOOL
|
|
*/
|
|
BOOL CALLBACK
|
|
SymDeliverDisassemblerSymbolMapCallback(SYMBOL_INFO * SymInfo, ULONG SymbolSize, PVOID UserContext)
|
|
{
|
|
if (SymInfo != 0 && g_SymbolMapForDisassembler != NULL)
|
|
{
|
|
//
|
|
// Call the remote callback
|
|
//
|
|
g_SymbolMapForDisassembler(SymInfo->Address, g_CurrentModuleName, SymInfo->Name, SymInfo->Size);
|
|
}
|
|
|
|
//
|
|
// Continue enumeration
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Show symbols details
|
|
*
|
|
* @param SymInfo
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymShowSymbolDetails(SYMBOL_INFO & SymInfo)
|
|
{
|
|
if (g_CurrentModuleName == NULL)
|
|
{
|
|
//
|
|
// Name Address
|
|
//
|
|
ShowMessages("%s ", SymSeparateTo64BitValue(SymInfo.Address).c_str());
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Module!Name Address
|
|
//
|
|
ShowMessages("%s %s!", SymSeparateTo64BitValue(SymInfo.Address).c_str(), g_CurrentModuleName);
|
|
}
|
|
|
|
//
|
|
// Name
|
|
//
|
|
ShowMessages("%s\n", SymInfo.Name);
|
|
|
|
#ifndef DoNotShowDetailedResult
|
|
|
|
//
|
|
// Size
|
|
//
|
|
ShowMessages(" size: %u", SymInfo.Size);
|
|
|
|
//
|
|
// Kind of symbol (tag)
|
|
//
|
|
ShowMessages(" symbol: %s ", SymTagStr(SymInfo.Tag));
|
|
|
|
#endif // !DoNotShowDetailedResult
|
|
}
|
|
|
|
/**
|
|
* @brief Interpret different tags for pdbs
|
|
*
|
|
* @param Tag
|
|
*
|
|
* @return const char *
|
|
*/
|
|
const char *
|
|
SymTagStr(ULONG Tag)
|
|
{
|
|
switch (Tag)
|
|
{
|
|
case SymTagNull:
|
|
return ("Null");
|
|
|
|
case SymTagExe:
|
|
return ("Exe");
|
|
|
|
case SymTagCompiland:
|
|
return ("Compiland");
|
|
|
|
case SymTagCompilandDetails:
|
|
return ("CompilandDetails");
|
|
|
|
case SymTagCompilandEnv:
|
|
return ("CompilandEnv");
|
|
|
|
case SymTagFunction:
|
|
return ("Function");
|
|
|
|
case SymTagBlock:
|
|
return ("Block");
|
|
|
|
case SymTagData:
|
|
return ("Data");
|
|
|
|
case SymTagAnnotation:
|
|
return ("Annotation");
|
|
|
|
case SymTagLabel:
|
|
return ("Label");
|
|
|
|
case SymTagPublicSymbol:
|
|
return ("PublicSymbol");
|
|
|
|
case SymTagUDT:
|
|
return ("UDT");
|
|
|
|
case SymTagEnum:
|
|
return ("Enum");
|
|
|
|
case SymTagFunctionType:
|
|
return ("FunctionType");
|
|
|
|
case SymTagPointerType:
|
|
return ("PointerType");
|
|
|
|
case SymTagArrayType:
|
|
return ("ArrayType");
|
|
|
|
case SymTagBaseType:
|
|
return ("BaseType");
|
|
|
|
case SymTagTypedef:
|
|
return ("Typedef");
|
|
|
|
case SymTagBaseClass:
|
|
return ("BaseClass");
|
|
|
|
case SymTagFriend:
|
|
return ("Friend");
|
|
|
|
case SymTagFunctionArgType:
|
|
return ("FunctionArgType");
|
|
|
|
case SymTagFuncDebugStart:
|
|
return ("FuncDebugStart");
|
|
|
|
case SymTagFuncDebugEnd:
|
|
return ("FuncDebugEnd");
|
|
|
|
case SymTagUsingNamespace:
|
|
return ("UsingNamespace");
|
|
|
|
case SymTagVTableShape:
|
|
return ("VTableShape");
|
|
|
|
case SymTagVTable:
|
|
return ("VTable");
|
|
|
|
case SymTagCustom:
|
|
return ("Custom");
|
|
|
|
case SymTagThunk:
|
|
return ("Thunk");
|
|
|
|
case SymTagCustomType:
|
|
return ("CustomType");
|
|
|
|
case SymTagManagedType:
|
|
return ("ManagedType");
|
|
|
|
case SymTagDimension:
|
|
return ("Dimension");
|
|
|
|
default:
|
|
return ("Unknown");
|
|
}
|
|
|
|
return ("");
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a DLL to a Microsoft Symbol path
|
|
*
|
|
* @param LocalFilePath
|
|
* @param ResultPath
|
|
* @param ResultPathSize the size of the result path buffer
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymConvertFileToPdbPath(const char * LocalFilePath, char * ResultPath, SIZE_T ResultPathSize)
|
|
{
|
|
std::vector<BYTE> FileBytes;
|
|
|
|
if (LocalFilePath == NULL && ResultPath == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SymReadFileBytes(LocalFilePath, FileBytes);
|
|
|
|
return SymFormatPdbIdentityFromPeImageOrFallback(FileBytes.empty() ? NULL : FileBytes.data(),
|
|
FileBytes.size(),
|
|
ResultPath,
|
|
ResultPathSize,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
SymSrvGetFileIndexInfoFallback,
|
|
(PVOID)LocalFilePath);
|
|
}
|
|
|
|
/**
|
|
* @brief Convert redirection of 32-bit compatibility path
|
|
*
|
|
* @param LocalFilePath
|
|
* @param Wow64ConvertedPath the converted path for Wow64 compatibility
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymConvertWow64CompatibilityPaths(const char * LocalFilePath, std::string & Wow64ConvertedPath)
|
|
{
|
|
std::string FilePath(LocalFilePath);
|
|
|
|
// Convert the path to lowercase
|
|
std::transform(FilePath.begin(), FilePath.end(), FilePath.begin(), ::tolower);
|
|
|
|
// Replace "\windows\system32" with "\windows\syswow64"
|
|
SIZE_T Pos = FilePath.find(":\\windows\\system32");
|
|
if (Pos != std::string::npos)
|
|
{
|
|
FilePath.replace(Pos, 18, ":\\windows\\syswow64");
|
|
}
|
|
|
|
// Replace "\program files" with "\program files (x86)"
|
|
Pos = FilePath.find(":\\program files");
|
|
if (Pos != std::string::npos)
|
|
{
|
|
FilePath.replace(Pos, 15, ":\\program files (x86)");
|
|
}
|
|
|
|
//
|
|
// Copy path to destination
|
|
//
|
|
Wow64ConvertedPath = FilePath;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert a DLL to a Microsoft Symbol details
|
|
* like pdb file path and GUID
|
|
*
|
|
* @param LocalFilePath
|
|
* @param PdbFilePath
|
|
* @param GuidAndAgeDetails
|
|
* @param Is32BitModule
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymConvertFileToPdbFileAndGuidAndAgeDetails(const char * LocalFilePath, char * PdbFilePath, char * GuidAndAgeDetails, BOOLEAN Is32BitModule)
|
|
{
|
|
std::string Wow64ConvertedPath;
|
|
std::vector<BYTE> FileBytes;
|
|
const char * ActualLocalFilePath = NULL;
|
|
|
|
if (Is32BitModule)
|
|
{
|
|
SymConvertWow64CompatibilityPaths(LocalFilePath, Wow64ConvertedPath);
|
|
ActualLocalFilePath = Wow64ConvertedPath.c_str();
|
|
// ShowMessages("local file path: %s | final file path: %s\n", LocalFilePath, ActualLocalFilePath);
|
|
}
|
|
else
|
|
{
|
|
ActualLocalFilePath = LocalFilePath;
|
|
}
|
|
|
|
// ShowMessages("the final (actual) address is: %s\n", ActualLocalFilePath);
|
|
|
|
SymReadFileBytes(ActualLocalFilePath, FileBytes);
|
|
|
|
return SymFormatPdbIdentityFromPeImageOrFallback(FileBytes.empty() ? NULL : FileBytes.data(),
|
|
FileBytes.size(),
|
|
NULL,
|
|
0,
|
|
PdbFilePath,
|
|
MAX_PATH,
|
|
GuidAndAgeDetails,
|
|
MAXIMUM_GUID_AND_AGE_SIZE,
|
|
SymSrvGetFileIndexInfoFallback,
|
|
(PVOID)ActualLocalFilePath);
|
|
}
|
|
|
|
BOOLEAN
|
|
SymConvertLoadedModuleToPdbFileAndGuidAndAgeDetails(const BYTE * LoadedImageBytes,
|
|
SIZE_T LoadedImageSize,
|
|
const char * LocalFilePath,
|
|
char * PdbFilePath,
|
|
char * GuidAndAgeDetails,
|
|
BOOLEAN Is32BitModule)
|
|
{
|
|
std::string Wow64ConvertedPath;
|
|
const char * ActualLocalFilePath = NULL;
|
|
|
|
if (Is32BitModule)
|
|
{
|
|
SymConvertWow64CompatibilityPaths(LocalFilePath, Wow64ConvertedPath);
|
|
ActualLocalFilePath = Wow64ConvertedPath.c_str();
|
|
// ShowMessages("local file path: %s | final file path: %s\n", LocalFilePath, ActualLocalFilePath);
|
|
}
|
|
else
|
|
{
|
|
ActualLocalFilePath = LocalFilePath;
|
|
}
|
|
|
|
// ShowMessages("the final (actual) address is: %s\n", ActualLocalFilePath);
|
|
|
|
return SymFormatPdbIdentityFromLoadedPeImageOrFallback(LoadedImageBytes,
|
|
LoadedImageSize,
|
|
NULL,
|
|
0,
|
|
PdbFilePath,
|
|
MAX_PATH,
|
|
GuidAndAgeDetails,
|
|
MAXIMUM_GUID_AND_AGE_SIZE,
|
|
SymSrvGetFileIndexInfoFallback,
|
|
(PVOID)ActualLocalFilePath);
|
|
}
|
|
|
|
/**
|
|
* @brief check if the pdb files of loaded symbols are available or not
|
|
*
|
|
* @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 DownloadIfAvailable Download the file if its available online
|
|
* @param SymbolPath The path of symbols
|
|
* @param IsSilentLoad
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolInitLoad(PVOID BufferToStoreDetails,
|
|
UINT32 StoredLength,
|
|
BOOLEAN DownloadIfAvailable,
|
|
const char * SymbolPath,
|
|
BOOLEAN IsSilentLoad)
|
|
{
|
|
string Tmp, SymDir, CustomModuleNameStr;
|
|
const char * CustomModuleName = NULL;
|
|
string SymPath(SymbolPath);
|
|
PMODULE_SYMBOL_DETAIL BufferToStoreDetailsConverted = (PMODULE_SYMBOL_DETAIL)BufferToStoreDetails;
|
|
|
|
vector<string> SplitedSymPath = Split(SymPath, '*');
|
|
if (SplitedSymPath.size() < 2)
|
|
return FALSE;
|
|
if (SplitedSymPath[1].find(":\\") == string::npos)
|
|
return FALSE;
|
|
|
|
Tmp = SymDir = SplitedSymPath[1];
|
|
|
|
//
|
|
// Split each module and details
|
|
//
|
|
for (SIZE_T i = 0; i < StoredLength / sizeof(MODULE_SYMBOL_DETAIL); i++)
|
|
{
|
|
//
|
|
// Check for abort
|
|
//
|
|
if (g_AbortLoadingExecution)
|
|
{
|
|
g_AbortLoadingExecution = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if symbol pdb detail is available in the module
|
|
//
|
|
if (!BufferToStoreDetailsConverted[i].IsSymbolDetailsFound)
|
|
{
|
|
//
|
|
// Ignore the module
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if it's a local path (a path) or a microsoft symbol
|
|
//
|
|
if (BufferToStoreDetailsConverted[i].IsLocalSymbolPath)
|
|
{
|
|
//
|
|
// If this is a local driver, then load the pdb
|
|
//
|
|
if (IsFileExists(BufferToStoreDetailsConverted[i].ModuleSymbolPath))
|
|
{
|
|
BufferToStoreDetailsConverted[i].IsSymbolPDBAvaliable = TRUE;
|
|
|
|
//
|
|
// Load symbol locally
|
|
//
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("loading symbol '%s'...", Tmp.c_str());
|
|
}
|
|
|
|
//
|
|
// Check if we need an alternative module name (in 32-bit modules)
|
|
//
|
|
CustomModuleName = NULL;
|
|
|
|
// ShowMessages("name: %s , is 32-bit? %s\n", BufferToStoreDetailsConverted[i].FilePath, BufferToStoreDetailsConverted[i].Is32Bit ? "true" : "false");
|
|
|
|
//
|
|
// Check for alternative module names
|
|
//
|
|
if (BufferToStoreDetailsConverted[i].Is32Bit &&
|
|
SymCheckAndRemoveWow64Prefix(BufferToStoreDetailsConverted[i].FilePath,
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolPath,
|
|
CustomModuleNameStr))
|
|
{
|
|
//
|
|
// The name of the module contains a prefix which should be removed
|
|
//
|
|
CustomModuleName = CustomModuleNameStr.c_str();
|
|
}
|
|
else if (!BufferToStoreDetailsConverted[i].Is32Bit &&
|
|
SymCheckNtoskrnlPrefix(BufferToStoreDetailsConverted[i].ModuleSymbolPath, CustomModuleNameStr))
|
|
{
|
|
//
|
|
// This is an nt module
|
|
//
|
|
CustomModuleName = CustomModuleNameStr.c_str();
|
|
}
|
|
|
|
if (SymLoadFileSymbol(BufferToStoreDetailsConverted[i].BaseAddress,
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolPath,
|
|
CustomModuleName) == 0)
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tloaded\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tnot loaded (already loaded?)\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It might be a Windows symbol
|
|
//
|
|
Tmp = SymDir +
|
|
"\\" +
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolPath +
|
|
"\\" +
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolGuidAndAge +
|
|
"\\" +
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolPath;
|
|
|
|
//
|
|
// Download the symbols file if not available
|
|
//
|
|
if (DownloadIfAvailable && IsFileExists(Tmp) == FALSE)
|
|
{
|
|
//
|
|
// Download the symbol
|
|
//
|
|
SymbolPdbDownload(BufferToStoreDetailsConverted[i].ModuleSymbolPath,
|
|
BufferToStoreDetailsConverted[i].ModuleSymbolGuidAndAge,
|
|
SymPath,
|
|
IsSilentLoad);
|
|
}
|
|
|
|
//
|
|
// Check again to see if the symbol already download or not
|
|
//
|
|
if (IsFileExists(Tmp))
|
|
{
|
|
BufferToStoreDetailsConverted[i].IsSymbolPDBAvaliable = TRUE;
|
|
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("loading symbol '%s'...", Tmp.c_str());
|
|
}
|
|
|
|
//
|
|
// Check if we need an alternative module name (in 32-bit modules)
|
|
//
|
|
CustomModuleName = NULL;
|
|
|
|
// ShowMessages("name: %s , is 32-bit? %s\n", BufferToStoreDetailsConverted[i].FilePath, BufferToStoreDetailsConverted[i].Is32Bit ? "true" : "false");
|
|
|
|
//
|
|
// Check for alternative module names
|
|
//
|
|
if (BufferToStoreDetailsConverted[i].Is32Bit &&
|
|
SymCheckAndRemoveWow64Prefix(BufferToStoreDetailsConverted[i].FilePath,
|
|
Tmp.c_str(),
|
|
CustomModuleNameStr))
|
|
{
|
|
//
|
|
// The name of the module contains a prefix which should be removed
|
|
//
|
|
CustomModuleName = CustomModuleNameStr.c_str();
|
|
}
|
|
else if (!BufferToStoreDetailsConverted[i].Is32Bit &&
|
|
SymCheckNtoskrnlPrefix(Tmp.c_str(), CustomModuleNameStr))
|
|
{
|
|
//
|
|
// This is an nt module
|
|
//
|
|
CustomModuleName = CustomModuleNameStr.c_str();
|
|
}
|
|
|
|
if (SymLoadFileSymbol(BufferToStoreDetailsConverted[i].BaseAddress, Tmp.c_str(), CustomModuleName) == 0)
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tloaded\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tnot loaded (already loaded?)\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief download pdb file
|
|
*
|
|
* @param SymName the name of the symbol (pdb file name)
|
|
* @param GUID the GUID and age string identifying the symbol version
|
|
* @param SymPath the symbol search path
|
|
* @param IsSilentLoad download without any message
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymbolPdbDownload(std::string SymName, const std::string & GUID, const std::string & SymPath, BOOLEAN IsSilentLoad)
|
|
{
|
|
vector<string> SplitedSymPath = Split(SymPath, '*');
|
|
if (SplitedSymPath.size() < 2)
|
|
return FALSE;
|
|
if (SplitedSymPath[1].find(":\\") == string::npos)
|
|
return FALSE;
|
|
if (SplitedSymPath[2].find("http:") == string::npos && SplitedSymPath[2].find("https:") == string::npos)
|
|
return FALSE;
|
|
|
|
string SymDir = SplitedSymPath[1];
|
|
string SymDownloadServer = SplitedSymPath[2];
|
|
string DownloadURL = SymDownloadServer + "/" + SymName + "/" + GUID + "/" + SymName;
|
|
string SymFullDir = SymDir + "\\" + SymName + "\\" + GUID + "\\";
|
|
if (!CreateDirectoryRecursive(SymFullDir))
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("err, unable to create sympath directory '%s'\n", SymFullDir.c_str());
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("downloading symbol '%s'...", SymName.c_str());
|
|
}
|
|
|
|
HRESULT Result = URLDownloadToFileA(NULL, DownloadURL.c_str(), (SymFullDir + "\\" + SymName).c_str(), 0, NULL);
|
|
|
|
if (Result == S_OK)
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tdownloaded\n");
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!IsSilentLoad)
|
|
{
|
|
ShowMessages("\tcould not be downloaded (%x) \n", Result);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief In the case of pressing CTRL+C, it sets a flag
|
|
* to abort the execution of the 'reload'ing and the 'download'ing
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
SymbolAbortLoading()
|
|
{
|
|
if (!g_AbortLoadingExecution)
|
|
{
|
|
g_AbortLoadingExecution = TRUE;
|
|
ShowMessages("\naborting, please wait...\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Perform task for showing structures and data
|
|
* @details used by dt command
|
|
*
|
|
* @param TypeName
|
|
* @param Address
|
|
* @param IsStruct
|
|
* @param BufferAddress
|
|
* @param AdditionalParameters
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
SymShowDataBasedOnSymbolTypes(const char * TypeName,
|
|
UINT64 Address,
|
|
BOOLEAN IsStruct,
|
|
PVOID BufferAddress,
|
|
const char * AdditionalParameters)
|
|
{
|
|
vector<string> SplitedSymPath;
|
|
char ** ArgvArray = NULL;
|
|
PSYMBOL_LOADED_MODULE_DETAILS SymbolInfo = NULL;
|
|
UINT32 SizeOfArgv = 0;
|
|
UINT32 TypeNameIndex = 0;
|
|
|
|
//
|
|
// Find the symbol info (to get the PDB address)
|
|
//
|
|
SymbolInfo = SymGetModuleBaseFromSearchMask(TypeName, FALSE);
|
|
|
|
if (!SymbolInfo)
|
|
{
|
|
//
|
|
// Symbol not found
|
|
//
|
|
ShowMessages("err, couldn't resolve error at '%s'\n", TypeName);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert char* to string
|
|
//
|
|
std::string AdditionalParametersString(AdditionalParameters);
|
|
|
|
//
|
|
// Split the arguments by space
|
|
//
|
|
SplitPathAndArgs(SplitedSymPath, AdditionalParametersString);
|
|
|
|
//
|
|
// Allocate buffer to convert it to the char*
|
|
// + 3 is because of
|
|
// 1. file name
|
|
// 2. type (structure) name
|
|
// 3. PDB file location
|
|
//
|
|
SizeOfArgv = (UINT32)SplitedSymPath.size() + 3;
|
|
ArgvArray = (char **)malloc(SizeOfArgv * sizeof(char *));
|
|
|
|
if (ArgvArray == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(ArgvArray, SizeOfArgv * sizeof(char *));
|
|
|
|
//
|
|
// First argument is the file name, we let it blank
|
|
//
|
|
ArgvArray[0] = (char *)NULL;
|
|
|
|
//
|
|
// Remove the module name (if any)
|
|
//
|
|
while (TypeName[TypeNameIndex] != NULL)
|
|
{
|
|
if (TypeName[TypeNameIndex] == '!')
|
|
{
|
|
TypeName = &TypeName[++TypeNameIndex];
|
|
break;
|
|
}
|
|
|
|
TypeNameIndex++;
|
|
}
|
|
|
|
//
|
|
// Second argument is the type (structure) name
|
|
//
|
|
ArgvArray[1] = (char *)TypeName;
|
|
|
|
//
|
|
// Third argument is the PDB file location
|
|
//
|
|
ArgvArray[2] = SymbolInfo->PdbFilePath;
|
|
|
|
//
|
|
// Fill the parameter with char array
|
|
//
|
|
for (SIZE_T i = 3; i < SizeOfArgv; i++)
|
|
{
|
|
ArgvArray[i] = (char *)SplitedSymPath.at(i - 3).c_str();
|
|
}
|
|
|
|
//
|
|
// Call the pdbex wrapper
|
|
//
|
|
if (IsStruct)
|
|
{
|
|
pdbex_export(SizeOfArgv, ArgvArray, true, BufferAddress);
|
|
}
|
|
else
|
|
{
|
|
pdbex_export(SizeOfArgv, ArgvArray, false, BufferAddress);
|
|
}
|
|
|
|
//
|
|
// Free the buffer allocated for argv
|
|
//
|
|
free(ArgvArray);
|
|
|
|
return TRUE;
|
|
}
|