add find hooks feture + some cleanup

This commit is contained in:
Akash Mozumdar 2019-06-02 02:09:17 -04:00
parent 8d763e4b3d
commit 125d160ef6
16 changed files with 404 additions and 139 deletions

View File

@ -48,6 +48,11 @@ namespace
}).detach(); }).detach();
} }
Host::HookEventHandler OnHookFound = [](HookParam hp, DWORD processId, const std::wstring& text)
{
Host::AddConsoleOutput(Util::GenerateCode(hp, 0) + L": " + text);
};
private: private:
DWORD processId; DWORD processId;
HANDLE pipe; HANDLE pipe;
@ -100,6 +105,17 @@ namespace
while (ReadFile(hookPipe, buffer, PIPE_BUFFER_SIZE, &bytesRead, nullptr)) while (ReadFile(hookPipe, buffer, PIPE_BUFFER_SIZE, &bytesRead, nullptr))
switch (*(HostNotificationType*)buffer) switch (*(HostNotificationType*)buffer)
{ {
case HOST_NOTIFICATION_FOUND_HOOK:
{
auto info = *(HookFoundNotif*)buffer;
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound;
OnHookFound(info.hp, processId, info.text);
info.hp.type = USING_STRING;
if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) if (converted->size() > 12) OnHookFound(info.hp, processId, converted.value());
info.hp.codepage = CP_UTF8;
if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8)) if (converted->size() > 12) OnHookFound(info.hp, processId, converted.value());
}
break;
case HOST_NOTIFICATION_RMVHOOK: case HOST_NOTIFICATION_RMVHOOK:
{ {
auto info = *(HookRemovedNotif*)buffer; auto info = *(HookRemovedNotif*)buffer;
@ -206,6 +222,12 @@ namespace Host
processRecordsByIds->at(processId).Send(InsertHookCmd(hp)); processRecordsByIds->at(processId).Send(InsertHookCmd(hp));
} }
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound)
{
if (HookFound) processRecordsByIds->at(processId).OnHookFound = HookFound;
processRecordsByIds->at(processId).Send(FindHookCmd(sp));
}
HookParam GetHookParam(ThreadParam tp) HookParam GetHookParam(ThreadParam tp)
{ {
return processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp; return processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp;

View File

@ -7,11 +7,13 @@ namespace Host
{ {
using ProcessEventHandler = std::function<void(DWORD)>; using ProcessEventHandler = std::function<void(DWORD)>;
using ThreadEventHandler = std::function<void(TextThread&)>; using ThreadEventHandler = std::function<void(TextThread&)>;
using HookEventHandler = std::function<void(HookParam, DWORD processId, const std::wstring& text)>;
void Start(ProcessEventHandler Connect, ProcessEventHandler Disconnect, ThreadEventHandler Create, ThreadEventHandler Destroy, TextThread::OutputCallback Output); void Start(ProcessEventHandler Connect, ProcessEventHandler Disconnect, ThreadEventHandler Create, ThreadEventHandler Destroy, TextThread::OutputCallback Output);
void InjectProcess(DWORD processId); void InjectProcess(DWORD processId);
void DetachProcess(DWORD processId); void DetachProcess(DWORD processId);
void InsertHook(DWORD processId, HookParam hp); void InsertHook(DWORD processId, HookParam hp);
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {});
HookParam GetHookParam(ThreadParam tp); HookParam GetHookParam(ThreadParam tp);

View File

@ -6,6 +6,7 @@
#include <shellapi.h> #include <shellapi.h>
#include <winhttp.h> #include <winhttp.h>
#include <QFormLayout> #include <QFormLayout>
#include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QCheckBox> #include <QCheckBox>
#include <QSpinBox> #include <QSpinBox>
@ -18,6 +19,7 @@ extern const char* LAUNCH;
extern const char* DETACH; extern const char* DETACH;
extern const char* ADD_HOOK; extern const char* ADD_HOOK;
extern const char* SAVE_HOOKS; extern const char* SAVE_HOOKS;
extern const char* FIND_HOOKS;
extern const char* SETTINGS; extern const char* SETTINGS;
extern const char* EXTENSIONS; extern const char* EXTENSIONS;
extern const char* SELECT_PROCESS; extern const char* SELECT_PROCESS;
@ -25,6 +27,15 @@ extern const char* ATTACH_INFO;
extern const char* SEARCH_GAME; extern const char* SEARCH_GAME;
extern const char* PROCESSES; extern const char* PROCESSES;
extern const char* CODE_INFODUMP; extern const char* CODE_INFODUMP;
extern const char* HOOH_SEARCH_UNSTABLE_WARNING;
extern const char* SEARCH_PATTERN;
extern const char* SEARCH_DURATION;
extern const char* PATTERN_OFFSET;
extern const char* MIN_ADDRESS;
extern const char* MAX_ADDRESS;
extern const char* START_HOOK_SEARCH;
extern const char* SAVE_SEARCH_RESULTS;
extern const char* TEXT_FILES;
extern const char* SAVE_SETTINGS; extern const char* SAVE_SETTINGS;
extern const char* USE_JP_LOCALE; extern const char* USE_JP_LOCALE;
extern const char* FILTER_REPETITION; extern const char* FILTER_REPETITION;
@ -49,6 +60,7 @@ MainWindow::MainWindow(QWidget *parent) :
{ DETACH, &MainWindow::DetachProcess }, { DETACH, &MainWindow::DetachProcess },
{ ADD_HOOK, &MainWindow::AddHook }, { ADD_HOOK, &MainWindow::AddHook },
{ SAVE_HOOKS, &MainWindow::SaveHooks }, { SAVE_HOOKS, &MainWindow::SaveHooks },
{ "Find hooks", &MainWindow::FindHooks },
{ SETTINGS, &MainWindow::Settings }, { SETTINGS, &MainWindow::Settings },
{ EXTENSIONS, &MainWindow::Extensions } { EXTENSIONS, &MainWindow::Extensions }
}) })
@ -317,6 +329,74 @@ void MainWindow::SaveHooks()
} }
} }
void MainWindow::FindHooks()
{
QMessageBox::information(this, FIND_HOOKS, HOOH_SEARCH_UNSTABLE_WARNING);
struct : QDialog
{
using QDialog::QDialog;
void launch()
{
auto layout = new QFormLayout(this);
auto patternInput = new QLineEdit("8B FF 55 8B EC", this);
layout->addRow(SEARCH_PATTERN, patternInput);
for (auto[value, label] : Array<std::tuple<int&, const char*>>{
{ sp.searchTime, SEARCH_DURATION },
{ sp.offset, PATTERN_OFFSET },
})
{
auto spinBox = new QSpinBox(this);
spinBox->setMaximum(INT_MAX);
spinBox->setValue(value);
layout->addRow(label, spinBox);
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [=, &value] { value = spinBox->value(); });
}
for (auto[value, label] : Array<std::tuple<uintptr_t&, const char*>>{
{ sp.minAddress, MIN_ADDRESS },
{ sp.maxAddress, MAX_ADDRESS },
})
{
auto input = new QLineEdit(QString::number(value, 16), this);
layout->addRow(label, input);
connect(input, &QLineEdit::textEdited, [&value](QString input)
{
bool ok;
if (uintptr_t newValue = input.toULongLong(&ok, 16); ok) value = newValue;
});
}
auto save = new QPushButton(START_HOOK_SEARCH, this);
layout->addWidget(save);
connect(save, &QPushButton::clicked, this, &QDialog::accept);
connect(save, &QPushButton::clicked, [this, patternInput]
{
QByteArray pattern = QByteArray::fromHex(patternInput->text().toUtf8());
if (pattern.size() < 3) return;
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25));
auto hooks = std::make_shared<QString>();
Host::FindHooks(processId, sp, [hooks](HookParam hp, DWORD processId, const std::wstring& text)
{
hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n");
});
QString fileName = QFileDialog::getSaveFileName(this, SAVE_SEARCH_RESULTS, "./Hooks.txt", TEXT_FILES);
if (fileName.isEmpty()) fileName = "Hooks.txt";
std::thread([hooks, fileName]
{
for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size();
QTextFile(fileName, QIODevice::WriteOnly | QIODevice::Truncate).write(hooks->toUtf8());
hooks->clear();
}).detach();
});
setWindowTitle(FIND_HOOKS);
exec();
}
SearchParam sp = {};
DWORD processId;
} searchDialog(this, Qt::WindowCloseButtonHint);
searchDialog.processId = GetSelectedProcessId();
searchDialog.launch();
}
void MainWindow::Settings() void MainWindow::Settings()
{ {
struct : QDialog struct : QDialog

View File

@ -36,6 +36,7 @@ private:
void DetachProcess(); void DetachProcess();
void AddHook(); void AddHook();
void SaveHooks(); void SaveHooks();
void FindHooks();
void Settings(); void Settings();
void Extensions(); void Extensions();
void ViewThread(int index); void ViewThread(int index);

View File

@ -6,9 +6,9 @@
enum Misc { MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 }; enum Misc { MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 };
enum HostCommandType { HOST_COMMAND_NEW_HOOK, HOST_COMMAND_REMOVE_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 };
enum HostNotificationType { HOST_NOTIFICATION_TEXT, HOST_NOTIFICATION_NEWHOOK, HOST_NOTIFICATION_RMVHOOK }; enum HostNotificationType { HOST_NOTIFICATION_TEXT, HOST_NOTIFICATION_NEWHOOK, HOST_NOTIFICATION_FOUND_HOOK, HOST_NOTIFICATION_RMVHOOK };
enum HookParamType : unsigned enum HookParamType : unsigned
{ {

View File

@ -58,6 +58,14 @@ struct ThreadParam
uint64_t ctx2; // The subcontext of the hook: 0 by default, generated in a method specific to the hook uint64_t ctx2; // The subcontext of the hook: 0 by default, generated in a method specific to the hook
}; };
struct SearchParam
{
BYTE pattern[25] = {}; // pattern in memory to search for
int length, // length of pattern
offset = 0, // offset from start of pattern to add hook
searchTime = 30000; // ms
uintptr_t minAddress = 0, maxAddress = (uintptr_t)-1LL;
};
struct InsertHookCmd // From host struct InsertHookCmd // From host
{ {
@ -66,14 +74,29 @@ struct InsertHookCmd // From host
HookParam hp; HookParam hp;
}; };
struct ConsoleOutputNotif // From hook struct FindHookCmd // From host
{
FindHookCmd(SearchParam sp) : sp(sp) {}
HostCommandType command = HOST_COMMAND_FIND_HOOK;
SearchParam sp;
};
struct ConsoleOutputNotif // From dll
{ {
ConsoleOutputNotif(std::string message = "") { strncpy_s(this->message, message.c_str(), MESSAGE_SIZE - 1); } ConsoleOutputNotif(std::string message = "") { strncpy_s(this->message, message.c_str(), MESSAGE_SIZE - 1); }
HostNotificationType command = HOST_NOTIFICATION_TEXT; HostNotificationType command = HOST_NOTIFICATION_TEXT;
char message[MESSAGE_SIZE] = {}; char message[MESSAGE_SIZE] = {};
}; };
struct HookRemovedNotif // From hook struct HookFoundNotif // From dll
{
HookFoundNotif(HookParam hp, wchar_t* text) : hp(hp) { wcsncpy_s(this->text, text, MESSAGE_SIZE - 1); }
HostNotificationType command = HOST_NOTIFICATION_FOUND_HOOK;
HookParam hp;
wchar_t text[MESSAGE_SIZE] = {}; // though type is wchar_t, may not be encoded in UTF-16 (it's just convenient to use wcs* functions)
};
struct HookRemovedNotif // From dll
{ {
HookRemovedNotif(uint64_t address) : address(address) {}; HookRemovedNotif(uint64_t address) : address(address) {};
HostNotificationType command = HOST_NOTIFICATION_RMVHOOK; HostNotificationType command = HOST_NOTIFICATION_RMVHOOK;

View File

@ -11,6 +11,7 @@ const char* LAUNCH = u8"Launch game";
const char* DETACH = u8"Detach from game"; const char* DETACH = u8"Detach from game";
const char* ADD_HOOK = u8"Add hook"; const char* ADD_HOOK = u8"Add hook";
const char* SAVE_HOOKS = u8"Save hook(s)"; const char* SAVE_HOOKS = u8"Save hook(s)";
const char* FIND_HOOKS = u8"Find hooks";
const char* SETTINGS = u8"Settings"; const char* SETTINGS = u8"Settings";
const char* EXTENSIONS = u8"Extensions"; const char* EXTENSIONS = u8"Extensions";
const char* SELECT_PROCESS = u8"Select process"; const char* SELECT_PROCESS = u8"Select process";
@ -42,6 +43,15 @@ const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Drag and drop extension (.dll) file
Drag and drop within the list to reorder Drag and drop within the list to reorder
Press delete with an extension selected to remove it)"; Press delete with an extension selected to remove it)";
const char* USE_JP_LOCALE = u8"Emulate japanese locale?"; const char* USE_JP_LOCALE = u8"Emulate japanese locale?";
extern const char* HOOH_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!";
extern const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)";
extern const char* SEARCH_DURATION = u8"Search duration (ms)";
extern const char* PATTERN_OFFSET = u8"Offset from pattern start";
extern const char* MIN_ADDRESS = u8"Minimum address (hex)";
extern const char* MAX_ADDRESS = u8"Maximum address (hex)";
extern const char* START_HOOK_SEARCH = u8"Start hook search";
extern const char* SAVE_SEARCH_RESULTS = u8"Save search results";
extern const char* TEXT_FILES = u8"Text (*.txt)";
const char* FILTER_REPETITION = u8"Repetition Filter"; const char* FILTER_REPETITION = u8"Repetition Filter";
const char* DEFAULT_CODEPAGE = u8"Default Codepage"; const char* DEFAULT_CODEPAGE = u8"Default Codepage";
const char* FLUSH_DELAY = u8"Flush Delay"; const char* FLUSH_DELAY = u8"Flush Delay";
@ -70,7 +80,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* 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_FINISHED = u8"Textractor: hook search finished, %d results found";
const char* FUNC_MISSING = u8"Textractor: function not present"; const char* FUNC_MISSING = u8"Textractor: function not present";
const char* MODULE_MISSING = u8"Textractor: module not present"; const char* MODULE_MISSING = u8"Textractor: module not present";
const char* GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read"; const char* GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read";

View File

@ -4,6 +4,7 @@ if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(texthook_src set(texthook_src
main.cc main.cc
texthook.cc texthook.cc
hookfinder.cc
engine/match64.cc engine/match64.cc
engine/native/pchooks.cc engine/native/pchooks.cc
util/ithsys/ithsys.cc util/ithsys/ithsys.cc
@ -13,6 +14,7 @@ else()
set(texthook_src set(texthook_src
main.cc main.cc
texthook.cc texthook.cc
hookfinder.cc
engine/engine.cc engine/engine.cc
engine/match.cc engine/match.cc
engine/native/pchooks.cc engine/native/pchooks.cc
@ -25,20 +27,4 @@ endif()
add_library(texthook SHARED ${texthook_src}) add_library(texthook SHARED ${texthook_src})
set_target_properties(texthook PROPERTIES target_link_libraries(texthook minhook)
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
)
set(texthook_libs
Version.lib
minhook
)
target_link_libraries(texthook ${texthook_libs})
target_compile_definitions(texthook
PRIVATE
_CRT_NON_CONFORMING_SWPRINTFS
_SCL_SECURE_NO_WARNINGS # config.pri
_CRT_SECURE_NO_WARNINGS
)

View File

@ -7,6 +7,9 @@
# pragma warning (disable:4819) # pragma warning (disable:4819)
#endif // _MSC_VER #endif // _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include "engine/engine.h" #include "engine/engine.h"
#include "engine/match.h" #include "engine/match.h"
#include "util/util.h" #include "util/util.h"
@ -19,7 +22,6 @@
#include "disasm/disasm.h" #include "disasm/disasm.h"
#include "cpputil/cppcstring.h" #include "cpputil/cppcstring.h"
#include "mono/monoobject.h" #include "mono/monoobject.h"
#include "growl.h"
#include "const.h" #include "const.h"
#include "native/pchooks.h" #include "native/pchooks.h"
//#include <boost/foreach.hpp> //#include <boost/foreach.hpp>
@ -16876,25 +16878,25 @@ bool InsertPPSSPPHooks()
// http://stackoverflow.com/questions/940707/how-do-i-programatically-get-the-version-of-a-dll-or-exe-file // http://stackoverflow.com/questions/940707/how-do-i-programatically-get-the-version-of-a-dll-or-exe-file
// get the version info for the file requested // get the version info for the file requested
if (DWORD dwSize = ::GetFileVersionInfoSizeW(processPath, nullptr)) { // if (DWORD dwSize = ::GetFileVersionInfoSizeW(processPath, nullptr)) {
UINT len = 0; // UINT len = 0;
BYTE * buf = new BYTE[dwSize]; // BYTE * buf = new BYTE[dwSize];
VS_FIXEDFILEINFO * info = nullptr; // VS_FIXEDFILEINFO * info = nullptr;
if (::GetFileVersionInfoW(processPath, 0, dwSize, buf) // if (::GetFileVersionInfoW(processPath, 0, dwSize, buf)
&& ::VerQueryValueW(buf, L"\\", (LPVOID*)&info, &len) // && ::VerQueryValueW(buf, L"\\", (LPVOID*)&info, &len)
&& info) // && info)
{ // {
PPSSPP_VERSION[0] = HIWORD(info->dwFileVersionMS), // PPSSPP_VERSION[0] = HIWORD(info->dwFileVersionMS),
PPSSPP_VERSION[1] = LOWORD(info->dwFileVersionMS), // PPSSPP_VERSION[1] = LOWORD(info->dwFileVersionMS),
PPSSPP_VERSION[2] = HIWORD(info->dwFileVersionLS), // PPSSPP_VERSION[2] = HIWORD(info->dwFileVersionLS),
PPSSPP_VERSION[3] = LOWORD(info->dwFileVersionLS); // PPSSPP_VERSION[3] = LOWORD(info->dwFileVersionLS);
//
} // }
else // else
ConsoleOutput("vnreng: failed to get PPSSPP version"); // ConsoleOutput("vnreng: failed to get PPSSPP version");
delete[] buf; // delete[] buf;
//
} //}
InsertPPSSPPHLEHooks(); InsertPPSSPPHLEHooks();

View File

@ -2,15 +2,9 @@
// 8/9/2013 jichi // 8/9/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133 // Branch: ITH_Engine/engine.cpp, revision 133
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "engine/match.h" #include "engine/match.h"
#include "engine/engine.h" #include "engine/engine.h"
#include "engine/native/pchooks.h" #include "engine/native/pchooks.h"
#include "util/growl.h"
#include "util/util.h" #include "util/util.h"
#include "main.h" #include "main.h"
#include "ithsys/ithsys.h" #include "ithsys/ithsys.h"
@ -455,8 +449,8 @@ bool DetermineEngineByFile4()
bool DetermineEngineByProcessName() bool DetermineEngineByProcessName()
{ {
WCHAR str[MAX_PATH]; WCHAR str[MAX_PATH];
wcscpy(str, processName); wcscpy_s(str, processName);
_wcslwr(str); // lower case _wcslwr_s(str); // lower case
if (wcsstr(str,L"reallive") || Util::CheckFile(L"Reallive.exe") || Util::CheckFile(L"REALLIVEDATA\\Start.ini")) { if (wcsstr(str,L"reallive") || Util::CheckFile(L"Reallive.exe") || Util::CheckFile(L"REALLIVEDATA\\Start.ini")) {
InsertRealliveHook(); InsertRealliveHook();
@ -541,8 +535,8 @@ bool DetermineEngineByProcessName()
} }
// This must appear at last since str is modified // This must appear at last since str is modified
wcscpy(str + len - 4, L"_checksum.exe"); //wcscpy(str + len - 4, L"_checksum.exe");
if (Util::CheckFile(str)) { if (Util::CheckFile(L"*_checksum.exe")) {
InsertRyokuchaHook(); InsertRyokuchaHook();
if (Util::CheckFile(L"*.iar") && Util::CheckFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games if (Util::CheckFile(L"*.iar") && Util::CheckFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games

196
texthook/hookfinder.cc Normal file
View File

@ -0,0 +1,196 @@
#include "hookfinder.h"
#include "defs.h"
#include "main.h"
#include "util.h"
extern const char* STARTING_SEARCH;
extern const char* HOOK_SEARCH_INITIALIZED;
extern const char* HOOK_SEARCH_FINISHED;
extern WinMutex viewMutex;
namespace
{
constexpr int CACHE_SIZE = 500'000;
struct HookRecord
{
HookRecord() : address(0) {}
~HookRecord() { if (address) NotifyHookFound(address, offset, text); }
uint64_t address;
int offset;
wchar_t text[200];
};
std::unique_ptr<HookRecord[]> records;
long recordsAvailable;
uint64_t addressCharCache[CACHE_SIZE] = {};
long sumCache[CACHE_SIZE] = {};
DWORD DUMMY;
#ifndef _WIN64
BYTE trampoline[32] =
{
0x9c, // pushfd
0x60, // pushad
0x68, 0,0,0,0, // push @addr ; after this a total of 0x28 bytes are pushed
0x8d, 0x44, 0x24, 0x28, // lea eax,[esp+0x28]
0x50, // push eax ; stack
0xbb, 0,0,0,0, // mov ebx,@Send
0xff, 0xd3, // call ebx
0x83, 0xc4, 0x08, // add esp, 0x8 ; doesn't matter which register
0x61, // popad
0x9d, // popfd
0x68, 0,0,0,0, // push @original
0xc3 // ret ; basically absolute jmp to @original
};
constexpr int addr_offset = 3, send_offset = 13, original_offset = 25, registers = 8;
#else
BYTE trampoline[128] = {
0x9c, // push rflags
0x50, // push rax
0x53, // push rbx
0x51, // push rcx
0x52, // push rdx
0x54, // push rsp
0x55, // push rbp
0x56, // push rsi
0x57, // push rdi
0x41, 0x50, // push r8
0x41, 0x51, // push r9
0x41, 0x52, // push r10
0x41, 0x53, // push r11
0x41, 0x54, // push r12
0x41, 0x55, // push r13
0x41, 0x56, // push r14
0x41, 0x57, // push r15
// https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers
0x48, 0x83, 0xec, 0x20, // sub rsp,0x20
0xc5, 0xfa, 0x7f, 0x24, 0x24, // vmovdqu [rsp],xmm4
0xc5, 0xfa, 0x7f, 0x6c, 0x24, 0x10, // vmovdqu [rsp+0x10],xmm5
0x48, 0x8d, 0x8c, 0x24, 0xa8, 0x00, 0x00, 0x00, // lea rcx,[rsp+0xa8]
0x48, 0xba, 0,0,0,0,0,0,0,0, // mov rcx,@addr
0x48, 0xb8, 0,0,0,0,0,0,0,0, // mov rax,@Send
0xff, 0xd0, // call rax
0xc5, 0xfa, 0x6f, 0x6c, 0x24, 0x10, // vmovdqu xmm5,XMMWORD PTR[rsp + 0x10]
0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp]
0x48, 0x83, 0xc4, 0x20, // add rsp,0x20
0x41, 0x5f, // pop r15
0x41, 0x5e, // pop r14
0x41, 0x5d, // pop r13
0x41, 0x5c, // pop r12
0x41, 0x5b, // pop r11
0x41, 0x5a, // pop r10
0x41, 0x59, // pop r9
0x41, 0x58, // pop r8
0x5f, // pop rdi
0x5e, // pop rsi
0x5d, // pop rbp
0x5c, // pop rsp
0x5a, // pop rdx
0x59, // pop rcx
0x5b, // pop rbx
0x58, // pop rax
0x9d, // pop rflags
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [0] ; relative to next instruction (i.e. jmp @original)
0,0,0,0,0,0,0,0 // @original
};
constexpr int addr_offset = 50, send_offset = 60, original_offset = 116, registers = 16;
#endif
}
void Send(wchar_t** stack, uintptr_t address)
{
// it is unsafe to call ANY external functions from this, as they may have been hooked (if called the hook would call this function making an infinite loop)
// the exceptions are compiler intrinsics like _InterlockedDecrement
if (recordsAvailable <= 0) return;
for (int i = -registers; i < 6; ++i)
{
int length = 0, sum = 0;
__try { for (wchar_t* str = stack[i]; str[length] && length < 200; ++length) sum += str[length]; }
__except (EXCEPTION_EXECUTE_HANDLER) {}
if (length > 7 && length < 199)
{
__try
{
// many duplicate results with same address and second character will be found: filter them out
uint64_t addressAndChar = (((uint64_t)stack[i][1]) << 48) | address;
if (addressCharCache[addressAndChar % CACHE_SIZE] == addressAndChar) continue;
addressCharCache[addressAndChar % CACHE_SIZE] = addressAndChar;
// if there are huge amount of strings that are the same, it's probably garbage: filter them out
// can't store all the strings, so use sum as heuristic instead
if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) continue;
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
long n = _InterlockedDecrement(&recordsAvailable);
__try
{
if (n > 0)
{
records[n].address = address;
records[n].offset = i * sizeof(wchar_t*);
for (int j = 0; j < length; ++j) records[n].text[j] = stack[i][j];
records[n].text[length] = 0;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) { records[n].address = 0; }
}
}
}
void SearchForHooks(SearchParam sp)
{
std::thread([=]
{
static std::mutex m;
std::scoped_lock lock(m);
try
{
records = std::make_unique<HookRecord[]>(recordsAvailable = CACHE_SIZE);
}
catch (std::bad_alloc&) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); }
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_NOACCESS);
moduleStopAddress -= info.RegionSize;
ConsoleOutput(STARTING_SEARCH);
std::vector<uint64_t> addresses = Util::SearchMemory(sp.pattern, sp.length);
for (auto& addr : addresses) addr += sp.offset;
addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr)
{
return (addr > moduleStartAddress && addr < moduleStopAddress) || addr > sp.maxAddress || addr < sp.minAddress;
}), addresses.end());
*(void**)(trampoline + send_offset) = Send;
auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE);
VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, &DUMMY);
for (int i = 0; i < addresses.size(); ++i)
{
void* original;
MH_CreateHook((void*)addresses[i], trampolines[i], &original);
MH_QueueEnableHook((void*)addresses[i]);
memcpy(trampolines[i], trampoline, sizeof(trampoline));
*(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i];
*(void**)(trampolines[i] + original_offset) = original;
}
ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size());
MH_ApplyQueued();
Sleep(sp.searchTime);
for (auto addr : addresses) MH_QueueDisableHook((void*)addr);
MH_ApplyQueued();
Sleep(1000);
for (auto addr : addresses) MH_RemoveHook((void*)addr);
records.reset();
VirtualFree(trampolines, 0, MEM_RELEASE);
for (int i = 0; i < CACHE_SIZE; ++i) addressCharCache[i] = sumCache[i] = 0;
ConsoleOutput(HOOK_SEARCH_FINISHED, CACHE_SIZE - recordsAvailable);
}).detach();
}

7
texthook/hookfinder.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
#include "types.h"
void SearchForText(wchar_t* text);
void SearchForHooks(SearchParam sp);

View File

@ -7,6 +7,7 @@
#include "defs.h" #include "defs.h"
#include "engine/match.h" #include "engine/match.h"
#include "texthook.h" #include "texthook.h"
#include "hookfinder.h"
#include "util.h" #include "util.h"
extern const char* PIPE_CONNECTED; extern const char* PIPE_CONNECTED;
@ -14,6 +15,7 @@ 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* NOT_ENOUGH_TEXT;
extern const char* COULD_NOT_FIND; extern const char* COULD_NOT_FIND;
@ -63,6 +65,12 @@ DWORD WINAPI Pipe(LPVOID)
NewHook(info.hp, "UserHook", 0); NewHook(info.hp, "UserHook", 0);
} }
break; break;
case HOST_COMMAND_FIND_HOOK:
{
auto info = *(FindHookCmd*)buffer;
SearchForHooks(info.sp);
}
break;
case HOST_COMMAND_DETACH: case HOST_COMMAND_DETACH:
{ {
running = false; running = false;
@ -91,7 +99,17 @@ void ConsoleOutput(LPCSTR text, ...)
ConsoleOutputNotif buffer; ConsoleOutputNotif buffer;
va_list args; va_list args;
va_start(args, text); va_start(args, text);
vsprintf_s<MESSAGE_SIZE>(buffer.message, text, args); vsnprintf(buffer.message, MESSAGE_SIZE, text, args);
WriteFile(hookPipe, &buffer, sizeof(buffer), &DUMMY, nullptr);
}
void NotifyHookFound(uint64_t addr, int offset, wchar_t* text)
{
HookParam hp = {};
hp.offset = offset;
hp.type = USING_UNICODE | USING_STRING;
hp.address = addr;
HookFoundNotif buffer(hp, text);
WriteFile(hookPipe, &buffer, sizeof(buffer), &DUMMY, nullptr); WriteFile(hookPipe, &buffer, sizeof(buffer), &DUMMY, nullptr);
} }
@ -144,6 +162,7 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
char codepageText[MAX_MODULE_SIZE * 4] = {}; char codepageText[MAX_MODULE_SIZE * 4] = {};
WideCharToMultiByte(hp.codepage, 0, hp.text, MAX_MODULE_SIZE, codepageText, MAX_MODULE_SIZE * 4, nullptr, nullptr); 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); 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>>{ for (auto[addrs, type] : Array<std::tuple<std::vector<uint64_t>, HookParamType>>{
{ Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 }, { Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 },
{ Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING }, { Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING },

View File

@ -9,6 +9,7 @@
void TextOutput(ThreadParam tp, BYTE* text, int len); void TextOutput(ThreadParam tp, BYTE* text, int len);
void ConsoleOutput(LPCSTR text, ...); void ConsoleOutput(LPCSTR text, ...);
void NotifyHookFound(uint64_t addr, int offset, wchar_t* text);
void NotifyHookRemove(uint64_t addr, LPCSTR name); void NotifyHookRemove(uint64_t addr, LPCSTR name);
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE); void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
void RemoveHook(uint64_t addr, int maxOffset = 9); void RemoveHook(uint64_t addr, int maxOffset = 9);
@ -48,9 +49,14 @@ extern "C" // minhook library
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
MH_STATUS WINAPI MH_ApplyQueued(VOID);
const char* WINAPI MH_StatusToString(MH_STATUS status); const char* WINAPI MH_StatusToString(MH_STATUS status);
} }
#define MH_ALL_HOOKS NULL
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only #define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
#define ITH_TRY __try #define ITH_TRY __try
#define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER) #define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)

View File

@ -1,85 +0,0 @@
#pragma once
// growl.h
// 9/17/2013 jichi
//#ifdef GROWL_HAS_GROWL
#include <windows.h>
#include <cstdio>
#define GROWL_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
#define GROWL_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
#define GROWL_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
#define GROWL_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
inline void GROWL_DWORD(DWORD value)
{
WCHAR buf[100];
swprintf(buf, L"DWORD: %x", value);
GROWL_MSG(buf);
}
inline void GROWL_DWORD2(DWORD v, DWORD v2)
{
WCHAR buf[100];
swprintf(buf, L"DWORD2: %x,%x", v, v2);
GROWL_MSG(buf);
}
inline void GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
{
WCHAR buf[100];
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
GROWL_MSG(buf);
}
inline void GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
{
WCHAR buf[100];
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
GROWL_MSG(buf);
}
inline void GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
{
WCHAR buf[100];
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
GROWL_MSG(buf);
}
inline void GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
{
WCHAR buf[100];
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
GROWL_MSG(buf);
}
inline void GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
{
WCHAR buf[100];
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
GROWL_MSG(buf);
}
inline void GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
{
WCHAR buf[100];
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
GROWL_MSG(buf);
}
inline void GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
{
WCHAR buf[100];
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
GROWL_MSG(buf);
}
inline void GROWL(DWORD v) { GROWL_DWORD(v); }
inline void GROWL(LPCWSTR v) { GROWL_MSG(v); }
inline void GROWL(LPCSTR v) { GROWL_MSG_A(v); }
//#endif // GROWL_HAS_GROWL
// EOF

View File

@ -6,7 +6,6 @@
#include "util/util.h" #include "util/util.h"
#include "ithsys/ithsys.h" #include "ithsys/ithsys.h"
#include "main.h" #include "main.h"
#include "growl.h"
namespace { // unnamed namespace { // unnamed
@ -215,7 +214,7 @@ bool CheckFile(LPCWSTR name)
wchar_t path[MAX_PATH * 2]; wchar_t path[MAX_PATH * 2];
wchar_t* end = path + GetModuleFileNameW(nullptr, path, MAX_PATH); wchar_t* end = path + GetModuleFileNameW(nullptr, path, MAX_PATH);
while (*(--end) != L'\\'); while (*(--end) != L'\\');
wcscpy(end + 1, name); wcscpy_s(end + 1, MAX_PATH, name);
file = FindFirstFileW(path, &unused); file = FindFirstFileW(path, &unused);
if (file != INVALID_HANDLE_VALUE) if (file != INVALID_HANDLE_VALUE)
{ {