Merge remote-tracking branch 'hentaitaku/dev'
This commit is contained in:
commit
8ad82f0467
@ -7983,6 +7983,7 @@ bool InsertCotophaHook2()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool InsertCotophaHook4()
|
bool InsertCotophaHook4()
|
||||||
{
|
{
|
||||||
//by Blu3train
|
//by Blu3train
|
||||||
@ -8022,10 +8023,29 @@ bool InsertCotophaHook4()
|
|||||||
}
|
}
|
||||||
return false;
|
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()
|
bool InsertCotophaHook()
|
||||||
{
|
{
|
||||||
return InsertCotophaHook4() || InsertCotophaHook1() || InsertCotophaHook2();
|
return InsertCotophaHook4() || InsertCotophaHook1() || InsertCotophaHook3() || InsertCotophaHook2();
|
||||||
}
|
}
|
||||||
|
|
||||||
// jichi 5/10/2014
|
// jichi 5/10/2014
|
||||||
@ -10980,12 +11000,63 @@ bool InsertWolf2Hook()
|
|||||||
NewHook(hp, "WolfRPG2");
|
NewHook(hp, "WolfRPG2");
|
||||||
return true;
|
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
|
} // WolfRPG namespace
|
||||||
|
|
||||||
bool InsertWolfHook()
|
bool InsertWolfHook()
|
||||||
{
|
{
|
||||||
return InsertOldWolfHook(), InsertWolf2Hook();
|
return InsertOldWolfHook(), InsertWolf2Hook(), InsertWolf3Hook(), InsertWolf4Hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
||||||
@ -11570,7 +11641,7 @@ static bool InsertNewWillPlusHook()
|
|||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
hook cmp esi,0x3000
|
hook cmp esi or ebx,0x3000
|
||||||
Sample games:
|
Sample games:
|
||||||
https://vndb.org/r54549
|
https://vndb.org/r54549
|
||||||
https://vndb.org/v22705
|
https://vndb.org/v22705
|
||||||
@ -11578,17 +11649,25 @@ static bool InsertNewWillPlusHook()
|
|||||||
https://vndb.org/v25719
|
https://vndb.org/v25719
|
||||||
https://vndb.org/v27227
|
https://vndb.org/v27227
|
||||||
https://vndb.org/v27385
|
https://vndb.org/v27385
|
||||||
|
https://vndb.org/v34544
|
||||||
|
https://vndb.org/v35279
|
||||||
|
https://vndb.org/r94284
|
||||||
*/
|
*/
|
||||||
const BYTE pattern[] =
|
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))
|
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 = {};
|
HookParam hp = {};
|
||||||
hp.address = addr;
|
hp.address = addr + 8;
|
||||||
hp.type = USING_UNICODE;
|
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;
|
hp.length_offset = 1;
|
||||||
NewHook(hp, "WillPlus3");
|
NewHook(hp, "WillPlus3");
|
||||||
found = true;
|
found = true;
|
||||||
@ -19904,9 +19983,115 @@ bool InsertRenpyHook()
|
|||||||
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void InsertMonoHook(HMODULE h)
|
||||||
{
|
{
|
||||||
static HMODULE mono = h;
|
static HMODULE mono = h;
|
||||||
|
if (InsertMonoHooksByAssembly(mono))
|
||||||
|
return ;
|
||||||
/* Artikash 2/13/2019:
|
/* Artikash 2/13/2019:
|
||||||
How to hook Mono/Unity3D:
|
How to hook Mono/Unity3D:
|
||||||
Find all standard function prologs in memory with write/execute permission: these represent possible JIT compiled functions
|
Find all standard function prologs in memory with write/execute permission: these represent possible JIT compiled functions
|
||||||
@ -19993,7 +20178,49 @@ bool NoAsciiFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
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 Anim3Filter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
bool Anim3Filter(LPVOID data, DWORD *size, HookParam *, BYTE)
|
||||||
{
|
{
|
||||||
auto text = reinterpret_cast<LPSTR>(data);
|
auto text = reinterpret_cast<LPSTR>(data);
|
||||||
|
@ -157,6 +157,9 @@ bool InsertWillPlusHook(); // WillPlus: Rio.arc
|
|||||||
bool InsertWolfHook(); // Wolf: Data.wolf
|
bool InsertWolfHook(); // Wolf: Data.wolf
|
||||||
bool InsertYukaSystemHooks(); // YukaSystem2: *.ykc
|
bool InsertYukaSystemHooks(); // YukaSystem2: *.ykc
|
||||||
bool InsertYurisHook(); // YU-RIS: *.ypf
|
bool InsertYurisHook(); // YU-RIS: *.ypf
|
||||||
|
bool InsertAnimHook(); //
|
||||||
|
bool InsertAnim2Hook(); //
|
||||||
|
bool InsertCotophaHook3();
|
||||||
bool InsertONScripterruHooks(); // ONScripter-RU: resource string
|
bool InsertONScripterruHooks(); // ONScripter-RU: resource string
|
||||||
bool InsertSakanaGLHook(); // SakanaGL: sakanagl.dll
|
bool InsertSakanaGLHook(); // SakanaGL: sakanagl.dll
|
||||||
bool InsertDebonosuWorksHook(); // DebonosuWorks: resource string
|
bool InsertDebonosuWorksHook(); // DebonosuWorks: resource string
|
||||||
|
@ -556,9 +556,10 @@ bool DetermineEngineByFile4()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Util::CheckFile(L"voice\\*.pck")) {
|
if (Util::CheckFile(L"voice\\*.pck")) {
|
||||||
return /*InsertAnimHook() || InsertAnim2Hook() ||*/ InsertAnim3Hook();
|
return InsertAnimHook() || InsertAnim2Hook() || InsertAnim3Hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// jichi 11/22/2015: 凍京NECRO 体験版
|
// jichi 11/22/2015: 凍京NECRO 体験版
|
||||||
// Jazzinghen 23/05/2020: Add check for 凍京NECRO
|
// Jazzinghen 23/05/2020: Add check for 凍京NECRO
|
||||||
// ResEdit shows multiple potential strings:
|
// ResEdit shows multiple potential strings:
|
||||||
|
@ -383,9 +383,132 @@ namespace Engine
|
|||||||
}
|
}
|
||||||
return found;
|
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)
|
bool InsertMonoHooks(HMODULE module)
|
||||||
{
|
{
|
||||||
|
return InsertMonoHooksByAssembly(module);
|
||||||
auto SpecialHookMonoString = nullptr;
|
auto SpecialHookMonoString = nullptr;
|
||||||
static HMODULE mono = module;
|
static HMODULE mono = module;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
@ -567,7 +690,42 @@ namespace Engine
|
|||||||
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
ConsoleOutput("Textractor: Ren'py failed: failed to find python2X.dll");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool InsertGodotHook2_X64() {
|
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 InsertGodotHook2_X64() {
|
||||||
//by Blu3train
|
//by Blu3train
|
||||||
/*
|
/*
|
||||||
* Sample games:
|
* Sample games:
|
||||||
|
Loading…
Reference in New Issue
Block a user