diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index 275219a..303bb45 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -16,7 +16,6 @@ namespace { public: ProcessRecord(DWORD processId, HANDLE pipe) : - processId(processId), pipe(pipe), mappedFile(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), view(*(const TextHook(*)[MAX_HOOK])MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size @@ -47,23 +46,19 @@ namespace }).detach(); } - Host::HookEventHandler OnHookFound = [](HookParam hp, DWORD processId, const std::wstring& text) + Host::HookEventHandler OnHookFound = [](HookParam hp, const std::wstring& text) { Host::AddConsoleOutput(Util::GenerateCode(hp, 0) + L": " + text); }; private: - DWORD processId; HANDLE pipe; AutoHandle<> mappedFile; const TextHook(&view)[MAX_HOOK]; WinMutex viewMutex; }; - size_t HashThreadParam(ThreadParam tp) - { - return std::hash()(tp.processId + tp.addr) + std::hash()(tp.ctx + tp.ctx2); - } + size_t HashThreadParam(ThreadParam tp) { return std::hash()(tp.processId + tp.addr) + std::hash()(tp.ctx + tp.ctx2); } Synchronized>, std::recursive_mutex> textThreadsByParams; Synchronized, std::recursive_mutex> processRecordsByIds; @@ -109,13 +104,13 @@ namespace auto info = *(HookFoundNotif*)buffer; auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound; std::wstring wide = info.text; - if (wide.size() > STRING) OnHookFound(info.hp, processId, info.text); + if (wide.size() > STRING) OnHookFound(info.hp, info.text); info.hp.type = USING_STRING; if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) - if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); + if (converted->size() > STRING) OnHookFound(info.hp, converted.value()); info.hp.codepage = CP_UTF8; if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8)) - if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); + if (converted->size() > STRING) OnHookFound(info.hp, converted.value()); } break; case HOST_NOTIFICATION_RMVHOOK: @@ -134,13 +129,14 @@ namespace { auto tp = *(ThreadParam*)buffer; auto textThreadsByParams = ::textThreadsByParams.Acquire(); - if (textThreadsByParams->count(tp) == 0) try + auto textThread = textThreadsByParams->find(tp); + if (textThread == textThreadsByParams->end()) { - TextThread& created = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first->second; - OnCreate(created); + try { textThread = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first; } + catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again + OnCreate(textThread->second); } - catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again - textThreadsByParams->find(tp)->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); + textThread->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp)); } break; } @@ -226,6 +222,11 @@ namespace Host processRecordsByIds->at(processId).Send(InsertHookCmd(hp)); } + void RemoveHook(DWORD processId, uint64_t address) + { + processRecordsByIds->at(processId).Send(RemoveHookCmd(address)); + } + void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound) { if (HookFound) processRecordsByIds->at(processId).OnHookFound = HookFound; diff --git a/GUI/host/host.h b/GUI/host/host.h index 6f39405..9a203f5 100644 --- a/GUI/host/host.h +++ b/GUI/host/host.h @@ -7,17 +7,18 @@ namespace Host { using ProcessEventHandler = std::function; using ThreadEventHandler = std::function; - using HookEventHandler = std::function; + using HookEventHandler = std::function; void Start(ProcessEventHandler Connect, ProcessEventHandler Disconnect, ThreadEventHandler Create, ThreadEventHandler Destroy, TextThread::OutputCallback Output); void InjectProcess(DWORD processId); void DetachProcess(DWORD processId); void InsertHook(DWORD processId, HookParam hp); + void RemoveHook(DWORD processId, uint64_t address); void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {}); HookParam GetHookParam(ThreadParam tp); - TextThread& GetThread(ThreadParam tp); + void AddConsoleOutput(std::wstring text); inline int defaultCodepage = SHIFT_JIS; diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index d1c372b..5e124fe 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ extern const char* ATTACH; extern const char* LAUNCH; extern const char* DETACH; extern const char* ADD_HOOK; +extern const char* REMOVE_HOOKS; extern const char* SAVE_HOOKS; extern const char* FIND_HOOKS; extern const char* SETTINGS; @@ -38,6 +40,7 @@ extern const char* HOOK_SEARCH_FILTER; extern const char* START_HOOK_SEARCH; extern const char* SAVE_SEARCH_RESULTS; extern const char* TEXT_FILES; +extern const char* DOUBLE_CLICK_TO_REMOVE_HOOK; extern const char* SAVE_SETTINGS; extern const char* USE_JP_LOCALE; extern const char* FILTER_REPETITION; @@ -61,6 +64,7 @@ MainWindow::MainWindow(QWidget *parent) : { LAUNCH, &MainWindow::LaunchProcess }, { DETACH, &MainWindow::DetachProcess }, { ADD_HOOK, &MainWindow::AddHook }, + { REMOVE_HOOKS, &MainWindow::RemoveHooks }, { SAVE_HOOKS, &MainWindow::SaveHooks }, { FIND_HOOKS, &MainWindow::FindHooks }, { SETTINGS, &MainWindow::Settings }, @@ -267,6 +271,7 @@ void MainWindow::LaunchProcess() PROCESS_INFORMATION info = {}; if (QMessageBox::question(this, SELECT_PROCESS, USE_JP_LOCALE) == QMessageBox::Yes) + { if (HMODULE localeEmulator = LoadLibraryOnce(L"LoaderDll")) { // see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252 @@ -285,6 +290,7 @@ void MainWindow::LaunchProcess() ((LONG(__stdcall*)(decltype(&LEB), LPCWSTR appName, LPWSTR commandLine, LPCWSTR currentDir, void*, void*, PROCESS_INFORMATION*, void*, void*, void*, void*)) GetProcAddress(localeEmulator, "LeCreateProcess"))(&LEB, process.c_str(), NULL, path.c_str(), NULL, NULL, &info, NULL, NULL, NULL, NULL); } + } if (info.hProcess == NULL) { STARTUPINFOW DUMMY = { sizeof(DUMMY) }; @@ -308,6 +314,33 @@ void MainWindow::AddHook() else Host::AddConsoleOutput(INVALID_CODE); } +void MainWindow::RemoveHooks() +{ + DWORD processId = GetSelectedProcessId(); + std::unordered_map hooks; + for (int i = 0; i < ui->ttCombo->count(); ++i) + { + ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i)); + if (tp.processId == GetSelectedProcessId()) hooks[tp.addr] = Host::GetHookParam(tp); + } + auto hookList = new QListWidget(this); + hookList->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint); + hookList->setMinimumSize({ 300, 50 }); + hookList->setWindowTitle(DOUBLE_CLICK_TO_REMOVE_HOOK); + for (auto[address, hp] : hooks) + new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList); + connect(hookList, &QListWidget::itemDoubleClicked, [processId, hookList](QListWidgetItem* item) + { + try + { + Host::RemoveHook(processId, item->text().split("@")[1].toULongLong(nullptr, 16)); + delete item; + } + catch (std::out_of_range) { hookList->close(); } + }); + hookList->show(); +} + void MainWindow::SaveHooks() { if (auto processName = Util::GetModuleFilename(GetSelectedProcessId())) @@ -378,7 +411,8 @@ void MainWindow::FindHooks() if (!filterInput->text().isEmpty()) try { filter = std::wregex(S(filterInput->text())); } catch (std::regex_error) {}; memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25)); auto hooks = std::make_shared(); - Host::FindHooks(processId, sp, [hooks, filter](HookParam hp, DWORD processId, const std::wstring& text) + DWORD processId = this->processId; + Host::FindHooks(processId, sp, [processId, hooks, filter](HookParam hp, const std::wstring& text) { if (std::regex_search(text, filter)) hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n"); }); diff --git a/GUI/mainwindow.h b/GUI/mainwindow.h index c271d61..bf9cfb3 100644 --- a/GUI/mainwindow.h +++ b/GUI/mainwindow.h @@ -36,6 +36,7 @@ private: void LaunchProcess(); void DetachProcess(); void AddHook(); + void RemoveHooks(); void SaveHooks(); void FindHooks(); void Settings(); diff --git a/include/common.h b/include/common.h index a183d40..fdb55d7 100644 --- a/include/common.h +++ b/include/common.h @@ -31,7 +31,7 @@ constexpr bool x64 = false; template using Array = T[]; -template +template class Synchronized { public: @@ -40,17 +40,17 @@ public: struct Locker { - E* operator->() { return ptr; } + T* operator->() { return &contents; } std::unique_lock lock; - E* ptr; + T& contents; }; - Locker Acquire() { return { std::unique_lock(mtx), &contents }; } + Locker Acquire() { return { std::unique_lock(m), contents }; } Locker operator->() { return Acquire(); } private: - E contents; - M mtx; + T contents; + M m; }; template diff --git a/include/const.h b/include/const.h index 62bd90d..16fc358 100644 --- a/include/const.h +++ b/include/const.h @@ -27,5 +27,5 @@ enum HookParamType : unsigned FIXING_SPLIT = 0x1000, DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code HOOK_ENGINE = 0x4000, - HOOK_ADDITIONAL = 0x8000 + HOOK_ADDITIONAL = 0x8000, }; diff --git a/include/types.h b/include/types.h index 73c369b..e900e0d 100644 --- a/include/types.h +++ b/include/types.h @@ -75,6 +75,13 @@ struct InsertHookCmd // From host HookParam hp; }; +struct RemoveHookCmd // From host +{ + RemoveHookCmd(uint64_t address) : address(address) {} + HostCommandType command = HOST_COMMAND_REMOVE_HOOK; + uint64_t address; +}; + struct FindHookCmd // From host { FindHookCmd(SearchParam sp) : sp(sp) {} diff --git a/text.cpp b/text.cpp index 383a43b..122119b 100644 --- a/text.cpp +++ b/text.cpp @@ -10,6 +10,7 @@ const char* ATTACH = u8"Attach to game"; const char* LAUNCH = u8"Launch game"; const char* DETACH = u8"Detach from game"; const char* ADD_HOOK = u8"Add hook"; +const char* REMOVE_HOOKS = u8"Remove hook(s)"; const char* SAVE_HOOKS = u8"Save hook(s)"; const char* FIND_HOOKS = u8"Find hooks"; const char* SETTINGS = u8"Settings"; @@ -60,6 +61,7 @@ const char* HOOK_SEARCH_FILTER = u8"Results must match this regex"; const char* START_HOOK_SEARCH = u8"Start hook search"; const char* SAVE_SEARCH_RESULTS = u8"Save search results"; const char* TEXT_FILES = u8"Text (*.txt)"; +const char* DOUBLE_CLICK_TO_REMOVE_HOOK = u8"Double click a hook to remove it"; const char* FILTER_REPETITION = u8"Repetition Filter"; const char* DEFAULT_CODEPAGE = u8"Default Codepage"; const char* FLUSH_DELAY = u8"Flush Delay"; diff --git a/texthook/main.cc b/texthook/main.cc index c5aade7..e3bc567 100644 --- a/texthook/main.cc +++ b/texthook/main.cc @@ -61,7 +61,14 @@ DWORD WINAPI Pipe(LPVOID) case HOST_COMMAND_NEW_HOOK: { auto info = *(InsertHookCmd*)buffer; - NewHook(info.hp, "UserHook", 0); + static int userHooks = 0; + NewHook(info.hp, ("UserHook" + std::to_string(userHooks += 1)).c_str(), 0); + } + break; + case HOST_COMMAND_REMOVE_HOOK: + { + auto info = *(RemoveHookCmd*)buffer; + RemoveHook(info.address, 0); } break; case HOST_COMMAND_FIND_HOOK: @@ -180,8 +187,12 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag) if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS); if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1); ConsoleOutput(INSERTING_HOOK, hp.name); - if (hp.address) RemoveHook(hp.address, 0); - if (!(*hooks)[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED); + RemoveHook(hp.address, 0); + if (!(*hooks)[currentHook].Insert(hp, flag)) + { + ConsoleOutput(HOOK_FAILED); + (*hooks)[currentHook].Clear(); + } } } diff --git a/texthook/texthook.cc b/texthook/texthook.cc index ca13886..c261062 100644 --- a/texthook/texthook.cc +++ b/texthook/texthook.cc @@ -286,6 +286,7 @@ void TextHook::RemoveReadCode() void TextHook::Clear() { std::scoped_lock lock(viewMutex); + if (address == 0) return; if (hp.type & DIRECT_READ) RemoveReadCode(); else RemoveHookCode(); NotifyHookRemove(address, hp.name);