diff --git a/texthook/engine/engine.h b/texthook/engine/engine.h index 7394b07..af4cda6 100644 --- a/texthook/engine/engine.h +++ b/texthook/engine/engine.h @@ -16,6 +16,7 @@ namespace Engine { extern wchar_t *processName, // cached processPath[MAX_PATH]; // cached +// Artikash 6/17/2019 TODO: These have the wrong values on x64 /** jichi 12/24/2014 * @param addr function address * @param frame real address of the function, supposed to be the same as addr diff --git a/texthook/engine/match64.cc b/texthook/engine/match64.cc index 0461ecb..d178b47 100644 --- a/texthook/engine/match64.cc +++ b/texthook/engine/match64.cc @@ -1,6 +1,9 @@ #include "match.h" #include "main.h" +#include "texthook.h" #include "native/pchooks.h" +#include "mono/monoobject.h" +#include "mono/funcinfo.h" #include "engine.h" #include "util.h" @@ -43,10 +46,71 @@ namespace Engine return found; } + bool InsertMonoHooks(HMODULE module) + { + auto SpecialHookMonoString = nullptr; + static HMODULE mono = module; + bool ret = false; + for (auto func : Array{ MONO_FUNCTIONS_INITIALIZER }) + { + HookParam hp = {}; + if (!(hp.address = (uintptr_t)GetProcAddress(mono, func.functionName))) continue; + hp.type = HOOK_EMPTY; + NewHook(hp, "Mono Searcher"); + ret = true; + } + /* Artikash 2/13/2019: + How to hook Mono/Unity3D: + Find all standard function prologs in memory with write/execute permission: these represent possible JIT compiled functions + Then use Mono APIs to reflect what these functions are, and hook them if they are string member functions + Mono calling convention uses 'this' as first argument on stack + Must be dynamic hook bootstrapped from other mono api or mono_domain_get won't work + */ + trigger_fun = [](LPVOID addr, DWORD, DWORD) + { + static auto getDomain = (MonoDomain*(*)())GetProcAddress(mono, "mono_domain_get"); + static auto getJitInfo = (MonoObject*(*)(MonoDomain*, uintptr_t))GetProcAddress(mono, "mono_jit_info_table_find"); + static auto getName = (char*(*)(uintptr_t))GetProcAddress(mono, "mono_pmip"); + if (!getDomain || !getName || !getJitInfo) goto failed; + static auto domain = getDomain(); + if (!domain) goto failed; + const BYTE prolog[] = { 0x55, 0x48, 0x8b, 0xec }; + for (auto addr : Util::SearchMemory(prolog, sizeof(prolog), PAGE_EXECUTE_READWRITE)) + { + [](uint64_t addr) + { + __try + { + if (getJitInfo(domain, addr)) + if (char* name = getName(addr)) + if (strstr(name, "string:") && !strstr(name, "string:mem")) + { + HookParam hp = {}; + hp.address = addr; + hp.type = USING_STRING | USING_UNICODE; + hp.offset = -0x20; + hp.padding = 20; + NewHook(hp, name); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) {} + }(addr); + } + return true; + failed: + ConsoleOutput("Textractor: Mono Dynamic failed"); + return true; + }; + SetTrigger(); + return ret; + } + bool UnsafeDetermineEngineType() { if (Util::CheckFile(L"PPSSPP*.exe") && FindPPSSPP()) return true; + for (const wchar_t* monoName : { L"mono", L"mono-2.0-bdwgc" }) if (HMODULE module = GetModuleHandleW(monoName)) if (InsertMonoHooks(module)) return true; + for (std::wstring DXVersion : { L"d3dx9", L"d3dx10" }) if (HMODULE module = GetModuleHandleW(DXVersion.c_str())) PcHooks::hookD3DXFunctions(module); else for (int i = 0; i < 50; ++i) diff --git a/texthook/texthook.cc b/texthook/texthook.cc index 2f70638..dc0ddde 100644 --- a/texthook/texthook.cc +++ b/texthook/texthook.cc @@ -122,13 +122,14 @@ void TextHook::Send(uintptr_t dwDataBase) { __try { + if (trigger) trigger = Engine::InsertDynamicHook(location, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase - 0x18)); + #ifndef _WIN64 DWORD dwCount = 0, dwSplit = 0, dwDataIn = *(DWORD*)(dwDataBase + hp.offset), // default values dwRetn = *(DWORD*)dwDataBase; // first value on stack (if hooked start of function, this is return address) - if (trigger) trigger = Engine::InsertDynamicHook(location, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase - 0x18)); // jichi 10/24/2014: generic hook function if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp)) hp.hook_fun = nullptr; @@ -166,6 +167,7 @@ void TextHook::Send(uintptr_t dwDataBase) TextOutput({ GetCurrentProcessId(), address, dwRetn, dwSplit }, pbData, dwCount); #else // _WIN32 + if (hp.type & HOOK_EMPTY) return; // jichi 10/24/2014: dummy hook only for dynamic hook int count = 0; ThreadParam tp = { GetCurrentProcessId(), address, *(uintptr_t*)dwDataBase, 0 }; // first value on stack (if hooked start of function, this is return address) uintptr_t data = *(uintptr_t*)(dwDataBase + hp.offset); // default value