1
0
Fork 0

Support user directory

Package managers can create a "userdir" file next to x64dbg.exe with a file path (UTF-8) in there. If no path is specified it will be %APPDATA%\<executable name>. This directory contains the "db" folder, INI settings, memory dumps
This commit is contained in:
Duncan Ogilvie 2022-09-25 01:41:46 +02:00
parent 10d978deb4
commit c59271893b
11 changed files with 117 additions and 23 deletions

View File

@ -7,10 +7,12 @@
#include "_global.h"
#include "bridgemain.h"
#include <stdio.h>
#include <ShlObj.h>
#include "Utf8Ini.h"
static HINSTANCE hInst;
static Utf8Ini settings;
static wchar_t szUserDirectory[MAX_PATH] = L"";
static wchar_t szIniFile[MAX_PATH] = L"";
static CRITICAL_SECTION csIni;
static CRITICAL_SECTION csTranslate;
@ -42,6 +44,27 @@ static bool bDisableGUIUpdate;
return szError; \
}
static std::wstring Utf8ToUtf16(const char* str)
{
std::wstring convertedString;
if(!str || !*str)
return convertedString;
int requiredSize = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
if(requiredSize > 0)
{
convertedString.resize(requiredSize - 1);
if(!MultiByteToWideChar(CP_UTF8, 0, str, -1, (wchar_t*)convertedString.c_str(), requiredSize))
convertedString.clear();
}
return convertedString;
}
static bool DirExists(const wchar_t* dir)
{
DWORD attrib = GetFileAttributesW(dir);
return (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}
BRIDGE_IMPEXP const wchar_t* BridgeInit()
{
//Initialize critial section
@ -49,15 +72,75 @@ BRIDGE_IMPEXP const wchar_t* BridgeInit()
InitializeCriticalSection(&csTranslate);
//Settings load
if(!GetModuleFileNameW(0, szIniFile, MAX_PATH))
if(!GetModuleFileNameW(0, szUserDirectory, _countof(szUserDirectory)))
return L"Error getting module path!";
int len = (int)wcslen(szIniFile);
while(szIniFile[len] != L'.' && szIniFile[len] != L'\\' && len)
len--;
if(szIniFile[len] == L'\\')
wcscat_s(szIniFile, L".ini");
auto backslash = wcsrchr(szUserDirectory, L'\\');
if(backslash == nullptr)
return L"Error getting module directory!";
*backslash = L'\0';
// Extract the file name of the x64dbg executable (without extension)
auto fileNameWithoutExtension = backslash + 1;
auto period = wcschr(fileNameWithoutExtension, L'.');
if(period != nullptr)
{
*period = L'\0';
}
wchar_t szFolderRedirect[MAX_PATH];
wcscpy_s(szFolderRedirect, szUserDirectory);
wcscat_s(szFolderRedirect, L"\\userdir");
std::wstring userDirUtf16;
{
std::vector<char> userDirUtf8;
auto hFile = CreateFileW(szFolderRedirect, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if(hFile != INVALID_HANDLE_VALUE)
{
auto size = GetFileSize(hFile, nullptr);
userDirUtf8.resize(size + 1);
DWORD read = 0;
if(!ReadFile(hFile, userDirUtf8.data(), size, &read, nullptr))
userDirUtf8.clear();
CloseHandle(hFile);
userDirUtf16 = Utf8ToUtf16(userDirUtf8.data());
}
else
{
userDirUtf16 = szUserDirectory;
}
}
if(userDirUtf16.empty())
{
wchar_t szAppData[MAX_PATH] = L"";
if(!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, szAppData)))
return L"Error getting AppData path";
userDirUtf16 = szAppData;
if(userDirUtf16.back() != L'\\')
userDirUtf16 += L'\\';
userDirUtf16 += fileNameWithoutExtension;
CreateDirectoryW(userDirUtf16.c_str(), nullptr);
}
else
wcscpy_s(&szIniFile[len], _countof(szIniFile) - len, L".ini");
{
if(userDirUtf16.back() == L'\\')
userDirUtf16.pop_back();
}
if(!DirExists(userDirUtf16.c_str()))
return L"Specified user directory doesn't exist";
wcscpy_s(szIniFile, userDirUtf16.c_str());
wcscat_s(szIniFile, L"\\");
wcscat_s(szIniFile, fileNameWithoutExtension);
wcscat_s(szIniFile, L".ini");
MessageBoxW(0, szIniFile, L"szIniFile", MB_SYSTEMMODAL);
wcscpy_s(szUserDirectory, userDirUtf16.c_str());
HINSTANCE hInst;
const wchar_t* szLib;
@ -283,6 +366,11 @@ BRIDGE_IMPEXP unsigned int BridgeGetNtBuildNumber()
return NtBuildNumber;
}
BRIDGE_IMPEXP const wchar_t* BridgeUserDirectory()
{
return szUserDirectory;
}
BRIDGE_IMPEXP bool DbgMemRead(duint va, void* dest, duint size)
{
#ifdef _DEBUG

View File

@ -136,6 +136,11 @@ BRIDGE_IMPEXP bool BridgeIsProcessElevated();
/// <returns>NtBuildNumber</returns>
BRIDGE_IMPEXP unsigned int BridgeGetNtBuildNumber();
/// <summary>
/// Returns the user directory (without trailing backslash).
/// </summary>
BRIDGE_IMPEXP const wchar_t* BridgeUserDirectory();
#ifdef __cplusplus
}
#endif

View File

@ -195,7 +195,7 @@ bool cbInstrSavedata(int argc, char* argv[])
String name = stringformatinline(argv[1]);
if(name == ":memdump:")
name = StringUtils::sprintf("%s\\memdumps\\memdump_%X_%p_%x.bin", szProgramDir, fdProcessInfo->dwProcessId, addr, size);
name = StringUtils::sprintf("%s\\memdumps\\memdump_%X_%p_%x.bin", szUserDir, fdProcessInfo->dwProcessId, addr, size);
if(!FileHelper::WriteAllData(name, data(), data.size()))
{

View File

@ -71,6 +71,7 @@ static duint exceptionDispatchAddr = 0;
static bool bPausedOnException = false;
static HANDLE DebugDLLFileMapping = 0;
char szProgramDir[MAX_PATH] = "";
char szUserDir[MAX_PATH] = "";
char szDebuggeePath[MAX_PATH] = "";
char szDllLoaderPath[MAX_PATH] = "";
char szSymbolCachePath[MAX_PATH] = "";
@ -2679,7 +2680,7 @@ static void* InitDLLDebugW(const wchar_t* szFileName, const wchar_t* szCommandLi
WString loaderPath = StringUtils::Utf8ToUtf16(szDllLoaderPath);
if(!CopyFileW(loaderPath.c_str(), debuggeeLoaderPath.c_str(), FALSE))
{
debuggeeLoaderPath = StringUtils::Utf8ToUtf16(szProgramDir);
debuggeeLoaderPath = StringUtils::Utf8ToUtf16(szUserDir);
debuggeeLoaderPath += loaderFilename;
if(!CopyFileW(loaderPath.c_str(), debuggeeLoaderPath.c_str(), FALSE))
{

View File

@ -138,6 +138,7 @@ extern PROCESS_INFORMATION* fdProcessInfo;
extern HANDLE hActiveThread;
extern HANDLE hProcessToken;
extern char szProgramDir[MAX_PATH];
extern char szUserDir[MAX_PATH];
extern char szDebuggeePath[MAX_PATH];
extern char szDllLoaderPath[MAX_PATH];
extern char szSymbolCachePath[MAX_PATH];

View File

@ -13,6 +13,8 @@ extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
hInst = hinstDLL;
// Get program directory
strcpy_s(szUserDir, StringUtils::Utf16ToUtf8(BridgeUserDirectory()).c_str());
{
wchar_t wszDir[deflen] = L"";
if(GetModuleFileNameW(hInst, wszDir, deflen))

View File

@ -621,7 +621,7 @@ static DWORD WINAPI loadDbThread(LPVOID hEvent)
// Load global notes
dputs(QT_TRANSLATE_NOOP("DBG", "Reading notes file..."));
notesFile = String(szProgramDir) + "\\notes.txt";
notesFile = String(szUserDir) + "\\notes.txt";
String text;
if(!FileExists(notesFile.c_str()) || FileHelper::ReadAllText(notesFile, text))
GuiSetGlobalNotes(text.c_str());
@ -687,9 +687,6 @@ PfnDliHook __pfnDliNotifyHook2 = delayHook;
extern "C" DLL_EXPORT const char* _dbg_dbginit()
{
if(!*szProgramDir)
return "GetModuleFileNameW failed!";
if(!EngineCheckStructAlignment(UE_STRUCT_TITAN_ENGINE_CONTEXT, sizeof(TITAN_ENGINE_CONTEXT_t)))
return "Invalid TITAN_ENGINE_CONTEXT_t alignment!";
@ -699,7 +696,7 @@ extern "C" DLL_EXPORT const char* _dbg_dbginit()
strcat_s(szDllLoaderPath, "\\loaddll.exe");
#ifdef ENABLE_MEM_TRACE
strcpy_s(alloctrace, szProgramDir);
strcpy_s(alloctrace, szUserDir);
strcat_s(alloctrace, "\\alloctrace.txt");
DeleteFileW(StringUtils::Utf8ToUtf16(alloctrace).c_str());
setalloctrace(alloctrace);
@ -720,7 +717,7 @@ extern "C" DLL_EXPORT const char* _dbg_dbginit()
Zydis::GlobalInitialize();
dputs(QT_TRANSLATE_NOOP("DBG", "Getting directory information..."));
strcpy_s(scriptDllDir, szProgramDir);
strcpy_s(scriptDllDir, szUserDir);
strcat_s(scriptDllDir, "\\scripts\\");
initDataInstMap();
@ -734,10 +731,10 @@ extern "C" DLL_EXPORT const char* _dbg_dbginit()
}
// Create database directory in the local debugger folder
DbSetPath(StringUtils::sprintf("%s\\db", szProgramDir).c_str(), nullptr);
DbSetPath(StringUtils::sprintf("%s\\db", szUserDir).c_str(), nullptr);
char szLocalSymbolPath[MAX_PATH] = "";
strcpy_s(szLocalSymbolPath, szProgramDir);
strcpy_s(szLocalSymbolPath, szUserDir);
strcat_s(szLocalSymbolPath, "\\symbols");
Memory<char*> cachePath(MAX_SETTING_SIZE + 1);
@ -750,7 +747,7 @@ extern "C" DLL_EXPORT const char* _dbg_dbginit()
{
if(_strnicmp(cachePath(), ".\\", 2) == 0)
{
strncpy_s(szSymbolCachePath, szProgramDir, _TRUNCATE);
strncpy_s(szSymbolCachePath, szUserDir, _TRUNCATE);
strncat_s(szSymbolCachePath, cachePath() + 1, _TRUNCATE);
}
else
@ -805,7 +802,7 @@ extern "C" DLL_EXPORT const char* _dbg_dbginit()
strcpy_s(plugindir, szProgramDir);
strcat_s(plugindir, "\\plugins");
CreateDirectoryW(StringUtils::Utf8ToUtf16(plugindir).c_str(), nullptr);
CreateDirectoryW(StringUtils::Utf8ToUtf16(StringUtils::sprintf("%s\\memdumps", szProgramDir)).c_str(), nullptr);
CreateDirectoryW(StringUtils::Utf8ToUtf16(StringUtils::sprintf("%s\\memdumps", szUserDir)).c_str(), nullptr);
dputs(QT_TRANSLATE_NOOP("DBG", "Initialization successful!"));
bIsStopped = false;
dputs(QT_TRANSLATE_NOOP("DBG", "Loading plugins..."));

View File

@ -384,7 +384,7 @@ void LogView::redirectLogSlot()
}
else
{
BrowseDialog browse(this, tr("Redirect log to file"), tr("Enter the file to which you want to redirect log messages."), tr("Log files (*.txt);;All files (*.*)"), QCoreApplication::applicationDirPath(), true);
BrowseDialog browse(this, tr("Redirect log to file"), tr("Enter the file to which you want to redirect log messages."), tr("Log files (*.txt);;All files (*.*)"), QString::fromWCharArray(BridgeUserDirectory()), true);
if(browse.exec() == QDialog::Accepted)
{
logRedirection = _wfopen(browse.path.toStdWString().c_str(), L"ab");

View File

@ -2409,7 +2409,7 @@ void MainWindow::customizeMenu()
void MainWindow::on_actionImportSettings_triggered()
{
auto filename = QFileDialog::getOpenFileName(this, tr("Open file"), QCoreApplication::applicationDirPath(), tr("Settings (*.ini);;All files (*.*)"));
auto filename = QFileDialog::getOpenFileName(this, tr("Open file"), QString::fromWCharArray(BridgeUserDirectory()), tr("Settings (*.ini);;All files (*.*)"));
if(!filename.length())
return;
importSettings(filename);

View File

@ -90,7 +90,7 @@ void SystemBreakpointScriptDialog::on_openGlobal_clicked()
{
// The new script is at app dir
QString defaultFileName("autorun.txt");
defaultFileName = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + defaultFileName);
defaultFileName = QDir::toNativeSeparators(QString::fromWCharArray(BridgeUserDirectory()) + QDir::separator() + defaultFileName);
// Create it
if(!QFile::exists(defaultFileName))
{

View File

@ -352,7 +352,7 @@ QIcon DIconHelper(QString name)
QString getDbPath(const QString & filename, bool addDateTimeSuffix)
{
auto path = QString("%1/db").arg(QCoreApplication::applicationDirPath());
auto path = QString("%1/db").arg(QString::fromWCharArray(BridgeUserDirectory()));
if(!filename.isEmpty())
{
path += '/';