improve hook search ux

This commit is contained in:
Akash Mozumdar 2021-03-08 08:37:02 -07:00
parent bf97055155
commit 54a285b53b
3 changed files with 36 additions and 27 deletions

View File

@ -35,6 +35,7 @@ extern const char* PROCESSES;
extern const char* CODE_INFODUMP; extern const char* CODE_INFODUMP;
extern const char* FAILED_TO_CREATE_CONFIG_FILE; extern const char* FAILED_TO_CREATE_CONFIG_FILE;
extern const char* HOOK_SEARCH_UNSTABLE_WARNING; extern const char* HOOK_SEARCH_UNSTABLE_WARNING;
extern const char* HOOK_SEARCH_STARTING_VIEW_CONSOLE;
extern const char* SEARCH_CJK; extern const char* SEARCH_CJK;
extern const char* SEARCH_PATTERN; extern const char* SEARCH_PATTERN;
extern const char* SEARCH_DURATION; extern const char* SEARCH_DURATION;
@ -99,7 +100,7 @@ namespace
ThreadParam ParseTextThreadString(QString ttString) ThreadParam ParseTextThreadString(QString ttString)
{ {
QStringList threadParam = ttString.split(":"); auto threadParam = ttString.splitRef(":");
return { threadParam[1].toUInt(nullptr, 16), threadParam[2].toULongLong(nullptr, 16), threadParam[3].toULongLong(nullptr, 16), threadParam[4].toULongLong(nullptr, 16) }; return { threadParam[1].toUInt(nullptr, 16), threadParam[2].toULongLong(nullptr, 16), threadParam[3].toULongLong(nullptr, 16), threadParam[4].toULongLong(nullptr, 16) };
} }
@ -143,6 +144,13 @@ namespace
return {}; return {};
} }
void ViewThread(int index)
{
ui.ttCombo->setCurrentIndex(index);
ui.textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui.ttCombo->itemText(index))))->storage->c_str())));
ui.textOutput->moveCursor(QTextCursor::End);
}
void AttachProcess() void AttachProcess()
{ {
QMultiHash<QString, DWORD> allProcesses; QMultiHash<QString, DWORD> allProcesses;
@ -170,7 +178,7 @@ namespace
{ {
if (HMODULE localeEmulator = LoadLibraryW(L"LoaderDll")) if (HMODULE localeEmulator = LoadLibraryW(L"LoaderDll"))
{ {
// see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252 // https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252
struct struct
{ {
ULONG AnsiCodePage = SHIFT_JIS; ULONG AnsiCodePage = SHIFT_JIS;
@ -229,13 +237,10 @@ namespace
} }
} }
void FindHooks();
void AddHook(QString hook) void AddHook(QString hook)
{ {
if (QString hookCode = QInputDialog::getText(This, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, hook, &ok, Qt::WindowCloseButtonHint); ok) if (QString hookCode = QInputDialog::getText(This, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, hook, &ok, Qt::WindowCloseButtonHint); ok)
if (hookCode.startsWith("S") || hookCode.startsWith("/S")) FindHooks(); if (auto hp = HookCode::Parse(S(hookCode))) try { Host::InsertHook(selectedProcessId, hp.value()); } catch (std::out_of_range) {}
else if (auto hp = HookCode::Parse(S(hookCode))) try { Host::InsertHook(selectedProcessId, hp.value()); } catch (std::out_of_range) {}
else Host::AddConsoleOutput(INVALID_CODE); else Host::AddConsoleOutput(INVALID_CODE);
} }
@ -334,7 +339,11 @@ namespace
layout.addRow(&confirm); layout.addRow(&confirm);
if (!dialog.exec()) return; if (!dialog.exec()) return;
wcsncpy_s(sp.text, S(textEdit.text()).c_str(), PATTERN_SIZE - 1); wcsncpy_s(sp.text, S(textEdit.text()).c_str(), PATTERN_SIZE - 1);
try { Host::FindHooks(selectedProcessId, sp); } catch (std::out_of_range) {} try
{
Host::FindHooks(selectedProcessId, sp);
ViewThread(0);
} catch (std::out_of_range) {}
return; return;
} }
@ -404,6 +413,7 @@ namespace
[hooks, filter](HookParam hp, std::wstring text) { if (filter.match(S(text)).hasMatch()) *hooks << sanitize(S(HookCode::Generate(hp) + L" => " + text)); }); [hooks, filter](HookParam hp, std::wstring text) { if (filter.match(S(text)).hasMatch()) *hooks << sanitize(S(HookCode::Generate(hp) + L" => " + text)); });
} }
catch (std::out_of_range) { return; } catch (std::out_of_range) { return; }
ViewThread(0);
std::thread([hooks] std::thread([hooks]
{ {
for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size(); for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size();
@ -434,6 +444,7 @@ namespace
} }
hooks->clear(); hooks->clear();
}).detach(); }).detach();
QMessageBox::information(This, SEARCH_FOR_HOOKS, HOOK_SEARCH_STARTING_VIEW_CONSOLE);
} }
void OpenSettings() void OpenSettings()
@ -485,13 +496,6 @@ namespace
extenWindow->showNormal(); extenWindow->showNormal();
} }
void ViewThread(int index)
{
ui.ttCombo->setCurrentIndex(index);
ui.textOutput->setPlainText(sanitize(S((current = &Host::GetThread(ParseTextThreadString(ui.ttCombo->itemText(index))))->storage->c_str())));
ui.textOutput->moveCursor(QTextCursor::End);
}
void SetOutputFont(QString fontString) void SetOutputFont(QString fontString)
{ {
QFont font = ui.textOutput->font(); QFont font = ui.textOutput->font();

View File

@ -69,6 +69,7 @@ const char* EXTENSION_WRITE_ERROR = u8"Failed to save extension";
const char* USE_JP_LOCALE = u8"Emulate japanese locale?"; const char* USE_JP_LOCALE = u8"Emulate japanese locale?";
const char* FAILED_TO_CREATE_CONFIG_FILE = u8"Failed to create config file \"%1\""; const char* FAILED_TO_CREATE_CONFIG_FILE = u8"Failed to create config file \"%1\"";
const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!"; const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!";
const char* HOOK_SEARCH_STARTING_VIEW_CONSOLE = u8"Initializing hook search - please check console for further instructions";
const char* SEARCH_CJK = u8"Search for Chinese/Japanese/Korean"; const char* SEARCH_CJK = u8"Search for Chinese/Japanese/Korean";
const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)"; const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)";
const char* SEARCH_DURATION = u8"Search duration (ms)"; const char* SEARCH_DURATION = u8"Search duration (ms)";
@ -120,9 +121,10 @@ const char* INSERTING_HOOK = u8"Textractor: inserting hook: %s";
const char* REMOVING_HOOK = u8"Textractor: removing hook: %s"; const char* REMOVING_HOOK = u8"Textractor: removing hook: %s";
const char* HOOK_FAILED = u8"Textractor: failed to insert hook"; const char* HOOK_FAILED = u8"Textractor: failed to insert hook";
const char* TOO_MANY_HOOKS = u8"Textractor: too many hooks: can't insert"; const char* TOO_MANY_HOOKS = u8"Textractor: too many hooks: can't insert";
const char* STARTING_SEARCH = u8"Textractor: starting search"; const char* HOOK_SEARCH_STARTING = u8"Textractor: starting hook search";
const char* HOOK_SEARCH_INITIALIZING = u8"Textractor: initializing hook search (%f%%)";
const char* NOT_ENOUGH_TEXT = u8"Textractor: not enough text to search accurately"; const char* NOT_ENOUGH_TEXT = u8"Textractor: not enough text to search accurately";
const char* HOOK_SEARCH_INITIALIZED = u8"Textractor: search initialized with %zd hooks"; const char* HOOK_SEARCH_INITIALIZED = u8"Textractor: initialized hook search with %zd hooks";
const char* MAKE_GAME_PROCESS_TEXT = u8"Textractor: please click around in the game to force it to process text during the next %d seconds"; const char* MAKE_GAME_PROCESS_TEXT = u8"Textractor: please click around in the game to force it to process text during the next %d seconds";
const char* HOOK_SEARCH_FINISHED = u8"Textractor: hook search finished, %d results found"; const char* HOOK_SEARCH_FINISHED = u8"Textractor: hook search finished, %d results found";
const char* OUT_OF_RECORDS_RETRY = u8"Textractor: out of search records, please retry if results are poor (default record count increased)"; const char* OUT_OF_RECORDS_RETRY = u8"Textractor: out of search records, please retry if results are poor (default record count increased)";
@ -145,7 +147,7 @@ const wchar_t* TOO_MANY_TRANS_REQUESTS = L"Rate limit exceeded: refuse to make m
const wchar_t* TRANSLATION_ERROR = L"Error while translating"; const wchar_t* TRANSLATION_ERROR = L"Error while translating";
const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context"; const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context";
const char* API_KEY = u8"API key"; const char* API_KEY = u8"API key";
const char* CHROME_LOCATION = "Google Chrome location"; const char* CHROME_LOCATION = "Google Chrome file location";
const char* START_DEVTOOLS = u8"Start DevTools"; const char* START_DEVTOOLS = u8"Start DevTools";
const char* STOP_DEVTOOLS = u8"Stop DevTools"; const char* STOP_DEVTOOLS = u8"Stop DevTools";
const char* HEADLESS_MODE = u8"Headless mode"; const char* HEADLESS_MODE = u8"Headless mode";
@ -397,7 +399,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior
REMOVING_HOOK = u8"Textractor: 移除钩子: %s"; REMOVING_HOOK = u8"Textractor: 移除钩子: %s";
HOOK_FAILED = u8"Textractor: 钩子注入失败"; HOOK_FAILED = u8"Textractor: 钩子注入失败";
TOO_MANY_HOOKS = u8"Textractor: 钩子数量已达上限: 无法注入"; TOO_MANY_HOOKS = u8"Textractor: 钩子数量已达上限: 无法注入";
STARTING_SEARCH = u8"Textractor: 开始搜索钩子"; HOOK_SEARCH_STARTING = u8"Textractor: 开始搜索钩子";
NOT_ENOUGH_TEXT = u8"Textractor: 没有足够的文本用来精确搜索"; NOT_ENOUGH_TEXT = u8"Textractor: 没有足够的文本用来精确搜索";
HOOK_SEARCH_INITIALIZED = u8"Textractor: 搜索初始化完成, 创建了 %zd 个钩子"; HOOK_SEARCH_INITIALIZED = u8"Textractor: 搜索初始化完成, 创建了 %zd 个钩子";
MAKE_GAME_PROCESS_TEXT = u8"Textractor: 请点击游戏区域, 在接下来的 %d 秒内使游戏强制处理文本"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: 请点击游戏区域, 在接下来的 %d 秒内使游戏强制处理文本";
@ -573,7 +575,7 @@ padding: длина добавочных данных перед строкой
REMOVING_HOOK = u8"Textractor: удаление хука: %s"; REMOVING_HOOK = u8"Textractor: удаление хука: %s";
HOOK_FAILED = u8"Textractor: не удалось вставить хук"; HOOK_FAILED = u8"Textractor: не удалось вставить хук";
TOO_MANY_HOOKS = u8"Textractor: слишком много хуков: невозможно вставить"; TOO_MANY_HOOKS = u8"Textractor: слишком много хуков: невозможно вставить";
STARTING_SEARCH = u8"Textractor: начало поиска"; HOOK_SEARCH_STARTING = u8"Textractor: начало поиска";
NOT_ENOUGH_TEXT = u8"Textractor: не достаточно текста для точного поиска"; NOT_ENOUGH_TEXT = u8"Textractor: не достаточно текста для точного поиска";
HOOK_SEARCH_INITIALIZED = u8"Textractor: поиск инициализирован с %zd хуками"; HOOK_SEARCH_INITIALIZED = u8"Textractor: поиск инициализирован с %zd хуками";
MAKE_GAME_PROCESS_TEXT = u8"Textractor: покликайте в игре, чтобы вызвать смену текста в течение %d секунд"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: покликайте в игре, чтобы вызвать смену текста в течение %d секунд";
@ -831,7 +833,7 @@ esempio: Textractor -p4466 -p"My Game.exe" sta tentando di inniettare i processi
REMOVING_HOOK = u8"Textractor: rimuovi gancio: %s"; REMOVING_HOOK = u8"Textractor: rimuovi gancio: %s";
HOOK_FAILED = u8"Textractor: inserimento gancio non riuscito"; HOOK_FAILED = u8"Textractor: inserimento gancio non riuscito";
TOO_MANY_HOOKS = u8"Textractor: troppi ganci: impossibile inserirli"; TOO_MANY_HOOKS = u8"Textractor: troppi ganci: impossibile inserirli";
STARTING_SEARCH = u8"Textractor: avvia la ricerca"; HOOK_SEARCH_STARTING = u8"Textractor: avvia la ricerca";
NOT_ENOUGH_TEXT = u8"Textractor: testo insufficente per la ricerca accurata"; NOT_ENOUGH_TEXT = u8"Textractor: testo insufficente per la ricerca accurata";
HOOK_SEARCH_INITIALIZED = u8"Textractor: ricerca inizializzata con %zd ganci"; HOOK_SEARCH_INITIALIZED = u8"Textractor: ricerca inizializzata con %zd ganci";
MAKE_GAME_PROCESS_TEXT = u8"Textractor: clicca intorno al gioco per forzarlo nel testo del processo durante i prossimi %d secondi"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: clicca intorno al gioco per forzarlo nel testo del processo durante i prossimi %d secondi";
@ -977,7 +979,7 @@ Se você gostou desse projeto, divulgue a todos :))";
REMOVING_HOOK = u8"Textractor: removendo hook: %s"; REMOVING_HOOK = u8"Textractor: removendo hook: %s";
HOOK_FAILED = u8"Textractor: falha na inserção do hook"; HOOK_FAILED = u8"Textractor: falha na inserção do hook";
TOO_MANY_HOOKS = u8"Textractor: há hooks de mais: não é possível inserir mais"; TOO_MANY_HOOKS = u8"Textractor: há hooks de mais: não é possível inserir mais";
STARTING_SEARCH = u8"Textractor: iniciando busca "; HOOK_SEARCH_STARTING = u8"Textractor: iniciando busca ";
NOT_ENOUGH_TEXT = u8"Textractor: não há texto suficiente para uma buscar precisa"; NOT_ENOUGH_TEXT = u8"Textractor: não há texto suficiente para uma buscar precisa";
HOOK_SEARCH_INITIALIZED = u8"Textractor: busca inicializada com %zd hooks"; HOOK_SEARCH_INITIALIZED = u8"Textractor: busca inicializada com %zd hooks";
HOOK_SEARCH_FINISHED = u8"Textractor: busca por hooks finalizada, %d resultados encontrados"; HOOK_SEARCH_FINISHED = u8"Textractor: busca por hooks finalizada, %d resultados encontrados";
@ -1251,7 +1253,7 @@ example: Textractor -p4466 -p"My Game.exe" tries to inject processes with id 446
REMOVING_HOOK = u8"Textractor: enlève le hook: %s"; REMOVING_HOOK = u8"Textractor: enlève le hook: %s";
HOOK_FAILED = u8"Textractor: n'a pas réussi à insérer un hook"; HOOK_FAILED = u8"Textractor: n'a pas réussi à insérer un hook";
TOO_MANY_HOOKS = u8"Textractor: trop de hooks: impossible d'insérer"; TOO_MANY_HOOKS = u8"Textractor: trop de hooks: impossible d'insérer";
STARTING_SEARCH = u8"Textractor: démarrage de la recherche"; HOOK_SEARCH_STARTING = u8"Textractor: démarrage de la recherche";
NOT_ENOUGH_TEXT = u8"Textractor: pas assez de texte pour effectuer une recherche précise"; NOT_ENOUGH_TEXT = u8"Textractor: pas assez de texte pour effectuer une recherche précise";
HOOK_SEARCH_INITIALIZED = u8"Textractor: la recherche a été initialisé avec %zd hooks"; HOOK_SEARCH_INITIALIZED = u8"Textractor: la recherche a été initialisé avec %zd hooks";
MAKE_GAME_PROCESS_TEXT = u8"Textractor: veuillez cliquer dans le jeu pour le forcer à traiter le texte lors de la prochaine %d seconds"; MAKE_GAME_PROCESS_TEXT = u8"Textractor: veuillez cliquer dans le jeu pour le forcer à traiter le texte lors de la prochaine %d seconds";

View File

@ -3,7 +3,8 @@
#include "main.h" #include "main.h"
#include "util.h" #include "util.h"
extern const char* STARTING_SEARCH; extern const char* HOOK_SEARCH_STARTING;
extern const char* HOOK_SEARCH_INITIALIZING;
extern const char* HOOK_SEARCH_INITIALIZED; extern const char* HOOK_SEARCH_INITIALIZED;
extern const char* MAKE_GAME_PROCESS_TEXT; extern const char* MAKE_GAME_PROCESS_TEXT;
extern const char* HOOK_SEARCH_FINISHED; extern const char* HOOK_SEARCH_FINISHED;
@ -200,12 +201,12 @@ void SearchForHooks(SearchParam spUser)
sp = spUser.length == 0 ? spDefault : spUser; sp = spUser.length == 0 ? spDefault : spUser;
ConsoleOutput(HOOK_SEARCH_INITIALIZING, 0.);
do do
try { records = std::make_unique<HookRecord[]>(recordsAvailable = sp.maxRecords); } try { records = std::make_unique<HookRecord[]>(recordsAvailable = sp.maxRecords); }
catch (std::bad_alloc) { ConsoleOutput("Textractor: SearchForHooks ERROR: out of memory, retrying to allocate %d", sp.maxRecords /= 2); } catch (std::bad_alloc) { ConsoleOutput("Textractor: SearchForHooks ERROR: out of memory, retrying to allocate %d", sp.maxRecords /= 2); }
while (!records && sp.maxRecords); while (!records && sp.maxRecords);
ConsoleOutput(STARTING_SEARCH);
std::vector<uint64_t> addresses; std::vector<uint64_t> addresses;
if (*sp.boundaryModule) std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); if (*sp.boundaryModule) std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule));
if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule)); if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule));
@ -224,9 +225,11 @@ void SearchForHooks(SearchParam spUser)
memcpy(trampolines[i], trampoline, sizeof(trampoline)); memcpy(trampolines[i], trampoline, sizeof(trampoline));
*(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i]; *(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i];
*(void**)(trampolines[i] + original_offset) = original; *(void**)(trampolines[i] + original_offset) = original;
if (i % 2500 == 0) ConsoleOutput(HOOK_SEARCH_INITIALIZING, 1 + 98. * i / addresses.size());
} }
ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size()); ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size());
MH_ApplyQueued(); MH_ApplyQueued();
ConsoleOutput(HOOK_SEARCH_STARTING);
ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000); ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000);
Sleep(sp.searchTime); Sleep(sp.searchTime);
for (auto addr : addresses) MH_QueueDisableHook((void*)addr); for (auto addr : addresses) MH_QueueDisableHook((void*)addr);
@ -261,7 +264,7 @@ void SearchForText(wchar_t* text, UINT codepage)
char codepageText[PATTERN_SIZE * 4] = {}; char codepageText[PATTERN_SIZE * 4] = {};
WideCharToMultiByte(codepage, 0, text, PATTERN_SIZE, codepageText, PATTERN_SIZE * 4, nullptr, nullptr); 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); if (strlen(utf8Text) < 4 || strlen(codepageText) < 4 || wcslen(text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT);
ConsoleOutput(STARTING_SEARCH); ConsoleOutput(HOOK_SEARCH_STARTING);
auto GenerateHooks = [&](std::vector<uint64_t> addresses, HookParamType type) auto GenerateHooks = [&](std::vector<uint64_t> addresses, HookParamType type)
{ {
for (auto addr : addresses) for (auto addr : addresses)