From 5ef1ff63526ba98db0b56fb4b6dd17b64afafbb6 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 1 Dec 2018 15:55:32 -0500 Subject: [PATCH] using raii for processrecords and qfileinfo for files and other refactors --- GUI/extenwindow.cpp | 9 +++---- GUI/extenwindow.h | 2 +- GUI/host/host.cpp | 55 ++++++++++++++++++++++------------------- GUI/host/host.h | 10 +++----- GUI/host/textthread.cpp | 15 ++++++++--- GUI/host/textthread.h | 8 +++--- GUI/main.cpp | 21 ++++++++-------- GUI/mainwindow.cpp | 23 ++++++++--------- GUI/mainwindow.h | 2 +- GUI/misc.cpp | 44 +++++++++------------------------ GUI/misc.h | 2 -- GUI/qtcommon.h | 1 + 12 files changed, 89 insertions(+), 103 deletions(-) diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index b522a67..db645a9 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -105,12 +105,11 @@ void ExtenWindow::Sync() } } -void ExtenWindow::Add(QString fileName) +void ExtenWindow::Add(QFileInfo extenFile) { - if (!fileName.endsWith(".dll")) return; - QString extenName = fileName.mid(fileName.lastIndexOf("/") + 1); - QFile::copy(fileName, extenName); - Load(extenName.split(".dll")[0]); + if (extenFile.suffix() != "dll") return; + QFile::copy(extenFile.fileName(), extenFile.absoluteFilePath()); + Load(extenFile.completeBaseName()); Sync(); } diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h index 5cc747f..aa99ebf 100644 --- a/GUI/extenwindow.h +++ b/GUI/extenwindow.h @@ -26,7 +26,7 @@ private slots: void on_rmvButton_clicked(); private: - void Add(QString fileName); + void Add(QFileInfo extenFile); void Sync(); bool eventFilter(QObject* target, QEvent* event); void dragEnterEvent(QDragEnterEvent* event); diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index d4b8e7f..9874532 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -15,9 +15,9 @@ namespace ProcessRecord(DWORD processId, HANDLE pipe) : processId(processId), pipe(pipe), - fileMapping(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), - mappedView(MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size - sectionMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)) + mappedFile(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), + view(MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size + viewMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)) { OnConnect(processId); } @@ -25,14 +25,14 @@ namespace ~ProcessRecord() { OnDisconnect(processId); - UnmapViewOfFile(mappedView); + UnmapViewOfFile(view); } TextHook GetHook(uint64_t addr) { - if (mappedView == nullptr) return {}; - LOCK(sectionMutex); - auto hooks = (const TextHook*)mappedView; + if (view == nullptr) return {}; + LOCK(viewMutex); + auto hooks = (const TextHook*)view; for (int i = 0; i < MAX_HOOK; ++i) if (hooks[i].hp.insertion_address == addr) return hooks[i]; return {}; @@ -41,16 +41,16 @@ namespace template void Send(T data) { - DWORD DUMMY; + std::enable_if_t DUMMY; WriteFile(pipe, &data, sizeof(data), &DUMMY, nullptr); } private: DWORD processId; HANDLE pipe; - AutoHandle<> fileMapping; - LPCVOID mappedView; - WinMutex sectionMutex; + AutoHandle<> mappedFile; + LPCVOID view; + WinMutex viewMutex; }; ThreadSafePtr>> textThreadsByParams; @@ -60,8 +60,8 @@ namespace void RemoveThreads(std::function removeIf) { - auto lockedTextThreadsByParams = textThreadsByParams.operator->(); - for (auto it = lockedTextThreadsByParams->begin(); it != lockedTextThreadsByParams->end(); removeIf(it->first) ? it = lockedTextThreadsByParams->erase(it) : ++it); + auto[lock, textThreadsByParams] = ::textThreadsByParams.operator->(); + for (auto it = textThreadsByParams->begin(); it != textThreadsByParams->end(); removeIf(it->first) ? it = textThreadsByParams->erase(it) : ++it); } void CreatePipe() @@ -104,9 +104,9 @@ namespace auto tp = *(ThreadParam*)buffer; if (textThreadsByParams->count(tp) == 0) { - auto textThread = textThreadsByParams->insert({ tp, std::make_shared(tp, Host::GetHookParam(tp), Host::GetHookName(tp)) }).first->second; - if (textThreadsByParams->size() > MAX_THREAD_COUNT) Host::AddConsoleOutput(TOO_MANY_THREADS); - else textThread->Start(); + auto textThread = textThreadsByParams->insert({ tp, std::make_shared(tp, Host::GetHookParam(tp)) }).first->second; + if (textThreadsByParams->size() < MAX_THREAD_COUNT) textThread->Start(); + else Host::AddConsoleOutput(TOO_MANY_THREADS); } textThreadsByParams->at(tp)->Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); } @@ -146,6 +146,16 @@ namespace Host CreatePipe(); } + void Shutdown() + { + auto NOP = [](auto... args) { return NULL; }; + ProcessRecord::OnConnect = NOP; + ProcessRecord::OnDisconnect = NOP; + TextThread::OnCreate = NOP; + TextThread::OnDestroy = NOP; + TextThread::Output = NOP; + } + bool InjectProcess(DWORD processId, DWORD timeout) { if (processId == GetCurrentProcessId()) return false; @@ -193,19 +203,14 @@ namespace Host processRecordsByIds->at(processId)->Send(HostCommandType(HOST_COMMAND_DETACH)); } - void InsertHook(DWORD processId, HookParam hp, std::string name) + void InsertHook(DWORD processId, HookParam hp) { - processRecordsByIds->at(processId)->Send(InsertHookCmd(hp, name)); + processRecordsByIds->at(processId)->Send(InsertHookCmd(hp)); } - HookParam GetHookParam(DWORD processId, uint64_t addr) + HookParam GetHookParam(ThreadParam tp) { - return processRecordsByIds->at(processId)->GetHook(addr).hp; - } - - std::wstring GetHookName(DWORD processId, uint64_t addr) - { - return Util::StringToWideString(processRecordsByIds->at(processId)->GetHook(addr).hookName).value(); + return processRecordsByIds->at(tp.processId)->GetHook(tp.addr).hp; } std::shared_ptr GetThread(ThreadParam tp) diff --git a/GUI/host/host.h b/GUI/host/host.h index 81d1843..569a1f3 100644 --- a/GUI/host/host.h +++ b/GUI/host/host.h @@ -5,17 +5,15 @@ namespace Host { - typedef std::function ProcessEventCallback; + using ProcessEventCallback = std::function; void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output); + void Shutdown(); bool InjectProcess(DWORD processId, DWORD timeout = 5000); void DetachProcess(DWORD processId); - void InsertHook(DWORD processId, HookParam hp, std::string name = ""); + void InsertHook(DWORD processId, HookParam hp); - HookParam GetHookParam(DWORD processId, uint64_t addr); - inline HookParam GetHookParam(ThreadParam tp) { return GetHookParam(tp.processId, tp.addr); } - std::wstring GetHookName(DWORD processId, uint64_t addr); - inline std::wstring GetHookName(ThreadParam tp) { return GetHookName(tp.processId, tp.addr); } + HookParam GetHookParam(ThreadParam tp); std::shared_ptr GetThread(ThreadParam tp); void AddConsoleOutput(std::wstring text); diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index 0ece4b8..697961b 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -4,16 +4,23 @@ #include "host.h" #include "util.h" -TextThread::TextThread(ThreadParam tp, HookParam hp, std::wstring name) : handle(threadCounter++), name(name), tp(tp), hp(hp) +TextThread::TextThread(ThreadParam tp, HookParam hp, std::optional name) : + handle(threadCounter++), + name(name.value_or(Util::StringToWideString(hp.name).value())), + tp(tp), + hp(hp) { OnCreate(this); } TextThread::~TextThread() { - SetEvent(deletionEvent); - flushThread.join(); - OnDestroy(this); + if (flushThread.joinable()) + { + SetEvent(deletionEvent); + flushThread.join(); + OnDestroy(this); + } } std::wstring TextThread::GetStorage() diff --git a/GUI/host/textthread.h b/GUI/host/textthread.h index 10d6cbd..8fe7d17 100644 --- a/GUI/host/textthread.h +++ b/GUI/host/textthread.h @@ -6,8 +6,8 @@ class TextThread { public: - typedef std::function EventCallback; - typedef std::function OutputCallback; + using EventCallback = std::function; + using OutputCallback = std::function; inline static EventCallback OnCreate, OnDestroy; inline static OutputCallback Output; @@ -16,9 +16,7 @@ public: inline static int defaultCodepage = SHIFT_JIS; inline static int threadCounter = 0; - TextThread(ThreadParam tp, HookParam hp, std::wstring name); - TextThread(TextThread&) = delete; - TextThread& operator=(TextThread) = delete; + TextThread(ThreadParam tp, HookParam hp, std::optional name = {}); ~TextThread(); std::wstring GetStorage(); diff --git a/GUI/main.cpp b/GUI/main.cpp index e64e71c..ef6bb7c 100644 --- a/GUI/main.cpp +++ b/GUI/main.cpp @@ -2,6 +2,7 @@ #include "host/util.h" #include #include +#include namespace { @@ -15,8 +16,16 @@ namespace thread_local std::wstring lastError = L"Unknown error"; + __declspec(noreturn) void Terminate() + { + MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR); + std::abort(); + } + LONG WINAPI ExceptionLogger(EXCEPTION_POINTERS* exception) { + thread_local static auto terminateSetter = std::invoke(std::set_terminate, Terminate); + MEMORY_BASIC_INFORMATION info = {}; VirtualQuery(exception->ExceptionRecord->ExceptionAddress, &info, sizeof(info)); @@ -36,14 +45,6 @@ namespace lastError = errorMsg.str(); return EXCEPTION_CONTINUE_SEARCH; } - - __declspec(noreturn) void Terminate() - { - MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR); - std::abort(); - } - - thread_local auto _ = [] { return std::set_terminate(Terminate); }(); } int main(int argc, char *argv[]) @@ -51,9 +52,7 @@ int main(int argc, char *argv[]) AddVectoredExceptionHandler(FALSE, ExceptionLogger); SetUnhandledExceptionFilter([](auto) -> LONG { Terminate(); }); - std::wstring exe = Util::GetModuleFileName().value(); - while (exe.back() != L'\\') exe.pop_back(); - SetCurrentDirectoryW(exe.c_str()); + QDir::setCurrent(QFileInfo().absolutePath()); QApplication a(argc, argv); MainWindow w; diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 870c7ad..a3449cb 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -4,6 +4,7 @@ #include "extenwindow.h" #include "setdialog.h" #include "misc.h" +#include "host/util.h" #include #include @@ -35,6 +36,7 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { + Host::Shutdown(); settings.setValue(WINDOW, geometry()); settings.sync(); delete ui; @@ -46,7 +48,7 @@ void MainWindow::closeEvent(QCloseEvent*) } -void MainWindow::InvokeOnMainThread(std::function&& f) +void MainWindow::InvokeOnMainThread(std::function f) { QMetaObject::invokeMethod(this, f); } @@ -56,16 +58,15 @@ void MainWindow::ProcessConnected(DWORD processId) if (processId == 0) return; InvokeOnMainThread([&, processId] { - processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); - QString processName = GetFullModuleName(processId); + QString process = QString::fromStdWString(Util::GetModuleFileName(processId).value()); + processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + QFileInfo(process).fileName()); + QStringList allProcesses = QString(QAutoFile(HOOK_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split("\r", QString::SkipEmptyParts); - for (auto hooks = allProcesses.rbegin(); hooks != allProcesses.rend(); ++hooks) - if (hooks->contains(processName)) - { - for (auto hook : hooks->split(" , ")) - if (auto hp = ParseCode(hook)) Host::InsertHook(processId, hp.value()); - return; - } + // Can't use QFileInfo::absoluteFilePath since hook save file has '\\' as path separator + auto hookList = std::find_if(allProcesses.rbegin(), allProcesses.rend(), [&](QString hookList) { return hookList.contains(process); }); + if (hookList != allProcesses.rend()) + for (auto hookCode : hookList->split(" , ")) + if (auto hp = ParseCode(hookCode)) Host::InsertHook(processId, hp.value()); }); } @@ -185,7 +186,7 @@ void MainWindow::on_saveButton_clicked() ThreadParam tp = ParseTextThreadString(ttCombo->itemText(i)); if (tp.processId == GetSelectedProcessId() && !(Host::GetHookParam(tp).type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(Host::GetHookParam(tp), tp.processId); } - QString hookList = GetFullModuleName(GetSelectedProcessId()); + QString hookList = QString::fromStdWString(Util::GetModuleFileName(GetSelectedProcessId()).value()); for (auto hookCode : hookCodes) hookList += " , " + hookCode; QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8()); } diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index ece749f..1d94bc3 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -32,7 +32,7 @@ private slots: private: void closeEvent(QCloseEvent*); - void InvokeOnMainThread(std::function&& f); + void InvokeOnMainThread(std::function f); void ProcessConnected(DWORD processId); void ProcessDisconnected(DWORD processId); void ThreadAdded(TextThread* thread); diff --git a/GUI/misc.cpp b/GUI/misc.cpp index b074fa1..e356d2b 100644 --- a/GUI/misc.cpp +++ b/GUI/misc.cpp @@ -1,32 +1,16 @@ #include "misc.h" #include "const.h" +#include "host/util.h" #include #include -QString GetFullModuleName(DWORD processId, HMODULE module) -{ - HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); - wchar_t buffer[MAX_PATH] = {}; - GetModuleFileNameExW(handle, module, buffer, MAX_PATH); - CloseHandle(handle); - return QString::fromWCharArray(buffer); -} - -QString GetModuleName(DWORD processId, HMODULE module) -{ - QString fullName = GetFullModuleName(processId, module); - return fullName.remove(0, fullName.lastIndexOf("\\") + 1); -} - QMultiHash GetAllProcesses() { - DWORD allProcessIds[0x1000]; - DWORD spaceUsed; + DWORD allProcessIds[5000] = {}, spaceUsed = 0; + EnumProcesses(allProcessIds, sizeof(allProcessIds), &spaceUsed); QMultiHash ret; - if (!EnumProcesses(allProcessIds, sizeof(allProcessIds), &spaceUsed)) return ret; for (int i = 0; i < spaceUsed / sizeof(DWORD); ++i) - if (GetModuleName(allProcessIds[i]).size()) - ret.insert(GetModuleName(allProcessIds[i]), allProcessIds[i]); + if (auto processName = Util::GetModuleFileName(allProcessIds[i])) ret.insert(QFileInfo(QString::fromStdWString(processName.value())).fileName(), allProcessIds[i]); return ret; } @@ -235,19 +219,15 @@ namespace // Attempt to make the address relative if (!(hp.type & MODULE_OFFSET)) - { - HANDLE processHandle; - if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) goto fin; - MEMORY_BASIC_INFORMATION info; - if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) goto fin; - QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase); - if (moduleName == "") goto fin; - hp.type |= MODULE_OFFSET; - hp.address -= (uint64_t)info.AllocationBase; - wcscpy_s(hp.module, moduleName.toStdWString().c_str()); - } + if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) + if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info))) + if (auto moduleName = Util::GetModuleFileName(processId, (HMODULE)info.AllocationBase)) + { + hp.type |= MODULE_OFFSET; + hp.address -= (uint64_t)info.AllocationBase; + wcscpy_s(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1); + } - fin: codeBuilder << "@" << hp.address; if (hp.type & MODULE_OFFSET) codeBuilder << ":" << QString::fromWCharArray(hp.module); if (hp.type & FUNCTION_OFFSET) codeBuilder << ":" << hp.function; diff --git a/GUI/misc.h b/GUI/misc.h index 7833f33..4120a3e 100644 --- a/GUI/misc.h +++ b/GUI/misc.h @@ -13,8 +13,6 @@ private: QFile f; }; -QString GetFullModuleName(DWORD processId, HMODULE module = NULL); -QString GetModuleName(DWORD processId, HMODULE module = NULL); QMultiHash GetAllProcesses(); std::optional ParseCode(QString HCode); QString GenerateCode(HookParam hp, DWORD processId); diff --git a/GUI/qtcommon.h b/GUI/qtcommon.h index 4aa443b..f48dd54 100644 --- a/GUI/qtcommon.h +++ b/GUI/qtcommon.h @@ -7,4 +7,5 @@ #include #include #include +#include #include