diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index 39f3285..fb7a595 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -59,7 +59,7 @@ namespace }; size_t HashThreadParam(ThreadParam tp) { return std::hash()(tp.processId + tp.addr) + std::hash()(tp.ctx + tp.ctx2); } - Synchronized>, std::recursive_mutex> textThreadsByParams; + Synchronized>> textThreadsByParams; Synchronized> processRecordsByIds; Host::ProcessEventHandler OnConnect, OnDisconnect; @@ -68,7 +68,10 @@ namespace void RemoveThreads(std::function removeIf) { std::vector threadsToRemove; - std::for_each(textThreadsByParams->begin(), textThreadsByParams->end(), [&](auto& it) { if (removeIf(it.first)) threadsToRemove.push_back(&it.second); }); + { + auto textThreadsByParams = ::textThreadsByParams.Acquire(); + std::for_each(textThreadsByParams->begin(), textThreadsByParams->end(), [&](auto& it) { if (removeIf(it.first)) threadsToRemove.push_back(&it.second); }); + } for (auto thread : threadsToRemove) { OnDestroy(*thread); @@ -106,10 +109,9 @@ namespace std::wstring wide = info.text; if (wide.size() > STRING) OnHookFound(info.hp, info.text); info.hp.type &= ~USING_UNICODE; - if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) + if (auto converted = Util::StringToWideString((char*)info.text, info.hp.codepage)) 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 (auto converted = Util::StringToWideString((char*)info.text, info.hp.codepage = CP_UTF8)) if (converted->size() > STRING) OnHookFound(info.hp, converted.value()); } break; diff --git a/GUI/host/util.cpp b/GUI/host/util.cpp index 0922dae..a40fc9e 100644 --- a/GUI/host/util.cpp +++ b/GUI/host/util.cpp @@ -1,5 +1,4 @@ #include "util.h" -#include "host.h" #include #include @@ -47,28 +46,6 @@ namespace return hp; } - std::optional ParseSCode(std::wstring SCode) - { - std::wsmatch match; - HookParam hp = {}; - hp.type |= READ_SEARCH; - - // [codepage#] - if (std::regex_search(SCode, match, std::wregex(L"^([0-9]+)#"))) - { - hp.codepage = std::stoi(match[1]); - SCode.erase(0, match[0].length()); - } - else - { - hp.codepage = Host::defaultCodepage; - } - - wcsncpy_s(hp.text, SCode.c_str(), MAX_MODULE_SIZE - 1); - - return hp; - } - std::optional ParseHCode(std::wstring HCode) { std::wsmatch match; @@ -325,7 +302,6 @@ namespace Util { if (code[0] == L'/') code.erase(0, 1); // legacy/AGTH compatibility if (code[0] == L'R') return ParseRCode(code.erase(0, 1)); - else if (code[0] == L'S') return ParseSCode(code.erase(0, 1)); else if (code[0] == L'H') return ParseHCode(code.erase(0, 1)); return {}; } diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index a5f0be4..7b35d8e 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -35,6 +35,9 @@ extern const char* MAX_ADDRESS; extern const char* STRING_OFFSET; extern const char* MAX_HOOK_SEARCH_RECORDS; extern const char* HOOK_SEARCH_FILTER; +extern const char* SEARCH_FOR_TEXT; +extern const char* TEXT; +extern const char* CODEPAGE; extern const char* START_HOOK_SEARCH; extern const char* SAVE_SEARCH_RESULTS; extern const char* TEXT_FILES; @@ -360,7 +363,8 @@ void MainWindow::AddHook() void MainWindow::AddHook(QString hook) { if (QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, hook, &ok, Qt::WindowCloseButtonHint); ok) - if (auto hp = Util::ParseCode(S(hookCode))) try { Host::InsertHook(GetSelectedProcessId(), hp.value()); } catch (std::out_of_range) {} + if (hookCode.startsWith("S") || hookCode.startsWith("/S")) FindHooks(); + else if (auto hp = Util::ParseCode(S(hookCode))) try { Host::InsertHook(GetSelectedProcessId(), hp.value()); } catch (std::out_of_range) {} else Host::AddConsoleOutput(INVALID_CODE); } @@ -418,23 +422,48 @@ void MainWindow::FindHooks() DWORD processId = GetSelectedProcessId(); SearchParam sp = {}; - bool customSettings = false; + sp.codepage = Host::defaultCodepage; + bool searchForText = false, customSettings = false; std::wregex filter(L"."); QDialog dialog(this, Qt::WindowCloseButtonHint); QFormLayout layout(&dialog); QCheckBox cjkCheckbox(&dialog); layout.addRow(SEARCH_CJK, &cjkCheckbox); - QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help, &dialog); + QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help | QDialogButtonBox::Retry, &dialog); layout.addRow(&confirm); confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH); + confirm.button(QDialogButtonBox::Retry)->setText(SEARCH_FOR_TEXT); confirm.button(QDialogButtonBox::Help)->setText(SETTINGS); - connect(&confirm, &QDialogButtonBox::helpRequested, [&customSettings] { customSettings = true; }); - connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - connect(&confirm, &QDialogButtonBox::helpRequested, &dialog, &QDialog::accept); + connect(&confirm, &QDialogButtonBox::clicked, [&](QAbstractButton* button) + { + if (button == confirm.button(QDialogButtonBox::Retry)) searchForText = true; + if (button == confirm.button(QDialogButtonBox::Help)) customSettings = true; + dialog.accept(); + }); dialog.setWindowTitle(SEARCH_FOR_HOOKS); if (!dialog.exec()) return; + if (searchForText) + { + QDialog dialog(this, Qt::WindowCloseButtonHint); + QFormLayout layout(&dialog); + QLineEdit textInput(&dialog); + layout.addRow(TEXT, &textInput); + QSpinBox codepageInput(&dialog); + codepageInput.setMaximum(INT_MAX); + codepageInput.setValue(sp.codepage); + layout.addRow(CODEPAGE, &codepageInput); + QDialogButtonBox confirm(QDialogButtonBox::Ok); + connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + layout.addRow(&confirm); + if (!dialog.exec()) return; + wcsncpy_s(sp.text, S(textInput.text()).c_str(), PATTERN_SIZE - 1); + try { Host::FindHooks(GetSelectedProcessId(), sp); } + catch (std::out_of_range) {} + return; + } + if (customSettings) { QDialog dialog(this, Qt::WindowCloseButtonHint); @@ -446,6 +475,7 @@ void MainWindow::FindHooks() { sp.searchTime, SEARCH_DURATION }, { sp.offset, PATTERN_OFFSET }, { sp.maxRecords, MAX_HOOK_SEARCH_RECORDS }, + { sp.codepage, CODEPAGE }, }) { auto spinBox = new QSpinBox(&dialog); diff --git a/include/const.h b/include/const.h index 59ab8f0..40e015a 100644 --- a/include/const.h +++ b/include/const.h @@ -4,7 +4,7 @@ // 8/23/2013 jichi // Branch: ITH/common.h, rev 128 -enum { STRING = 12, MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 }; +enum { STRING = 12, MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, PATTERN_SIZE = 30, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 }; enum WildcardByte { XX = 0x11 }; enum HostCommandType { HOST_COMMAND_NEW_HOOK, HOST_COMMAND_REMOVE_HOOK, HOST_COMMAND_FIND_HOOK, HOST_COMMAND_MODIFY_HOOK, HOST_COMMAND_HIJACK_PROCESS, HOST_COMMAND_DETACH }; @@ -22,12 +22,11 @@ enum HookParamType : unsigned MODULE_OFFSET = 0x40, // address is relative to module FUNCTION_OFFSET = 0x80, // address is relative to function USING_UTF8 = 0x100, - READ_SEARCH = 0x200, // unspecified address: search for text instead - NO_CONTEXT = 0x400, - HOOK_EMPTY = 0x800, - FIXING_SPLIT = 0x1000, - DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code - FULL_STRING = 0x4000, - HOOK_ENGINE = 0x8000, - HOOK_ADDITIONAL = 0x10000, + NO_CONTEXT = 0x200, + HOOK_EMPTY = 0x400, + FIXING_SPLIT = 0x800, + DIRECT_READ = 0x1000, // /R read code instead of classic /H hook code + FULL_STRING = 0x2000, + HOOK_ENGINE = 0x4000, + HOOK_ADDITIONAL = 0x8000, }; diff --git a/include/types.h b/include/types.h index 05ff97a..aae1ba2 100644 --- a/include/types.h +++ b/include/types.h @@ -31,11 +31,9 @@ struct HookParam split, // offset of the split character split_index, // deref_offset2 null_length; - union - { - wchar_t module[MAX_MODULE_SIZE]; - wchar_t text[MAX_MODULE_SIZE]; - }; + + wchar_t module[MAX_MODULE_SIZE]; + char function[MAX_MODULE_SIZE]; DWORD type; // flags UINT codepage; // text encoding @@ -62,12 +60,14 @@ struct ThreadParam struct SearchParam { - BYTE pattern[25] = { 0xcc, 0xcc, x64 ? 0x48 : 0x55, x64 ? 0x89 : 0x8b, 0xec }; // pattern in memory to search for + 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 searchTime = 20000, // ms - maxRecords = 100000; + maxRecords = 100000, + codepage = SHIFT_JIS; uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1; + wchar_t text[PATTERN_SIZE] = {}; // text to search for void(*hookPostProcessor)(HookParam&) = nullptr; }; diff --git a/text.cpp b/text.cpp index 72d9229..a3ec696 100644 --- a/text.cpp +++ b/text.cpp @@ -63,6 +63,9 @@ const char* MIN_ADDRESS = u8"Minimum address (hex)"; const char* MAX_ADDRESS = u8"Maximum address (hex)"; const char* STRING_OFFSET = u8"String offset (hex)"; const char* HOOK_SEARCH_FILTER = u8"Results must match this regex"; +const char* TEXT = u8"Text"; +const char* CODEPAGE = u8"Codepage"; +const char* SEARCH_FOR_TEXT = u8"Search for specific text"; 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)"; diff --git a/texthook/hookfinder.cc b/texthook/hookfinder.cc index aad6388..93ffc0c 100644 --- a/texthook/hookfinder.cc +++ b/texthook/hookfinder.cc @@ -6,6 +6,8 @@ extern const char* STARTING_SEARCH; extern const char* HOOK_SEARCH_INITIALIZED; extern const char* HOOK_SEARCH_FINISHED; +extern const char* NOT_ENOUGH_TEXT; +extern const char* COULD_NOT_FIND; extern WinMutex viewMutex; @@ -24,6 +26,7 @@ namespace hp.type = USING_UNICODE | USING_STRING; hp.address = address; hp.padding = sp.padding; + hp.codepage = sp.codepage; if (sp.hookPostProcessor) sp.hookPostProcessor(hp); NotifyHookFound(hp, (wchar_t*)text); } @@ -204,3 +207,31 @@ void SearchForHooks(SearchParam spUser) ConsoleOutput(HOOK_SEARCH_FINISHED, sp.maxRecords - recordsAvailable); }).detach(); } + +void SearchForText(wchar_t* text, UINT codepage) +{ + bool found = false; + char utf8Text[PATTERN_SIZE * 4] = {}; + WideCharToMultiByte(CP_UTF8, 0, text, PATTERN_SIZE, utf8Text, PATTERN_SIZE * 4, nullptr, nullptr); + char codepageText[PATTERN_SIZE * 4] = {}; + WideCharToMultiByte(codepage, 0, text, PATTERN_SIZE, codepageText, PATTERN_SIZE * 4, nullptr, nullptr); + if (strlen(utf8Text) < 4 || strlen(codepageText) < 4 || wcslen(text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT); + ConsoleOutput(STARTING_SEARCH); + auto GenerateHooks = [&](std::vector addresses, HookParamType type) + { + for (auto addr : addresses) + { + if (abs((long long)(utf8Text - addr)) < 20000) continue; // don't add read code if text is on this thread's stack + found = true; + HookParam hp = {}; + hp.type = DIRECT_READ | type; + hp.address = addr; + hp.codepage = codepage; + NewHook(hp, "Search", 0); + } + }; + GenerateHooks(Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8); + GenerateHooks(Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING); + GenerateHooks(Util::SearchMemory(text, wcslen(text) * sizeof(wchar_t), PAGE_READWRITE), USING_UNICODE); + if (!found) ConsoleOutput(COULD_NOT_FIND); +} diff --git a/texthook/hookfinder.h b/texthook/hookfinder.h index 202a073..977e5d4 100644 --- a/texthook/hookfinder.h +++ b/texthook/hookfinder.h @@ -3,5 +3,5 @@ #include "common.h" #include "types.h" -void SearchForText(wchar_t* text); +void SearchForText(wchar_t* text, UINT codepage); void SearchForHooks(SearchParam sp); diff --git a/texthook/main.cc b/texthook/main.cc index 6658ff1..d0317bb 100644 --- a/texthook/main.cc +++ b/texthook/main.cc @@ -15,9 +15,6 @@ extern const char* INSERTING_HOOK; extern const char* REMOVING_HOOK; extern const char* HOOK_FAILED; extern const char* TOO_MANY_HOOKS; -extern const char* STARTING_SEARCH; -extern const char* NOT_ENOUGH_TEXT; -extern const char* COULD_NOT_FIND; WinMutex viewMutex; @@ -74,7 +71,8 @@ DWORD WINAPI Pipe(LPVOID) case HOST_COMMAND_FIND_HOOK: { auto info = *(FindHookCmd*)buffer; - SearchForHooks(info.sp); + if (*info.sp.text) SearchForText(info.sp.text, info.sp.codepage); + else SearchForHooks(info.sp); } break; case HOST_COMMAND_DETACH: @@ -156,43 +154,14 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID) void NewHook(HookParam hp, LPCSTR lpname, DWORD flag) { - if (hp.type & READ_SEARCH) + 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); + RemoveHook(hp.address, 0); + if (!(*hooks)[currentHook].Insert(hp, flag)) { - bool found = false; - char utf8Text[MAX_MODULE_SIZE * 4] = {}; - WideCharToMultiByte(CP_UTF8, 0, hp.text, MAX_MODULE_SIZE, utf8Text, MAX_MODULE_SIZE * 4, nullptr, nullptr); - char codepageText[MAX_MODULE_SIZE * 4] = {}; - WideCharToMultiByte(hp.codepage, 0, hp.text, MAX_MODULE_SIZE, codepageText, MAX_MODULE_SIZE * 4, nullptr, nullptr); - if (strlen(utf8Text) < 8 || strlen(codepageText) < 8 || wcslen(hp.text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT); - ConsoleOutput(STARTING_SEARCH); - for (auto [addrs, type] : Array, HookParamType>>{ - { Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 }, - { Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING }, - { Util::SearchMemory(hp.text, wcslen(hp.text) * 2, PAGE_READWRITE), USING_UNICODE } - }) - for (auto addr : addrs) - { - if (abs((long long)(utf8Text - addr)) < 20000) continue; // don't add read code if text is on this thread's stack - found = true; - HookParam h = {}; - h.type = DIRECT_READ | type; - h.address = addr; - h.codepage = hp.codepage; - NewHook(h, lpname, 0); - } - if (!found) ConsoleOutput(COULD_NOT_FIND); - } - else - { - 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); - RemoveHook(hp.address, 0); - if (!(*hooks)[currentHook].Insert(hp, flag)) - { - ConsoleOutput(HOOK_FAILED); - (*hooks)[currentHook].Clear(); - } + ConsoleOutput(HOOK_FAILED); + (*hooks)[currentHook].Clear(); } }