forked from Public-Mirror/Textractor
more consistent hook searching ui and fix critical error in searching for text and custom codepage for hook searches
This commit is contained in:
parent
3f004554f8
commit
562c6e1a3a
@ -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); }
|
||||
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;
|
||||
|
||||
Host::ProcessEventHandler OnConnect, OnDisconnect;
|
||||
@ -68,7 +68,10 @@ namespace
|
||||
void RemoveThreads(std::function<bool(ThreadParam)> removeIf)
|
||||
{
|
||||
std::vector<TextThread*> 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;
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "util.h"
|
||||
#include "host.h"
|
||||
#include <sstream>
|
||||
#include <Psapi.h>
|
||||
|
||||
@ -47,28 +46,6 @@ namespace
|
||||
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::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 {};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
3
text.cpp
3
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)";
|
||||
|
@ -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<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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<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 (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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user