From 9f6bb23540a67be6e9cf21e8dc3e25cce42684ae Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Thu, 9 Jun 2022 08:36:16 +0800 Subject: [PATCH 01/17] =?UTF-8?q?Godot=20Engine=20=EF=BC=8Cadd=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit example-game: [NoMeme] PHOENIXES --- texthook/engine/match64.cc | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/texthook/engine/match64.cc b/texthook/engine/match64.cc index 859f746..8a9b104 100644 --- a/texthook/engine/match64.cc +++ b/texthook/engine/match64.cc @@ -213,11 +213,50 @@ namespace Engine ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll"); return false; } + int getGodoStringLength(uintptr_t stack, uintptr_t data) { + int len = *(int*)(data - 4); + len--; + int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0; + //检查是否为错误的unicode字符 + for (size_t i = 0; i < checkLength; i++) + { + if (*(WORD*)(data + i * 2) == 0x0) + return 0; + } + return checkLength * 2; + } + //BY:IOV + bool InsertGodotHook_X64() { + const BYTE bytes[] = { 0x8B,0x40,0xFC,0x83,0xF8,0x01,0x83,0xD0,0xFF,0x41,0x39,0xC6 }; + + ULONG64 range = min(processStopAddress - processStartAddress, X64_MAX_REL_ADDR); + for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStartAddress + range)) { + HookParam myhp = {}; + myhp.address = addr; + + myhp.type = USING_STRING | USING_UNICODE | NO_CONTEXT; // /HQ 不使用上下文区分 把所有线程的文本都提取 + //myhp.padding = 0xc;//[esp+4]+padding + // data_offset + myhp.offset = -0xC-4;//RCX + myhp.length_fun = getGodoStringLength; + char nameForUser[HOOK_NAME_SIZE] = "RichTextLabel_add_text"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: Godot_add_text_X64 Hook "); + return true; + } + + ConsoleOutput("vnreng:Godot_x64: pattern not found"); + return false; + } bool UnsafeDetermineEngineType() { if (Util::CheckFile(L"PPSSPP*.exe") && FindPPSSPP()) return true; - + + if (Util::CheckFile(L"*.pck")) { + return InsertGodotHook_X64(); + } + for (const wchar_t* moduleName : { (const wchar_t*)NULL, L"node.dll", L"nw.dll" }) if (InsertV8Hook(GetModuleHandleW(moduleName))) return true; if (GetModuleHandleW(L"GameAssembly.dll")) // TODO: is there a way to autofind hook? @@ -241,4 +280,4 @@ namespace Engine PcHooks::hookGDIPlusFunctions(); return false; } -} \ No newline at end of file +} From 36201b9ff3e93a19dde0696c65093f2f3ba855b0 Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Wed, 31 Aug 2022 11:03:43 +0800 Subject: [PATCH 03/17] Unity_add_HOOK Unity_add_HOOK --- texthook/engine/match64.cc | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/texthook/engine/match64.cc b/texthook/engine/match64.cc index 8a9b104..2072073 100644 --- a/texthook/engine/match64.cc +++ b/texthook/engine/match64.cc @@ -51,9 +51,132 @@ namespace Engine } return found; } + //MonoImage* mono_assembly_get_image(MonoAssembly* assembly):获取程序集的镜像。后面几乎所有的操作都会以MonoImage* 为第一个参数。 + static uintptr_t (*mono_assembly_get_image)(uintptr_t) = NULL; + // const char* mono_image_get_name(MonoImage * image) :获取程序集名。我们用它判断哪个程序集是我们的目标 + static char* (*mono_image_get_name)(uintptr_t) = NULL; + //MonoClass* mono_class_from_name (MonoImage *image, const char* name_space, const char *name):通过类名获取类(非实例)。 + static uintptr_t(*mono_class_from_name)(uintptr_t, char*, char*) = NULL; + //MonoVTable* mono_class_vtable (MonoDomain *domain, MonoClass *klass):获取vtable,我们通过它可以找到静态字段的起始地址。 + static uintptr_t(*mono_class_vtable)(uintptr_t, uintptr_t) = NULL; + //void* mono_vtable_get_static_field_data (MonoVTable *vt):获取静态字段的起始地址。 + static void* (*mono_vtable_get_static_field_data)(uintptr_t) = NULL; + //MonoMethod* mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count):获取方法(非native code地址)。 + //其中param_count是参数数量,可以输入-1来省略。此函数无法获取重载的方法,但对于我们来说足够了。 + static uintptr_t(*mono_class_get_method_from_name)(uintptr_t, char*,int) = NULL; + //获取属性。用它可以进一步获得属性的getter和setter。 + //MonoProperty* mono_class_get_property_from_name(MonoClass* klass, const char* name): + static uintptr_t(*mono_class_get_property_from_name)(uintptr_t, char*) = NULL; + //获取属性的getter和setter。 + //MonoMethod* mono_property_get_get_method(MonoProperty* prop) 与 MonoMethod* mono_property_get_set_method(MonoProperty* prop): + static uintptr_t(*mono_property_get_set_method)(uintptr_t) = NULL; + // (不安全)返回方法的地址,如果方法尚未编译,则JIT开始编译。这个是解决问题的核心方法。 gpointer mono_compile_method (MonoMethod *method): + static uint64_t* (*mono_compile_method)(uintptr_t) = NULL; + //获取函数的非托管块指针 (native) gpointer mono_method_get_unmanaged_thunk (MonoMethod *method) + //使用这个来获取native代码 方法尚未编译,会执行编译 并提取 x86版本可能使用的是__stdcall + static uint64_t* (*mono_method_get_unmanaged_thunk)(uintptr_t) = NULL; + //MonoDomain* mono_get_root_domain (void) :获取主作用域。用于附加线程以及获取静态字段的地址。 + static MonoDomain* (*mono_get_root_domain)() = NULL; + //void mono_thread_attach (MonoDomain*):附加到进程的主线程。这个操作是必须的。 + static void (*mono_thread_attach)(MonoDomain*) = NULL; + + //MonoAssembly* assembly,而后者则是void* user_data + int getV8StringLength(uintptr_t stack, uintptr_t data) { + int len = *(int*)(data - 4); + int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0; + //检查是否为错误的unicode字符 + for (size_t i = 0; i < checkLength; i++) + { + if (*(WORD*)(data + i * 2) == 0x0) + return 0; + } + return checkLength * 2; + + } + void MonoCallBack(uintptr_t assembly, void* userData) { + uintptr_t mono_property = NULL; + uintptr_t image=mono_assembly_get_image(assembly); + // TMP_Text TextMeshProUGUI + auto mono_tmp_class=mono_class_from_name(image, "TMPro", "TMP_Text"); + auto mono_ugui_class = mono_class_from_name(image, "UnityEngine.UI", "Text"); + auto mono_ngui_class = mono_class_from_name(image, "", "UILabel"); + if (!mono_tmp_class && !mono_ugui_class && !mono_ngui_class) + return; + if (mono_tmp_class) { + mono_property = mono_class_get_property_from_name(mono_tmp_class, "text"); + } + else if (mono_ugui_class) + { + mono_property = mono_class_get_property_from_name(mono_ugui_class, "text"); + } + else if (mono_ngui_class) { + mono_property = mono_class_get_property_from_name(mono_ngui_class, "text"); + } + + if (mono_property == NULL) + return; + auto mono_set_method= mono_property_get_set_method(mono_property); + //注意必须调用mono_thread_attach 附加到主domain 才能调用 mono_method_get_unmanaged_thunk mono_compile_method 或mono_runtime_invoke + mono_thread_attach(mono_get_root_domain()); + uint64_t* method_pointer= mono_compile_method(mono_set_method); + if (method_pointer) { + HookParam hp = {}; + hp.type = USING_STRING | USING_UNICODE; + hp.address = (uint64_t)method_pointer; + hp.offset = -0x28; // rdx + //hp.index = 0; + hp.padding = 0x14; + if (mono_tmp_class) { + ConsoleOutput("Mono_X64,Insert: TextMeshProUGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "TextMeshProUGUI_set_text"); + } + else if (mono_ugui_class) + { + ConsoleOutput("Mono_X64,Insert: UGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "UGUI_set_text"); + } + else if (mono_ngui_class) + { + ConsoleOutput("Mono_X64,Insert: NGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "NGUI_set_text"); + } + + } + + } + + bool InsertMonoHooksByAssembly(HMODULE module) { + //void mono_assembly_foreach (GFunc func, gpointer user_data) + //遍历程序集。用于获取目标程序集的指针。其中的func 是一个回调函数,要自己写。它有两个参数,前者就是MonoAssembly*,而后者则是user_data + static auto mono_assembly_foreach = (void (*)(void (*)(uintptr_t, void*), uintptr_t))GetProcAddress(module, "mono_assembly_foreach"); + mono_assembly_get_image= (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_assembly_get_image"); + mono_image_get_name = (char* (*)(uintptr_t))GetProcAddress(module, "mono_image_get_name"); + mono_class_from_name = (uintptr_t(*)(uintptr_t, char*, char*))GetProcAddress(module, "mono_class_from_name"); + mono_class_get_property_from_name = (uintptr_t(*)(uintptr_t, char*))GetProcAddress(module, "mono_class_get_property_from_name"); + mono_property_get_set_method = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_property_get_set_method"); + mono_compile_method = (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_compile_method"); + //mono_method_get_unmanaged_thunk= (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_method_get_unmanaged_thunk"); + mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(module, "mono_get_root_domain"); + + mono_thread_attach = (void (*)(MonoDomain*))GetProcAddress(module, "mono_thread_attach"); + if (mono_assembly_foreach && mono_assembly_get_image && mono_image_get_name && mono_class_from_name && + mono_class_get_property_from_name && mono_property_get_set_method && mono_compile_method && + mono_get_root_domain && mono_thread_attach) { + mono_assembly_foreach(MonoCallBack, NULL); + return true; + } + else + { + return false; + } + } bool InsertMonoHooks(HMODULE module) { + return InsertMonoHooksByAssembly(module); auto SpecialHookMonoString = nullptr; static HMODULE mono = module; bool ret = false; From 5da847e06a6104d109351916ee70de18eb6975b3 Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:39:32 +0800 Subject: [PATCH 04/17] add unity_x86 mono HOOK,WOLFRPG HOOK. --- texthook/engine/engine.cc | 158 +++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/texthook/engine/engine.cc b/texthook/engine/engine.cc index c2c7abc..e14c9b5 100644 --- a/texthook/engine/engine.cc +++ b/texthook/engine/engine.cc @@ -9029,12 +9029,63 @@ bool InsertWolf2Hook() NewHook(hp, "WolfRPG2"); return true; } +//example-game:妹!せいかつ~ファンタジー~ by:iov +bool InsertWolf3Hook() +{ + const BYTE bytes[] = { 0xC7,0x45,0xFC,0x00,0x00,0x00,0x00,0x8B,0x45,0x94,0x83,0xE0,0x01 }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) { + ConsoleOutput("vnreng:WolfRPG: pattern3 not found"); + return false; + } + HookParam myhp = {}; + myhp.address = addr+41; + + myhp.type = USING_STRING | NO_CONTEXT; + myhp.offset = pusha_eax_off - 4; + myhp.type |= DATA_INDIRECT; + + myhp.index = 4; + + char nameForUser[HOOK_NAME_SIZE] = "WolfRPG_String_Copy"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: WolfRPG_String_Copy Hook"); + return true; +} + +bool InsertWolf4Hook() { + const BYTE bytes[] = {0xC6,0x45,0xFC,0x29,0x8B,0x8D,0xE0,0xEF,0xFF,0xFF,0xE8,XX4,0x50,0x8B,0x4D,0xE8,0x2B,0x4D,0xEC }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) { + ConsoleOutput("vnreng:WolfRPG: pattern4 not found"); + return false; + } + + HookParam myhp = {}; + myhp.address = addr + 16; + + myhp.type = USING_STRING | NO_CONTEXT; + myhp.offset = pusha_eax_off - 4; + // myhp.type |= DATA_INDIRECT; + + // myhp.index = 4; + + char nameForUser[HOOK_NAME_SIZE] = "WolfRPG4"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: WolfRPG4 Hook"); + return true; +} + + + } // WolfRPG namespace bool InsertWolfHook() { - return InsertOldWolfHook(), InsertWolf2Hook(); + return InsertOldWolfHook(), InsertWolf2Hook(), InsertWolf3Hook(), InsertWolf4Hook(); } bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack) @@ -16983,9 +17034,114 @@ bool InsertRenpyHook() return false; } + static uintptr_t(*mono_assembly_get_image)(uintptr_t) = NULL; +static char* (*mono_image_get_name)(uintptr_t) = NULL; +static uintptr_t(*mono_class_from_name)(uintptr_t, char*, char*) = NULL; +static uintptr_t(*mono_class_vtable)(uintptr_t, uintptr_t) = NULL; +static void* (*mono_vtable_get_static_field_data)(uintptr_t) = NULL; +static uintptr_t(*mono_class_get_method_from_name)(uintptr_t, char*, int) = NULL; +static uintptr_t(*mono_class_get_property_from_name)(uintptr_t, char*) = NULL; +static uintptr_t(*mono_property_get_set_method)(uintptr_t) = NULL; +static uint64_t* (*mono_compile_method)(uintptr_t) = NULL; +static MonoDomain* (*mono_get_root_domain)() = NULL; +static void (*mono_thread_attach)(MonoDomain*) = NULL; +int getV8StringLength(uintptr_t stack, uintptr_t data) { + int len = *(int*)(data - 4); + int checkLength = len > 0 && len < PIPE_BUFFER_SIZE ? len : 0; + for (size_t i = 0; i < checkLength; i++) + { + if (*(WORD*)(data + i * 2) == 0x0) + return 0; + } + return checkLength * 2; + +} +void MonoCallBack(uintptr_t assembly, void* userData) { + uintptr_t mono_property = NULL; + uintptr_t image = mono_assembly_get_image(assembly); + // TMP_Text TextMeshProUGUI + auto mono_tmp_class = mono_class_from_name(image, "TMPro", "TMP_Text"); + auto mono_ugui_class = mono_class_from_name(image, "UnityEngine.UI", "Text"); + auto mono_ngui_class = mono_class_from_name(image, "", "UILabel"); + if (!mono_tmp_class && !mono_ugui_class && !mono_ngui_class) + return; + if (mono_tmp_class) { + mono_property = mono_class_get_property_from_name(mono_tmp_class, "text"); + } + else if(mono_ugui_class) + { + mono_property = mono_class_get_property_from_name(mono_ugui_class, "text"); + } + else if (mono_ngui_class) { + mono_property = mono_class_get_property_from_name(mono_ngui_class, "text"); + } + + if (mono_property == NULL) + return; + auto mono_set_method = mono_property_get_set_method(mono_property); + //注意必须调用mono_thread_attach 附加到主domain 才能调用 mono_method_get_unmanaged_thunk mono_compile_method 或mono_runtime_invoke + mono_thread_attach(mono_get_root_domain()); + uint64_t* method_pointer = mono_compile_method(mono_set_method); + if (method_pointer) { + HookParam hp = {}; + hp.type = USING_STRING | USING_UNICODE |DATA_INDIRECT; + hp.address = (uint64_t)method_pointer; + hp.offset = pusha_esp_off-4; // esp+8 + hp.index = 12; + hp.padding = 12; + + if (mono_tmp_class) { + ConsoleOutput("Mono_X86,Insert: TextMeshProUGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "TextMeshProUGUI_set_text"); + } + else if(mono_ugui_class) + { + ConsoleOutput("Mono_X86,Insert: UGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "UGUI_set_text"); + } + else if(mono_ngui_class) + { + ConsoleOutput("Mono_X86,Insert: NGUI_set_text Hook BY:IOV"); + hp.length_fun = getV8StringLength; + NewHook(hp, "NGUI_set_text"); + } + } + +} +bool InsertMonoHooksByAssembly(HMODULE module) { + //void mono_assembly_foreach (GFunc func, gpointer user_data) + //遍历程序集。用于获取目标程序集的指针。其中的func 是一个回调函数,要自己写。它有两个参数,前者就是MonoAssembly*,而后者则是user_data + static auto mono_assembly_foreach = (void (*)(void (*)(uintptr_t, void*), uintptr_t))GetProcAddress(module, "mono_assembly_foreach"); + mono_assembly_get_image = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_assembly_get_image"); + mono_image_get_name = (char* (*)(uintptr_t))GetProcAddress(module, "mono_image_get_name"); + mono_class_from_name = (uintptr_t(*)(uintptr_t, char*, char*))GetProcAddress(module, "mono_class_from_name"); + mono_class_get_property_from_name = (uintptr_t(*)(uintptr_t, char*))GetProcAddress(module, "mono_class_get_property_from_name"); + mono_property_get_set_method = (uintptr_t(*)(uintptr_t))GetProcAddress(module, "mono_property_get_set_method"); + mono_compile_method = (uint64_t * (*)(uintptr_t))GetProcAddress(module, "mono_compile_method"); + mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(module, "mono_get_root_domain"); + + mono_thread_attach = (void (*)(MonoDomain*))GetProcAddress(module, "mono_thread_attach"); + if (mono_assembly_foreach && mono_assembly_get_image && mono_image_get_name && mono_class_from_name && + mono_class_get_property_from_name && mono_property_get_set_method && mono_compile_method && + mono_get_root_domain && mono_thread_attach) { + mono_assembly_foreach(MonoCallBack, NULL); + return true; + } + else + { + return false; + } +} + + + void InsertMonoHook(HMODULE h) { static HMODULE mono = h; + if (InsertMonoHooksByAssembly(mono)) + return ; /* 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 From 7d2fb06e826b9acf3d165b124f87d65e7207c591 Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:42:17 +0800 Subject: [PATCH 05/17] add Anim HOOK --- texthook/engine/match32.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/texthook/engine/match32.cc b/texthook/engine/match32.cc index fffc4c7..4c3b5a0 100644 --- a/texthook/engine/match32.cc +++ b/texthook/engine/match32.cc @@ -435,6 +435,10 @@ bool DetermineEngineByFile4() return true; } + + if (Util::CheckFile(L"voice\\*.pck")) { + return InsertAnimHook() || InsertAnim2Hook(); + } // jichi 11/22/2015: 凍京NECRO 体験版 // Jazzinghen 23/05/2020: Add check for 凍京NECRO // ResEdit shows multiple potential strings: From 7a55c350065f28064dcbf3406b86144a8ed86bbe Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:44:36 +0800 Subject: [PATCH 06/17] add anim hook --- texthook/engine/engine.cc | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/texthook/engine/engine.cc b/texthook/engine/engine.cc index e14c9b5..e1f97c6 100644 --- a/texthook/engine/engine.cc +++ b/texthook/engine/engine.cc @@ -17228,7 +17228,49 @@ bool NoAsciiFilter(LPVOID data, DWORD *size, HookParam *, BYTE) return true; return false; } +bool InsertAnimHook() { + const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x4D,0x10,0x51,0x8D,0x8D,0x40,0x7E,0xFF,0xFF }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) { + ConsoleOutput("vnreng:Anim: pattern not found"); + return false; + } + HookParam myhp = {}; + myhp.address = addr+10; + myhp.type = USING_STRING| NO_CONTEXT; // /HQ 不使用上下文区分 把所有线程的文本都提取 + + // data_offset + myhp.offset = pusha_ecx_off - 4;//esp+4 + + char nameForUser[HOOK_NAME_SIZE] = "Anim"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: Anim Hook by:IOV"); + return true; +} + +bool InsertAnim2Hook() { + const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x45,0x10,0x50,0x8D,0x8D,0xAC,0x7E,0xFF,0xFF }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) { + ConsoleOutput("vnreng:Anim2: pattern not found"); + return false; + } + HookParam myhp = {}; + myhp.address = addr + 10; + + myhp.type = USING_STRING | NO_CONTEXT; + + // data_offset + myhp.offset = pusha_eax_off - 4;//esp+4 + + char nameForUser[HOOK_NAME_SIZE] = "Anim2"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: Anim2 Hook by:IOV"); + return true; +} bool InsertMonoHooks() { HMODULE h = ::GetModuleHandleA("mono.dll"); From 9d00d565822f010f54c9cf91c4804567ec0308ca Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:46:40 +0800 Subject: [PATCH 07/17] Update engine.cc --- texthook/engine/engine.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/texthook/engine/engine.cc b/texthook/engine/engine.cc index e1f97c6..c247b65 100644 --- a/texthook/engine/engine.cc +++ b/texthook/engine/engine.cc @@ -6392,10 +6392,30 @@ bool InsertCotophaHook2() } return false; } +bool InsertCotophaHook3() { + const BYTE bytes[] = { 0x8B,0x75,0xB8,0x8B,0xCE,0x50,0xC6,0x45,0xFC,0x01,0xE8 }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) { + ConsoleOutput("vnreng:Cotopha3: Cotopha3 not found"); + return false; + } + HookParam myhp = {}; + myhp.address = addr; + + myhp.type = USING_UNICODE | USING_STRING | NO_CONTEXT; + myhp.offset = pusha_eax_off - 4; + + char nameForUser[HOOK_NAME_SIZE] = "Cotopha3_EWideString"; + NewHook(myhp, nameForUser); + ConsoleOutput("Insert: Cotopha3_EWideString Hook BY:IOV"); + return true; +} bool InsertCotophaHook() { - return InsertCotophaHook1() | InsertCotophaHook2(); + InsertCotophaHook1(); + return InsertCotophaHook3() || InsertCotophaHook2(); } // jichi 5/10/2014 From 9dedecf7f4a0ed6e9e1dfd9088be94ef46814b80 Mon Sep 17 00:00:00 2001 From: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:48:27 +0800 Subject: [PATCH 08/17] add EntisGLS hook --- texthook/engine/engine.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/texthook/engine/engine.h b/texthook/engine/engine.h index 5e167b2..a85a801 100644 --- a/texthook/engine/engine.h +++ b/texthook/engine/engine.h @@ -156,7 +156,9 @@ bool InsertWillPlusHook(); // WillPlus: Rio.arc bool InsertWolfHook(); // Wolf: Data.wolf bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc bool InsertYurisHook(); // YU-RIS: *.ypf - +bool InsertAnimHook(); // +bool InsertAnim2Hook(); // +bool InsertCotophaHook3(); void InsertBrunsHook(); // Bruns: bruns.exe void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe void InsertLucifenHook(); // Lucifen@Navel: *.lpk From 1f7d8d77a822a5cf99339b76cd60e6de43c598bf Mon Sep 17 00:00:00 2001 From: Shoaib Shakeel Date: Mon, 19 Sep 2022 20:53:12 +0200 Subject: [PATCH 16/17] Add Unityx86, Mono, WOLFRPG and Godot engine hook support (#17) example-game: [NoMeme] PHOENIXES Fix By [DDWSdwqdq](https://github.com/Artikash/Textractor/pull/834) Co-authored-by: DDWSdwqdq <53893117+DDWSdwqdq@users.noreply.github.com> --- texthook/engine/match64.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/texthook/engine/match64.cc b/texthook/engine/match64.cc index 2072073..d796762 100644 --- a/texthook/engine/match64.cc +++ b/texthook/engine/match64.cc @@ -9,6 +9,7 @@ namespace Engine { + enum : DWORD { X64_MAX_REL_ADDR = 0x00300000 }; /** Artikash 6/7/2019 * PPSSPP JIT code has pointers, but they are all added to an offset before being used. Find that offset so that hook searching works properly. From ff5ea30230c48889352f39d32fa4aee8f5bea226 Mon Sep 17 00:00:00 2001 From: Shoaib Shakeel Date: Mon, 19 Sep 2022 21:05:33 +0200 Subject: [PATCH 17/17] WillPlus3 hook (#18) Fixed By [lgztx96](https://github.com/Artikash/Textractor/pull/880) Co-authored-by: lgztx --- texthook/engine/engine.cc | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/texthook/engine/engine.cc b/texthook/engine/engine.cc index c247b65..7343116 100644 --- a/texthook/engine/engine.cc +++ b/texthook/engine/engine.cc @@ -9672,7 +9672,7 @@ static bool InsertNewWillPlusHook() found = true; } /* - hook cmp esi,0x3000 + hook cmp esi or ebx,0x3000 Sample games: https://vndb.org/r54549 https://vndb.org/v22705 @@ -9680,17 +9680,25 @@ static bool InsertNewWillPlusHook() https://vndb.org/v25719 https://vndb.org/v27227 https://vndb.org/v27385 + https://vndb.org/v34544 + https://vndb.org/v35279 + https://vndb.org/r94284 */ const BYTE pattern[] = { - 0x81,0xfe,0x00,0x30,0x00,0x00 //81FE 00300000 cmp esi,0x3000 + 0x81,XX, 0x00,0x30,0x00,0x00 // 81FE or FB 00300000 cmp esi or ebx,0x3000 + // je xx + // hook here }; for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress)) { + BYTE byte = *(BYTE*)(addr + 1); + if (byte != 0xfe && byte != 0xfb) + continue; HookParam hp = {}; - hp.address = addr; + hp.address = addr + 8; hp.type = USING_UNICODE; - hp.offset = pusha_esi_off - 4; + hp.offset = byte == 0xfe ? pusha_esi_off - 4 : pusha_ebx_off - 4; hp.length_offset = 1; NewHook(hp, "WillPlus3"); found = true; @@ -9703,10 +9711,10 @@ static bool InsertNewWillPlusHook() bool InsertWillPlusHook() { - bool ok = InsertOldWillPlusHook(); - ok = InsertWillPlusWHook() || InsertWillPlusAHook() || InsertNewWillPlusHook() || ok; - if (!ok) PcHooks::hookOtherPcFunctions(); - return ok; + bool ok = InsertOldWillPlusHook(); + ok = InsertWillPlusWHook() || InsertNewWillPlusHook() || InsertWillPlusAHook() || ok; + if (!ok) PcHooks::hookOtherPcFunctions(); + return ok; } /** jichi 9/14/2013