DBG: fully implement symdownload command without dbghelp usage
This commit is contained in:
parent
ff11a39533
commit
d5ae04dce4
|
|
@ -25,6 +25,7 @@ static int _download(const char* url,
|
|||
DWORD dwError = 0;
|
||||
DWORD dwRead = 0;
|
||||
DWORD dwLen = 0;
|
||||
DWORD dwLastError = ERROR_SUCCESS;
|
||||
BOOL bRet = FALSE;
|
||||
unsigned long long read_bytes = 0;
|
||||
unsigned long long total_bytes = 0;
|
||||
|
|
@ -124,12 +125,14 @@ static int _download(const char* url,
|
|||
}
|
||||
|
||||
cleanup:
|
||||
dwLastError = GetLastError();
|
||||
if(hFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hFile);
|
||||
if(hUrl != NULL)
|
||||
InternetCloseHandle(hUrl);
|
||||
if(hInternet != NULL)
|
||||
InternetCloseHandle(hInternet);
|
||||
SetLastError(dwLastError);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "exception.h"
|
||||
#include "TraceRecord.h"
|
||||
#include "dbghelp_safe.h"
|
||||
#include "WinInet-Downloader/downslib.h"
|
||||
|
||||
bool cbInstrAnalyse(int argc, char* argv[])
|
||||
{
|
||||
|
|
@ -155,107 +154,6 @@ bool cbInstrVirtualmod(int argc, char* argv[])
|
|||
|
||||
bool cbDebugDownloadSymbol(int argc, char* argv[])
|
||||
{
|
||||
if(argc < 2) //no arguments
|
||||
{
|
||||
dputs("not enough arguments...");
|
||||
return false;
|
||||
}
|
||||
duint modbase = ModBaseFromName(argv[1]);
|
||||
String pdbSignature, pdbFile;
|
||||
{
|
||||
SHARED_ACQUIRE(LockModules);
|
||||
auto info = ModInfoFromAddr(modbase);
|
||||
if(!info)
|
||||
{
|
||||
dputs("module not found...");
|
||||
return false;
|
||||
}
|
||||
pdbSignature = info->pdbSignature;
|
||||
pdbFile = info->pdbFile;
|
||||
}
|
||||
if(pdbSignature.empty() || pdbFile.empty()) // TODO: allow using module filename instead of pdbFile ?
|
||||
{
|
||||
dputs("module has no symbol information...");
|
||||
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())
|
||||
{
|
||||
dputs("no destination symbol path specified...");
|
||||
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);
|
||||
|
||||
Memory<char*> szDefaultStore(MAX_SETTING_SIZE + 1);
|
||||
const char* szSymbolStore = szDefaultStore();
|
||||
if(!BridgeSettingGet("Symbols", "DefaultStore", szDefaultStore())) //get default symbol store from settings
|
||||
{
|
||||
strcpy_s(szDefaultStore(), MAX_SETTING_SIZE, "https://msdl.microsoft.com/download/symbols");
|
||||
BridgeSettingSet("Symbols", "DefaultStore", szDefaultStore());
|
||||
}
|
||||
|
||||
String symbolUrl(szSymbolStore);
|
||||
if(symbolUrl.empty())
|
||||
{
|
||||
dputs("no symbol store URL specified...");
|
||||
return false;
|
||||
}
|
||||
if(symbolUrl.back() != '/')
|
||||
symbolUrl += '/';
|
||||
symbolUrl += StringUtils::sprintf("%s/%s/%s", pdbBaseFile, pdbSignature.c_str(), pdbBaseFile);
|
||||
|
||||
dprintf("downloading symbol \"%s\", signature: %s, destination: \"%S\", url: \"%s\"\n", pdbBaseFile, pdbSignature.c_str(), destinationPath.c_str(), symbolUrl.c_str());
|
||||
|
||||
auto result = downslib_download(symbolUrl.c_str(), destinationPath.c_str(), "x64dbg", 1000, [](unsigned long long read_bytes, unsigned long long total_bytes)
|
||||
{
|
||||
if(total_bytes)
|
||||
{
|
||||
auto progress = (double)read_bytes / (double)total_bytes;
|
||||
GuiSymbolSetProgress((int)(progress * 100.0));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
GuiSymbolSetProgress(0);
|
||||
dprintf("download_download = %d\n", result);
|
||||
|
||||
// TODO: handle errors
|
||||
switch(result)
|
||||
{
|
||||
case DOWNSLIB_ERROR_OK:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_INETOPEN:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_OPENURL:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_STATUSCODE:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_CREATEFILE:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_CANCEL:
|
||||
break;
|
||||
case DOWNSLIB_ERROR_INCOMPLETE:
|
||||
break;
|
||||
default:
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
// TODO: actually load the symbol from the cache
|
||||
|
||||
return true;
|
||||
/*
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "This may take very long, depending on your network connection and data in the debug directory..."));
|
||||
Memory<char*> szDefaultStore(MAX_SETTING_SIZE + 1);
|
||||
const char* szSymbolStore = szDefaultStore();
|
||||
|
|
@ -278,52 +176,16 @@ bool cbDebugDownloadSymbol(int argc, char* argv[])
|
|||
dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid module \"%s\"!\n"), argv[1]);
|
||||
return false;
|
||||
}
|
||||
wchar_t wszModulePath[MAX_PATH] = L"";
|
||||
if(!GetModuleFileNameExW(fdProcessInfo->hProcess, (HMODULE)modbase, wszModulePath, MAX_PATH))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "GetModuleFileNameExW failed!"));
|
||||
return false;
|
||||
}
|
||||
wchar_t szOldSearchPath[MAX_PATH] = L"";
|
||||
if(!SafeSymGetSearchPathW(fdProcessInfo->hProcess, szOldSearchPath, MAX_PATH)) //backup current search path
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymGetSearchPath failed!"));
|
||||
return false;
|
||||
}
|
||||
char szServerSearchPath[MAX_PATH * 2] = "";
|
||||
if(argc > 2)
|
||||
szSymbolStore = argv[2];
|
||||
sprintf_s(szServerSearchPath, "SRV*%s*%s", szSymbolCachePath, szSymbolStore);
|
||||
if(!SafeSymSetSearchPathW(fdProcessInfo->hProcess, StringUtils::Utf8ToUtf16(szServerSearchPath).c_str())) //set new search path
|
||||
if(!SymDownloadSymbol(modbase, szSymbolStore))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymSetSearchPath (1) failed!"));
|
||||
return false;
|
||||
}
|
||||
if(!SafeSymUnloadModule64(fdProcessInfo->hProcess, (DWORD64)modbase)) //unload module
|
||||
{
|
||||
SafeSymSetSearchPathW(fdProcessInfo->hProcess, szOldSearchPath);
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymUnloadModule64 failed!"));
|
||||
return false;
|
||||
}
|
||||
auto symOptions = SafeSymGetOptions();
|
||||
SafeSymSetOptions(symOptions & ~SYMOPT_IGNORE_CVREC);
|
||||
if(!SymLoadModuleExW(fdProcessInfo->hProcess, 0, wszModulePath, 0, (DWORD64)modbase, 0, 0, 0)) //load module
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymLoadModuleEx failed!"));
|
||||
SafeSymSetOptions(symOptions);
|
||||
SafeSymSetSearchPathW(fdProcessInfo->hProcess, szOldSearchPath);
|
||||
return false;
|
||||
}
|
||||
SafeSymSetOptions(symOptions);
|
||||
if(!SafeSymSetSearchPathW(fdProcessInfo->hProcess, szOldSearchPath))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymSetSearchPathW (2) failed!"));
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Symbol download failed... See symbol log for more information"));
|
||||
return false;
|
||||
}
|
||||
GuiSymbolRefreshCurrent();
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Done! See symbol log for more information"));
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
bool cbInstrImageinfo(int argc, char* argv[])
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include "module.h"
|
||||
#include "addrinfo.h"
|
||||
#include "dbghelp_safe.h"
|
||||
#include "exception.h"
|
||||
#include "WinInet-Downloader/downslib.h"
|
||||
|
||||
struct SYMBOLCBDATA
|
||||
{
|
||||
|
|
@ -145,113 +147,148 @@ void SymUpdateModuleList()
|
|||
GuiSymbolUpdateModuleList((int)moduleCount, data);
|
||||
}
|
||||
|
||||
bool SymDownloadSymbol(duint Base, const char* SymbolStore)
|
||||
{
|
||||
#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, [](unsigned long long read_bytes, unsigned long long total_bytes)
|
||||
{
|
||||
if(total_bytes)
|
||||
{
|
||||
auto progress = (double)read_bytes / (double)total_bytes;
|
||||
GuiSymbolSetProgress((int)(progress * 100.0));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
GuiSymbolSetProgress(0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// insert the downloaded symbol path in the beginning of the PDB load order
|
||||
auto destPathUtf8 = StringUtils::Utf16ToUtf8(destinationPath);
|
||||
for(auto it = info->pdbPaths.begin(); it != info->pdbPaths.end(); ++it)
|
||||
{
|
||||
if(*it == destPathUtf8)
|
||||
{
|
||||
info->pdbPaths.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
info->pdbPaths.insert(info->pdbPaths.begin(), destPathUtf8);
|
||||
|
||||
// trigger a symbol load
|
||||
info->loadSymbols();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#undef symprintf
|
||||
}
|
||||
|
||||
void SymDownloadAllSymbols(const char* SymbolStore)
|
||||
{
|
||||
// Default to Microsoft's symbol server
|
||||
if(!SymbolStore)
|
||||
SymbolStore = "https://msdl.microsoft.com/download/symbols";
|
||||
|
||||
// Build the vector of modules
|
||||
std::vector<SYMBOLMODULEINFO> modList;
|
||||
|
||||
if(!SymGetModuleList(&modList))
|
||||
return;
|
||||
|
||||
// Skip loading if there aren't any found modules
|
||||
if(modList.empty())
|
||||
return;
|
||||
|
||||
// Backup the current symbol search path
|
||||
wchar_t oldSearchPath[MAX_PATH];
|
||||
|
||||
if(!SafeSymGetSearchPathW(fdProcessInfo->hProcess, oldSearchPath, MAX_PATH))
|
||||
//TODO: refactor this in a function because this pattern will become common
|
||||
std::vector<duint> mods;
|
||||
ModEnum([&mods](const MODINFO & info)
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymGetSearchPathW failed!"));
|
||||
return;
|
||||
}
|
||||
mods.push_back(info.base);
|
||||
});
|
||||
|
||||
// Use the custom server path and directory
|
||||
char customSearchPath[MAX_PATH * 2];
|
||||
sprintf_s(customSearchPath, "SRV*%s*%s", szSymbolCachePath, SymbolStore);
|
||||
|
||||
auto symOptions = SafeSymGetOptions();
|
||||
SafeSymSetOptions(symOptions & ~SYMOPT_IGNORE_CVREC);
|
||||
|
||||
const WString search_paths[] =
|
||||
{
|
||||
WString(),
|
||||
StringUtils::Utf8ToUtf16(customSearchPath)
|
||||
};
|
||||
|
||||
// Reload
|
||||
for(auto & module : modList)
|
||||
{
|
||||
for(unsigned k = 0; k < sizeof(search_paths) / sizeof(*search_paths); k++)
|
||||
{
|
||||
const WString & cur_path = search_paths[k];
|
||||
|
||||
if(!SafeSymSetSearchPathW(fdProcessInfo->hProcess, cur_path.c_str()))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymSetSearchPathW (1) failed!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Downloading symbols for %s...\n"), module.name);
|
||||
|
||||
wchar_t modulePath[MAX_PATH];
|
||||
if(!GetModuleFileNameExW(fdProcessInfo->hProcess, (HMODULE)module.base, modulePath, MAX_PATH))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "GetModuleFileNameExW (%p) failed!\n"), module.base);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!SafeSymUnloadModule64(fdProcessInfo->hProcess, (DWORD64)module.base))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "SymUnloadModule64 (%p) failed!\n"), module.base);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!SafeSymLoadModuleExW(fdProcessInfo->hProcess, 0, modulePath, 0, (DWORD64)module.base, 0, 0, 0))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "SymLoadModuleEx (%p) failed!\n"), module.base);
|
||||
continue;
|
||||
}
|
||||
|
||||
// symbols are lazy-loaded so let's load them and get the real return value
|
||||
|
||||
IMAGEHLP_MODULEW64 info;
|
||||
info.SizeOfStruct = sizeof(info);
|
||||
|
||||
if(!SafeSymGetModuleInfoW64(fdProcessInfo->hProcess, (DWORD64)module.base, &info))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "SymGetModuleInfo64 (%p) failed!\n"), module.base);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool status;
|
||||
|
||||
switch(info.SymType)
|
||||
{
|
||||
// XXX there may be more enum values meaning proper load
|
||||
case SymPdb:
|
||||
status = 1;
|
||||
break;
|
||||
default:
|
||||
case SymExport: // always treat export symbols as failure
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SafeSymSetOptions(symOptions);
|
||||
|
||||
// Restore the old search path
|
||||
if(!SafeSymSetSearchPathW(fdProcessInfo->hProcess, oldSearchPath))
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "SymSetSearchPathW (2) failed!"));
|
||||
for(duint base : mods)
|
||||
SymDownloadSymbol(base, SymbolStore);
|
||||
}
|
||||
|
||||
bool SymAddrFromName(const char* Name, duint* Address)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ void SymEnum(duint Base, CBSYMBOLENUM EnumCallback, void* UserData);
|
|||
void SymEnumFromCache(duint Base, CBSYMBOLENUM EnumCallback, void* UserData);
|
||||
bool SymGetModuleList(std::vector<SYMBOLMODULEINFO>* List);
|
||||
void SymUpdateModuleList();
|
||||
bool SymDownloadSymbol(duint Base, const char* SymbolStore);
|
||||
void SymDownloadAllSymbols(const char* SymbolStore);
|
||||
bool SymAddrFromName(const char* Name, duint* Address);
|
||||
String SymGetSymbolicName(duint Address);
|
||||
|
|
|
|||
Loading…
Reference in New Issue