From 3db37ac450a1a5097882a764ac1256047ff5f6dd Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 10 Nov 2019 14:13:54 -0500 Subject: [PATCH] limit hook searches to a named module, also make 0 CCs default for x86 due to perf/stability improvements --- GUI/mainwindow.cpp | 8 ++++++-- include/types.h | 12 +++++++----- text.cpp | 1 + texthook/hookfinder.cc | 15 ++++----------- texthook/util/util.cc | 14 ++++++++++++++ texthook/util/util.h | 1 + 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 203fffc..ece888f 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -31,6 +31,7 @@ extern const char* HOOK_SEARCH_UNSTABLE_WARNING; extern const char* SEARCH_CJK; extern const char* SEARCH_PATTERN; extern const char* SEARCH_DURATION; +extern const char* SEARCH_MODULE; extern const char* PATTERN_OFFSET; extern const char* MIN_ADDRESS; extern const char* MAX_ADDRESS; @@ -471,7 +472,7 @@ void MainWindow::FindHooks() { QDialog dialog(this, Qt::WindowCloseButtonHint); QFormLayout layout(&dialog); - QLineEdit patternInput(x64 ? "CC CC 48 89" : "CC CC 55 8B EC", &dialog); + QLineEdit patternInput(x64 ? "CC CC 48 89" : "55 8B EC", &dialog); assert(QByteArray::fromHex(patternInput.text().toUtf8()) == QByteArray((const char*)sp.pattern, sp.length)); layout.addRow(SEARCH_PATTERN, &patternInput); for (auto [value, label] : Array>{ @@ -487,6 +488,8 @@ void MainWindow::FindHooks() layout.addRow(label, spinBox); connect(spinBox, qOverload(&QSpinBox::valueChanged), [&value](int newValue) { value = newValue; }); } + QLineEdit boundInput(QFileInfo(S(Util::GetModuleFilename(GetSelectedProcessId()).value_or(L""))).fileName(), &dialog); + layout.addRow(SEARCH_MODULE, &boundInput); for (auto [value, label] : Array>{ { sp.minAddress, MIN_ADDRESS }, { sp.maxAddress, MAX_ADDRESS }, @@ -505,7 +508,7 @@ void MainWindow::FindHooks() if (!dialog.exec()) return; if (patternInput.text().contains('.')) { - wcsncpy_s(sp.module, S(patternInput.text()).c_str(), MAX_MODULE_SIZE - 1); + wcsncpy_s(sp.exportModule, S(patternInput.text()).c_str(), MAX_MODULE_SIZE - 1); sp.length = 1; } else @@ -513,6 +516,7 @@ void MainWindow::FindHooks() QByteArray pattern = QByteArray::fromHex(patternInput.text().replace("??", QString::number(XX, 16)).toUtf8()); memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), PATTERN_SIZE)); } + wcsncpy_s(sp.boundaryModule, S(boundInput.text()).c_str(), MAX_MODULE_SIZE - 1); try { filter = S(filterInput.text()); } catch (std::regex_error) {} } else diff --git a/include/types.h b/include/types.h index b96ae22..f983a0c 100644 --- a/include/types.h +++ b/include/types.h @@ -60,14 +60,16 @@ struct ThreadParam struct SearchParam { - BYTE pattern[PATTERN_SIZE] = { 0xcc, 0xcc, x64 ? 0x48 : 0x55, x64 ? 0x89 : 0x8b, 0xec }; // pattern in memory to search for - int length = x64 ? 4 : 5, // length of pattern (zero means this SearchParam is invalid and the default should be used) - offset = 2, // offset from start of pattern to add hook + BYTE pattern[PATTERN_SIZE] = { x64 ? 0xcc : 0x55, x64 ? 0xcc : 0x8b, x64 ? 0x48 : 0xec, 0x89 }; // pattern in memory to search for + int length = x64 ? 4 : 3, // length of pattern (zero means this SearchParam is invalid and the default should be used) + offset = x64 ? 2 : 0, // offset from start of pattern to add hook searchTime = 20000, // ms maxRecords = 100000, codepage = SHIFT_JIS; - uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1; - wchar_t module[MAX_MODULE_SIZE] = {}; + uintptr_t padding = 0, // same as hook param padding + minAddress = 0, maxAddress = (uintptr_t)-1; // hook all functions between these addresses (used only if both modules empty) + wchar_t boundaryModule[MAX_MODULE_SIZE] = {}; // hook all functions within this module (middle priority) + wchar_t exportModule[MAX_MODULE_SIZE] = {}; // hook the exports of this module (highest priority) wchar_t text[PATTERN_SIZE] = {}; // text to search for void(*hookPostProcessor)(HookParam&) = nullptr; }; diff --git a/text.cpp b/text.cpp index d5d6c01..c10c04f 100644 --- a/text.cpp +++ b/text.cpp @@ -53,6 +53,7 @@ const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! B const char* SEARCH_CJK = u8"Search for Chinese/Japanese/Korean"; const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)"; const char* SEARCH_DURATION = u8"Search duration (ms)"; +const char* SEARCH_MODULE = u8"Search within module"; const char* PATTERN_OFFSET = u8"Offset from pattern start"; const char* MAX_HOOK_SEARCH_RECORDS = u8"Search result cap"; const char* MIN_ADDRESS = u8"Minimum address (hex)"; diff --git a/texthook/hookfinder.cc b/texthook/hookfinder.cc index 1e69619..f45470b 100644 --- a/texthook/hookfinder.cc +++ b/texthook/hookfinder.cc @@ -200,19 +200,12 @@ void SearchForHooks(SearchParam spUser) ConsoleOutput(STARTING_SEARCH); std::vector addresses; - if (*sp.module) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.module)); + if (*sp.boundaryModule) std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); + if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule)); else for (auto& addr : addresses = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress)) addr += sp.offset; - uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL); - uintptr_t moduleStopAddress = moduleStartAddress; - MEMORY_BASIC_INFORMATION info; - do - { - VirtualQuery((void*)moduleStopAddress, &info, sizeof(info)); - moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize; - } while (info.Protect >= PAGE_EXECUTE); - moduleStopAddress -= info.RegionSize; - addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > moduleStartAddress && addr < moduleStopAddress; }), addresses.end()); + auto limits = Util::QueryModuleLimits(GetModuleHandleW(ITH_DLL)); + addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > limits.first && addr < limits.second; }), addresses.end()); auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE); VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, DUMMY); diff --git a/texthook/util/util.cc b/texthook/util/util.cc index 7f344af..8ca8239 100644 --- a/texthook/util/util.cc +++ b/texthook/util/util.cc @@ -301,6 +301,20 @@ bool SearchResourceString(LPCWSTR str) return false; } +std::pair QueryModuleLimits(HMODULE module) +{ + uintptr_t moduleStartAddress = (uintptr_t)module + 0x1000; + uintptr_t moduleStopAddress = moduleStartAddress; + MEMORY_BASIC_INFORMATION info; + do + { + VirtualQuery((void*)moduleStopAddress, &info, sizeof(info)); + moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize; + } while (info.Protect >= PAGE_EXECUTE); + moduleStopAddress -= info.RegionSize; + return { moduleStartAddress, moduleStopAddress }; +} + std::vector SearchMemory(const void* bytes, short length, DWORD protect, uintptr_t minAddr, uintptr_t maxAddr) { SYSTEM_INFO systemInfo; diff --git a/texthook/util/util.h b/texthook/util/util.h index e37eb69..91d7920 100644 --- a/texthook/util/util.h +++ b/texthook/util/util.h @@ -22,6 +22,7 @@ bool CheckFile(LPCWSTR name); bool SearchResourceString(LPCWSTR str); +std::pair QueryModuleLimits(HMODULE module); std::vector SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE, uintptr_t minAddr = 0, uintptr_t maxAddr = -1ULL); } // namespace Util