add find hooks feture + some cleanup
This commit is contained in:
parent
8d763e4b3d
commit
125d160ef6
@ -48,6 +48,11 @@ namespace
|
||||
}).detach();
|
||||
}
|
||||
|
||||
Host::HookEventHandler OnHookFound = [](HookParam hp, DWORD processId, const std::wstring& text)
|
||||
{
|
||||
Host::AddConsoleOutput(Util::GenerateCode(hp, 0) + L": " + text);
|
||||
};
|
||||
|
||||
private:
|
||||
DWORD processId;
|
||||
HANDLE pipe;
|
||||
@ -100,6 +105,17 @@ namespace
|
||||
while (ReadFile(hookPipe, buffer, PIPE_BUFFER_SIZE, &bytesRead, nullptr))
|
||||
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:
|
||||
{
|
||||
auto info = *(HookRemovedNotif*)buffer;
|
||||
@ -206,6 +222,12 @@ namespace Host
|
||||
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)
|
||||
{
|
||||
return processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp;
|
||||
|
@ -7,11 +7,13 @@ namespace Host
|
||||
{
|
||||
using ProcessEventHandler = std::function<void(DWORD)>;
|
||||
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 InjectProcess(DWORD processId);
|
||||
void DetachProcess(DWORD processId);
|
||||
void InsertHook(DWORD processId, HookParam hp);
|
||||
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {});
|
||||
|
||||
HookParam GetHookParam(ThreadParam tp);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <shellapi.h>
|
||||
#include <winhttp.h>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
@ -18,6 +19,7 @@ extern const char* LAUNCH;
|
||||
extern const char* DETACH;
|
||||
extern const char* ADD_HOOK;
|
||||
extern const char* SAVE_HOOKS;
|
||||
extern const char* FIND_HOOKS;
|
||||
extern const char* SETTINGS;
|
||||
extern const char* EXTENSIONS;
|
||||
extern const char* SELECT_PROCESS;
|
||||
@ -25,6 +27,15 @@ extern const char* ATTACH_INFO;
|
||||
extern const char* SEARCH_GAME;
|
||||
extern const char* PROCESSES;
|
||||
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* USE_JP_LOCALE;
|
||||
extern const char* FILTER_REPETITION;
|
||||
@ -49,6 +60,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
{ DETACH, &MainWindow::DetachProcess },
|
||||
{ ADD_HOOK, &MainWindow::AddHook },
|
||||
{ SAVE_HOOKS, &MainWindow::SaveHooks },
|
||||
{ "Find hooks", &MainWindow::FindHooks },
|
||||
{ SETTINGS, &MainWindow::Settings },
|
||||
{ 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()
|
||||
{
|
||||
struct : QDialog
|
||||
|
@ -36,6 +36,7 @@ private:
|
||||
void DetachProcess();
|
||||
void AddHook();
|
||||
void SaveHooks();
|
||||
void FindHooks();
|
||||
void Settings();
|
||||
void Extensions();
|
||||
void ViewThread(int index);
|
||||
|
@ -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 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
|
||||
{
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
@ -66,14 +74,29 @@ struct InsertHookCmd // From host
|
||||
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); }
|
||||
HostNotificationType command = HOST_NOTIFICATION_TEXT;
|
||||
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) {};
|
||||
HostNotificationType command = HOST_NOTIFICATION_RMVHOOK;
|
||||
|
13
text.cpp
13
text.cpp
@ -11,6 +11,7 @@ const char* LAUNCH = u8"Launch game";
|
||||
const char* DETACH = u8"Detach from game";
|
||||
const char* ADD_HOOK = u8"Add hook";
|
||||
const char* SAVE_HOOKS = u8"Save hook(s)";
|
||||
const char* FIND_HOOKS = u8"Find hooks";
|
||||
const char* SETTINGS = u8"Settings";
|
||||
const char* EXTENSIONS = u8"Extensions";
|
||||
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
|
||||
Press delete with an extension selected to remove it)";
|
||||
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* DEFAULT_CODEPAGE = u8"Default Codepage";
|
||||
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* HOOK_FAILED = u8"Textractor: failed to insert hook";
|
||||
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* 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* MODULE_MISSING = u8"Textractor: module not present";
|
||||
const char* GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read";
|
||||
|
@ -4,6 +4,7 @@ if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
|
||||
set(texthook_src
|
||||
main.cc
|
||||
texthook.cc
|
||||
hookfinder.cc
|
||||
engine/match64.cc
|
||||
engine/native/pchooks.cc
|
||||
util/ithsys/ithsys.cc
|
||||
@ -13,6 +14,7 @@ else()
|
||||
set(texthook_src
|
||||
main.cc
|
||||
texthook.cc
|
||||
hookfinder.cc
|
||||
engine/engine.cc
|
||||
engine/match.cc
|
||||
engine/native/pchooks.cc
|
||||
@ -25,20 +27,4 @@ endif()
|
||||
|
||||
add_library(texthook SHARED ${texthook_src})
|
||||
|
||||
set_target_properties(texthook PROPERTIES
|
||||
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
|
||||
)
|
||||
target_link_libraries(texthook minhook)
|
||||
|
@ -7,6 +7,9 @@
|
||||
# pragma warning (disable:4819)
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define _SCL_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include "engine/engine.h"
|
||||
#include "engine/match.h"
|
||||
#include "util/util.h"
|
||||
@ -19,7 +22,6 @@
|
||||
#include "disasm/disasm.h"
|
||||
#include "cpputil/cppcstring.h"
|
||||
#include "mono/monoobject.h"
|
||||
#include "growl.h"
|
||||
#include "const.h"
|
||||
#include "native/pchooks.h"
|
||||
//#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
|
||||
// get the version info for the file requested
|
||||
if (DWORD dwSize = ::GetFileVersionInfoSizeW(processPath, nullptr)) {
|
||||
UINT len = 0;
|
||||
BYTE * buf = new BYTE[dwSize];
|
||||
VS_FIXEDFILEINFO * info = nullptr;
|
||||
if (::GetFileVersionInfoW(processPath, 0, dwSize, buf)
|
||||
&& ::VerQueryValueW(buf, L"\\", (LPVOID*)&info, &len)
|
||||
&& info)
|
||||
{
|
||||
PPSSPP_VERSION[0] = HIWORD(info->dwFileVersionMS),
|
||||
PPSSPP_VERSION[1] = LOWORD(info->dwFileVersionMS),
|
||||
PPSSPP_VERSION[2] = HIWORD(info->dwFileVersionLS),
|
||||
PPSSPP_VERSION[3] = LOWORD(info->dwFileVersionLS);
|
||||
|
||||
}
|
||||
else
|
||||
ConsoleOutput("vnreng: failed to get PPSSPP version");
|
||||
delete[] buf;
|
||||
|
||||
}
|
||||
// if (DWORD dwSize = ::GetFileVersionInfoSizeW(processPath, nullptr)) {
|
||||
// UINT len = 0;
|
||||
// BYTE * buf = new BYTE[dwSize];
|
||||
// VS_FIXEDFILEINFO * info = nullptr;
|
||||
// if (::GetFileVersionInfoW(processPath, 0, dwSize, buf)
|
||||
// && ::VerQueryValueW(buf, L"\\", (LPVOID*)&info, &len)
|
||||
// && info)
|
||||
// {
|
||||
// PPSSPP_VERSION[0] = HIWORD(info->dwFileVersionMS),
|
||||
// PPSSPP_VERSION[1] = LOWORD(info->dwFileVersionMS),
|
||||
// PPSSPP_VERSION[2] = HIWORD(info->dwFileVersionLS),
|
||||
// PPSSPP_VERSION[3] = LOWORD(info->dwFileVersionLS);
|
||||
//
|
||||
// }
|
||||
// else
|
||||
// ConsoleOutput("vnreng: failed to get PPSSPP version");
|
||||
// delete[] buf;
|
||||
//
|
||||
//}
|
||||
|
||||
InsertPPSSPPHLEHooks();
|
||||
|
||||
|
@ -2,15 +2,9 @@
|
||||
// 8/9/2013 jichi
|
||||
// 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/engine.h"
|
||||
#include "engine/native/pchooks.h"
|
||||
#include "util/growl.h"
|
||||
#include "util/util.h"
|
||||
#include "main.h"
|
||||
#include "ithsys/ithsys.h"
|
||||
@ -455,8 +449,8 @@ bool DetermineEngineByFile4()
|
||||
bool DetermineEngineByProcessName()
|
||||
{
|
||||
WCHAR str[MAX_PATH];
|
||||
wcscpy(str, processName);
|
||||
_wcslwr(str); // lower case
|
||||
wcscpy_s(str, processName);
|
||||
_wcslwr_s(str); // lower case
|
||||
|
||||
if (wcsstr(str,L"reallive") || Util::CheckFile(L"Reallive.exe") || Util::CheckFile(L"REALLIVEDATA\\Start.ini")) {
|
||||
InsertRealliveHook();
|
||||
@ -541,8 +535,8 @@ bool DetermineEngineByProcessName()
|
||||
}
|
||||
|
||||
// This must appear at last since str is modified
|
||||
wcscpy(str + len - 4, L"_checksum.exe");
|
||||
if (Util::CheckFile(str)) {
|
||||
//wcscpy(str + len - 4, L"_checksum.exe");
|
||||
if (Util::CheckFile(L"*_checksum.exe")) {
|
||||
InsertRyokuchaHook();
|
||||
|
||||
if (Util::CheckFile(L"*.iar") && Util::CheckFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
|
||||
|
196
texthook/hookfinder.cc
Normal file
196
texthook/hookfinder.cc
Normal 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
7
texthook/hookfinder.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
void SearchForText(wchar_t* text);
|
||||
void SearchForHooks(SearchParam sp);
|
@ -7,6 +7,7 @@
|
||||
#include "defs.h"
|
||||
#include "engine/match.h"
|
||||
#include "texthook.h"
|
||||
#include "hookfinder.h"
|
||||
#include "util.h"
|
||||
|
||||
extern const char* PIPE_CONNECTED;
|
||||
@ -14,6 +15,7 @@ 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;
|
||||
|
||||
@ -63,6 +65,12 @@ DWORD WINAPI Pipe(LPVOID)
|
||||
NewHook(info.hp, "UserHook", 0);
|
||||
}
|
||||
break;
|
||||
case HOST_COMMAND_FIND_HOOK:
|
||||
{
|
||||
auto info = *(FindHookCmd*)buffer;
|
||||
SearchForHooks(info.sp);
|
||||
}
|
||||
break;
|
||||
case HOST_COMMAND_DETACH:
|
||||
{
|
||||
running = false;
|
||||
@ -91,7 +99,17 @@ void ConsoleOutput(LPCSTR text, ...)
|
||||
ConsoleOutputNotif buffer;
|
||||
va_list args;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -144,6 +162,7 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
|
||||
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 },
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
void TextOutput(ThreadParam tp, BYTE* text, int len);
|
||||
void ConsoleOutput(LPCSTR text, ...);
|
||||
void NotifyHookFound(uint64_t addr, int offset, wchar_t* text);
|
||||
void NotifyHookRemove(uint64_t addr, LPCSTR name);
|
||||
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
|
||||
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_DisableHook(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);
|
||||
}
|
||||
|
||||
#define MH_ALL_HOOKS NULL
|
||||
|
||||
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
|
||||
#define ITH_TRY __try
|
||||
#define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
|
||||
|
@ -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
|
@ -6,7 +6,6 @@
|
||||
#include "util/util.h"
|
||||
#include "ithsys/ithsys.h"
|
||||
#include "main.h"
|
||||
#include "growl.h"
|
||||
|
||||
namespace { // unnamed
|
||||
|
||||
@ -215,7 +214,7 @@ bool CheckFile(LPCWSTR name)
|
||||
wchar_t path[MAX_PATH * 2];
|
||||
wchar_t* end = path + GetModuleFileNameW(nullptr, path, MAX_PATH);
|
||||
while (*(--end) != L'\\');
|
||||
wcscpy(end + 1, name);
|
||||
wcscpy_s(end + 1, MAX_PATH, name);
|
||||
file = FindFirstFileW(path, &unused);
|
||||
if (file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user