more consistent hook searching ui and fix critical error in searching for text and custom codepage for hook searches

This commit is contained in:
Akash Mozumdar 2019-08-19 16:15:08 -04:00
parent 3f004554f8
commit 562c6e1a3a
9 changed files with 102 additions and 92 deletions

View File

@ -59,7 +59,7 @@ namespace
}; };
size_t HashThreadParam(ThreadParam tp) { return std::hash<int64_t>()(tp.processId + tp.addr) + std::hash<int64_t>()(tp.ctx + tp.ctx2); } size_t HashThreadParam(ThreadParam tp) { return std::hash<int64_t>()(tp.processId + tp.addr) + std::hash<int64_t>()(tp.ctx + tp.ctx2); }
Synchronized<std::unordered_map<ThreadParam, TextThread, Functor<HashThreadParam>>, std::recursive_mutex> textThreadsByParams; Synchronized<std::unordered_map<ThreadParam, TextThread, Functor<HashThreadParam>>> textThreadsByParams;
Synchronized<std::unordered_map<DWORD, ProcessRecord>> processRecordsByIds; Synchronized<std::unordered_map<DWORD, ProcessRecord>> processRecordsByIds;
Host::ProcessEventHandler OnConnect, OnDisconnect; Host::ProcessEventHandler OnConnect, OnDisconnect;
@ -68,7 +68,10 @@ namespace
void RemoveThreads(std::function<bool(ThreadParam)> removeIf) void RemoveThreads(std::function<bool(ThreadParam)> removeIf)
{ {
std::vector<TextThread*> threadsToRemove; std::vector<TextThread*> threadsToRemove;
{
auto textThreadsByParams = ::textThreadsByParams.Acquire();
std::for_each(textThreadsByParams->begin(), textThreadsByParams->end(), [&](auto& it) { if (removeIf(it.first)) threadsToRemove.push_back(&it.second); }); std::for_each(textThreadsByParams->begin(), textThreadsByParams->end(), [&](auto& it) { if (removeIf(it.first)) threadsToRemove.push_back(&it.second); });
}
for (auto thread : threadsToRemove) for (auto thread : threadsToRemove)
{ {
OnDestroy(*thread); OnDestroy(*thread);
@ -106,10 +109,9 @@ namespace
std::wstring wide = info.text; std::wstring wide = info.text;
if (wide.size() > STRING) OnHookFound(info.hp, info.text); if (wide.size() > STRING) OnHookFound(info.hp, info.text);
info.hp.type &= ~USING_UNICODE; 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()); if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
info.hp.codepage = CP_UTF8; if (auto converted = Util::StringToWideString((char*)info.text, info.hp.codepage = CP_UTF8))
if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8))
if (converted->size() > STRING) OnHookFound(info.hp, converted.value()); if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
} }
break; break;

View File

@ -1,5 +1,4 @@
#include "util.h" #include "util.h"
#include "host.h"
#include <sstream> #include <sstream>
#include <Psapi.h> #include <Psapi.h>
@ -47,28 +46,6 @@ namespace
return hp; return hp;
} }
std::optional<HookParam> 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<HookParam> ParseHCode(std::wstring HCode) std::optional<HookParam> ParseHCode(std::wstring HCode)
{ {
std::wsmatch match; std::wsmatch match;
@ -325,7 +302,6 @@ namespace Util
{ {
if (code[0] == L'/') code.erase(0, 1); // legacy/AGTH compatibility if (code[0] == L'/') code.erase(0, 1); // legacy/AGTH compatibility
if (code[0] == L'R') return ParseRCode(code.erase(0, 1)); 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)); else if (code[0] == L'H') return ParseHCode(code.erase(0, 1));
return {}; return {};
} }

View File

@ -35,6 +35,9 @@ extern const char* MAX_ADDRESS;
extern const char* STRING_OFFSET; extern const char* STRING_OFFSET;
extern const char* MAX_HOOK_SEARCH_RECORDS; extern const char* MAX_HOOK_SEARCH_RECORDS;
extern const char* HOOK_SEARCH_FILTER; 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* START_HOOK_SEARCH;
extern const char* SAVE_SEARCH_RESULTS; extern const char* SAVE_SEARCH_RESULTS;
extern const char* TEXT_FILES; extern const char* TEXT_FILES;
@ -360,7 +363,8 @@ void MainWindow::AddHook()
void MainWindow::AddHook(QString hook) void MainWindow::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 (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); else Host::AddConsoleOutput(INVALID_CODE);
} }
@ -418,23 +422,48 @@ void MainWindow::FindHooks()
DWORD processId = GetSelectedProcessId(); DWORD processId = GetSelectedProcessId();
SearchParam sp = {}; SearchParam sp = {};
bool customSettings = false; sp.codepage = Host::defaultCodepage;
bool searchForText = false, customSettings = false;
std::wregex filter(L"."); std::wregex filter(L".");
QDialog dialog(this, Qt::WindowCloseButtonHint); QDialog dialog(this, Qt::WindowCloseButtonHint);
QFormLayout layout(&dialog); QFormLayout layout(&dialog);
QCheckBox cjkCheckbox(&dialog); QCheckBox cjkCheckbox(&dialog);
layout.addRow(SEARCH_CJK, &cjkCheckbox); layout.addRow(SEARCH_CJK, &cjkCheckbox);
QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help, &dialog); QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help | QDialogButtonBox::Retry, &dialog);
layout.addRow(&confirm); layout.addRow(&confirm);
confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH); confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH);
confirm.button(QDialogButtonBox::Retry)->setText(SEARCH_FOR_TEXT);
confirm.button(QDialogButtonBox::Help)->setText(SETTINGS); confirm.button(QDialogButtonBox::Help)->setText(SETTINGS);
connect(&confirm, &QDialogButtonBox::helpRequested, [&customSettings] { customSettings = true; }); connect(&confirm, &QDialogButtonBox::clicked, [&](QAbstractButton* button)
connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); {
connect(&confirm, &QDialogButtonBox::helpRequested, &dialog, &QDialog::accept); if (button == confirm.button(QDialogButtonBox::Retry)) searchForText = true;
if (button == confirm.button(QDialogButtonBox::Help)) customSettings = true;
dialog.accept();
});
dialog.setWindowTitle(SEARCH_FOR_HOOKS); dialog.setWindowTitle(SEARCH_FOR_HOOKS);
if (!dialog.exec()) return; 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) if (customSettings)
{ {
QDialog dialog(this, Qt::WindowCloseButtonHint); QDialog dialog(this, Qt::WindowCloseButtonHint);
@ -446,6 +475,7 @@ void MainWindow::FindHooks()
{ sp.searchTime, SEARCH_DURATION }, { sp.searchTime, SEARCH_DURATION },
{ sp.offset, PATTERN_OFFSET }, { sp.offset, PATTERN_OFFSET },
{ sp.maxRecords, MAX_HOOK_SEARCH_RECORDS }, { sp.maxRecords, MAX_HOOK_SEARCH_RECORDS },
{ sp.codepage, CODEPAGE },
}) })
{ {
auto spinBox = new QSpinBox(&dialog); auto spinBox = new QSpinBox(&dialog);

View File

@ -4,7 +4,7 @@
// 8/23/2013 jichi // 8/23/2013 jichi
// Branch: ITH/common.h, rev 128 // 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 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 }; 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 MODULE_OFFSET = 0x40, // address is relative to module
FUNCTION_OFFSET = 0x80, // address is relative to function FUNCTION_OFFSET = 0x80, // address is relative to function
USING_UTF8 = 0x100, USING_UTF8 = 0x100,
READ_SEARCH = 0x200, // unspecified address: search for text instead NO_CONTEXT = 0x200,
NO_CONTEXT = 0x400, HOOK_EMPTY = 0x400,
HOOK_EMPTY = 0x800, FIXING_SPLIT = 0x800,
FIXING_SPLIT = 0x1000, DIRECT_READ = 0x1000, // /R read code instead of classic /H hook code
DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code FULL_STRING = 0x2000,
FULL_STRING = 0x4000, HOOK_ENGINE = 0x4000,
HOOK_ENGINE = 0x8000, HOOK_ADDITIONAL = 0x8000,
HOOK_ADDITIONAL = 0x10000,
}; };

View File

@ -31,11 +31,9 @@ struct HookParam
split, // offset of the split character split, // offset of the split character
split_index, // deref_offset2 split_index, // deref_offset2
null_length; null_length;
union
{
wchar_t module[MAX_MODULE_SIZE]; wchar_t module[MAX_MODULE_SIZE];
wchar_t text[MAX_MODULE_SIZE];
};
char function[MAX_MODULE_SIZE]; char function[MAX_MODULE_SIZE];
DWORD type; // flags DWORD type; // flags
UINT codepage; // text encoding UINT codepage; // text encoding
@ -62,12 +60,14 @@ struct ThreadParam
struct SearchParam 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) 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 offset = 2, // offset from start of pattern to add hook
searchTime = 20000, // ms searchTime = 20000, // ms
maxRecords = 100000; maxRecords = 100000,
codepage = SHIFT_JIS;
uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1; uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1;
wchar_t text[PATTERN_SIZE] = {}; // text to search for
void(*hookPostProcessor)(HookParam&) = nullptr; void(*hookPostProcessor)(HookParam&) = nullptr;
}; };

View File

@ -63,6 +63,9 @@ const char* MIN_ADDRESS = u8"Minimum address (hex)";
const char* MAX_ADDRESS = u8"Maximum address (hex)"; const char* MAX_ADDRESS = u8"Maximum address (hex)";
const char* STRING_OFFSET = u8"String offset (hex)"; const char* STRING_OFFSET = u8"String offset (hex)";
const char* HOOK_SEARCH_FILTER = u8"Results must match this regex"; 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* START_HOOK_SEARCH = u8"Start hook search";
const char* SAVE_SEARCH_RESULTS = u8"Save search results"; const char* SAVE_SEARCH_RESULTS = u8"Save search results";
const char* TEXT_FILES = u8"Text (*.txt)"; const char* TEXT_FILES = u8"Text (*.txt)";

View File

@ -6,6 +6,8 @@
extern const char* STARTING_SEARCH; extern const char* STARTING_SEARCH;
extern const char* HOOK_SEARCH_INITIALIZED; extern const char* HOOK_SEARCH_INITIALIZED;
extern const char* HOOK_SEARCH_FINISHED; extern const char* HOOK_SEARCH_FINISHED;
extern const char* NOT_ENOUGH_TEXT;
extern const char* COULD_NOT_FIND;
extern WinMutex viewMutex; extern WinMutex viewMutex;
@ -24,6 +26,7 @@ namespace
hp.type = USING_UNICODE | USING_STRING; hp.type = USING_UNICODE | USING_STRING;
hp.address = address; hp.address = address;
hp.padding = sp.padding; hp.padding = sp.padding;
hp.codepage = sp.codepage;
if (sp.hookPostProcessor) sp.hookPostProcessor(hp); if (sp.hookPostProcessor) sp.hookPostProcessor(hp);
NotifyHookFound(hp, (wchar_t*)text); NotifyHookFound(hp, (wchar_t*)text);
} }
@ -204,3 +207,31 @@ void SearchForHooks(SearchParam spUser)
ConsoleOutput(HOOK_SEARCH_FINISHED, sp.maxRecords - recordsAvailable); ConsoleOutput(HOOK_SEARCH_FINISHED, sp.maxRecords - recordsAvailable);
}).detach(); }).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<uint64_t> 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);
}

View File

@ -3,5 +3,5 @@
#include "common.h" #include "common.h"
#include "types.h" #include "types.h"
void SearchForText(wchar_t* text); void SearchForText(wchar_t* text, UINT codepage);
void SearchForHooks(SearchParam sp); void SearchForHooks(SearchParam sp);

View File

@ -15,9 +15,6 @@ extern const char* INSERTING_HOOK;
extern const char* REMOVING_HOOK; extern const char* REMOVING_HOOK;
extern const char* HOOK_FAILED; extern const char* HOOK_FAILED;
extern const char* TOO_MANY_HOOKS; 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; WinMutex viewMutex;
@ -74,7 +71,8 @@ DWORD WINAPI Pipe(LPVOID)
case HOST_COMMAND_FIND_HOOK: case HOST_COMMAND_FIND_HOOK:
{ {
auto info = *(FindHookCmd*)buffer; auto info = *(FindHookCmd*)buffer;
SearchForHooks(info.sp); if (*info.sp.text) SearchForText(info.sp.text, info.sp.codepage);
else SearchForHooks(info.sp);
} }
break; break;
case HOST_COMMAND_DETACH: case HOST_COMMAND_DETACH:
@ -156,34 +154,6 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID)
void NewHook(HookParam hp, LPCSTR lpname, DWORD flag) void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
{ {
if (hp.type & READ_SEARCH)
{
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<std::tuple<std::vector<uint64_t>, 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 (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS);
if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1); if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1);
ConsoleOutput(INSERTING_HOOK, hp.name); ConsoleOutput(INSERTING_HOOK, hp.name);
@ -193,7 +163,6 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
ConsoleOutput(HOOK_FAILED); ConsoleOutput(HOOK_FAILED);
(*hooks)[currentHook].Clear(); (*hooks)[currentHook].Clear();
} }
}
} }
void RemoveHook(uint64_t addr, int maxOffset) void RemoveHook(uint64_t addr, int maxOffset)