From efa8d26ada45d57cba3c6b729aa078d73bbc253b Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Tue, 27 Nov 2018 15:54:04 -0500 Subject: [PATCH] holy shit you can overload operator-> to do WHAT?? --- GUI/extenwindow.cpp | 15 ++-- GUI/extenwindow.h | 3 - GUI/host/host.cpp | 174 ++++++++++++++++------------------------ GUI/host/host.h | 6 +- GUI/host/textthread.cpp | 24 +++--- GUI/host/textthread.h | 14 ++-- GUI/host/util.cpp | 26 +++++- GUI/host/util.h | 3 + GUI/main.cpp | 23 +++--- GUI/mainwindow.cpp | 144 ++++++++++++++++----------------- GUI/mainwindow.h | 23 ++---- GUI/misc.h | 9 +++ 12 files changed, 224 insertions(+), 240 deletions(-) diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index 3cbfcf3..b522a67 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -1,7 +1,10 @@ #include "extenwindow.h" #include "ui_extenwindow.h" #include "text.h" +#include "defs.h" #include "types.h" +#include "misc.h" +#include #include #include #include @@ -81,12 +84,7 @@ ExtenWindow::ExtenWindow(QWidget* parent) : extenList = findChild("extenList"); extenList->installEventFilter(this); - if (extensions.empty()) - { - extenSaveFile.open(QIODevice::ReadOnly); - for (auto extenName : QString(extenSaveFile.readAll()).split(">")) Load(extenName); - extenSaveFile.close(); - } + for (auto extenName : QString(QAutoFile(EXTEN_SAVE_FILE, QIODevice::ReadOnly)->readAll()).split(">")) Load(extenName); Sync(); } @@ -98,14 +96,13 @@ ExtenWindow::~ExtenWindow() void ExtenWindow::Sync() { extenList->clear(); - extenSaveFile.open(QIODevice::WriteOnly | QIODevice::Truncate); + QAutoFile extenSaveFile(EXTEN_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate); std::shared_lock sharedLock(extenMutex); for (auto extenName : extenNames) { extenList->addItem(extenName); - extenSaveFile.write((extenName + ">").toUtf8()); + extenSaveFile->write((extenName + ">").toUtf8()); } - extenSaveFile.close(); } void ExtenWindow::Add(QString fileName) diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h index 041dff2..5cc747f 100644 --- a/GUI/extenwindow.h +++ b/GUI/extenwindow.h @@ -2,8 +2,6 @@ #define EXTENSIONS_H #include "qtcommon.h" -#include "defs.h" -#include #include #include #include @@ -35,7 +33,6 @@ private: void dropEvent(QDropEvent* event); Ui::ExtenWindow* ui; - QFile extenSaveFile = QFile(EXTEN_SAVE_FILE); QListWidget* extenList; }; diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index 5579b55..a27525d 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -10,86 +10,58 @@ namespace class ProcessRecord { public: - ProcessRecord(DWORD processId, HANDLE hostPipe) : - hostPipe(hostPipe), - section(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), - sectionMap(MapViewOfFile(section, 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)) - {} + inline static Host::ProcessEventCallback OnConnect, OnDisconnect; - ProcessRecord(ProcessRecord&) = delete; - ProcessRecord& operator=(ProcessRecord) = delete; + 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)) + { + OnConnect(processId); + } ~ProcessRecord() { - UnmapViewOfFile(sectionMap); - CloseHandle(section); + OnDisconnect(processId); + UnmapViewOfFile(mappedView); } TextHook GetHook(uint64_t addr) { - if (sectionMap == nullptr) return {}; + if (mappedView == nullptr) return {}; LOCK(sectionMutex); - auto hooks = (const TextHook*)sectionMap; + auto hooks = (const TextHook*)mappedView; for (int i = 0; i < MAX_HOOK; ++i) if (hooks[i].hp.insertion_address == addr) return hooks[i]; return {}; } - HANDLE hostPipe; + template + void Send(T data) + { + DWORD DUMMY; + WriteFile(pipe, &data, sizeof(data), &DUMMY, nullptr); + } private: - HANDLE section; - LPVOID sectionMap; + DWORD processId; + HANDLE pipe; + AutoHandle<> fileMapping; + LPCVOID mappedView; WinMutex sectionMutex; }; - ThreadEventCallback OnCreate, OnDestroy; - ProcessEventCallback OnAttach, OnDetach; + ThreadSafePtr>> textThreadsByParams; + ThreadSafePtr>> processRecordsByIds; - std::unordered_map> textThreadsByParams; - std::unordered_map> processRecordsByIds; - - std::recursive_mutex hostMutex; - - DWORD DUMMY; ThreadParam CONSOLE{ 0, -1ULL, -1ULL, -1ULL }, CLIPBOARD{ 0, 0, -1ULL, -1ULL }; - void DispatchText(ThreadParam tp, const BYTE* text, int len) - { - LOCK(hostMutex); - if (textThreadsByParams[tp] == nullptr) - { - if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(TOO_MANY_THREADS); - OnCreate(textThreadsByParams[tp] = std::make_shared(tp, Host::GetHookParam(tp), Host::GetHookName(tp))); - } - textThreadsByParams[tp]->Push(text, len); - } - void RemoveThreads(std::function removeIf) { - LOCK(hostMutex); - for (auto it = textThreadsByParams.begin(); it != textThreadsByParams.end();) - if (auto curr = it++; removeIf(curr->first)) - { - OnDestroy(curr->second); - textThreadsByParams.erase(curr->first); - } - } - - void RegisterProcess(DWORD processId, HANDLE hostPipe) - { - LOCK(hostMutex); - processRecordsByIds.insert({ processId, std::make_unique(processId, hostPipe) }); - OnAttach(processId); - } - - void UnregisterProcess(DWORD processId) - { - OnDetach(processId); - LOCK(hostMutex); - processRecordsByIds.erase(processId); - RemoveThreads([&](ThreadParam tp) { return tp.processId == processId; }); + auto lockedTextThreadsByParams = textThreadsByParams.operator->(); + for (auto it = lockedTextThreadsByParams->begin(); it != lockedTextThreadsByParams->end(); removeIf(it->first) ? it = lockedTextThreadsByParams->erase(it) : ++it); } void CreatePipe() @@ -100,14 +72,15 @@ namespace InitializeSecurityDescriptor(&pipeSD, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&pipeSD, TRUE, NULL, FALSE); // Allow non-admin processes to connect to pipe created by admin host SECURITY_ATTRIBUTES pipeSA = { sizeof(SECURITY_ATTRIBUTES), &pipeSD, FALSE }; - HANDLE hookPipe = CreateNamedPipeW(HOOK_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, 0, PIPE_BUFFER_SIZE, MAXDWORD, &pipeSA); - HANDLE hostPipe = CreateNamedPipeW(HOST_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, 0, MAXDWORD, &pipeSA); + AutoHandle + hookPipe = CreateNamedPipeW(HOOK_PIPE, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, 0, PIPE_BUFFER_SIZE, MAXDWORD, &pipeSA), + hostPipe = CreateNamedPipeW(HOST_PIPE, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER_SIZE, 0, MAXDWORD, &pipeSA); ConnectNamedPipe(hookPipe, nullptr); BYTE buffer[PIPE_BUFFER_SIZE] = {}; DWORD bytesRead, processId; ReadFile(hookPipe, &processId, sizeof(processId), &bytesRead, nullptr); - RegisterProcess(processId, hostPipe); + processRecordsByIds->insert({ processId, std::make_unique(processId, hostPipe) }); CreatePipe(); @@ -129,16 +102,19 @@ namespace default: { auto tp = *(ThreadParam*)buffer; - DispatchText(tp, buffer + sizeof(tp), bytesRead - sizeof(tp)); + 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(); + } + textThreadsByParams->at(tp)->Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); } break; } - UnregisterProcess(processId); - DisconnectNamedPipe(hookPipe); - DisconnectNamedPipe(hostPipe); - CloseHandle(hookPipe); - CloseHandle(hostPipe); + RemoveThreads([&](ThreadParam tp) { return tp.processId == processId; }); + processRecordsByIds->erase(processId); }).detach(); } @@ -156,12 +132,16 @@ namespace namespace Host { - void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onDestroy, TextThread::OutputCallback output) + void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output) { - OnAttach = onAttach; OnDetach = onDetach; OnCreate = onCreate; OnDestroy = onDestroy; TextThread::Output = output; - RegisterProcess(CONSOLE.processId, INVALID_HANDLE_VALUE); - OnCreate(textThreadsByParams[CONSOLE] = std::make_shared(CONSOLE, HookParam{}, L"Console")); - OnCreate(textThreadsByParams[CLIPBOARD] = std::make_shared(CLIPBOARD, HookParam{}, L"Clipboard")); + ProcessRecord::OnConnect = OnConnect; + ProcessRecord::OnDisconnect = OnDisconnect; + TextThread::OnCreate = OnCreate; + TextThread::OnDestroy = OnDestroy; + TextThread::Output = Output; + processRecordsByIds->insert({ CONSOLE.processId, std::make_unique(CONSOLE.processId, INVALID_HANDLE_VALUE) }); + textThreadsByParams->insert({ CONSOLE, std::make_shared(CONSOLE, HookParam{}, L"Console") }); + textThreadsByParams->insert({ CLIPBOARD, std::make_shared(CLIPBOARD, HookParam{}, L"Clipboard") }); StartCapturingClipboard(); CreatePipe(); } @@ -170,9 +150,10 @@ namespace Host { // Artikash 7/25/2018: This is only called when Textractor is closed, at which point Windows should free everything itself...right? #ifdef _DEBUG // Check memory leaks - LOCK(hostMutex); - processRecordsByIds.clear(); - textThreadsByParams.clear(); + ProcessRecord::OnConnect = ProcessRecord::OnDisconnect = [](auto) {}; + TextThread::OnCreate = TextThread::OnDestroy = [](auto) {}; + processRecordsByIds->clear(); + textThreadsByParams->clear(); #endif } @@ -180,43 +161,37 @@ namespace Host { if (processId == GetCurrentProcessId()) return false; - CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str())); + WinMutex(ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)); if (GetLastError() == ERROR_ALREADY_EXISTS) { AddConsoleOutput(ALREADY_INJECTED); return false; } - HMODULE textHooker = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES); - wchar_t textHookerPath[MAX_PATH]; - DWORD textHookerPathSize = GetModuleFileNameW(textHooker, textHookerPath, MAX_PATH) * 2 + 2; - FreeLibrary(textHooker); + static HMODULE vnrhook = LoadLibraryExW(ITH_DLL, nullptr, DONT_RESOLVE_DLL_REFERENCES); + static std::wstring location = Util::GetModuleFileName(vnrhook).value(); - if (HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId)) + if (AutoHandle<> process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId)) { #ifdef _WIN64 BOOL invalidProcess = FALSE; - IsWow64Process(processHandle, &invalidProcess); + IsWow64Process(process, &invalidProcess); if (invalidProcess) { AddConsoleOutput(ARCHITECTURE_MISMATCH); - CloseHandle(processHandle); return false; } #endif - if (LPVOID remoteData = VirtualAllocEx(processHandle, nullptr, textHookerPathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) + if (LPVOID remoteData = VirtualAllocEx(process, nullptr, location.size() * 2 + 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) { - WriteProcessMemory(processHandle, remoteData, textHookerPath, textHookerPathSize, nullptr); - if (HANDLE thread = CreateRemoteThread(processHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr)) + WriteProcessMemory(process, remoteData, location.c_str(), location.size() * 2 + 2, nullptr); + if (AutoHandle<> thread = CreateRemoteThread(process, nullptr, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, remoteData, 0, nullptr)) { WaitForSingleObject(thread, timeout); - CloseHandle(thread); - VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE); - CloseHandle(processHandle); + VirtualFreeEx(process, remoteData, 0, MEM_RELEASE); return true; } - VirtualFreeEx(processHandle, remoteData, 0, MEM_RELEASE); - CloseHandle(processHandle); + VirtualFreeEx(process, remoteData, 0, MEM_RELEASE); } } @@ -226,41 +201,32 @@ namespace Host void DetachProcess(DWORD processId) { - LOCK(hostMutex); - HostCommandType buffer(HOST_COMMAND_DETACH); - WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr); + processRecordsByIds->at(processId)->Send(HostCommandType(HOST_COMMAND_DETACH)); } void InsertHook(DWORD processId, HookParam hp, std::string name) { - LOCK(hostMutex); - InsertHookCmd buffer(hp, name); - WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr); + processRecordsByIds->at(processId)->Send(InsertHookCmd(hp, name)); } void RemoveHook(DWORD processId, uint64_t addr) { - LOCK(hostMutex); - RemoveHookCmd buffer(addr); - WriteFile(processRecordsByIds.at(processId)->hostPipe, &buffer, sizeof(buffer), &DUMMY, nullptr); + processRecordsByIds->at(processId)->Send(RemoveHookCmd(addr)); } HookParam GetHookParam(DWORD processId, uint64_t addr) { - LOCK(hostMutex); - return processRecordsByIds.at(processId)->GetHook(addr).hp; + return processRecordsByIds->at(processId)->GetHook(addr).hp; } std::wstring GetHookName(DWORD processId, uint64_t addr) { - LOCK(hostMutex); - return Util::StringToWideString(processRecordsByIds.at(processId)->GetHook(addr).hookName).value(); + return Util::StringToWideString(processRecordsByIds->at(processId)->GetHook(addr).hookName).value(); } std::shared_ptr GetThread(ThreadParam tp) { - LOCK(hostMutex); - return textThreadsByParams[tp]; + return textThreadsByParams->at(tp); } void AddConsoleOutput(std::wstring text) diff --git a/GUI/host/host.h b/GUI/host/host.h index f14604e..1337140 100644 --- a/GUI/host/host.h +++ b/GUI/host/host.h @@ -3,12 +3,10 @@ #include "common.h" #include "textthread.h" -typedef std::function ProcessEventCallback; -typedef std::function)> ThreadEventCallback; - namespace Host { - void Start(ProcessEventCallback onAttach, ProcessEventCallback onDetach, ThreadEventCallback onCreate, ThreadEventCallback onDestroy, TextThread::OutputCallback output); + typedef std::function ProcessEventCallback; + void Start(ProcessEventCallback OnConnect, ProcessEventCallback OnDisconnect, TextThread::EventCallback OnCreate, TextThread::EventCallback OnDestroy, TextThread::OutputCallback Output); void Close(); bool InjectProcess(DWORD processId, DWORD timeout = 5000); diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index 3ec8d97..0ece4b8 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -4,33 +4,37 @@ #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::wstring name) : handle(threadCounter++), name(name), tp(tp), hp(hp) +{ + OnCreate(this); +} TextThread::~TextThread() { SetEvent(deletionEvent); flushThread.join(); - CloseHandle(deletionEvent); + OnDestroy(this); } std::wstring TextThread::GetStorage() { - LOCK(storageMutex); - return storage; + return storage->c_str(); +} + +void TextThread::Start() +{ + deletionEvent = CreateEventW(nullptr, FALSE, FALSE, NULL); + flushThread = std::thread([&] { while (WaitForSingleObject(deletionEvent, 10) == WAIT_TIMEOUT) Flush(); }); } void TextThread::AddSentence(std::wstring sentence) { - if (Output(this, sentence)) - { - LOCK(storageMutex); - storage += sentence; - } + if (Output(this, sentence)) storage->append(sentence); } void TextThread::Push(const BYTE* data, int len) { - if (len < 0) return; + if (!flushThread.joinable() || len < 0) return; LOCK(bufferMutex); if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2); else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : defaultCodepage)) buffer += converted.value(); diff --git a/GUI/host/textthread.h b/GUI/host/textthread.h index d793e89..10d6cbd 100644 --- a/GUI/host/textthread.h +++ b/GUI/host/textthread.h @@ -6,8 +6,9 @@ class TextThread { public: + typedef std::function EventCallback; typedef std::function OutputCallback; - + inline static EventCallback OnCreate, OnDestroy; inline static OutputCallback Output; inline static int flushDelay = 400; // flush every 400ms by default @@ -21,6 +22,7 @@ public: ~TextThread(); std::wstring GetStorage(); + void Start(); void AddSentence(std::wstring sentence); void Push(const BYTE* data, int len); @@ -32,13 +34,11 @@ public: private: void Flush(); + ThreadSafePtr storage; std::wstring buffer; std::unordered_set repeatingChars; std::mutex bufferMutex; - std::wstring storage; - std::mutex storageMutex; - - HANDLE deletionEvent = CreateEventW(nullptr, FALSE, FALSE, NULL); - std::thread flushThread = std::thread([&] { while (WaitForSingleObject(deletionEvent, 10) == WAIT_TIMEOUT) Flush(); }); - DWORD lastPushTime = GetTickCount(); + AutoHandle<> deletionEvent = NULL; + std::thread flushThread; + DWORD lastPushTime; }; diff --git a/GUI/host/util.cpp b/GUI/host/util.cpp index ad2065e..4792ab8 100644 --- a/GUI/host/util.cpp +++ b/GUI/host/util.cpp @@ -1,16 +1,36 @@ #include "util.h" +#include "types.h" +#include namespace Util { + std::optional GetModuleFileName(DWORD processId, HMODULE module) + { + if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) + { + std::vector buffer(MAX_PATH); + if (GetModuleFileNameExW(process, module, buffer.data(), MAX_PATH)) return buffer.data(); + else return {}; + } + return {}; + } + + std::optional GetModuleFileName(HMODULE module) + { + std::vector buffer(MAX_PATH); + if (::GetModuleFileNameW(module, buffer.data(), MAX_PATH)) return buffer.data(); + else return {}; + } + std::optional GetClipboardText() { if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) return {}; if (!OpenClipboard(NULL)) return {}; - if (HANDLE clipboardHandle = GetClipboardData(CF_UNICODETEXT)) + if (HANDLE clipboard = GetClipboardData(CF_UNICODETEXT)) { - std::wstring ret = (wchar_t*)GlobalLock(clipboardHandle); - GlobalUnlock(clipboardHandle); + std::wstring ret = (wchar_t*)GlobalLock(clipboard); + GlobalUnlock(clipboard); CloseClipboard(); return ret; } diff --git a/GUI/host/util.h b/GUI/host/util.h index b703fa5..53cc087 100644 --- a/GUI/host/util.h +++ b/GUI/host/util.h @@ -4,6 +4,9 @@ namespace Util { + struct NamedPipeHandleCloser { void operator()(void* h) { DisconnectNamedPipe(h); CloseHandle(h); } }; + std::optional GetModuleFileName(DWORD processId, HMODULE module = NULL); + std::optional GetModuleFileName(HMODULE module = NULL); std::optional GetClipboardText(); std::optional StringToWideString(std::string text, UINT encoding = CP_UTF8); // return true if repetition found (see https://github.com/Artikash/Textractor/issues/40) diff --git a/GUI/main.cpp b/GUI/main.cpp index c26a5e8..8b2fed5 100644 --- a/GUI/main.cpp +++ b/GUI/main.cpp @@ -1,5 +1,5 @@ #include "mainwindow.h" -#include "misc.h" +#include "host/util.h" #include #include @@ -19,17 +19,19 @@ namespace { MEMORY_BASIC_INFORMATION info = {}; VirtualQuery(exception->ExceptionRecord->ExceptionAddress, &info, sizeof(info)); - wchar_t moduleName[MAX_PATH] = {}; - GetModuleFileNameW((HMODULE)info.AllocationBase, moduleName, MAX_PATH); std::wstringstream errorMsg; errorMsg << std::uppercase << std::hex << L"Error code: " << exception->ExceptionRecord->ExceptionCode << std::endl << - L"Error address: " << (uint64_t)exception->ExceptionRecord->ExceptionAddress << std::endl << - L"Error in module: " << moduleName << std::endl; + L"Error address: " << exception->ExceptionRecord->ExceptionAddress << std::endl << + L"Error in module: " << Util::GetModuleFileName((HMODULE)info.AllocationBase).value_or(L"Could not find") << std::endl << + L"Additional info: " << info.AllocationBase << std::endl; if (exception->ExceptionRecord->ExceptionCode == 0xE06D7363) + { errorMsg << L"Additional info: " << GetCppExceptionInfo(exception) << std::endl; + if (errorMsg.str().find(L"exception")) errorMsg << ((std::exception*)exception->ExceptionRecord->ExceptionInformation[1])->what(); + } for (int i = 0; i < exception->ExceptionRecord->NumberParameters; ++i) errorMsg << L"Additional info: " << exception->ExceptionRecord->ExceptionInformation[i] << std::endl; @@ -38,7 +40,7 @@ namespace return EXCEPTION_CONTINUE_SEARCH; } - void Terminate() + __declspec(noreturn) void Terminate() { MessageBoxW(NULL, lastError.c_str(), L"Textractor ERROR", MB_ICONERROR); std::abort(); @@ -50,9 +52,12 @@ namespace int main(int argc, char *argv[]) { AddVectoredExceptionHandler(FALSE, ExceptionLogger); - SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)Terminate); - QString exe = GetFullModuleName(GetCurrentProcessId()); - SetCurrentDirectoryW(exe.left(exe.lastIndexOf("\\")).toStdWString().c_str()); + SetUnhandledExceptionFilter([](auto) -> LONG { Terminate(); }); + + std::wstring exe = Util::GetModuleFileName().value(); + while (exe.back() != L'\\') exe.pop_back(); + SetCurrentDirectoryW(exe.c_str()); + QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index c6e9d1a..3fe942b 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -4,6 +4,7 @@ #include "extenwindow.h" #include "setdialog.h" #include "misc.h" +#include #include MainWindow::MainWindow(QWidget *parent) : @@ -22,20 +23,12 @@ MainWindow::MainWindow(QWidget *parent) : if (settings.contains(MAX_BUFFER_SIZE)) TextThread::maxBufferSize = settings.value(MAX_BUFFER_SIZE).toInt(); if (settings.contains(DEFAULT_CODEPAGE)) TextThread::defaultCodepage = settings.value(DEFAULT_CODEPAGE).toInt(); - qRegisterMetaType>(); - - connect(this, &MainWindow::SigAddProcess, this, &MainWindow::AddProcess); - connect(this, &MainWindow::SigRemoveProcess, this, &MainWindow::RemoveProcess); - connect(this, &MainWindow::SigAddThread, this, &MainWindow::AddThread); - connect(this, &MainWindow::SigRemoveThread, this, &MainWindow::RemoveThread); - connect(this, &MainWindow::SigThreadOutput, this, &MainWindow::ThreadOutput); - Host::Start( - [&](DWORD processId) { emit SigAddProcess(processId); }, - [&](DWORD processId) { emit SigRemoveProcess(processId); }, - [&](std::shared_ptr thread) { emit SigAddThread(thread); }, - [&](std::shared_ptr thread) { emit SigRemoveThread(thread); }, - [&](TextThread* thread, std::wstring& output) { return ProcessThreadOutput(thread, output); } + [&](DWORD processId) { ProcessConnected(processId); }, + [&](DWORD processId) { ProcessDisconnected(processId); }, + [&](TextThread* thread) { ThreadAdded(thread); }, + [&](TextThread* thread) { ThreadRemoved(thread); }, + [&](TextThread* thread, std::wstring& output) { return SentenceReceived(thread, output); } ); Host::AddConsoleOutput(ABOUT); } @@ -44,8 +37,8 @@ MainWindow::~MainWindow() { settings.setValue(WINDOW, geometry()); settings.sync(); - delete ui; Host::Close(); + delete ui; } void MainWindow::closeEvent(QCloseEvent*) @@ -53,66 +46,71 @@ void MainWindow::closeEvent(QCloseEvent*) QCoreApplication::quit(); // Need to do this to kill any windows that might've been made by extensions } -void MainWindow::AddProcess(unsigned processId) + +void MainWindow::InvokeOnMainThread(std::function&& f) +{ + QMetaObject::invokeMethod(this, f); +} + +void MainWindow::ProcessConnected(DWORD processId) { if (processId == 0) return; - processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); - QFile file(HOOK_SAVE_FILE); - file.open(QIODevice::ReadOnly); - QString processName = GetFullModuleName(processId); - QStringList allProcesses = QString(file.readAll()).split("\r", QString::SkipEmptyParts); - for (auto hooks = allProcesses.rbegin(); hooks != allProcesses.rend(); ++hooks) - if (hooks->contains(processName)) + InvokeOnMainThread([&, processId] + { + processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId)); + QString processName = GetFullModuleName(processId); + 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; + } + }); +} + +void MainWindow::ProcessDisconnected(DWORD processId) +{ + InvokeOnMainThread([&, processId] { processCombo->removeItem(processCombo->findText(QString::number(processId, 16).toUpper() + ":", Qt::MatchStartsWith)); }); +} + +void MainWindow::ThreadAdded(TextThread* thread) +{ + QString ttString = TextThreadString(thread) + QString::fromStdWString(thread->name) + " (" + GenerateCode(thread->hp, thread->tp.processId) + ")"; + InvokeOnMainThread([&, ttString] { ttCombo->addItem(ttString); }); +} + +void MainWindow::ThreadRemoved(TextThread* thread) +{ + QString ttString = TextThreadString(thread); + InvokeOnMainThread([&, ttString] + { + int threadIndex = ttCombo->findText(ttString, Qt::MatchStartsWith); + if (threadIndex == ttCombo->currentIndex()) { - for (auto hook : hooks->split(" , ")) - if (auto hp = ParseCode(hook)) Host::InsertHook(processId, hp.value()); - return; + ttCombo->setCurrentIndex(0); + on_ttCombo_activated(0); } + ttCombo->removeItem(threadIndex); + }); } -void MainWindow::RemoveProcess(unsigned processId) +bool MainWindow::SentenceReceived(TextThread* thread, std::wstring& sentence) { - processCombo->removeItem(processCombo->findText(QString::number(processId, 16).toUpper() + ":", Qt::MatchStartsWith)); -} - -void MainWindow::AddThread(std::shared_ptr thread) -{ - ttCombo->addItem( - TextThreadString(thread.get()) + - QString::fromStdWString(thread->name) + - " (" + - GenerateCode(thread->hp, thread->tp.processId) + - ")" - ); -} - -void MainWindow::RemoveThread(std::shared_ptr thread) -{ - int threadIndex = ttCombo->findText(TextThreadString(thread.get()), Qt::MatchStartsWith); - if (threadIndex == ttCombo->currentIndex()) + if (DispatchSentenceToExtensions(sentence, GetMiscInfo(thread))) { - ttCombo->setCurrentIndex(0); - on_ttCombo_activated(0); - } - ttCombo->removeItem(threadIndex); -} - -void MainWindow::ThreadOutput(QString threadString, QString output) -{ - if (ttCombo->currentText().startsWith(threadString)) - { - textOutput->moveCursor(QTextCursor::End); - textOutput->insertPlainText(output); - textOutput->moveCursor(QTextCursor::End); - } -} - -bool MainWindow::ProcessThreadOutput(TextThread* thread, std::wstring& output) -{ - if (DispatchSentenceToExtensions(output, GetMiscInfo(thread))) - { - output += L"\r\n"; - emit SigThreadOutput(TextThreadString(thread), QString::fromStdWString(output)); + sentence += L"\r\n"; + QString ttString = TextThreadString(thread); + InvokeOnMainThread([&, ttString, sentence] + { + if (ttCombo->currentText().startsWith(ttString)) + { + textOutput->moveCursor(QTextCursor::End); + textOutput->insertPlainText(QString::fromStdWString(sentence)); + textOutput->moveCursor(QTextCursor::End); + } + }); return true; } return false; @@ -130,9 +128,9 @@ QString MainWindow::TextThreadString(TextThread* thread) ).toUpper(); } -ThreadParam MainWindow::ParseTextThreadString(QString textThreadString) +ThreadParam MainWindow::ParseTextThreadString(QString ttString) { - QStringList threadParam = textThreadString.split(":"); + QStringList threadParam = ttString.split(":"); return { threadParam[1].toUInt(nullptr, 16), threadParam[2].toULongLong(nullptr, 16), threadParam[3].toULongLong(nullptr, 16), threadParam[4].toULongLong(nullptr, 16) }; } @@ -214,14 +212,10 @@ void MainWindow::on_unhookButton_clicked() void MainWindow::on_saveButton_clicked() { - auto hooks = GetAllHooks(GetSelectedProcessId()); QString hookList = GetFullModuleName(GetSelectedProcessId()); - for (auto hp : hooks) - if (!(hp.type & HOOK_ENGINE)) - hookList += " , " + GenerateCode(hp, GetSelectedProcessId()); - QFile file(HOOK_SAVE_FILE); - file.open(QIODevice::Append); - file.write((hookList + "\r\n").toUtf8()); + for (auto hp : GetAllHooks(GetSelectedProcessId())) + if (!(hp.type & HOOK_ENGINE)) hookList += " , " + GenerateCode(hp, GetSelectedProcessId()); + QAutoFile(HOOK_SAVE_FILE, QIODevice::Append)->write((hookList + "\r\n").toUtf8()); } void MainWindow::on_setButton_clicked() diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index 7d60633..4dc7635 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -13,8 +13,6 @@ namespace Ui class MainWindow; } -Q_DECLARE_METATYPE(std::shared_ptr); - class MainWindow : public QMainWindow { Q_OBJECT @@ -23,19 +21,7 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); -signals: - void SigAddProcess(unsigned processId); - void SigRemoveProcess(unsigned processId); - void SigAddThread(std::shared_ptr); - void SigRemoveThread(std::shared_ptr); - void SigThreadOutput(QString threadString, QString output); - private slots: - void AddProcess(unsigned processId); - void RemoveProcess(unsigned processId); - void AddThread(std::shared_ptr thread); - void RemoveThread(std::shared_ptr thread); - void ThreadOutput(QString threadString, QString output); // this function doesn't take TextThread* because it might be destroyed on pipe thread void on_attachButton_clicked(); void on_detachButton_clicked(); void on_unhookButton_clicked(); @@ -46,9 +32,14 @@ private slots: void on_ttCombo_activated(int index); private: - bool ProcessThreadOutput(TextThread* thread, std::wstring& output); + void InvokeOnMainThread(std::function&& f); + void ProcessConnected(DWORD processId); + void ProcessDisconnected(DWORD processId); + void ThreadAdded(TextThread* thread); + void ThreadRemoved(TextThread* thread); + bool SentenceReceived(TextThread* thread, std::wstring& sentence); QString TextThreadString(TextThread* thread); - ThreadParam ParseTextThreadString(QString textThreadString); + ThreadParam ParseTextThreadString(QString ttString); DWORD GetSelectedProcessId(); std::unordered_map GetMiscInfo(TextThread* thread); QVector GetAllHooks(DWORD processId); diff --git a/GUI/misc.h b/GUI/misc.h index 00270a9..7833f33 100644 --- a/GUI/misc.h +++ b/GUI/misc.h @@ -4,6 +4,15 @@ #include "qtcommon.h" #include "types.h" +class QAutoFile +{ +public: + QAutoFile(QString name, QIODevice::OpenMode mode) : f(name) { f.open(mode); } + QFile* operator->() { return &f; } +private: + QFile f; +}; + QString GetFullModuleName(DWORD processId, HMODULE module = NULL); QString GetModuleName(DWORD processId, HMODULE module = NULL); QMultiHash GetAllProcesses();