1
0
Fork 0
x64dbg/src/dbg/symbolinfo.cpp

414 lines
13 KiB
C++

/**
@file symbolinfo.cpp
@brief Implements the symbolinfo class.
*/
#include "symbolinfo.h"
#include "debugger.h"
#include "console.h"
#include "module.h"
#include "addrinfo.h"
#include "dbghelp_safe.h"
#include "exception.h"
#include "WinInet-Downloader/downslib.h"
#include <shlwapi.h>
duint symbolDownloadingBase = 0;
struct SYMBOLCBDATA
{
CBSYMBOLENUM cbSymbolEnum;
void* user = nullptr;
std::vector<char> decoratedSymbol;
std::vector<char> undecoratedSymbol;
};
void SymEnum(duint Base, CBSYMBOLENUM EnumCallback, void* UserData)
{
SYMBOLCBDATA cbData;
cbData.cbSymbolEnum = EnumCallback;
cbData.user = UserData;
cbData.decoratedSymbol.resize(MAX_SYM_NAME + 1);
cbData.undecoratedSymbol.resize(MAX_SYM_NAME + 1);
{
SHARED_ACQUIRE(LockModules);
MODINFO* modInfo = ModInfoFromAddr(Base);
if(modInfo)
{
for(size_t i = 0; i < modInfo->exports.size(); i++)
{
SYMBOLPTR symbolptr;
symbolptr.modbase = Base;
symbolptr.symbol = &modInfo->exports.at(i);
cbData.cbSymbolEnum(&symbolptr, cbData.user);
}
// Emit pseudo entry point symbol
{
SYMBOLPTR symbolptr;
symbolptr.modbase = Base;
symbolptr.symbol = &modInfo->entrySymbol;
cbData.cbSymbolEnum(&symbolptr, cbData.user);
}
for(size_t i = 0; i < modInfo->imports.size(); i++)
{
SYMBOLPTR symbolptr;
symbolptr.modbase = Base;
symbolptr.symbol = &modInfo->imports.at(i);
cbData.cbSymbolEnum(&symbolptr, cbData.user);
}
if(modInfo->symbols->isOpen())
{
modInfo->symbols->enumSymbols([&cbData, Base](const SymbolInfo & info)
{
SYMBOLPTR symbolptr;
symbolptr.modbase = Base;
symbolptr.symbol = &info;
return cbData.cbSymbolEnum(&symbolptr, cbData.user);
});
}
}
}
}
void SymEnumFromCache(duint Base, CBSYMBOLENUM EnumCallback, void* UserData)
{
SymEnum(Base, EnumCallback, UserData);
}
bool SymGetModuleList(std::vector<SYMBOLMODULEINFO>* List)
{
ModEnum([List](const MODINFO & mod)
{
SYMBOLMODULEINFO curMod;
curMod.base = mod.base;
strcpy_s(curMod.name, mod.name);
strcat_s(curMod.name, mod.extension);
List->push_back(curMod);
});
return true;
}
void SymUpdateModuleList()
{
// Build the vector of modules
std::vector<SYMBOLMODULEINFO> modList;
if(!SymGetModuleList(&modList))
{
GuiSymbolUpdateModuleList(0, nullptr);
return;
}
// Create a new array to be sent to the GUI thread
size_t moduleCount = modList.size();
SYMBOLMODULEINFO* data = (SYMBOLMODULEINFO*)BridgeAlloc(moduleCount * sizeof(SYMBOLMODULEINFO));
// Direct copy from std::vector data
memcpy(data, modList.data(), moduleCount * sizeof(SYMBOLMODULEINFO));
// Send the module data to the GUI for updating
GuiSymbolUpdateModuleList((int)moduleCount, data);
}
static void SymSetProgress(int percentage, const char* pdbBaseFile)
{
if(percentage == 0)
GuiAddStatusBarMessage(StringUtils::sprintf("%s\n", pdbBaseFile).c_str());
else
GuiAddStatusBarMessage(StringUtils::sprintf("%s %d%%\n", pdbBaseFile, percentage).c_str());
GuiSymbolSetProgress(percentage);
}
bool SymDownloadSymbol(duint Base, const char* SymbolStore)
{
struct DownloadBaseGuard
{
DownloadBaseGuard(duint downloadBase) { symbolDownloadingBase = downloadBase; GuiRepaintTableView(); }
~DownloadBaseGuard() { symbolDownloadingBase = 0; GuiRepaintTableView(); }
} g(Base);
#define symprintf(format, ...) GuiSymbolLogAdd(StringUtils::sprintf(GuiTranslateText(format), __VA_ARGS__).c_str())
// Default to Microsoft's symbol server
if(!SymbolStore)
SymbolStore = "https://msdl.microsoft.com/download/symbols";
String pdbSignature, pdbFile;
{
SHARED_ACQUIRE(LockModules);
auto info = ModInfoFromAddr(Base);
if(!info)
{
symprintf(QT_TRANSLATE_NOOP("DBG", "Module not found...\n"));
return false;
}
pdbSignature = info->pdbSignature;
pdbFile = info->pdbFile;
}
if(pdbSignature.empty() || pdbFile.empty()) // TODO: allow using module filename instead of pdbFile ?
{
symprintf(QT_TRANSLATE_NOOP("DBG", "Module has no symbol information...\n"));
return false;
}
auto found = strrchr(pdbFile.c_str(), '\\');
auto pdbBaseFile = found ? found + 1 : pdbFile.c_str();
// TODO: strict checks if this path is absolute
WString destinationPath(StringUtils::Utf8ToUtf16(szSymbolCachePath));
if(destinationPath.empty())
{
symprintf(QT_TRANSLATE_NOOP("DBG", "No destination symbol path specified...\n"));
return false;
}
CreateDirectoryW(destinationPath.c_str(), nullptr);
if(destinationPath.back() != L'\\')
destinationPath += L'\\';
destinationPath += StringUtils::Utf8ToUtf16(pdbBaseFile);
CreateDirectoryW(destinationPath.c_str(), nullptr);
destinationPath += L'\\';
destinationPath += StringUtils::Utf8ToUtf16(pdbSignature);
CreateDirectoryW(destinationPath.c_str(), nullptr);
destinationPath += '\\';
destinationPath += StringUtils::Utf8ToUtf16(pdbBaseFile);
String symbolUrl(SymbolStore);
if(symbolUrl.empty())
{
symprintf(QT_TRANSLATE_NOOP("DBG", "No symbol store URL specified...\n"));
return false;
}
if(symbolUrl.back() != '/')
symbolUrl += '/';
symbolUrl += StringUtils::sprintf("%s/%s/%s", pdbBaseFile, pdbSignature.c_str(), pdbBaseFile);
symprintf(QT_TRANSLATE_NOOP("DBG", "Downloading symbol %s\n Signature: %s\n Destination: %s\n URL: %s\n"), pdbBaseFile, pdbSignature.c_str(), StringUtils::Utf16ToUtf8(destinationPath).c_str(), symbolUrl.c_str());
auto result = downslib_download(symbolUrl.c_str(), destinationPath.c_str(), "x64dbg", 1000, [](void* userdata, unsigned long long read_bytes, unsigned long long total_bytes)
{
if(total_bytes)
{
auto progress = (double)read_bytes / (double)total_bytes;
SymSetProgress((int)(progress * 100.0), (const char*)userdata);
}
return true;
}, (void*)pdbBaseFile);
SymSetProgress(0, pdbBaseFile);
switch(result)
{
case downslib_error::ok:
break;
case downslib_error::createfile:
//TODO: handle ERROR_SHARING_VIOLATION (unload symbols and try again)
symprintf(QT_TRANSLATE_NOOP("DBG", "Failed to create destination file (%s)...\n"), ErrorCodeToName(GetLastError()).c_str());
return false;
case downslib_error::inetopen:
symprintf(QT_TRANSLATE_NOOP("DBG", "InternetOpen failed (%s)...\n"), ErrorCodeToName(GetLastError()).c_str());
return false;
case downslib_error::openurl:
symprintf(QT_TRANSLATE_NOOP("DBG", "InternetOpenUrl failed (%s)...\n"), ErrorCodeToName(GetLastError()).c_str());
return false;
case downslib_error::statuscode:
symprintf(QT_TRANSLATE_NOOP("DBG", "Connection succeeded, but download failed (status code: %d)...\n"), GetLastError());
return false;
case downslib_error::cancel:
symprintf(QT_TRANSLATE_NOOP("DBG", "Download interrupted...\n"), ErrorCodeToName(GetLastError()).c_str());
return false;
case downslib_error::incomplete:
symprintf(QT_TRANSLATE_NOOP("DBG", "Download incomplete...\n"), ErrorCodeToName(GetLastError()).c_str());
return false;
default:
__debugbreak();
}
{
EXCLUSIVE_ACQUIRE(LockModules);
auto info = ModInfoFromAddr(Base);
if(!info)
{
// TODO: this really isn't supposed to happen, but could if the module is suddenly unloaded
dputs("module not found...");
return false;
}
// trigger a symbol load
info->loadSymbols(StringUtils::Utf16ToUtf8(destinationPath), bForceLoadSymbols);
}
return true;
#undef symprintf
}
void SymDownloadAllSymbols(const char* SymbolStore)
{
// Default to Microsoft's symbol server
if(!SymbolStore)
SymbolStore = "https://msdl.microsoft.com/download/symbols";
//TODO: refactor this in a function because this pattern will become common
std::vector<duint> mods;
ModEnum([&mods](const MODINFO & info)
{
mods.push_back(info.base);
});
for(duint base : mods)
SymDownloadSymbol(base, SymbolStore);
}
bool SymAddrFromName(const char* Name, duint* Address)
{
if(!Name || Name[0] == '\0')
return false;
if(!Address)
return false;
// Skip 'OrdinalXXX'
if(_strnicmp(Name, "Ordinal#", 8) == 0 && strlen(Name) > 8)
{
const char* Name1 = Name + 8;
bool notNonNumbersFound = true;
do
{
if(!(Name1[0] >= '0' && Name1[0] <= '9'))
{
notNonNumbersFound = false;
break;
}
Name1++;
}
while(Name1[0] != 0);
if(notNonNumbersFound)
return false;
}
//TODO: refactor this in a function because this pattern will become common
std::vector<duint> mods;
ModEnum([&mods](const MODINFO & info)
{
mods.push_back(info.base);
});
std::string name(Name);
for(duint base : mods)
{
SHARED_ACQUIRE(LockModules);
auto modInfo = ModInfoFromAddr(base);
if(modInfo && modInfo->symbols->isOpen())
{
SymbolInfo symInfo;
if(modInfo->symbols->findSymbolByName(name, symInfo, true))
{
*Address = base + symInfo.rva;
return true;
}
}
}
return false;
}
String SymGetSymbolicName(duint Address)
{
//
// This resolves an address to a module and symbol:
// [modname.]symbolname
//
char label[MAX_SYM_NAME];
char modname[MAX_MODULE_SIZE];
auto hasModule = ModNameFromAddr(Address, modname, false);
// User labels have priority, but if one wasn't found,
// default to a symbol lookup
if(!DbgGetLabelAt(Address, SEG_DEFAULT, label))
{
if(hasModule)
return StringUtils::sprintf("%s.%p", modname, Address);
return "";
}
if(hasModule)
return StringUtils::sprintf("<%s.%s>", modname, label);
return StringUtils::sprintf("<%s>", label);
}
bool SymGetSourceLine(duint Cip, char* FileName, int* Line, duint* disp)
{
SHARED_ACQUIRE(LockModules);
MODINFO* modInfo = ModInfoFromAddr(Cip);
if(!modInfo)
return false;
SymbolSourceBase* sym = modInfo->symbols;
if(!sym || sym == &EmptySymbolSource)
return false;
LineInfo lineInfo;
if(!sym->findSourceLineInfo(Cip - modInfo->base, lineInfo))
return false;
if(disp)
*disp = lineInfo.disp;
if(Line)
*Line = lineInfo.lineNumber;
if(FileName)
{
strncpy_s(FileName, MAX_STRING_SIZE, lineInfo.sourceFile.c_str(), _TRUNCATE);
// Check if it was a full path
if(!PathIsRelativeW(StringUtils::Utf8ToUtf16(lineInfo.sourceFile).c_str()))
return true;
// Construct full path from pdb path
{
SHARED_ACQUIRE(LockModules);
MODINFO* info = ModInfoFromAddr(Cip);
if(!info)
return true;
String sourceFilePath = info->symbols->loadedSymbolPath();
// Strip the name, leaving only the file directory
size_t bslash = sourceFilePath.rfind('\\');
if(bslash != String::npos)
sourceFilePath.resize(bslash + 1);
sourceFilePath += lineInfo.sourceFile;
// Attempt to remap the source file if it exists (more heuristics could be added in the future)
if(FileExists(sourceFilePath.c_str()))
{
if(info->symbols->mapSourceFilePdbToDisk(lineInfo.sourceFile, sourceFilePath))
{
strncpy_s(FileName, MAX_STRING_SIZE, sourceFilePath.c_str(), _TRUNCATE);
}
}
}
}
return true;
}
bool SymGetSourceAddr(duint Module, const char* FileName, int Line, duint* Address)
{
SHARED_ACQUIRE(LockModules);
auto modInfo = ModInfoFromAddr(Module);
if(!modInfo)
return false;
auto sym = modInfo->symbols;
if(!sym || sym == &EmptySymbolSource)
return false;
LineInfo lineInfo;
if(!sym->findSourceLineInfo(FileName, Line, lineInfo))
return false;
*Address = lineInfo.rva + modInfo->base;
return true;
}