add find hooks feture + some cleanup

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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -36,6 +36,7 @@ private:
void DetachProcess();
void AddHook();
void SaveHooks();
void FindHooks();
void Settings();
void Extensions();
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 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
{

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
};
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;

View File

@ -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";

View File

@ -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)

View File

@ -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();

View File

@ -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
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 "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 },

View File

@ -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)

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 "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)
{