From 44ad71eb812628b415b8ccb11b22aecd23d87049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <101191390+HIllya51@users.noreply.github.com> Date: Tue, 22 Oct 2024 00:49:26 +0800 Subject: [PATCH] ... --- LunaHook/engine32/WillPlus.cpp | 3035 ++++++++++++++++---------------- 1 file changed, 1554 insertions(+), 1481 deletions(-) diff --git a/LunaHook/engine32/WillPlus.cpp b/LunaHook/engine32/WillPlus.cpp index 95a8b29..bf44c4a 100644 --- a/LunaHook/engine32/WillPlus.cpp +++ b/LunaHook/engine32/WillPlus.cpp @@ -1,4 +1,4 @@ -#include"WillPlus.h" +#include "WillPlus.h" /** 1/18/2015 jichi Add new WillPlus * The old hook no longer works for new game. * Sample game: [150129] [honeybee] RE:BIRTHDAY SONG @@ -109,318 +109,339 @@ * 0012ed64 00951d88 ascii "2015/01/18" */ -namespace { // unnamed +namespace +{ // unnamed + void SpecialHookWillPlus(hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) + { + // static DWORD detect_offset; // jichi 1/18/2015: this makes sure it only runs once + // if (detect_offset) + // return; + DWORD i, l; + union + { + DWORD retn; + WORD *pw; + BYTE *pb; + }; + retn = stack->retaddr; // jichi 1/18/2015: dynamically find function return address + i = 0; + while (*pw != 0xc483) + { // add esp, $ + l = ::disasm(pb); + if (++i == 5) + // ConsoleOutput("Fail to detect offset."); + break; + retn += l; + } + // jichi 2/11/2015: Check baddaddr which might crash the game on Windows XP. + if (*pw == 0xc483 && !::IsBadReadPtr((LPCVOID)(pb + 2), 1) && !::IsBadReadPtr((LPCVOID)(*(pb + 2) - 8), 1)) + { + ConsoleOutput("WillPlus1 pattern found"); + // jichi 1/18/2015: + // By studying [honeybee] RE:BIRTHDAY SONG, it seems the scenario text is at fixed address + // This offset might be used to find fixed address + // However, this method cannot extract character name like GetGlyphOutlineA + hp->offset = *(pb + 2) - 8; -void SpecialHookWillPlus(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len) -{ - //static DWORD detect_offset; // jichi 1/18/2015: this makes sure it only runs once - //if (detect_offset) - // return; - DWORD i,l; - union { - DWORD retn; - WORD *pw; - BYTE *pb; + // Still extract the first text + // hp->type ^= EXTERN_HOOK; + char *str = *(char **)(stack->base + hp->offset); + *data = (DWORD)str; + *len = ::strlen(str); + *split = 0; // 8/3/2014 jichi: use return address as split + } + else + { // jichi 1/19/2015: Try willplus2 + ConsoleOutput("WillPlus1 pattern not found, try WillPlus2 instead"); + hp->offset = 4 * 8; // arg8, address of text + hp->type = USING_STRING | NO_CONTEXT | USING_SPLIT; // merge different scenario threads + hp->split = 4 * 1; // arg1 as split to get rid of saving message + // The first text is skipped here + // char *str = *(char **)(esp_base + hp->offset); + //*data = (DWORD)str; + //*len = ::strlen(str); + } + hp->text_fun = nullptr; // stop using text_fun any more + // detect_offset = 1; + } + + // Although the new hook also works for the old game, the old hook is still used by default for compatibility + bool InsertOldWillPlusHook() + { + //__debugbreak(); + enum + { + sub_esp = 0xec81 + }; // jichi: caller pattern: sub esp = 0x81,0xec byte + ULONG addr = MemDbg::findCallerAddress((ULONG)::GetGlyphOutlineA, sub_esp, processStartAddress, processStopAddress); + if (!addr) + { + ConsoleOutput("WillPlus: function call not found"); + return false; + } + + HookParam hp; + hp.address = addr; + hp.text_fun = SpecialHookWillPlus; + hp.type = USING_STRING; + ConsoleOutput("INSERT WillPlus"); + return NewHook(hp, "WillPlus"); + } + + const char *_willplus_trim_a(const char *text, size_t *size) + { + int textSize = ::strlen(text); + int prefix = 0; + if (text[0] == '%') + { + while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix + 1])) + { + prefix += 2; + while (::isupper(text[prefix])) + prefix++; + } + } + { + int pos = textSize; + for (int i = textSize - 1; i >= prefix; i--) + { + char ch = text[i]; + if (::isupper(ch)) + ; + else if (ch == '%') + pos = i; + else + break; + } + int suffix = textSize - pos; + if (size) + *size = textSize - prefix - suffix; + } + return text + prefix; + } + + const wchar_t *_willplus_trim_w(const wchar_t *text, size_t *size) + { + int textSize = ::wcslen(text); + int prefix = 0; + if (text[0] == '%') + { + while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix + 1])) + { + prefix += 2; + while (::isupper(text[prefix])) + prefix++; + } + } + { + int pos = textSize; + for (int i = textSize - 1; i >= prefix; i--) + { + wchar_t ch = text[i]; + if (::isupper(ch)) + ; + else if (ch == '%') + pos = i; + else + break; + } + int suffix = textSize - pos; + if (size) + *size = textSize - prefix - suffix; + } + return text + prefix; + } + + void SpecialHookWillPlusA(hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) + { + int index = 0; + auto text = (LPCSTR)stack->eax; + if (!text) + return; + if (index) // index == 1 is name + text -= 1024; + if (!*text) + return; + text = _willplus_trim_a(text, (size_t *)len); + *data = (DWORD)text; + *split = FIXED_SPLIT_VALUE << index; + } + bool WillPlus_extra_filter(void *data, size_t *size, HookParam *) + { + + auto text = reinterpret_cast(data); + StringFilter(text, size, L"%XS", 5); // remove %XS followed by 2 chars + std::wstring str = text; + str = str.substr(0, *size / 2); + strReplace(str, L"\\n", L"\n"); + std::wregex reg1(L"\\{(.*?):(.*?)\\}"); + std::wstring result1 = std::regex_replace(str, reg1, L"$1"); + + std::wregex reg11(L"\\{(.*?);(.*?)\\}"); + result1 = std::regex_replace(result1, reg11, L"$1"); + + std::wregex reg2(L"%[A-Z]+"); + result1 = std::regex_replace(result1, reg2, L""); + + write_string_overwrite(data, size, result1); + return true; }; - retn = stack->retaddr; // jichi 1/18/2015: dynamically find function return address - i = 0; - while (*pw != 0xc483) { // add esp, $ - l = ::disasm(pb); - if (++i == 5) - //ConsoleOutput("Fail to detect offset."); - break; - retn += l; - } - // jichi 2/11/2015: Check baddaddr which might crash the game on Windows XP. - if (*pw == 0xc483 && !::IsBadReadPtr((LPCVOID)(pb + 2), 1) && !::IsBadReadPtr((LPCVOID)(*(pb + 2) - 8), 1)) { - ConsoleOutput("WillPlus1 pattern found"); - // jichi 1/18/2015: - // By studying [honeybee] RE:BIRTHDAY SONG, it seems the scenario text is at fixed address - // This offset might be used to find fixed address - // However, this method cannot extract character name like GetGlyphOutlineA - hp->offset = *(pb + 2) - 8; - - // Still extract the first text - //hp->type ^= EXTERN_HOOK; - char *str = *(char **)(stack->base + hp->offset); - *data = (DWORD)str; - *len = ::strlen(str); - *split = 0; // 8/3/2014 jichi: use return address as split - - } else { // jichi 1/19/2015: Try willplus2 - ConsoleOutput("WillPlus1 pattern not found, try WillPlus2 instead"); - hp->offset = 4 * 8; // arg8, address of text - hp->type = USING_STRING|NO_CONTEXT|USING_SPLIT; // merge different scenario threads - hp->split = 4 * 1; // arg1 as split to get rid of saving message - // The first text is skipped here - //char *str = *(char **)(esp_base + hp->offset); - //*data = (DWORD)str; - //*len = ::strlen(str); - } - hp->text_fun = nullptr; // stop using text_fun any more - //detect_offset = 1; -} - -// Although the new hook also works for the old game, the old hook is still used by default for compatibility -bool InsertOldWillPlusHook() -{ - //__debugbreak(); - enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec byte - ULONG addr = MemDbg::findCallerAddress((ULONG)::GetGlyphOutlineA, sub_esp, processStartAddress, processStopAddress); - if (!addr) { - ConsoleOutput("WillPlus: function call not found"); - return false; - } - - HookParam hp; - hp.address = addr; - hp.text_fun = SpecialHookWillPlus; - hp.type = USING_STRING; - ConsoleOutput("INSERT WillPlus"); - return NewHook(hp, "WillPlus"); -} - -const char *_willplus_trim_a(const char *text, size_t *size) -{ - int textSize = ::strlen(text); - int prefix = 0; - if (text[0] == '%') { - while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix+1])) { - prefix += 2; - while (::isupper(text[prefix])) - prefix++; - } - } + bool InsertWillPlusAHook() { - int pos = textSize; - for (int i = textSize - 1; i >= prefix; i--) { - char ch = text[i]; - if (::isupper(ch)) - ; - else if (ch == '%') - pos = i; - else - break; - } - int suffix = textSize - pos; - if (size) - *size = textSize - prefix - suffix; - } - return text + prefix; -} - -const wchar_t *_willplus_trim_w(const wchar_t *text, size_t *size) -{ - int textSize = ::wcslen(text); - int prefix = 0; - if (text[0] == '%') { - while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix+1])) { - prefix += 2; - while (::isupper(text[prefix])) - prefix++; - } - } - { - int pos = textSize; - for (int i = textSize - 1; i >= prefix; i--) { - wchar_t ch = text[i]; - if (::isupper(ch)) - ; - else if (ch == '%') - pos = i; - else - break; - } - int suffix = textSize - pos; - if (size) - *size = textSize - prefix - suffix; - } - return text + prefix; -} - -void SpecialHookWillPlusA(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len) -{ - int index=0; - auto text = (LPCSTR)stack->eax; - if (!text) - return; - if (index) // index == 1 is name - text -= 1024; - if (!*text) - return; - text = _willplus_trim_a(text, (size_t *)len); - *data = (DWORD)text; - *split = FIXED_SPLIT_VALUE << index; -} -bool WillPlus_extra_filter(void* data, size_t* size, HookParam*) { - - auto text = reinterpret_cast(data); - StringFilter(text, size, L"%XS", 5); // remove %XS followed by 2 chars - std::wstring str = text; - str = str.substr(0, *size /2); - strReplace(str, L"\\n", L"\n"); - std::wregex reg1(L"\\{(.*?):(.*?)\\}"); - std::wstring result1 = std::regex_replace(str, reg1, L"$1"); - - std::wregex reg11(L"\\{(.*?);(.*?)\\}"); - result1 = std::regex_replace(result1, reg11, L"$1"); - - std::wregex reg2(L"%[A-Z]+"); - result1 = std::regex_replace(result1, reg2, L""); - - write_string_overwrite(data,size,result1); - return true; -}; -bool InsertWillPlusAHook() -{ - //by iov - const BYTE bytes2[] = { 0x8B,0x00,0xFF,0x76,0xFC,0x8B,0xCF,0x50 }; + // by iov + const BYTE bytes2[] = {0x8B, 0x00, 0xFF, 0x76, 0xFC, 0x8B, 0xCF, 0x50}; ULONG range2 = min(processStopAddress - processStartAddress, MAX_REL_ADDR); ULONG addr2 = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStartAddress + range2); - if (addr2) { - HookParam myhp; - myhp.address = addr2 + 2; + if (addr2) + { + HookParam myhp; + myhp.address = addr2 + 2; - myhp.type = CODEC_UTF16 | NO_CONTEXT | USING_STRING; - - myhp.offset=get_reg(regs::eax); - myhp.filter_fun=WillPlus_extra_filter; - char nameForUser[HOOK_NAME_SIZE] = "WillPlus3_memcpy"; - - ConsoleOutput("Insert: WillPlus3_memcpy Hook"); - return NewHook(myhp, nameForUser); - } - - const BYTE bytes[] = { - 0x81,0xec, 0x14,0x08,0x00,0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy - }; - DWORD addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); - if (!addr) { - ConsoleOutput("WillPlusA: pattern not found"); - return false; - } - HookParam hp; - hp.address = addr; - hp.text_fun = SpecialHookWillPlusA; - hp.type = NO_CONTEXT; - hp.filter_fun = NewLineStringFilterA; // remove two characters of "\\n" - ConsoleOutput("INSERT WillPlusA"); - return NewHook(hp, "WillPlusA"); -} + myhp.type = CODEC_UTF16 | NO_CONTEXT | USING_STRING; -void SpecialHookWillPlusW(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len) -{ - auto text = (LPCWSTR)stack->ecx; - if (!text || !*text) - return; - text = _willplus_trim_w(text, (size_t *)len); - *len *= 2; - *data = (DWORD)text; - *split = FIXED_SPLIT_VALUE << hp->user_value; -} + myhp.offset = get_reg(regs::eax); + myhp.filter_fun = WillPlus_extra_filter; + char nameForUser[HOOK_NAME_SIZE] = "WillPlus3_memcpy"; -bool InsertWillPlusWHook() -{ - const BYTE bytes1[] = { // scenario - 0x83,0xc0, 0x20, // 00452b02 83c0 20 add eax,0x20 ; jichi: hook before here, text in ecx - 0x33,0xd2, // 00452b05 33d2 xor edx,edx - 0x8b,0xc1, // 00452b07 8bc1 mov eax,ecx - 0xc7,0x84,0x24, 0xe0,0x01,0x00,0x00, 0x07,0x00,0x00,0x00 // 00452b09 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 - // 00452b14 c78424 dc010000 00000000 mov dword ptr ss:[esp+0x1dc],0x0 - }; - const BYTE bytes2[] = { // name - 0x33,0xdb, // 00453521 33db xor ebx,ebx ; jichi: hook here, text in ecx - 0x33,0xd2, // 00453523 33d2 xor edx,edx - 0x8b,0xc1, // 00453525 8bc1 mov eax,ecx - 0xc7,0x84,0x24, 0x88,0x00,0x00,0x00, 0x07,0x00,0x00,0x00 // 00453527 c78424 88000000 07000000 mov dword ptr ss:[esp+0x88],0x7 - // 00453532 899c24 84000000 mov dword ptr ss:[esp+0x84],ebx - }; - const BYTE *bytes[] = {bytes1, bytes2}; - const size_t sizes[] = {sizeof(bytes1), sizeof(bytes2)}; - auto succ=false; - for (int i = 0; i < 2; i++) { - DWORD addr = MemDbg::findBytes(bytes[i], sizes[i], processStartAddress, processStopAddress); - if (!addr) { - ConsoleOutput("WillPlusW: pattern not found"); + ConsoleOutput("Insert: WillPlus3_memcpy Hook"); + return NewHook(myhp, nameForUser); + } + + const BYTE bytes[] = { + 0x81, 0xec, 0x14, 0x08, 0x00, 0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy + }; + DWORD addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); + if (!addr) + { + ConsoleOutput("WillPlusA: pattern not found"); return false; } HookParam hp; hp.address = addr; - hp.text_fun = SpecialHookWillPlusW; - hp.type = NO_CONTEXT|CODEC_UTF16; - hp.user_value = i; - hp.filter_fun = NewLineStringFilterW; // remove two characters of "\\n" - ConsoleOutput("INSERT WillPlusW"); - succ|=NewHook(hp, "WillPlusW"); + hp.text_fun = SpecialHookWillPlusA; + hp.type = NO_CONTEXT; + hp.filter_fun = NewLineStringFilterA; // remove two characters of "\\n" + ConsoleOutput("INSERT WillPlusA"); + return NewHook(hp, "WillPlusA"); } - return succ; -} -/* - Artikash 9/29/2018: Updated WillPlus hook - Sample games: https://vndb.org/r54549 https://vndb.org/v22705 - Not too sure about the stability of this pattern, but it works for both of the above - Hook code for first game: /HQ-8*0@43D620. This seems fairly stable: __thiscall calling convention and first member points to string - Method to find hook code: trace call stack from GetGlyphOutlineW - Disassembly from first game (damekoi). The first few instructions are actually a common function prologue: not enough to locate hook - Hooking SysAllocString also seems to work, but has some garbage - 0043D61D - C2 0800 - ret 0008 { 8 } - 0043D620 - 55 - push ebp - 0043D621 - 8B EC - mov ebp,esp - 0043D623 - 6A FF - push -01 { 255 } - 0043D625 - 68 6B6D5400 - push 00546D6B { [139] } - 0043D62A - 64 A1 00000000 - mov eax,fs:[00000000] { 0 } - 0043D630 - 50 - push eax - 0043D631 - 81 EC 30010000 - sub esp,00000130 { 304 } - 0043D637 - A1 08E05800 - mov eax,[0058E008] { [6A9138CD] } - 0043D63C - 33 C5 - xor eax,ebp - 0043D63E - 89 45 EC - mov [ebp-14],eax - 0043D641 - 53 - push ebx - 0043D642 - 56 - push esi - 0043D643 - 57 - push edi - 0043D644 - 50 - push eax - 0043D645 - 8D 45 F4 - lea eax,[ebp-0C] - 0043D648 - 64 A3 00000000 - mov fs:[00000000],eax { 0 } - 0043D64E - 8B F9 - mov edi,ecx - 0043D650 - 89 BD E8FEFFFF - mov [ebp-00000118],edi - 0043D656 - 8B 45 08 - mov eax,[ebp+08] - 0043D659 - 8B 4D 14 - mov ecx,[ebp+14] - 0043D65C - F3 0F10 45 1C - movss xmm0,[ebp+1C] - 0043D661 - 8B 5D 18 - mov ebx,[ebp+18] - 0043D664 - 89 85 10FFFFFF - mov [ebp-000000F0],eax - 0043D66A - 8B 45 10 - mov eax,[ebp+10] - 0043D66D - 89 85 08FFFFFF - mov [ebp-000000F8],eax - 0043D673 - 89 47 68 - mov [edi+68],eax - 0043D676 - 8B 45 20 - mov eax,[ebp+20] - 0043D679 - 51 - push ecx - ... -*/ -static bool InsertNewWillPlusHook() -{ - bool found = false; - const BYTE characteristicInstructions[] = - { - 0xc2, 0x08, 0, // ret 0008; Seems to always be ret 8 before the hookable function. not sure why, not sure if stable. - 0x55, // push ebp; hook here - 0x8b, 0xec, // mov ebp,esp - 0x6a, 0xff, // push -01 - 0x68, XX4, // push ? - 0x64, 0xa1, 0, 0, 0, 0, // mov eax,fs:[0] - 0x50, // push eax - 0x81, 0xec, XX4, // sub esp,? - 0xa1, XX4, // mov eax,[?] - 0x33, 0xc5, // xor eax,ebp - //0x89, 0x45, 0xec // mov [ebp-14],eax; not sure if 0x14 is stable - }; - for (auto addr : Util::SearchMemory(characteristicInstructions, sizeof(characteristicInstructions), PAGE_EXECUTE, processStartAddress, processStopAddress)) - { - HookParam hp; - hp.address = addr + 3; - hp.type = USING_STRING | CODEC_UTF16 | DATA_INDIRECT; - hp.offset=get_reg(regs::ecx); - hp.index = 0; - found|=NewHook(hp, "WillPlus2"); - } + + void SpecialHookWillPlusW(hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) + { + auto text = (LPCWSTR)stack->ecx; + if (!text || !*text) + return; + text = _willplus_trim_w(text, (size_t *)len); + *len *= 2; + *data = (DWORD)text; + *split = FIXED_SPLIT_VALUE << hp->user_value; + } + + bool InsertWillPlusWHook() + { + const BYTE bytes1[] = { + // scenario + 0x83, 0xc0, 0x20, // 00452b02 83c0 20 add eax,0x20 ; jichi: hook before here, text in ecx + 0x33, 0xd2, // 00452b05 33d2 xor edx,edx + 0x8b, 0xc1, // 00452b07 8bc1 mov eax,ecx + 0xc7, 0x84, 0x24, 0xe0, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00 // 00452b09 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 + // 00452b14 c78424 dc010000 00000000 mov dword ptr ss:[esp+0x1dc],0x0 + }; + const BYTE bytes2[] = { + // name + 0x33, 0xdb, // 00453521 33db xor ebx,ebx ; jichi: hook here, text in ecx + 0x33, 0xd2, // 00453523 33d2 xor edx,edx + 0x8b, 0xc1, // 00453525 8bc1 mov eax,ecx + 0xc7, 0x84, 0x24, 0x88, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00 // 00453527 c78424 88000000 07000000 mov dword ptr ss:[esp+0x88],0x7 + // 00453532 899c24 84000000 mov dword ptr ss:[esp+0x84],ebx + }; + const BYTE *bytes[] = {bytes1, bytes2}; + const size_t sizes[] = {sizeof(bytes1), sizeof(bytes2)}; + auto succ = false; + for (int i = 0; i < 2; i++) + { + DWORD addr = MemDbg::findBytes(bytes[i], sizes[i], processStartAddress, processStopAddress); + if (!addr) + { + ConsoleOutput("WillPlusW: pattern not found"); + return false; + } + HookParam hp; + hp.address = addr; + hp.text_fun = SpecialHookWillPlusW; + hp.type = NO_CONTEXT | CODEC_UTF16; + hp.user_value = i; + hp.filter_fun = NewLineStringFilterW; // remove two characters of "\\n" + ConsoleOutput("INSERT WillPlusW"); + succ |= NewHook(hp, "WillPlusW"); + } + return succ; + } + /* + Artikash 9/29/2018: Updated WillPlus hook + Sample games: https://vndb.org/r54549 https://vndb.org/v22705 + Not too sure about the stability of this pattern, but it works for both of the above + Hook code for first game: /HQ-8*0@43D620. This seems fairly stable: __thiscall calling convention and first member points to string + Method to find hook code: trace call stack from GetGlyphOutlineW + Disassembly from first game (damekoi). The first few instructions are actually a common function prologue: not enough to locate hook + Hooking SysAllocString also seems to work, but has some garbage + 0043D61D - C2 0800 - ret 0008 { 8 } + 0043D620 - 55 - push ebp + 0043D621 - 8B EC - mov ebp,esp + 0043D623 - 6A FF - push -01 { 255 } + 0043D625 - 68 6B6D5400 - push 00546D6B { [139] } + 0043D62A - 64 A1 00000000 - mov eax,fs:[00000000] { 0 } + 0043D630 - 50 - push eax + 0043D631 - 81 EC 30010000 - sub esp,00000130 { 304 } + 0043D637 - A1 08E05800 - mov eax,[0058E008] { [6A9138CD] } + 0043D63C - 33 C5 - xor eax,ebp + 0043D63E - 89 45 EC - mov [ebp-14],eax + 0043D641 - 53 - push ebx + 0043D642 - 56 - push esi + 0043D643 - 57 - push edi + 0043D644 - 50 - push eax + 0043D645 - 8D 45 F4 - lea eax,[ebp-0C] + 0043D648 - 64 A3 00000000 - mov fs:[00000000],eax { 0 } + 0043D64E - 8B F9 - mov edi,ecx + 0043D650 - 89 BD E8FEFFFF - mov [ebp-00000118],edi + 0043D656 - 8B 45 08 - mov eax,[ebp+08] + 0043D659 - 8B 4D 14 - mov ecx,[ebp+14] + 0043D65C - F3 0F10 45 1C - movss xmm0,[ebp+1C] + 0043D661 - 8B 5D 18 - mov ebx,[ebp+18] + 0043D664 - 89 85 10FFFFFF - mov [ebp-000000F0],eax + 0043D66A - 8B 45 10 - mov eax,[ebp+10] + 0043D66D - 89 85 08FFFFFF - mov [ebp-000000F8],eax + 0043D673 - 89 47 68 - mov [edi+68],eax + 0043D676 - 8B 45 20 - mov eax,[ebp+20] + 0043D679 - 51 - push ecx + ... + */ + static bool InsertNewWillPlusHook() + { + bool found = false; + const BYTE characteristicInstructions[] = + { + 0xc2, 0x08, 0, // ret 0008; Seems to always be ret 8 before the hookable function. not sure why, not sure if stable. + 0x55, // push ebp; hook here + 0x8b, 0xec, // mov ebp,esp + 0x6a, 0xff, // push -01 + 0x68, XX4, // push ? + 0x64, 0xa1, 0, 0, 0, 0, // mov eax,fs:[0] + 0x50, // push eax + 0x81, 0xec, XX4, // sub esp,? + 0xa1, XX4, // mov eax,[?] + 0x33, 0xc5, // xor eax,ebp + // 0x89, 0x45, 0xec // mov [ebp-14],eax; not sure if 0x14 is stable + }; + for (auto addr : Util::SearchMemory(characteristicInstructions, sizeof(characteristicInstructions), PAGE_EXECUTE, processStartAddress, processStopAddress)) + { + HookParam hp; + hp.address = addr + 3; + hp.type = USING_STRING | CODEC_UTF16 | DATA_INDIRECT; + hp.offset = get_reg(regs::ecx); + hp.index = 0; + found |= NewHook(hp, "WillPlus2"); + } /* hook cmp reg,0x3000 Sample games: @@ -435,1274 +456,1326 @@ static bool InsertNewWillPlusHook() https://vndb.org/v36011 */ const BYTE pattern[] = - { - 0x81,XX, 0x00,0x30,0x00,0x00 // 81FE 00300000 cmp esi,0x3000 - // or 81FB 00300000 cmp ebx,0x3000 - // or 81FF 00300000 cmp edi,0x3000 - // je xx - // 8b4D A8 mov ecx,dword ptr ss:[ebp-??] hook here - // 85C9 test ecx,ecx - }; + { + 0x81, XX, 0x00, 0x30, 0x00, 0x00 // 81FE 00300000 cmp esi,0x3000 + // or 81FB 00300000 cmp ebx,0x3000 + // or 81FF 00300000 cmp edi,0x3000 + // je xx + // 8b4D A8 mov ecx,dword ptr ss:[ebp-??] hook here + // 85C9 test ecx,ecx + }; for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress)) { - if (*(WORD*)(addr + 0xb) != 0xC985) - continue; + if (*(WORD *)(addr + 0xb) != 0xC985) + continue; - BYTE byte = *(BYTE*)(addr + 1); - regs offset = regs::invalid; - switch (byte) { - case 0xf9: - offset = regs::ecx; - break; - case 0xfa: - offset = regs::edx; - break; - case 0xfb: - offset = regs::ebx; - break; - case 0xfc: - offset = regs::esp; - break; - case 0xfd: - offset = regs::ebp; - break; - case 0xfe: - offset = regs::esi; - break; - case 0xff: - offset = regs::edi; - break; - }; - if (offset!=regs::invalid) { - HookParam hp; - hp.address = addr + 8; - hp.type = CODEC_UTF16; - hp.offset=get_reg(offset); - found|=NewHook(hp, "WillPlus3"); - } + BYTE byte = *(BYTE *)(addr + 1); + regs offset = regs::invalid; + switch (byte) + { + case 0xf9: + offset = regs::ecx; + break; + case 0xfa: + offset = regs::edx; + break; + case 0xfb: + offset = regs::ebx; + break; + case 0xfc: + offset = regs::esp; + break; + case 0xfd: + offset = regs::ebp; + break; + case 0xfe: + offset = regs::esi; + break; + case 0xff: + offset = regs::edi; + break; + }; + if (offset != regs::invalid) + { + HookParam hp; + hp.address = addr + 8; + hp.type = CODEC_UTF16; + hp.offset = get_reg(offset); + found |= NewHook(hp, "WillPlus3"); + } } - if (!found) ConsoleOutput("WillPlus: failed to find instructions"); + if (!found) + ConsoleOutput("WillPlus: failed to find instructions"); return found; -} + } } // unnamed namespace - + bool InsertWillPlusHook() { - bool ok = InsertOldWillPlusHook(); - ok = InsertWillPlusWHook() || InsertNewWillPlusHook() || InsertWillPlusAHook() ||ok; - return ok; + bool ok = InsertOldWillPlusHook(); + ok = InsertWillPlusWHook() || InsertNewWillPlusHook() || InsertWillPlusAHook() || ok; + return ok; } -namespace will3{ - -int kp = 0;int lf=0;int lc=0; -bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role) -{ +namespace will3 +{ + + int kp = 0; + int lf = 0; + int lc = 0; + bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role) + { // DOUT(QString::fromUtf16((LPWSTR)s->stack[6]));//"MS UI Gothic" - //DOUT(QString::fromUtf16((LPWSTR)s->stack[7]));//"���������ˤˤʤꤿ����%K%P" - auto text = (LPWSTR)s->stack[7]; // text in arg1 - - if (!text || !*text) - return false; - auto split = s->stack[0]; // retaddr - - std::wstring str =((LPWSTR)s->stack[7] ); - kp=0;lf=0; - if (endWith(str,L"%K%P")){ - kp = 1; - - str = str.substr(0, str.size() - 4); - } - if(startWith(str,L"%LF")){ - lf=1; - str=str.substr(3); - } - if(startWith(str,L"%LC")){ - lc=1; - str=str.substr(3); - } - std::wregex reg1(L"\\{(.*?):(.*?)\\}"); - str = std::regex_replace(str, reg1, L"$1"); + // DOUT(QString::fromUtf16((LPWSTR)s->stack[7]));//"���������ˤˤʤꤿ����%K%P" + auto text = (LPWSTR)s->stack[7]; // text in arg1 - std::wregex reg11(L"\\{(.*?);(.*?)\\}"); - str = std::regex_replace(str, reg11, L"$1"); + if (!text || !*text) + return false; + auto split = s->stack[0]; // retaddr - write_string_overwrite(data,len,str); - - return true; - + std::wstring str = ((LPWSTR)s->stack[7]); + kp = 0; + lf = 0; + if (endWith(str, L"%K%P")) + { + kp = 1; + + str = str.substr(0, str.size() - 4); + } + if (startWith(str, L"%LF")) + { + lf = 1; + str = str.substr(3); + } + if (startWith(str, L"%LC")) + { + lc = 1; + str = str.substr(3); + } + std::wregex reg1(L"\\{(.*?):(.*?)\\}"); + str = std::regex_replace(str, reg1, L"$1"); + + std::wregex reg11(L"\\{(.*?);(.*?)\\}"); + str = std::regex_replace(str, reg11, L"$1"); + + write_string_overwrite(data, len, str); + + return true; + } + void hookafter(hook_stack *s, void *data, size_t len) + { + auto data_ = std::wstring((wchar_t *)data, len / 2); // EngineController::instance()->dispatchTextWSTD(innner, Engine::ScenarioRole, 0); + if (kp) + { + data_.append(L"%K%P"); + } + if (lf) + { + data_ = L"%LF" + data_; + } + if (lc) + { + data_ = L"%LC" + data_; + } + s->stack[7] = (ULONG)(data_.c_str()); + } } -void hookafter(hook_stack*s,void* data, size_t len){ - auto data_ =std::wstring((wchar_t*)data,len/2);// EngineController::instance()->dispatchTextWSTD(innner, Engine::ScenarioRole, 0); - if (kp) { - data_.append(L"%K%P"); - } - if(lf){ - data_=L"%LF"+data_; - }if(lc){ - data_=L"%LC"+data_; - } - s->stack[7] = (ULONG)(data_.c_str()); -} -} -bool InsertWillPlus4Hook() { - //星の乙女と六華の姉妹 +bool InsertWillPlus4Hook() +{ + // 星の乙女と六華の姉妹 const BYTE bytes[] = { - 0xc7,0x45,0xfc,0x00,0x00,0x00,0x00, - 0x33,0xc9, - 0xc7,0x47,0x78,0x00,0x00,0x00,0x00 - }; + 0xc7, 0x45, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x33, 0xc9, + 0xc7, 0x47, 0x78, 0x00, 0x00, 0x00, 0x00}; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); - if (addr == 0)return false; + if (addr == 0) + return false; addr = MemDbg::findEnclosingFunctionBeforeDword(0x83dc8b53, addr, MemDbg::MaximumFunctionSize, 1); - if (addr == 0)return false; + if (addr == 0) + return false; HookParam hp; hp.address = addr; - hp.offset =get_stack(7); - //hp.filter_fun = WillPlus_extra_filter; - hp.type = USING_STRING|CODEC_UTF16|EMBED_ABLE; - hp.hook_before=will3::hookBefore; - hp.newlineseperator=L"\\n"; - hp.hook_after=will3::hookafter; + hp.offset = get_stack(7); + // hp.filter_fun = WillPlus_extra_filter; + hp.type = USING_STRING | CODEC_UTF16 | EMBED_ABLE; + hp.hook_before = will3::hookBefore; + hp.newlineseperator = L"\\n"; + hp.hook_after = will3::hookafter; return NewHook(hp, "EmbedWillplus3"); } -bool InsertWillPlus5Hook() { - //ensemble 29th Project『乙女の剣と秘めごとコンチェルト』オフィシャルサイト 体验版 +bool InsertWillPlus5Hook() +{ + // ensemble 29th Project『乙女の剣と秘めごとコンチェルト』オフィシャルサイト 体验版 const BYTE bytes[] = { - 0x3d,XX2,0x00,0x00, - 0x72,XX, - 0x3d,XX2,0x00,0x00, - 0x77 - }; - /*if (v26 >= 0xE63E) - { - if (v26 <= 0xE757)*/ - /*3D 3E E6 00 00 cmp eax, 0E63Eh + 0x3d, XX2, 0x00, 0x00, + 0x72, XX, + 0x3d, XX2, 0x00, 0x00, + 0x77}; + /*if (v26 >= 0xE63E) + { + if (v26 <= 0xE757)*/ + /*3D 3E E6 00 00 cmp eax, 0E63Eh .text:0040A24B 72 6C jb short loc_40A2B9 .text : 0040A24B .text : 0040A24D 3D 57 E7 00 00 cmp eax, 0E757h .text : 0040A252 77 71 ja short loc_40A2C5*/ - - bool ok=false; - auto addrs= Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress); - for (auto addr : addrs) { + + bool ok = false; + auto addrs = Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress); + for (auto addr : addrs) + { HookParam hp; hp.address = addr; - hp.offset=get_reg(regs::eax); - hp.type = CODEC_UTF16; + hp.offset = get_reg(regs::eax); + hp.type = CODEC_UTF16; ConsoleOutput("INSERT WillPlus_extra2"); - ok|=NewHook(hp, "WillPlus_extra2"); - } - return ok; + ok |= NewHook(hp, "WillPlus_extra2"); + } + return ok; } -bool insertwillplus6(){ +bool insertwillplus6() +{ -/* 0x00492870 -0: 50 push eax -1: b8 01 00 00 00 mov eax,0x1 -6: 8d 74 24 18 lea esi,[esp+0x18] -a: e8 f1 f5 f6 ff call 0xfff6f600 -f: 6a 01 push 0x1 -11: 68 7c 47 55 00 push 0x55477c -16: 33 c0 xor eax,eax -18: 8b d6 mov edx,esi -1a: e8 21 8c f7 ff call 0xfff78c40 -//hook after call,但有的句子没有 -1f: 83 f8 ff cmp eax,0xffffffff -22: 75 dc jne 0x0 -//这里 -24: 8d 44 24 14 lea eax,[esp+0x14] -28: 8b cd mov ecx,ebp -2a: e8 81 f3 04 00 call 0x4f3b0 -2f: 83 7c 24 2c 08 cmp DWORD PTR [esp+0x2c],0x8 -34: 8b f0 mov esi,eax -36: 72 0d jb 0x45 -38: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] -3c: 50 push eax -3d: e8 5e d6 09 00 call 0x9d6a0 -42: 83 c4 04 add esp,0x4 -45: 33 c9 xor ecx,ecx -47: c7 44 24 2c 07 00 00 mov DWORD PTR [esp+0x2c],0x7 -*/ -//想いを捧げる乙女のメロディー + /* 0x00492870 + 0: 50 push eax + 1: b8 01 00 00 00 mov eax,0x1 + 6: 8d 74 24 18 lea esi,[esp+0x18] + a: e8 f1 f5 f6 ff call 0xfff6f600 + f: 6a 01 push 0x1 + 11: 68 7c 47 55 00 push 0x55477c + 16: 33 c0 xor eax,eax + 18: 8b d6 mov edx,esi + 1a: e8 21 8c f7 ff call 0xfff78c40 + //hook after call,但有的句子没有 + 1f: 83 f8 ff cmp eax,0xffffffff + 22: 75 dc jne 0x0 + //这里 + 24: 8d 44 24 14 lea eax,[esp+0x14] + 28: 8b cd mov ecx,ebp + 2a: e8 81 f3 04 00 call 0x4f3b0 + 2f: 83 7c 24 2c 08 cmp DWORD PTR [esp+0x2c],0x8 + 34: 8b f0 mov esi,eax + 36: 72 0d jb 0x45 + 38: 8b 44 24 18 mov eax,DWORD PTR [esp+0x18] + 3c: 50 push eax + 3d: e8 5e d6 09 00 call 0x9d6a0 + 42: 83 c4 04 add esp,0x4 + 45: 33 c9 xor ecx,ecx + 47: c7 44 24 2c 07 00 00 mov DWORD PTR [esp+0x2c],0x7 + */ + // 想いを捧げる乙女のメロディー const BYTE bytes[] = { - 0x6a,0x01, - 0x68,0x7c,0x47,0x55,0x00, - 0x33,0xc0, - 0x8b,0xd6, - 0xe8,XX4, - 0x83,0xf8, - 0xff,0x75,0xdc - }; + 0x6a, 0x01, + 0x68, 0x7c, 0x47, 0x55, 0x00, + 0x33, 0xc0, + 0x8b, 0xd6, + 0xe8, XX4, + 0x83, 0xf8, + 0xff, 0x75, 0xdc}; auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); - - if(addr==0)return false; - addr+=sizeof(bytes); - ConsoleOutput("%p %p %p",addr,processStartAddress, processStopAddress); + + if (addr == 0) + return false; + addr += sizeof(bytes); + ConsoleOutput("%p %p %p", addr, processStartAddress, processStopAddress); HookParam hp; - hp.address = addr; + hp.address = addr; hp.offset = get_stack(6); - hp.type = CODEC_UTF16|USING_STRING; + hp.type = CODEC_UTF16 | USING_STRING; ConsoleOutput("INSERT WillPlus6"); return NewHook(hp, "WillPlus6"); } -bool willX(){ -//世界でいちばんNGな恋 -// .text:0040EAE9 81 FE 94 81 00 00 cmp esi, 8194h -// .text:0040EAEF 74 2C jz short loc_40EB1D -// .text:0040EAEF -// .text:0040EAF1 81 FE 74 84 00 00 cmp esi, 8474h -// .text:0040EAF7 74 24 jz short loc_40EB1D -// .text:0040EAF7 -// .text:0040EAF9 81 FE 97 81 00 00 cmp esi, 8197h -// .text:0040EAFF 74 1C jz short loc_40EB1D -// .text:0040EAFF -// .text:0040EB01 81 FE 90 81 00 00 cmp esi, 8190h -// .text:0040EB07 74 14 jz short loc_40EB1D -// .text:0040EB07 -// .text:0040EB09 81 FE 59 81 00 00 cmp esi, 8159h -// .text:0040EB0F 74 0C jz short loc_40EB1D -// .text:0040EB0F -// .text:0040EB11 81 FE 96 81 00 00 cmp esi, 8196h -// .text:0040EB17 0F 85 FF 00 00 00 jnz loc_40EC1C +bool willX() +{ + // 世界でいちばんNGな恋 + // .text:0040EAE9 81 FE 94 81 00 00 cmp esi, 8194h + // .text:0040EAEF 74 2C jz short loc_40EB1D + // .text:0040EAEF + // .text:0040EAF1 81 FE 74 84 00 00 cmp esi, 8474h + // .text:0040EAF7 74 24 jz short loc_40EB1D + // .text:0040EAF7 + // .text:0040EAF9 81 FE 97 81 00 00 cmp esi, 8197h + // .text:0040EAFF 74 1C jz short loc_40EB1D + // .text:0040EAFF + // .text:0040EB01 81 FE 90 81 00 00 cmp esi, 8190h + // .text:0040EB07 74 14 jz short loc_40EB1D + // .text:0040EB07 + // .text:0040EB09 81 FE 59 81 00 00 cmp esi, 8159h + // .text:0040EB0F 74 0C jz short loc_40EB1D + // .text:0040EB0F + // .text:0040EB11 81 FE 96 81 00 00 cmp esi, 8196h + // .text:0040EB17 0F 85 FF 00 00 00 jnz loc_40EC1C const BYTE bytes[] = { - 0x81,0xFE,0x94,0x81,0x00,0x00, - 0x74,XX, - 0x81,0xFE,0x74,0x84,0x00,0x00, - 0x74,XX, - 0x81,0xFE,0x97,0x81,0x00,0x00, - 0x74,XX, - 0x81,0xFE,0x90,0x81,0x00,0x00, - 0x74,XX, - 0x81,0xFE,0x59,0x81,0x00,0x00, - 0x74,XX, - 0x81,0xFE,0x96,0x81,0x00,0x00 - }; + 0x81, 0xFE, 0x94, 0x81, 0x00, 0x00, + 0x74, XX, + 0x81, 0xFE, 0x74, 0x84, 0x00, 0x00, + 0x74, XX, + 0x81, 0xFE, 0x97, 0x81, 0x00, 0x00, + 0x74, XX, + 0x81, 0xFE, 0x90, 0x81, 0x00, 0x00, + 0x74, XX, + 0x81, 0xFE, 0x59, 0x81, 0x00, 0x00, + 0x74, XX, + 0x81, 0xFE, 0x96, 0x81, 0x00, 0x00}; auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); - - if(addr==0)return false; - auto succ=false; + + if (addr == 0) + return false; + auto succ = false; { HookParam hp; - hp.address = addr; - hp.offset=get_reg(regs::esi); - hp.type =NO_CONTEXT|CODEC_ANSI_BE; - succ|=NewHook(hp, "willAN"); + hp.address = addr; + hp.offset = get_reg(regs::esi); + hp.type = NO_CONTEXT | CODEC_ANSI_BE; + succ |= NewHook(hp, "willAN"); } - - addr=MemDbg::findEnclosingAlignedFunction(addr); - - if(addr ) + + addr = MemDbg::findEnclosingAlignedFunction(addr); + + if (addr) { HookParam hp; - hp.address = addr; - hp.offset =get_stack(7); - hp.type =USING_STRING; - succ|=NewHook(hp, "willS"); + hp.address = addr; + hp.offset = get_stack(7); + hp.type = USING_STRING; + succ |= NewHook(hp, "willS"); } return succ; } +namespace +{ // unnamed - - - - -namespace { // unnamed - -// Sample prefix: %LF -// Sample suffix: %L%P%W -template -strT trim(strT text, int *size) -{ - int length = *size; - if (text[0] == '%') { // handle prefix - int pos = 0; - while (pos < length - 1 && text[pos] == '%' && ::isupper(text[pos+1])) { - pos += 2; - while (::isupper(text[pos])) - pos++; - } - if (pos) { - length -= pos; - text += pos; - } - } - { // handle suffix - int pos = length; - for (int i = length - 1; i >= 0; i--) { - if (::isupper(text[i])) - ; - else if (text[i] == '%' && ::isupper(text[i+1])) - pos = i; - else - break; - } - length = pos; - } - *size = length; - return text; -} -struct textinfo{ - std::wstring text_; - int stackIndex_; - int role_; - }; -std::unordered_mapsavetyperef; -namespace TextHookW -{ - - // typedef TextHookW Self; - - template - bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role) + // Sample prefix: %LF + // Sample suffix: %L%P%W + template + strT trim(strT text, int *size) { - auto info=savetyperef.at(idx); - enum { sig = 0 }; - auto text = (LPCWSTR)s->stack[info->stackIndex_]; - if (!text || !*text) - return false; - int size = ::wcslen(text), - trimmedSize = size; - auto trimmedText = trim(text, &trimmedSize); - if (!trimmedSize || !*trimmedText) - return false; - std::wstring oldText = std::wstring(trimmedText, trimmedSize); - write_string_overwrite(data,len,oldText); - return true; + int length = *size; + if (text[0] == '%') + { // handle prefix + int pos = 0; + while (pos < length - 1 && text[pos] == '%' && ::isupper(text[pos + 1])) + { + pos += 2; + while (::isupper(text[pos])) + pos++; + } + if (pos) + { + length -= pos; + text += pos; + } + } + { // handle suffix + int pos = length; + for (int i = length - 1; i >= 0; i--) + { + if (::isupper(text[i])) + ; + else if (text[i] == '%' && ::isupper(text[i + 1])) + pos = i; + else + break; + } + length = pos; + } + *size = length; + return text; } - template - void hookafter(hook_stack*s,void* data, size_t len){ - auto newText =std::wstring((LPWSTR)data,len/2); - auto info=savetyperef.at(idx); - enum { sig = 0 }; - auto text = (LPCWSTR)s->stack[info->stackIndex_]; - if (!text || !*text) - return ; - int size = ::wcslen(text), - trimmedSize = size; - auto trimmedText = trim(text, &trimmedSize); - if (!trimmedSize || !*trimmedText) - return ; - std::wstring oldText = std::wstring(trimmedText, trimmedSize); - if (newText == oldText) - return ; - int prefixSize = trimmedText - text, - suffixSize = size - prefixSize - trimmedSize; - if (prefixSize) - newText.insert(0, std::wstring(text, prefixSize)); - if (suffixSize) - newText.append(std::wstring(trimmedText + trimmedSize, suffixSize)); - info->text_ = newText; - s->stack[info->stackIndex_] = (ULONG)info->text_.c_str(); - } - // explicit TextHookW(int hookStackIndex, int role = Engine::UnknownRole) : stackIndex_(hookStackIndex), role_(role) {} - template< int _type> - bool attach(const uint8_t *pattern, size_t patternSize, ULONG startAddress, ULONG stopAddress,int hookStackIndex, int role = Engine::UnknownRole) + struct textinfo { - ULONG addr = MemDbg::findBytes(pattern, patternSize, startAddress, stopAddress); - if(addr==0)return false; - HookParam hp; - hp.address=addr; - auto _tinfo=new textinfo{}; - _tinfo->role_=role; - _tinfo->stackIndex_=hookStackIndex; - savetyperef[_type]=_tinfo; - hp.hook_before=hookBefore<_type>; - hp.type=EMBED_ABLE|CODEC_UTF16; - hp.newlineseperator=L"\\n"; - hp.hook_after=hookafter<_type>; - hp.hook_font=F_MultiByteToWideChar|F_GetGlyphOutlineW; - char _[]="EmbedWillplusW0"; - _[sizeof(_)-2]+=_type; - return NewHook(hp,_); - } -}; - -/** - * Sample game: なついろレシピ - * See: http://capita.tistory.com/m/post/251 - * - * Scenario: - * 00452A8F 77 05 JA SHORT .00452A96 - * 00452A91 E8 A25B0B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo - * 00452A96 8B43 0C MOV EAX,DWORD PTR DS:[EBX+0xC] - * 00452A99 8B48 18 MOV ECX,DWORD PTR DS:[EAX+0x18] - * 00452A9C 83C0 10 ADD EAX,0x10 - * 00452A9F 33D2 XOR EDX,EDX - * 00452AA1 8BC1 MOV EAX,ECX - * 00452AA3 C78424 C4010000 >MOV DWORD PTR SS:[ESP+0x1C4],0x7 - * 00452AAE C78424 C0010000 >MOV DWORD PTR SS:[ESP+0x1C0],0x0 - * 00452AB9 66:899424 B00100>MOV WORD PTR SS:[ESP+0x1B0],DX - * 00452AC1 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] - * 00452AC4 66:8B10 MOV DX,WORD PTR DS:[EAX] - * 00452AC7 83C0 02 ADD EAX,0x2 - * 00452ACA 66:85D2 TEST DX,DX - * 00452ACD ^75 F5 JNZ SHORT .00452AC4 - * 00452ACF 2BC6 SUB EAX,ESI - * 00452AD1 D1F8 SAR EAX,1 - * 00452AD3 50 PUSH EAX - * 00452AD4 51 PUSH ECX - * 00452AD5 8DB424 B4010000 LEA ESI,DWORD PTR SS:[ESP+0x1B4] - * 00452ADC E8 DF4DFBFF CALL .004078C0 - * 00452AE1 C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0x8 - * 00452AE9 8B43 10 MOV EAX,DWORD PTR DS:[EBX+0x10] - * 00452AEC 2B43 0C SUB EAX,DWORD PTR DS:[EBX+0xC] - * 00452AEF C1F8 04 SAR EAX,0x4 - * 00452AF2 83F8 02 CMP EAX,0x2 - * 00452AF5 77 05 JA SHORT .00452AFC - * 00452AF7 E8 3C5B0B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo - * 00452AFC 8B43 0C MOV EAX,DWORD PTR DS:[EBX+0xC] - * 00452AFF 8B48 28 MOV ECX,DWORD PTR DS:[EAX+0x28] - * 00452B02 83C0 20 ADD EAX,0x20 ; jichi: hook before here, text in ecx - * 00452B05 33D2 XOR EDX,EDX - * 00452B07 8BC1 MOV EAX,ECX - * 00452B09 C78424 E0010000 07000000 MOV DWORD PTR SS:[ESP+0x1E0],0x7 ; jichi: key pattern is here, text in eax - * 00452B14 C78424 DC010000 00000000 MOV DWORD PTR SS:[ESP+0x1DC],0x0 - * 00452B27 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] - * 00452B2A 33DB XOR EBX,EBX - * 00452B2C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] - * 00452B30 66:8B10 MOV DX,WORD PTR DS:[EAX] - * 00452B33 83C0 02 ADD EAX,0x2 - * 00452B36 66:3BD3 CMP DX,BX - * 00452B39 ^75 F5 JNZ SHORT .00452B30 - * 00452B3B 2BC6 SUB EAX,ESI - * 00452B3D D1F8 SAR EAX,1 - * 00452B3F 50 PUSH EAX - * 00452B40 51 PUSH ECX - * 00452B41 8DB424 D0010000 LEA ESI,DWORD PTR SS:[ESP+0x1D0] - * 00452B48 E8 734DFBFF CALL .004078C0 - * 00452B4D C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0x9 - * 00452B55 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX - * 00452B59 395C24 14 CMP DWORD PTR SS:[ESP+0x14],EBX - * 00452B5D 0F84 77080000 JE .004533DA - * 00452B63 BE 07000000 MOV ESI,0x7 - * 00452B68 33C0 XOR EAX,EAX - * 00452B6A 895C24 20 MOV DWORD PTR SS:[ESP+0x20],EBX - * 00452B6E 89B424 FC010000 MOV DWORD PTR SS:[ESP+0x1FC],ESI - * 00452B75 899C24 F8010000 MOV DWORD PTR SS:[ESP+0x1F8],EBX - * 00452B7C 66:898424 E80100>MOV WORD PTR SS:[ESP+0x1E8],AX - * 00452B84 8D4C24 3C LEA ECX,DWORD PTR SS:[ESP+0x3C] - * 00452B88 51 PUSH ECX - * 00452B89 C68424 BC020000 >MOV BYTE PTR SS:[ESP+0x2BC],0xA - * 00452B91 E8 7AACFCFF CALL .0041D810 - * 00452B96 C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0xB - * 00452B9E 399C24 C0010000 CMP DWORD PTR SS:[ESP+0x1C0],EBX - * 00452BA5 0F84 BB020000 JE .00452E66 - * 00452BAB 81C7 14010000 ADD EDI,0x114 - */ -bool attachScenarioHookW1(ULONG startAddress, ULONG stopAddress) -{ - // ECX PTR: 83 C0 20 33 D2 8B C1 C7 84 24 E0 01 00 00 07 00 00 00 - const uint8_t bytes[] = { - 0x83,0xc0, 0x20, // 00452b02 83c0 20 add eax,0x20 ; jichi: hook before here, text in ecx - 0x33,0xd2, // 00452b05 33d2 xor edx,edx - 0x8b,0xc1, // 00452b07 8bc1 mov eax,ecx - 0xc7,0x84,0x24, 0xe0,0x01,0x00,0x00, 0x07,0x00,0x00,0x00 // 00452b09 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 - // 00452b14 c78424 dc010000 00000000 mov dword ptr ss:[esp+0x1dc],0x0 + std::wstring text_; + int stackIndex_; + int role_; }; - int ecx = get_reg(regs::ecx)/4; - return TextHookW::attach<1>(bytes, sizeof(bytes), startAddress, stopAddress,ecx,Engine::ScenarioRole); -} - -/** - * 1/9/2016: 見上げてごらん、夜空の星を 体験版 - * - * 0045580D C68424 B8020000 08 MOV BYTE PTR SS:[ESP+0x2B8],0x8 - * 00455815 8B47 10 MOV EAX,DWORD PTR DS:[EDI+0x10] - * 00455818 2B47 0C SUB EAX,DWORD PTR DS:[EDI+0xC] - * 0045581B C1F8 04 SAR EAX,0x4 - * 0045581E 83F8 02 CMP EAX,0x2 - * 00455821 77 05 JA SHORT .00455828 - * 00455823 E8 A0F70B00 CALL .00514FC8 ; JMP to msvcr90._invalid_parameter_noinfo - * 00455828 8B7F 0C MOV EDI,DWORD PTR DS:[EDI+0xC] - * 0045582B 83C7 20 ADD EDI,0x20 - * 0045582E 8B7F 08 MOV EDI,DWORD PTR DS:[EDI+0x8] - * 00455831 33C9 XOR ECX,ECX - * 00455833 8BC7 MOV EAX,EDI ; jichi: hook befoe here, text in eax assigned from edi - * 00455835 C78424 E0010000 07000000 MOV DWORD PTR SS:[ESP+0x1E0],0x7 ; jichi: key pattern is here, text i eax - * 00455840 899C24 DC010000 MOV DWORD PTR SS:[ESP+0x1DC],EBX - * 00455847 66:898C24 CC010000 MOV WORD PTR SS:[ESP+0x1CC],CX - * 0045584F 8D50 02 LEA EDX,DWORD PTR DS:[EAX+0x2] - * 00455852 66:8B08 MOV CX,WORD PTR DS:[EAX] - * 00455855 83C0 02 ADD EAX,0x2 - * 00455858 66:3BCB CMP CX,BX - * 0045585B ^75 F5 JNZ SHORT .00455852 - * 0045585D 2BC2 SUB EAX,EDX - * 0045585F D1F8 SAR EAX,1 - * 00455861 50 PUSH EAX - * 00455862 57 PUSH EDI - * 00455863 8DB424 D0010000 LEA ESI,DWORD PTR SS:[ESP+0x1D0] - * 0045586A E8 2120FBFF CALL .00407890 - * 0045586F C68424 B8020000 09 MOV BYTE PTR SS:[ESP+0x2B8],0x9 - * 00455877 895C24 30 MOV DWORD PTR SS:[ESP+0x30],EBX - * 0045587B 395C24 18 CMP DWORD PTR SS:[ESP+0x18],EBX - * 0045587F 0F84 D1080000 JE .00456156 - * 00455885 33D2 XOR EDX,EDX - * 00455887 895C24 24 MOV DWORD PTR SS:[ESP+0x24],EBX - * 0045588B C78424 FC010000 07000000 MOV DWORD PTR SS:[ESP+0x1FC],0x7 - * 00455896 899C24 F8010000 MOV DWORD PTR SS:[ESP+0x1F8],EBX - * 0045589D 66:899424 E8010000 MOV WORD PTR SS:[ESP+0x1E8],DX - * 004558A5 8D4424 3C LEA EAX,DWORD PTR SS:[ESP+0x3C] - */ -bool attachScenarioHookW2(ULONG startAddress, ULONG stopAddress) -{ - // key pattern: C78424 E0010000 07000000 - const uint8_t bytes[] = { - 0x8b,0xc7, // 00455833 8bc7 mov eax,edi ; jichi: text in eax assigned from edi - 0xc7,0x84,0x24, 0xe0,0x01,0x00,0x00, 0x07,0x00,0x00,0x00 // 00455835 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 ; jichi: key pattern is here, text i eax - }; - int edi = get_reg(regs::edi)/4; - return TextHookW::attach<2>(bytes, sizeof(bytes), startAddress, stopAddress,edi,Engine::ScenarioRole); - -} -/** - * Sample game: なついろレシピ - * See: http://capita.tistory.com/m/post/251 - * - * Name: - * - * 004534FA 64:A3 00000000 MOV DWORD PTR FS:[0],EAX - * 00453500 8B75 14 MOV ESI,DWORD PTR SS:[EBP+0x14] - * 00453503 8B46 10 MOV EAX,DWORD PTR DS:[ESI+0x10] - * 00453506 2B46 0C SUB EAX,DWORD PTR DS:[ESI+0xC] - * 00453509 8BF9 MOV EDI,ECX - * 0045350B C1F8 04 SAR EAX,0x4 - * 0045350E 897C24 14 MOV DWORD PTR SS:[ESP+0x14],EDI - * 00453512 85C0 TEST EAX,EAX - * 00453514 77 05 JA SHORT .0045351B - * 00453516 E8 1D510B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo - * 0045351B 8B76 0C MOV ESI,DWORD PTR DS:[ESI+0xC] - * 0045351E 8B4E 08 MOV ECX,DWORD PTR DS:[ESI+0x8] - * 00453521 33DB XOR EBX,EBX ; jichi: hook here, text in ecx - * 00453523 33D2 XOR EDX,EDX - * 00453525 8BC1 MOV EAX,ECX - * 00453527 C78424 88000000 07000000 MOV DWORD PTR SS:[ESP+0x88],0x7 - * 00453532 899C24 84000000 MOV DWORD PTR SS:[ESP+0x84],EBX - * 00453539 66:895424 74 MOV WORD PTR SS:[ESP+0x74],DX - * 0045353E 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] - * 00453541 66:8B10 MOV DX,WORD PTR DS:[EAX] - * 00453544 83C0 02 ADD EAX,0x2 - * 00453547 66:3BD3 CMP DX,BX - * 0045354A ^75 F5 JNZ SHORT .00453541 - * 0045354C 2BC6 SUB EAX,ESI - * 0045354E D1F8 SAR EAX,1 - * 00453550 50 PUSH EAX - * 00453551 51 PUSH ECX - * 00453552 8D7424 78 LEA ESI,DWORD PTR SS:[ESP+0x78] - * 00453556 E8 6543FBFF CALL .004078C0 - * 0045355B 899C24 70010000 MOV DWORD PTR SS:[ESP+0x170],EBX - * 00453562 A1 DCAA5500 MOV EAX,DWORD PTR DS:[0x55AADC] - * 00453567 894424 1C MOV DWORD PTR SS:[ESP+0x1C],EAX - * 0045356B B8 0F000000 MOV EAX,0xF - * 00453570 894424 6C MOV DWORD PTR SS:[ESP+0x6C],EAX - * 00453574 895C24 68 MOV DWORD PTR SS:[ESP+0x68],EBX - * 00453578 885C24 58 MOV BYTE PTR SS:[ESP+0x58],BL - * 0045357C 894424 50 MOV DWORD PTR SS:[ESP+0x50],EAX - * 00453580 895C24 4C MOV DWORD PTR SS:[ESP+0x4C],EBX - * 00453584 885C24 3C MOV BYTE PTR SS:[ESP+0x3C],BL - * 00453588 C68424 70010000 02 MOV BYTE PTR SS:[ESP+0x170],0x2 - * 00453590 8B8424 84000000 MOV EAX,DWORD PTR SS:[ESP+0x84] - * 00453597 8BF0 MOV ESI,EAX - * 00453599 3BC3 CMP EAX,EBX - * 0045359B 74 3D JE SHORT .004535DA - * 0045359D 83BC24 88000000 08 CMP DWORD PTR SS:[ESP+0x88],0x8 - * 004535A5 8B5424 74 MOV EDX,DWORD PTR SS:[ESP+0x74] - * 004535A9 73 04 JNB SHORT .004535AF - * 004535AB 8D5424 74 LEA EDX,DWORD PTR SS:[ESP+0x74] - */ -bool attachNameHookW(ULONG startAddress, ULONG stopAddress) -{ - // ECX PTR: 33 DB 33 D2 8B C1 C7 84 24 88 00 00 00 07 00 00 00 - const uint8_t bytes[] = { - 0x33,0xdb, // 00453521 33db xor ebx,ebx ; jichi: hook here, text in ecx - 0x33,0xd2, // 00453523 33d2 xor edx,edx - 0x8b,0xc1, // 00453525 8bc1 mov eax,ecx - 0xc7,0x84,0x24, 0x88,0x00,0x00,0x00, 0x07,0x00,0x00,0x00 // 00453527 c78424 88000000 07000000 mov dword ptr ss:[esp+0x88],0x7 - // 00453532 899c24 84000000 mov dword ptr ss:[esp+0x84],ebx - }; - - int ecx = get_reg(regs::ecx)/4; - return TextHookW::attach<3>(bytes, sizeof(bytes), startAddress, stopAddress,ecx,Engine::NameRole); - -} - -/** - * Sample game: なついろレシピ - * See: http://capita.tistory.com/m/post/251 - * - * Choice: - * 00470D95 72 05 JB SHORT .00470D9C - * 00470D97 E8 9C780900 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo - * 00470D9C 8BB5 EC020000 MOV ESI,DWORD PTR SS:[EBP+0x2EC] - * 00470DA2 037424 14 ADD ESI,DWORD PTR SS:[ESP+0x14] - * 00470DA6 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+0x10] - * 00470DA9 2B4E 0C SUB ECX,DWORD PTR DS:[ESI+0xC] - * 00470DAC C1F9 04 SAR ECX,0x4 - * 00470DAF 83F9 01 CMP ECX,0x1 - * 00470DB2 77 05 JA SHORT .00470DB9 - * 00470DB4 E8 7F780900 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo - * 00470DB9 8B46 0C MOV EAX,DWORD PTR DS:[ESI+0xC] - * 00470DBC 8B50 18 MOV EDX,DWORD PTR DS:[EAX+0x18] - * 00470DBF 83C0 10 ADD EAX,0x10 ; jichi: text in edx - * 00470DC2 52 PUSH EDX - * 00470DC3 8D8C24 7C040000 LEA ECX,DWORD PTR SS:[ESP+0x47C] - * 00470DCA 8D7424 4C LEA ESI,DWORD PTR SS:[ESP+0x4C] - * 00470DCE E8 EDA3F9FF CALL .0040B1C0 - * 00470DD3 83C4 04 ADD ESP,0x4 - * 00470DD6 6A FF PUSH -0x1 - * 00470DD8 53 PUSH EBX - * 00470DD9 50 PUSH EAX - * 00470DDA 8D8424 84040000 LEA EAX,DWORD PTR SS:[ESP+0x484] - * 00470DE1 C68424 B0040000 07 MOV BYTE PTR SS:[ESP+0x4B0],0x7 - * 00470DE9 E8 1251F9FF CALL .00405F00 - * 00470DEE BE 08000000 MOV ESI,0x8 - * 00470DF3 C68424 A4040000 06 MOV BYTE PTR SS:[ESP+0x4A4],0x6 - * 00470DFB 397424 60 CMP DWORD PTR SS:[ESP+0x60],ESI - * 00470DFF 72 0D JB SHORT .00470E0E - * 00470E01 8B4424 4C MOV EAX,DWORD PTR SS:[ESP+0x4C] - * 00470E05 50 PUSH EAX - * 00470E06 E8 65770900 CALL .00508570 ; JMP to msvcr90.??3@YAXPAX@Z - * 00470E0B 83C4 04 ADD ESP,0x4 - * 00470E0E 8B9424 7C040000 MOV EDX,DWORD PTR SS:[ESP+0x47C] - * 00470E15 33C9 XOR ECX,ECX - * 00470E17 C74424 60 07000000 MOV DWORD PTR SS:[ESP+0x60],0x7 - * 00470E1F 895C24 5C MOV DWORD PTR SS:[ESP+0x5C],EBX - * 00470E23 66:894C24 4C MOV WORD PTR SS:[ESP+0x4C],CX - * 00470E28 39B424 90040000 CMP DWORD PTR SS:[ESP+0x490],ESI - * 00470E2F 73 07 JNB SHORT .00470E38 - * 00470E31 8D9424 7C040000 LEA EDX,DWORD PTR SS:[ESP+0x47C] - * 00470E38 8B8424 44040000 MOV EAX,DWORD PTR SS:[ESP+0x444] - * 00470E3F B9 10000000 MOV ECX,0x10 - * 00470E44 398C24 58040000 CMP DWORD PTR SS:[ESP+0x458],ECX - * 00470E4B 73 07 JNB SHORT .00470E54 - * 00470E4D 8D8424 44040000 LEA EAX,DWORD PTR SS:[ESP+0x444] - * 00470E54 398C24 74040000 CMP DWORD PTR SS:[ESP+0x474],ECX - * 00470E5B 8B8C24 60040000 MOV ECX,DWORD PTR SS:[ESP+0x460] - */ -bool attachOtherHookW(ULONG startAddress, ULONG stopAddress) -{ - // EDX PTR : 83 C0 10 52 8D 8C 24 7C 04 00 00 8D 74 24 4C - const uint8_t bytes[] = { - 0x83,0xc0, 0x10, // 00470dbf 83c0 10 add eax,0x10 ; jichi: text in edx - 0x52, // 00470dc2 52 push edx - 0x8d,0x8c,0x24, 0x7c,0x04,0x00,0x00, // 00470dc3 8d8c24 7c040000 lea ecx,dword ptr ss:[esp+0x47c] - 0x8d,0x74,0x24, 0x4c // 00470dca 8d7424 4c lea esi,dword ptr ss:[esp+0x4c] - }; - - int edx = get_reg(regs::edx)/4; - return TextHookW::attach<4>(bytes, sizeof(bytes), startAddress, stopAddress,edx,Engine::OtherRole); - -} - -namespace PatchA { - -namespace Private { - // The second argument is always 0 and not used - bool isLeadByteChar(int ch, int) + std::unordered_map savetyperef; + namespace TextHookW { - return dynsjis::isleadchar(ch); - //return ::IsDBCSLeadByte(HIBYTE(testChar)); - } -} // namespace Private + // typedef TextHookW Self; -/** - * Sample game: Re:BIRTHDAY SONG - * - * 0x8140 is found by tracing the call of the caller of GetGlyphOutlineA. - - * 00487F8D 25 FF7F0000 AND EAX,0x7FFF - * 00487F92 C3 RETN - * 00487F93 8BFF MOV EDI,EDI - * 00487F95 55 PUSH EBP - * 00487F96 8BEC MOV EBP,ESP - * 00487F98 83EC 10 SUB ESP,0x10 - * 00487F9B FF75 0C PUSH DWORD PTR SS:[EBP+0xC] - * 00487F9E 8D4D F0 LEA ECX,DWORD PTR SS:[EBP-0x10] - * 00487FA1 E8 02EEFFFF CALL .00486DA8 - * 00487FA6 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] - * 00487FA9 C1E8 08 SHR EAX,0x8 - * 00487FAC 0FB6C8 MOVZX ECX,AL - * 00487FAF 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-0xC] - * 00487FB2 F64401 1D 04 TEST BYTE PTR DS:[ECX+EAX+0x1D],0x4 - * 00487FB7 74 10 JE SHORT .00487FC9 - * 00487FB9 0FB64D 08 MOVZX ECX,BYTE PTR SS:[EBP+0x8] - * 00487FBD F64401 1D 08 TEST BYTE PTR DS:[ECX+EAX+0x1D],0x8 - * 00487FC2 74 05 JE SHORT .00487FC9 - * 00487FC4 33C0 XOR EAX,EAX - * 00487FC6 40 INC EAX - * 00487FC7 EB 02 JMP SHORT .00487FCB - * 00487FC9 33C0 XOR EAX,EAX - * 00487FCB 807D FC 00 CMP BYTE PTR SS:[EBP-0x4],0x0 - * 00487FCF 74 07 JE SHORT .00487FD8 - * 00487FD1 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-0x8] - * 00487FD4 8361 70 FD AND DWORD PTR DS:[ECX+0x70],0xFFFFFFFD - * 00487FD8 C9 LEAVE - * 00487FD9 C3 RETN - * 00487FDA 8BFF MOV EDI,EDI ; jichi: called here, text in arg1 - * 00487FDC 55 PUSH EBP - * 00487FDD 8BEC MOV EBP,ESP - * 00487FDF 6A 00 PUSH 0x0 - * 00487FE1 FF75 08 PUSH DWORD PTR SS:[EBP+0x8] - * 00487FE4 E8 AAFFFFFF CALL .00487F93 ; jichi: called here - * 00487FE9 59 POP ECX - * 00487FEA 59 POP ECX - * 00487FEB 5D POP EBP - * 00487FEC C3 RETN - */ -using ulong=ULONG; -#define s1_call_ 0xe8 // near call, incomplete -#define s1_nop 0x90 // nop - -bool csmemcpy(void *dst, const void *src, size_t size) -{ - //return memcpy_(dst, src, size); - - DWORD oldProtect; - if (!::VirtualProtect(dst, size, PAGE_EXECUTE_READWRITE, &oldProtect)) - return false; - //HANDLE hProc = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, ::GetCurrentProcessId()); - //VirtualProtectEx(hProc, dst, size, PAGE_EXECUTE_READWRITE, &oldProtect); - - memcpy(dst, src, size); - - DWORD newProtect; - ::VirtualProtect(dst, size, oldProtect, &newProtect); // the error code is not checked for this function - //hProc = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, ::GetCurrentProcessId()); - //VirtualProtectEx(hProc, dst, size, oldProtect, &newProtect); - - return true; -} -ulong replace_near_call(ulong addr, ulong val) -{ - DWORD ret; - switch (::disasm((LPCVOID)addr)) { - case 5: // near call / short jmp: relative address - ret = *(DWORD *)(addr + 1) + (addr + 5); - val -= addr + 5; - return csmemcpy((LPVOID)(addr + 1), &val, sizeof(val)) ? ret : 0; - case 6: // far car / long jmp: absolute address + template + bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role) { - ret = *(DWORD *)(addr + 2); - BYTE data[6]; - data[0] = s1_call_; - data[5] = s1_nop; - *(DWORD *)(data + 1) = val - (addr + 5); - return csmemcpy((LPVOID)addr, data, sizeof(data)) ? ret : 0; + auto info = savetyperef.at(idx); + enum + { + sig = 0 + }; + auto text = (LPCWSTR)s->stack[info->stackIndex_]; + if (!text || !*text) + return false; + int size = ::wcslen(text), + trimmedSize = size; + auto trimmedText = trim(text, &trimmedSize); + if (!trimmedSize || !*trimmedText) + return false; + std::wstring oldText = std::wstring(trimmedText, trimmedSize); + write_string_overwrite(data, len, oldText); + return true; } - default: return 0; - } -} -ULONG patchEncoding(ULONG startAddress, ULONG stopAddress) -{ - const uint8_t bytes[] = { - 0x6a, 0x00, // 00487fdf 6a 00 push 0x0 - 0xff,0x75, 0x08, // 00487fe1 ff75 08 push dword ptr ss:[ebp+0x8] - 0xe8, 0xaa,0xff,0xff,0xff // 00487fe4 e8 aaffffff call .00487f93 ; jichi: called here - }; - enum { addr_offset = 5 }; - ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); - - return addr ;//&& replace_near_call(addr + addr_offset, (ULONG)Private::isLeadByteChar); -} - -} // namespace PatchA - -namespace ScenarioHookA { - -namespace Private { -/* - void dispatch(LPSTR text, int role) - { - enum { sig = 0 }; - if (!Engine::isAddressWritable(text) || !*text) // isAddressWritable is not needed for correct games - return; - int size = ::strlen(text), - trimmedSize = size; - auto trimmedText = trim(text, &trimmedSize); - if (!trimmedSize || !*trimmedText) - return; - std::string oldData(trimmedText, trimmedSize), - newData = EngineController::instance()->dispatchTextASTD(oldData, role, sig); - if (newData == oldData) - return; - if (trimmedText[trimmedSize]) - newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); - ::strcpy(text, newData.c_str()); - } -*/ - bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role) - { - auto text = (LPSTR)s->eax; - if (!text) - return false; - // dispatch(text - 1024, Engine::NameRole); - // dispatch(text, Engine::ScenarioRole); - - enum { sig = 0 }; - if (!Engine::isAddressWritable(text) || !*text) // isAddressWritable is not needed for correct games - return false; - int size = ::strlen(text), - trimmedSize = size; - auto trimmedText = trim(text, &trimmedSize); - if (!trimmedSize || !*trimmedText) - return false; - std::string oldData(trimmedText, trimmedSize); - - return write_string_overwrite(data,len,oldData); - /*newData = EngineController::instance()->dispatchTextASTD(oldData, role, sig); - if (newData == oldData) - return; - if (trimmedText[trimmedSize]) - newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); - ::strcpy(text, newData.c_str()); - return true;*/ - } - void hookafter(hook_stack*s,void* data, size_t len){ - - auto newData =std::string((char*)data,len); - auto text = (LPSTR)s->eax; - int size = ::strlen(text), - trimmedSize = size; - auto trimmedText = trim(text, &trimmedSize); - if (trimmedText[trimmedSize]) - newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); - ::strcpy(text, newData.c_str()); - } -} // namespace Private - -/** - * Sample games - * - [111028][PULLTOP] 神聖にして侵すべからず - * - Re:BIRTHDAY SONG~恋を唄う死神~(体験版) - * See: http://capita.tistory.com/m/post/84 - * - * ENCODEKOR,FORCEFONT(5),HOOK(0x0042B5E0,TRANS(0x004FFBF8,OVERWRITE(IGNORE)),RETNPOS(COPY),TRANS(0x004FF7F8,OVERWRITE(IGNORE))),HOOK(0x00413204,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x00424004,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x004242B9,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x00424109,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)) - * - * Scenario in eax - * Name in (eax - 1024) - * Memory can be directly overridden. - * - * 0042B5DE CC INT3 - * 0042B5DF CC INT3 - * 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy - * 0042B5E6 53 PUSH EBX - * 0042B5E7 55 PUSH EBP - * 0042B5E8 56 PUSH ESI - * 0042B5E9 33DB XOR EBX,EBX - * 0042B5EB 57 PUSH EDI - * 0042B5EC 8BF8 MOV EDI,EAX - * 0042B5EE 399C24 28080000 CMP DWORD PTR SS:[ESP+0x828],EBX - * 0042B5F5 75 13 JNZ SHORT .0042B60A - * 0042B5F7 68 74030000 PUSH 0x374 - * 0042B5FC 53 PUSH EBX - * 0042B5FD 68 7CC44F00 PUSH .004FC47C - * 0042B602 E8 09E60500 CALL .00489C10 - * 0042B607 83C4 0C ADD ESP,0xC - * 0042B60A 33F6 XOR ESI,ESI - * 0042B60C 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX - * 0042B610 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX - * 0042B614 381F CMP BYTE PTR DS:[EDI],BL - * 0042B616 0F84 0D020000 JE .0042B829 - * 0042B61C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] - * 0042B620 8A4C37 01 MOV CL,BYTE PTR DS:[EDI+ESI+0x1] - * 0042B624 84C9 TEST CL,CL - * 0042B626 0F84 E6010000 JE .0042B812 - * 0042B62C 66:0FB6043E MOVZX AX,BYTE PTR DS:[ESI+EDI] - * 0042B631 8D2C3E LEA EBP,DWORD PTR DS:[ESI+EDI] - * 0042B634 66:C1E0 08 SHL AX,0x8 - * 0042B638 0FB7C0 MOVZX EAX,AX - * 0042B63B 0FB6C9 MOVZX ECX,CL - * 0042B63E 0BC1 OR EAX,ECX - * 0042B640 50 PUSH EAX - * 0042B641 E8 34B40500 CALL .00486A7A - * 0042B646 83C4 04 ADD ESP,0x4 - * 0042B649 85C0 TEST EAX,EAX - * 0042B64B 74 14 JE SHORT .0042B661 - * 0042B64D 66:8B55 00 MOV DX,WORD PTR SS:[EBP] - * 0042B651 66:89541C 24 MOV WORD PTR SS:[ESP+EBX+0x24],DX - * 0042B656 83C3 02 ADD EBX,0x2 - * 0042B659 83C6 02 ADD ESI,0x2 - * 0042B65C E9 BA010000 JMP .0042B81B - * 0042B661 807D 00 7B CMP BYTE PTR SS:[EBP],0x7B - * 0042B665 0F85 60010000 JNZ .0042B7CB - * 0042B66B 8BC3 MOV EAX,EBX - * 0042B66D 2B4424 1C SUB EAX,DWORD PTR SS:[ESP+0x1C] - * 0042B671 46 INC ESI - * 0042B672 33ED XOR EBP,EBP - * 0042B674 894424 20 MOV DWORD PTR SS:[ESP+0x20],EAX - * 0042B678 896C24 14 MOV DWORD PTR SS:[ESP+0x14],EBP - * 0042B67C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] - * 0042B680 8A0C3E MOV CL,BYTE PTR DS:[ESI+EDI] - * 0042B683 84C9 TEST CL,CL - * 0042B685 0F84 B5010000 JE .0042B840 - * 0042B68B 0FB64437 01 MOVZX EAX,BYTE PTR DS:[EDI+ESI+0x1] - * 0042B690 66:0FB6C9 MOVZX CX,CL - * 0042B694 66:C1E1 08 SHL CX,0x8 - * 0042B698 0FB7D1 MOVZX EDX,CX - * 0042B69B 0BC2 OR EAX,EDX - * 0042B69D 50 PUSH EAX - * 0042B69E E8 D7B30500 CALL .00486A7A - * 0042B6A3 83C4 04 ADD ESP,0x4 - * 0042B6A6 85C0 TEST EAX,EAX - * 0042B6A8 74 1A JE SHORT .0042B6C4 - * 0042B6AA 66:8B043E MOV AX,WORD PTR DS:[ESI+EDI] - * 0042B6AE 834424 14 02 ADD DWORD PTR SS:[ESP+0x14],0x2 - * 0042B6B3 66:89441C 24 MOV WORD PTR SS:[ESP+EBX+0x24],AX - * 0042B6B8 83C3 02 ADD EBX,0x2 - * 0042B6BB 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX - * 0042B6BF 83C6 02 ADD ESI,0x2 - * 0042B6C2 ^EB BC JMP SHORT .0042B680 - * 0042B6C4 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] - * 0042B6C7 3C 3A CMP AL,0x3A - * 0042B6C9 74 10 JE SHORT .0042B6DB - * 0042B6CB FF4424 14 INC DWORD PTR SS:[ESP+0x14] - * 0042B6CF 88441C 24 MOV BYTE PTR SS:[ESP+EBX+0x24],AL - * 0042B6D3 43 INC EBX - * 0042B6D4 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX - * 0042B6D8 46 INC ESI - * 0042B6D9 ^EB A5 JMP SHORT .0042B680 - * 0042B6DB 896C24 18 MOV DWORD PTR SS:[ESP+0x18],EBP - * 0042B6DF 46 INC ESI - * 0042B6E0 8A0C3E MOV CL,BYTE PTR DS:[ESI+EDI] - * 0042B6E3 84C9 TEST CL,CL - * 0042B6E5 0F84 55010000 JE .0042B840 - * 0042B6EB 0FB64437 01 MOVZX EAX,BYTE PTR DS:[EDI+ESI+0x1] - * 0042B6F0 66:0FB6C9 MOVZX CX,CL - * 0042B6F4 66:C1E1 08 SHL CX,0x8 - * 0042B6F8 0FB7D1 MOVZX EDX,CX - * 0042B6FB 0BC2 OR EAX,EDX - * 0042B6FD 50 PUSH EAX - * 0042B6FE E8 77B30500 CALL .00486A7A - * 0042B703 83C4 04 ADD ESP,0x4 - * 0042B706 85C0 TEST EAX,EAX - * 0042B708 74 18 JE SHORT .0042B722 - * 0042B70A 66:8B043E MOV AX,WORD PTR DS:[ESI+EDI] - * 0042B70E FF4424 18 INC DWORD PTR SS:[ESP+0x18] - * 0042B712 66:89842C 240400>MOV WORD PTR SS:[ESP+EBP+0x424],AX - * 0042B71A 83C5 02 ADD EBP,0x2 - * 0042B71D 83C6 02 ADD ESI,0x2 - * 0042B720 ^EB BE JMP SHORT .0042B6E0 - * 0042B722 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] - * 0042B725 3C 7D CMP AL,0x7D - * 0042B727 74 0E JE SHORT .0042B737 - * 0042B729 FF4424 18 INC DWORD PTR SS:[ESP+0x18] - * 0042B72D 88842C 24040000 MOV BYTE PTR SS:[ESP+EBP+0x424],AL - * 0042B734 45 INC EBP - * 0042B735 ^EB A8 JMP SHORT .0042B6DF - * 0042B737 8D8424 24040000 LEA EAX,DWORD PTR SS:[ESP+0x424] - * 0042B73E 46 INC ESI - * 0042B73F C6842C 24040000 >MOV BYTE PTR SS:[ESP+EBP+0x424],0x0 - * 0042B747 8D50 01 LEA EDX,DWORD PTR DS:[EAX+0x1] - * 0042B74A 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX] - * 0042B750 8A08 MOV CL,BYTE PTR DS:[EAX] - * 0042B752 40 INC EAX - * 0042B753 84C9 TEST CL,CL - * 0042B755 ^75 F9 JNZ SHORT .0042B750 - * 0042B757 2BC2 SUB EAX,EDX - * 0042B759 83F8 1E CMP EAX,0x1E - * 0042B75C 0F87 DE000000 JA .0042B840 - * 0042B762 8B15 7CC44F00 MOV EDX,DWORD PTR DS:[0x4FC47C] - * 0042B768 83FA 14 CMP EDX,0x14 - * 0042B76B 0F8D AE000000 JGE .0042B81F - * 0042B771 6BD2 2C IMUL EDX,EDX,0x2C - * 0042B774 8D8C24 24040000 LEA ECX,DWORD PTR SS:[ESP+0x424] - * 0042B77B 81C2 8CC44F00 ADD EDX,.004FC48C - * 0042B781 8A01 MOV AL,BYTE PTR DS:[ECX] - * 0042B783 8802 MOV BYTE PTR DS:[EDX],AL - * 0042B785 41 INC ECX - * 0042B786 42 INC EDX - * 0042B787 84C0 TEST AL,AL - * 0042B789 ^75 F6 JNZ SHORT .0042B781 - * 0042B78B 8B0D 7CC44F00 MOV ECX,DWORD PTR DS:[0x4FC47C] - * 0042B791 8B5424 14 MOV EDX,DWORD PTR SS:[ESP+0x14] - * 0042B795 6BC9 2C IMUL ECX,ECX,0x2C - * 0042B798 8991 88C44F00 MOV DWORD PTR DS:[ECX+0x4FC488],EDX - * 0042B79E A1 7CC44F00 MOV EAX,DWORD PTR DS:[0x4FC47C] - * 0042B7A3 8B4C24 20 MOV ECX,DWORD PTR SS:[ESP+0x20] - * 0042B7A7 6BC0 2C IMUL EAX,EAX,0x2C - * 0042B7AA 8988 80C44F00 MOV DWORD PTR DS:[EAX+0x4FC480],ECX - * 0042B7B0 8B15 7CC44F00 MOV EDX,DWORD PTR DS:[0x4FC47C] - * 0042B7B6 8B4424 18 MOV EAX,DWORD PTR SS:[ESP+0x18] - * 0042B7BA 6BD2 2C IMUL EDX,EDX,0x2C - * 0042B7BD 8982 84C44F00 MOV DWORD PTR DS:[EDX+0x4FC484],EAX - * 0042B7C3 FF05 7CC44F00 INC DWORD PTR DS:[0x4FC47C] - * 0042B7C9 EB 54 JMP SHORT .0042B81F - * 0042B7CB 55 PUSH EBP - * 0042B7CC E8 7F000000 CALL .0042B850 - * 0042B7D1 8BD8 MOV EBX,EAX - * 0042B7D3 83C4 04 ADD ESP,0x4 - * 0042B7D6 85DB TEST EBX,EBX - * 0042B7D8 74 23 JE SHORT .0042B7FD - * 0042B7DA 53 PUSH EBX - * 0042B7DB 55 PUSH EBP - * 0042B7DC 8B6C24 18 MOV EBP,DWORD PTR SS:[ESP+0x18] - * 0042B7E0 8D4C2C 2C LEA ECX,DWORD PTR SS:[ESP+EBP+0x2C] - * 0042B7E4 51 PUSH ECX - * 0042B7E5 E8 A6E40500 CALL .00489C90 - * 0042B7EA 03EB ADD EBP,EBX - * 0042B7EC 03F3 ADD ESI,EBX - * 0042B7EE 83C4 0C ADD ESP,0xC - * 0042B7F1 015C24 1C ADD DWORD PTR SS:[ESP+0x1C],EBX - * 0042B7F5 896C24 10 MOV DWORD PTR SS:[ESP+0x10],EBP - * 0042B7F9 8BDD MOV EBX,EBP - * 0042B7FB EB 22 JMP SHORT .0042B81F - * 0042B7FD 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10] - * 0042B801 8A55 00 MOV DL,BYTE PTR SS:[EBP] - * 0042B804 40 INC EAX - * 0042B805 885404 23 MOV BYTE PTR SS:[ESP+EAX+0x23],DL - * 0042B809 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX - * 0042B80D 46 INC ESI - * 0042B80E 8BD8 MOV EBX,EAX - * 0042B810 EB 0D JMP SHORT .0042B81F - * 0042B812 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] - * 0042B815 88441C 24 MOV BYTE PTR SS:[ESP+EBX+0x24],AL - * 0042B819 43 INC EBX - * 0042B81A 46 INC ESI - * 0042B81B 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX - * 0042B81F 803C3E 00 CMP BYTE PTR DS:[ESI+EDI],0x0 - * 0042B823 ^0F85 F7FDFFFF JNZ .0042B620 - * 0042B829 8D4424 24 LEA EAX,DWORD PTR SS:[ESP+0x24] - * 0042B82D 8BC8 MOV ECX,EAX - * 0042B82F C6441C 24 00 MOV BYTE PTR SS:[ESP+EBX+0x24],0x0 - * 0042B834 2BF9 SUB EDI,ECX - * 0042B836 8A08 MOV CL,BYTE PTR DS:[EAX] - * 0042B838 880C07 MOV BYTE PTR DS:[EDI+EAX],CL - * 0042B83B 40 INC EAX - * 0042B83C 84C9 TEST CL,CL - * 0042B83E ^75 F6 JNZ SHORT .0042B836 - * 0042B840 5F POP EDI - * 0042B841 5E POP ESI - * 0042B842 5D POP EBP - * 0042B843 5B POP EBX - * 0042B844 81C4 14080000 ADD ESP,0x814 - * 0042B84A C3 RETN - * 0042B84B CC INT3 - * 0042B84C CC INT3 - * 0042B84D CC INT3 - * 0042B84E CC INT3 - * - * Skip scenario text: - * 00438EF1 51 PUSH ECX - * 00438EF2 56 PUSH ESI - * 00438EF3 57 PUSH EDI - * 00438EF4 52 PUSH EDX - * 00438EF5 6A 03 PUSH 0x3 ; jichi: scenario arg1 is always 3 - * 00438EF7 E8 14F3FDFF CALL .00418210 ; jichi: text called here - * 00438EFC 894424 4C MOV DWORD PTR SS:[ESP+0x4C],EAX - * 00438F00 8D4424 78 LEA EAX,DWORD PTR SS:[ESP+0x78] - * 00438F04 83C4 30 ADD ESP,0x30 - * 00438F07 897C24 34 MOV DWORD PTR SS:[ESP+0x34],EDI - * 00438F0B 897424 38 MOV DWORD PTR SS:[ESP+0x38],ESI - * 00438F0F 8D48 01 LEA ECX,DWORD PTR DS:[EAX+0x1] - * 00438F12 8A10 MOV DL,BYTE PTR DS:[EAX] - * 00438F14 40 INC EAX - * 00438F15 84D2 TEST DL,DL - */ -bool attach(ULONG startAddress, ULONG stopAddress) -{ - const uint8_t bytes[] = { - 0x81,0xec, 0x14,0x08,0x00,0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy - }; - ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); - if(addr==0)return false; - HookParam hp; - hp.address=addr; - - hp.hook_before= Private::hookBefore; - hp.type=EMBED_ABLE; - hp.newlineseperator=L"\\n"; - hp.hook_after=Private::hookafter; - hp.hook_font=F_GetGlyphOutlineA|F_TextOutA; - static ULONG paddr=(PatchA::patchEncoding(startAddress, stopAddress)); - ConsoleOutput("%p",paddr); - if(paddr){ - hp.type|=EMBED_DYNA_SJIS; - hp.hook_font=F_GetGlyphOutlineA|F_TextOutA; - patch_fun=[](){ - PatchA::replace_near_call(paddr + 5, (ULONG)PatchA::Private::isLeadByteChar); - - }; + template + void hookafter(hook_stack *s, void *data, size_t len) + { + auto newText = std::wstring((LPWSTR)data, len / 2); + auto info = savetyperef.at(idx); + enum + { + sig = 0 + }; + auto text = (LPCWSTR)s->stack[info->stackIndex_]; + if (!text || !*text) + return; + int size = ::wcslen(text), + trimmedSize = size; + auto trimmedText = trim(text, &trimmedSize); + if (!trimmedSize || !*trimmedText) + return; + std::wstring oldText = std::wstring(trimmedText, trimmedSize); + if (newText == oldText) + return; + int prefixSize = trimmedText - text, + suffixSize = size - prefixSize - trimmedSize; + if (prefixSize) + newText.insert(0, std::wstring(text, prefixSize)); + if (suffixSize) + newText.append(std::wstring(trimmedText + trimmedSize, suffixSize)); + info->text_ = newText; + s->stack[info->stackIndex_] = (ULONG)info->text_.c_str(); } - return NewHook(hp,"EmbedWillplusA"); -} + // explicit TextHookW(int hookStackIndex, int role = Engine::UnknownRole) : stackIndex_(hookStackIndex), role_(role) {} + template + bool attach(const uint8_t *pattern, size_t patternSize, ULONG startAddress, ULONG stopAddress, int hookStackIndex, int role = Engine::UnknownRole) + { + ULONG addr = MemDbg::findBytes(pattern, patternSize, startAddress, stopAddress); + if (addr == 0) + return false; + HookParam hp; + hp.address = addr; + auto _tinfo = new textinfo{}; + _tinfo->role_ = role; + _tinfo->stackIndex_ = hookStackIndex; + savetyperef[_type] = _tinfo; + hp.hook_before = hookBefore<_type>; + hp.type = EMBED_ABLE | CODEC_UTF16; + hp.newlineseperator = L"\\n"; + hp.hook_after = hookafter<_type>; + hp.hook_font = F_MultiByteToWideChar | F_GetGlyphOutlineW; + char _[] = "EmbedWillplusW0"; + _[sizeof(_) - 2] += _type; + return NewHook(hp, _); + } + }; -} // namespace ScenarioHookA - -namespace OtherHookA { - -namespace Private { - - bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role) + /** + * Sample game: なついろレシピ + * See: http://capita.tistory.com/m/post/251 + * + * Scenario: + * 00452A8F 77 05 JA SHORT .00452A96 + * 00452A91 E8 A25B0B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo + * 00452A96 8B43 0C MOV EAX,DWORD PTR DS:[EBX+0xC] + * 00452A99 8B48 18 MOV ECX,DWORD PTR DS:[EAX+0x18] + * 00452A9C 83C0 10 ADD EAX,0x10 + * 00452A9F 33D2 XOR EDX,EDX + * 00452AA1 8BC1 MOV EAX,ECX + * 00452AA3 C78424 C4010000 >MOV DWORD PTR SS:[ESP+0x1C4],0x7 + * 00452AAE C78424 C0010000 >MOV DWORD PTR SS:[ESP+0x1C0],0x0 + * 00452AB9 66:899424 B00100>MOV WORD PTR SS:[ESP+0x1B0],DX + * 00452AC1 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] + * 00452AC4 66:8B10 MOV DX,WORD PTR DS:[EAX] + * 00452AC7 83C0 02 ADD EAX,0x2 + * 00452ACA 66:85D2 TEST DX,DX + * 00452ACD ^75 F5 JNZ SHORT .00452AC4 + * 00452ACF 2BC6 SUB EAX,ESI + * 00452AD1 D1F8 SAR EAX,1 + * 00452AD3 50 PUSH EAX + * 00452AD4 51 PUSH ECX + * 00452AD5 8DB424 B4010000 LEA ESI,DWORD PTR SS:[ESP+0x1B4] + * 00452ADC E8 DF4DFBFF CALL .004078C0 + * 00452AE1 C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0x8 + * 00452AE9 8B43 10 MOV EAX,DWORD PTR DS:[EBX+0x10] + * 00452AEC 2B43 0C SUB EAX,DWORD PTR DS:[EBX+0xC] + * 00452AEF C1F8 04 SAR EAX,0x4 + * 00452AF2 83F8 02 CMP EAX,0x2 + * 00452AF5 77 05 JA SHORT .00452AFC + * 00452AF7 E8 3C5B0B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo + * 00452AFC 8B43 0C MOV EAX,DWORD PTR DS:[EBX+0xC] + * 00452AFF 8B48 28 MOV ECX,DWORD PTR DS:[EAX+0x28] + * 00452B02 83C0 20 ADD EAX,0x20 ; jichi: hook before here, text in ecx + * 00452B05 33D2 XOR EDX,EDX + * 00452B07 8BC1 MOV EAX,ECX + * 00452B09 C78424 E0010000 07000000 MOV DWORD PTR SS:[ESP+0x1E0],0x7 ; jichi: key pattern is here, text in eax + * 00452B14 C78424 DC010000 00000000 MOV DWORD PTR SS:[ESP+0x1DC],0x0 + * 00452B27 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] + * 00452B2A 33DB XOR EBX,EBX + * 00452B2C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] + * 00452B30 66:8B10 MOV DX,WORD PTR DS:[EAX] + * 00452B33 83C0 02 ADD EAX,0x2 + * 00452B36 66:3BD3 CMP DX,BX + * 00452B39 ^75 F5 JNZ SHORT .00452B30 + * 00452B3B 2BC6 SUB EAX,ESI + * 00452B3D D1F8 SAR EAX,1 + * 00452B3F 50 PUSH EAX + * 00452B40 51 PUSH ECX + * 00452B41 8DB424 D0010000 LEA ESI,DWORD PTR SS:[ESP+0x1D0] + * 00452B48 E8 734DFBFF CALL .004078C0 + * 00452B4D C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0x9 + * 00452B55 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX + * 00452B59 395C24 14 CMP DWORD PTR SS:[ESP+0x14],EBX + * 00452B5D 0F84 77080000 JE .004533DA + * 00452B63 BE 07000000 MOV ESI,0x7 + * 00452B68 33C0 XOR EAX,EAX + * 00452B6A 895C24 20 MOV DWORD PTR SS:[ESP+0x20],EBX + * 00452B6E 89B424 FC010000 MOV DWORD PTR SS:[ESP+0x1FC],ESI + * 00452B75 899C24 F8010000 MOV DWORD PTR SS:[ESP+0x1F8],EBX + * 00452B7C 66:898424 E80100>MOV WORD PTR SS:[ESP+0x1E8],AX + * 00452B84 8D4C24 3C LEA ECX,DWORD PTR SS:[ESP+0x3C] + * 00452B88 51 PUSH ECX + * 00452B89 C68424 BC020000 >MOV BYTE PTR SS:[ESP+0x2BC],0xA + * 00452B91 E8 7AACFCFF CALL .0041D810 + * 00452B96 C68424 B8020000 >MOV BYTE PTR SS:[ESP+0x2B8],0xB + * 00452B9E 399C24 C0010000 CMP DWORD PTR SS:[ESP+0x1C0],EBX + * 00452BA5 0F84 BB020000 JE .00452E66 + * 00452BAB 81C7 14010000 ADD EDI,0x114 + */ + bool attachScenarioHookW1(ULONG startAddress, ULONG stopAddress) { - static std::string data_; - if (s->stack[1] == 3) // skip scenario hook where arg1 is 3 - return false; - auto text = (LPCSTR)s->stack[8]; // text in arg8 - if (!Engine::isAddressReadable(text) || !*text || ::strlen(text) <= 2) // do not translate single character - return false; - *role = Engine::OtherRole ; - - return write_string_overwrite(data,len,text); + // ECX PTR: 83 C0 20 33 D2 8B C1 C7 84 24 E0 01 00 00 07 00 00 00 + const uint8_t bytes[] = { + 0x83, 0xc0, 0x20, // 00452b02 83c0 20 add eax,0x20 ; jichi: hook before here, text in ecx + 0x33, 0xd2, // 00452b05 33d2 xor edx,edx + 0x8b, 0xc1, // 00452b07 8bc1 mov eax,ecx + 0xc7, 0x84, 0x24, 0xe0, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00 // 00452b09 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 + // 00452b14 c78424 dc010000 00000000 mov dword ptr ss:[esp+0x1dc],0x0 + }; + int ecx = get_reg(regs::ecx) / 4; + return TextHookW::attach<1>(bytes, sizeof(bytes), startAddress, stopAddress, ecx, Engine::ScenarioRole); } -} // namespace Private + /** + * 1/9/2016: 見上げてごらん、夜空の星を 体験版 + * + * 0045580D C68424 B8020000 08 MOV BYTE PTR SS:[ESP+0x2B8],0x8 + * 00455815 8B47 10 MOV EAX,DWORD PTR DS:[EDI+0x10] + * 00455818 2B47 0C SUB EAX,DWORD PTR DS:[EDI+0xC] + * 0045581B C1F8 04 SAR EAX,0x4 + * 0045581E 83F8 02 CMP EAX,0x2 + * 00455821 77 05 JA SHORT .00455828 + * 00455823 E8 A0F70B00 CALL .00514FC8 ; JMP to msvcr90._invalid_parameter_noinfo + * 00455828 8B7F 0C MOV EDI,DWORD PTR DS:[EDI+0xC] + * 0045582B 83C7 20 ADD EDI,0x20 + * 0045582E 8B7F 08 MOV EDI,DWORD PTR DS:[EDI+0x8] + * 00455831 33C9 XOR ECX,ECX + * 00455833 8BC7 MOV EAX,EDI ; jichi: hook befoe here, text in eax assigned from edi + * 00455835 C78424 E0010000 07000000 MOV DWORD PTR SS:[ESP+0x1E0],0x7 ; jichi: key pattern is here, text i eax + * 00455840 899C24 DC010000 MOV DWORD PTR SS:[ESP+0x1DC],EBX + * 00455847 66:898C24 CC010000 MOV WORD PTR SS:[ESP+0x1CC],CX + * 0045584F 8D50 02 LEA EDX,DWORD PTR DS:[EAX+0x2] + * 00455852 66:8B08 MOV CX,WORD PTR DS:[EAX] + * 00455855 83C0 02 ADD EAX,0x2 + * 00455858 66:3BCB CMP CX,BX + * 0045585B ^75 F5 JNZ SHORT .00455852 + * 0045585D 2BC2 SUB EAX,EDX + * 0045585F D1F8 SAR EAX,1 + * 00455861 50 PUSH EAX + * 00455862 57 PUSH EDI + * 00455863 8DB424 D0010000 LEA ESI,DWORD PTR SS:[ESP+0x1D0] + * 0045586A E8 2120FBFF CALL .00407890 + * 0045586F C68424 B8020000 09 MOV BYTE PTR SS:[ESP+0x2B8],0x9 + * 00455877 895C24 30 MOV DWORD PTR SS:[ESP+0x30],EBX + * 0045587B 395C24 18 CMP DWORD PTR SS:[ESP+0x18],EBX + * 0045587F 0F84 D1080000 JE .00456156 + * 00455885 33D2 XOR EDX,EDX + * 00455887 895C24 24 MOV DWORD PTR SS:[ESP+0x24],EBX + * 0045588B C78424 FC010000 07000000 MOV DWORD PTR SS:[ESP+0x1FC],0x7 + * 00455896 899C24 F8010000 MOV DWORD PTR SS:[ESP+0x1F8],EBX + * 0045589D 66:899424 E8010000 MOV WORD PTR SS:[ESP+0x1E8],DX + * 004558A5 8D4424 3C LEA EAX,DWORD PTR SS:[ESP+0x3C] + */ + bool attachScenarioHookW2(ULONG startAddress, ULONG stopAddress) + { + // key pattern: C78424 E0010000 07000000 + const uint8_t bytes[] = { + 0x8b, 0xc7, // 00455833 8bc7 mov eax,edi ; jichi: text in eax assigned from edi + 0xc7, 0x84, 0x24, 0xe0, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00 // 00455835 c78424 e0010000 07000000 mov dword ptr ss:[esp+0x1e0],0x7 ; jichi: key pattern is here, text i eax + }; + int edi = get_reg(regs::edi) / 4; + return TextHookW::attach<2>(bytes, sizeof(bytes), startAddress, stopAddress, edi, Engine::ScenarioRole); + } + /** + * Sample game: なついろレシピ + * See: http://capita.tistory.com/m/post/251 + * + * Name: + * + * 004534FA 64:A3 00000000 MOV DWORD PTR FS:[0],EAX + * 00453500 8B75 14 MOV ESI,DWORD PTR SS:[EBP+0x14] + * 00453503 8B46 10 MOV EAX,DWORD PTR DS:[ESI+0x10] + * 00453506 2B46 0C SUB EAX,DWORD PTR DS:[ESI+0xC] + * 00453509 8BF9 MOV EDI,ECX + * 0045350B C1F8 04 SAR EAX,0x4 + * 0045350E 897C24 14 MOV DWORD PTR SS:[ESP+0x14],EDI + * 00453512 85C0 TEST EAX,EAX + * 00453514 77 05 JA SHORT .0045351B + * 00453516 E8 1D510B00 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo + * 0045351B 8B76 0C MOV ESI,DWORD PTR DS:[ESI+0xC] + * 0045351E 8B4E 08 MOV ECX,DWORD PTR DS:[ESI+0x8] + * 00453521 33DB XOR EBX,EBX ; jichi: hook here, text in ecx + * 00453523 33D2 XOR EDX,EDX + * 00453525 8BC1 MOV EAX,ECX + * 00453527 C78424 88000000 07000000 MOV DWORD PTR SS:[ESP+0x88],0x7 + * 00453532 899C24 84000000 MOV DWORD PTR SS:[ESP+0x84],EBX + * 00453539 66:895424 74 MOV WORD PTR SS:[ESP+0x74],DX + * 0045353E 8D70 02 LEA ESI,DWORD PTR DS:[EAX+0x2] + * 00453541 66:8B10 MOV DX,WORD PTR DS:[EAX] + * 00453544 83C0 02 ADD EAX,0x2 + * 00453547 66:3BD3 CMP DX,BX + * 0045354A ^75 F5 JNZ SHORT .00453541 + * 0045354C 2BC6 SUB EAX,ESI + * 0045354E D1F8 SAR EAX,1 + * 00453550 50 PUSH EAX + * 00453551 51 PUSH ECX + * 00453552 8D7424 78 LEA ESI,DWORD PTR SS:[ESP+0x78] + * 00453556 E8 6543FBFF CALL .004078C0 + * 0045355B 899C24 70010000 MOV DWORD PTR SS:[ESP+0x170],EBX + * 00453562 A1 DCAA5500 MOV EAX,DWORD PTR DS:[0x55AADC] + * 00453567 894424 1C MOV DWORD PTR SS:[ESP+0x1C],EAX + * 0045356B B8 0F000000 MOV EAX,0xF + * 00453570 894424 6C MOV DWORD PTR SS:[ESP+0x6C],EAX + * 00453574 895C24 68 MOV DWORD PTR SS:[ESP+0x68],EBX + * 00453578 885C24 58 MOV BYTE PTR SS:[ESP+0x58],BL + * 0045357C 894424 50 MOV DWORD PTR SS:[ESP+0x50],EAX + * 00453580 895C24 4C MOV DWORD PTR SS:[ESP+0x4C],EBX + * 00453584 885C24 3C MOV BYTE PTR SS:[ESP+0x3C],BL + * 00453588 C68424 70010000 02 MOV BYTE PTR SS:[ESP+0x170],0x2 + * 00453590 8B8424 84000000 MOV EAX,DWORD PTR SS:[ESP+0x84] + * 00453597 8BF0 MOV ESI,EAX + * 00453599 3BC3 CMP EAX,EBX + * 0045359B 74 3D JE SHORT .004535DA + * 0045359D 83BC24 88000000 08 CMP DWORD PTR SS:[ESP+0x88],0x8 + * 004535A5 8B5424 74 MOV EDX,DWORD PTR SS:[ESP+0x74] + * 004535A9 73 04 JNB SHORT .004535AF + * 004535AB 8D5424 74 LEA EDX,DWORD PTR SS:[ESP+0x74] + */ + bool attachNameHookW(ULONG startAddress, ULONG stopAddress) + { + // ECX PTR: 33 DB 33 D2 8B C1 C7 84 24 88 00 00 00 07 00 00 00 + const uint8_t bytes[] = { + 0x33, 0xdb, // 00453521 33db xor ebx,ebx ; jichi: hook here, text in ecx + 0x33, 0xd2, // 00453523 33d2 xor edx,edx + 0x8b, 0xc1, // 00453525 8bc1 mov eax,ecx + 0xc7, 0x84, 0x24, 0x88, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00 // 00453527 c78424 88000000 07000000 mov dword ptr ss:[esp+0x88],0x7 + // 00453532 899c24 84000000 mov dword ptr ss:[esp+0x84],ebx + }; -/** - * Sample games: Re:BIRTHDAY SONG~恋を唄う死神~(体験版) - * - * There are two GetGlyphOutlineA, that are called in the same functions. - * - * Caller of GetGlyphOutlineA, text in arg8. - */ -bool attach(ULONG startAddress, ULONG stopAddress) -{ - ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)::GetGlyphOutlineA, startAddress, stopAddress); - if(addr==0)return false; - HookParam hp; - hp.address=addr; - hp.hook_before=Private::hookBefore; - hp.type=EMBED_ABLE|EMBED_DYNA_SJIS|EMBED_AFTER_OVERWRITE; - hp.offset=get_stack(8); - return NewHook(hp,"EmbedWillplus_other"); -} + int ecx = get_reg(regs::ecx) / 4; + return TextHookW::attach<3>(bytes, sizeof(bytes), startAddress, stopAddress, ecx, Engine::NameRole); + } -} // namespace OtherHookA + /** + * Sample game: なついろレシピ + * See: http://capita.tistory.com/m/post/251 + * + * Choice: + * 00470D95 72 05 JB SHORT .00470D9C + * 00470D97 E8 9C780900 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo + * 00470D9C 8BB5 EC020000 MOV ESI,DWORD PTR SS:[EBP+0x2EC] + * 00470DA2 037424 14 ADD ESI,DWORD PTR SS:[ESP+0x14] + * 00470DA6 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+0x10] + * 00470DA9 2B4E 0C SUB ECX,DWORD PTR DS:[ESI+0xC] + * 00470DAC C1F9 04 SAR ECX,0x4 + * 00470DAF 83F9 01 CMP ECX,0x1 + * 00470DB2 77 05 JA SHORT .00470DB9 + * 00470DB4 E8 7F780900 CALL .00508638 ; JMP to msvcr90._invalid_parameter_noinfo + * 00470DB9 8B46 0C MOV EAX,DWORD PTR DS:[ESI+0xC] + * 00470DBC 8B50 18 MOV EDX,DWORD PTR DS:[EAX+0x18] + * 00470DBF 83C0 10 ADD EAX,0x10 ; jichi: text in edx + * 00470DC2 52 PUSH EDX + * 00470DC3 8D8C24 7C040000 LEA ECX,DWORD PTR SS:[ESP+0x47C] + * 00470DCA 8D7424 4C LEA ESI,DWORD PTR SS:[ESP+0x4C] + * 00470DCE E8 EDA3F9FF CALL .0040B1C0 + * 00470DD3 83C4 04 ADD ESP,0x4 + * 00470DD6 6A FF PUSH -0x1 + * 00470DD8 53 PUSH EBX + * 00470DD9 50 PUSH EAX + * 00470DDA 8D8424 84040000 LEA EAX,DWORD PTR SS:[ESP+0x484] + * 00470DE1 C68424 B0040000 07 MOV BYTE PTR SS:[ESP+0x4B0],0x7 + * 00470DE9 E8 1251F9FF CALL .00405F00 + * 00470DEE BE 08000000 MOV ESI,0x8 + * 00470DF3 C68424 A4040000 06 MOV BYTE PTR SS:[ESP+0x4A4],0x6 + * 00470DFB 397424 60 CMP DWORD PTR SS:[ESP+0x60],ESI + * 00470DFF 72 0D JB SHORT .00470E0E + * 00470E01 8B4424 4C MOV EAX,DWORD PTR SS:[ESP+0x4C] + * 00470E05 50 PUSH EAX + * 00470E06 E8 65770900 CALL .00508570 ; JMP to msvcr90.??3@YAXPAX@Z + * 00470E0B 83C4 04 ADD ESP,0x4 + * 00470E0E 8B9424 7C040000 MOV EDX,DWORD PTR SS:[ESP+0x47C] + * 00470E15 33C9 XOR ECX,ECX + * 00470E17 C74424 60 07000000 MOV DWORD PTR SS:[ESP+0x60],0x7 + * 00470E1F 895C24 5C MOV DWORD PTR SS:[ESP+0x5C],EBX + * 00470E23 66:894C24 4C MOV WORD PTR SS:[ESP+0x4C],CX + * 00470E28 39B424 90040000 CMP DWORD PTR SS:[ESP+0x490],ESI + * 00470E2F 73 07 JNB SHORT .00470E38 + * 00470E31 8D9424 7C040000 LEA EDX,DWORD PTR SS:[ESP+0x47C] + * 00470E38 8B8424 44040000 MOV EAX,DWORD PTR SS:[ESP+0x444] + * 00470E3F B9 10000000 MOV ECX,0x10 + * 00470E44 398C24 58040000 CMP DWORD PTR SS:[ESP+0x458],ECX + * 00470E4B 73 07 JNB SHORT .00470E54 + * 00470E4D 8D8424 44040000 LEA EAX,DWORD PTR SS:[ESP+0x444] + * 00470E54 398C24 74040000 CMP DWORD PTR SS:[ESP+0x474],ECX + * 00470E5B 8B8C24 60040000 MOV ECX,DWORD PTR SS:[ESP+0x460] + */ + bool attachOtherHookW(ULONG startAddress, ULONG stopAddress) + { + // EDX PTR : 83 C0 10 52 8D 8C 24 7C 04 00 00 8D 74 24 4C + const uint8_t bytes[] = { + 0x83, 0xc0, 0x10, // 00470dbf 83c0 10 add eax,0x10 ; jichi: text in edx + 0x52, // 00470dc2 52 push edx + 0x8d, 0x8c, 0x24, 0x7c, 0x04, 0x00, 0x00, // 00470dc3 8d8c24 7c040000 lea ecx,dword ptr ss:[esp+0x47c] + 0x8d, 0x74, 0x24, 0x4c // 00470dca 8d7424 4c lea esi,dword ptr ss:[esp+0x4c] + }; + + int edx = get_reg(regs::edx) / 4; + return TextHookW::attach<4>(bytes, sizeof(bytes), startAddress, stopAddress, edx, Engine::OtherRole); + } + + namespace PatchA + { + + namespace Private + { + // The second argument is always 0 and not used + bool isLeadByteChar(int ch, int) + { + return dynsjis::isleadchar(ch); + // return ::IsDBCSLeadByte(HIBYTE(testChar)); + } + + } // namespace Private + + /** + * Sample game: Re:BIRTHDAY SONG + * + * 0x8140 is found by tracing the call of the caller of GetGlyphOutlineA. + + * 00487F8D 25 FF7F0000 AND EAX,0x7FFF + * 00487F92 C3 RETN + * 00487F93 8BFF MOV EDI,EDI + * 00487F95 55 PUSH EBP + * 00487F96 8BEC MOV EBP,ESP + * 00487F98 83EC 10 SUB ESP,0x10 + * 00487F9B FF75 0C PUSH DWORD PTR SS:[EBP+0xC] + * 00487F9E 8D4D F0 LEA ECX,DWORD PTR SS:[EBP-0x10] + * 00487FA1 E8 02EEFFFF CALL .00486DA8 + * 00487FA6 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] + * 00487FA9 C1E8 08 SHR EAX,0x8 + * 00487FAC 0FB6C8 MOVZX ECX,AL + * 00487FAF 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-0xC] + * 00487FB2 F64401 1D 04 TEST BYTE PTR DS:[ECX+EAX+0x1D],0x4 + * 00487FB7 74 10 JE SHORT .00487FC9 + * 00487FB9 0FB64D 08 MOVZX ECX,BYTE PTR SS:[EBP+0x8] + * 00487FBD F64401 1D 08 TEST BYTE PTR DS:[ECX+EAX+0x1D],0x8 + * 00487FC2 74 05 JE SHORT .00487FC9 + * 00487FC4 33C0 XOR EAX,EAX + * 00487FC6 40 INC EAX + * 00487FC7 EB 02 JMP SHORT .00487FCB + * 00487FC9 33C0 XOR EAX,EAX + * 00487FCB 807D FC 00 CMP BYTE PTR SS:[EBP-0x4],0x0 + * 00487FCF 74 07 JE SHORT .00487FD8 + * 00487FD1 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-0x8] + * 00487FD4 8361 70 FD AND DWORD PTR DS:[ECX+0x70],0xFFFFFFFD + * 00487FD8 C9 LEAVE + * 00487FD9 C3 RETN + * 00487FDA 8BFF MOV EDI,EDI ; jichi: called here, text in arg1 + * 00487FDC 55 PUSH EBP + * 00487FDD 8BEC MOV EBP,ESP + * 00487FDF 6A 00 PUSH 0x0 + * 00487FE1 FF75 08 PUSH DWORD PTR SS:[EBP+0x8] + * 00487FE4 E8 AAFFFFFF CALL .00487F93 ; jichi: called here + * 00487FE9 59 POP ECX + * 00487FEA 59 POP ECX + * 00487FEB 5D POP EBP + * 00487FEC C3 RETN + */ + using ulong = ULONG; +#define s1_call_ 0xe8 // near call, incomplete +#define s1_nop 0x90 // nop + + bool csmemcpy(void *dst, const void *src, size_t size) + { + // return memcpy_(dst, src, size); + + DWORD oldProtect; + if (!::VirtualProtect(dst, size, PAGE_EXECUTE_READWRITE, &oldProtect)) + return false; + // HANDLE hProc = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, ::GetCurrentProcessId()); + // VirtualProtectEx(hProc, dst, size, PAGE_EXECUTE_READWRITE, &oldProtect); + + memcpy(dst, src, size); + + DWORD newProtect; + ::VirtualProtect(dst, size, oldProtect, &newProtect); // the error code is not checked for this function + // hProc = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, FALSE, ::GetCurrentProcessId()); + // VirtualProtectEx(hProc, dst, size, oldProtect, &newProtect); + + return true; + } + ulong replace_near_call(ulong addr, ulong val) + { + DWORD ret; + switch (::disasm((LPCVOID)addr)) + { + case 5: // near call / short jmp: relative address + ret = *(DWORD *)(addr + 1) + (addr + 5); + val -= addr + 5; + return csmemcpy((LPVOID)(addr + 1), &val, sizeof(val)) ? ret : 0; + case 6: // far car / long jmp: absolute address + { + ret = *(DWORD *)(addr + 2); + BYTE data[6]; + data[0] = s1_call_; + data[5] = s1_nop; + *(DWORD *)(data + 1) = val - (addr + 5); + return csmemcpy((LPVOID)addr, data, sizeof(data)) ? ret : 0; + } + default: + return 0; + } + } + ULONG patchEncoding(ULONG startAddress, ULONG stopAddress) + { + const uint8_t bytes[] = { + 0x6a, 0x00, // 00487fdf 6a 00 push 0x0 + 0xff, 0x75, 0x08, // 00487fe1 ff75 08 push dword ptr ss:[ebp+0x8] + 0xe8, 0xaa, 0xff, 0xff, 0xff // 00487fe4 e8 aaffffff call .00487f93 ; jichi: called here + }; + enum + { + addr_offset = 5 + }; + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); + + return addr; //&& replace_near_call(addr + addr_offset, (ULONG)Private::isLeadByteChar); + } + + } // namespace PatchA + + namespace ScenarioHookA + { + + namespace Private + { + /* + void dispatch(LPSTR text, int role) + { + enum { sig = 0 }; + if (!Engine::isAddressWritable(text) || !*text) // isAddressWritable is not needed for correct games + return; + int size = ::strlen(text), + trimmedSize = size; + auto trimmedText = trim(text, &trimmedSize); + if (!trimmedSize || !*trimmedText) + return; + std::string oldData(trimmedText, trimmedSize), + newData = EngineController::instance()->dispatchTextASTD(oldData, role, sig); + if (newData == oldData) + return; + if (trimmedText[trimmedSize]) + newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); + ::strcpy(text, newData.c_str()); + } + */ + bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role) + { + auto text = (LPSTR)s->eax; + if (!text) + return false; + // dispatch(text - 1024, Engine::NameRole); + // dispatch(text, Engine::ScenarioRole); + + enum + { + sig = 0 + }; + if (!Engine::isAddressWritable(text) || !*text) // isAddressWritable is not needed for correct games + return false; + int size = ::strlen(text), + trimmedSize = size; + auto trimmedText = trim(text, &trimmedSize); + if (!trimmedSize || !*trimmedText) + return false; + std::string oldData(trimmedText, trimmedSize); + + return write_string_overwrite(data, len, oldData); + /*newData = EngineController::instance()->dispatchTextASTD(oldData, role, sig); + if (newData == oldData) + return; + if (trimmedText[trimmedSize]) + newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); + ::strcpy(text, newData.c_str()); + return true;*/ + } + void hookafter(hook_stack *s, void *data, size_t len) + { + + auto newData = std::string((char *)data, len); + auto text = (LPSTR)s->eax; + int size = ::strlen(text), + trimmedSize = size; + auto trimmedText = trim(text, &trimmedSize); + if (trimmedText[trimmedSize]) + newData.append(trimmedText + trimmedSize); //, size - trimmedSize - (trimmedText - text)); + ::strcpy(text, newData.c_str()); + } + } // namespace Private + + /** + * Sample games + * - [111028][PULLTOP] 神聖にして侵すべからず + * - Re:BIRTHDAY SONG~恋を唄う死神~(体験版) + * See: http://capita.tistory.com/m/post/84 + * + * ENCODEKOR,FORCEFONT(5),HOOK(0x0042B5E0,TRANS(0x004FFBF8,OVERWRITE(IGNORE)),RETNPOS(COPY),TRANS(0x004FF7F8,OVERWRITE(IGNORE))),HOOK(0x00413204,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x00424004,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x004242B9,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)),HOOK(0x00424109,TRANS([ESP+0x1c],PTRCHEAT),RETNPOS(SOURCE)) + * + * Scenario in eax + * Name in (eax - 1024) + * Memory can be directly overridden. + * + * 0042B5DE CC INT3 + * 0042B5DF CC INT3 + * 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy + * 0042B5E6 53 PUSH EBX + * 0042B5E7 55 PUSH EBP + * 0042B5E8 56 PUSH ESI + * 0042B5E9 33DB XOR EBX,EBX + * 0042B5EB 57 PUSH EDI + * 0042B5EC 8BF8 MOV EDI,EAX + * 0042B5EE 399C24 28080000 CMP DWORD PTR SS:[ESP+0x828],EBX + * 0042B5F5 75 13 JNZ SHORT .0042B60A + * 0042B5F7 68 74030000 PUSH 0x374 + * 0042B5FC 53 PUSH EBX + * 0042B5FD 68 7CC44F00 PUSH .004FC47C + * 0042B602 E8 09E60500 CALL .00489C10 + * 0042B607 83C4 0C ADD ESP,0xC + * 0042B60A 33F6 XOR ESI,ESI + * 0042B60C 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX + * 0042B610 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX + * 0042B614 381F CMP BYTE PTR DS:[EDI],BL + * 0042B616 0F84 0D020000 JE .0042B829 + * 0042B61C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] + * 0042B620 8A4C37 01 MOV CL,BYTE PTR DS:[EDI+ESI+0x1] + * 0042B624 84C9 TEST CL,CL + * 0042B626 0F84 E6010000 JE .0042B812 + * 0042B62C 66:0FB6043E MOVZX AX,BYTE PTR DS:[ESI+EDI] + * 0042B631 8D2C3E LEA EBP,DWORD PTR DS:[ESI+EDI] + * 0042B634 66:C1E0 08 SHL AX,0x8 + * 0042B638 0FB7C0 MOVZX EAX,AX + * 0042B63B 0FB6C9 MOVZX ECX,CL + * 0042B63E 0BC1 OR EAX,ECX + * 0042B640 50 PUSH EAX + * 0042B641 E8 34B40500 CALL .00486A7A + * 0042B646 83C4 04 ADD ESP,0x4 + * 0042B649 85C0 TEST EAX,EAX + * 0042B64B 74 14 JE SHORT .0042B661 + * 0042B64D 66:8B55 00 MOV DX,WORD PTR SS:[EBP] + * 0042B651 66:89541C 24 MOV WORD PTR SS:[ESP+EBX+0x24],DX + * 0042B656 83C3 02 ADD EBX,0x2 + * 0042B659 83C6 02 ADD ESI,0x2 + * 0042B65C E9 BA010000 JMP .0042B81B + * 0042B661 807D 00 7B CMP BYTE PTR SS:[EBP],0x7B + * 0042B665 0F85 60010000 JNZ .0042B7CB + * 0042B66B 8BC3 MOV EAX,EBX + * 0042B66D 2B4424 1C SUB EAX,DWORD PTR SS:[ESP+0x1C] + * 0042B671 46 INC ESI + * 0042B672 33ED XOR EBP,EBP + * 0042B674 894424 20 MOV DWORD PTR SS:[ESP+0x20],EAX + * 0042B678 896C24 14 MOV DWORD PTR SS:[ESP+0x14],EBP + * 0042B67C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP] + * 0042B680 8A0C3E MOV CL,BYTE PTR DS:[ESI+EDI] + * 0042B683 84C9 TEST CL,CL + * 0042B685 0F84 B5010000 JE .0042B840 + * 0042B68B 0FB64437 01 MOVZX EAX,BYTE PTR DS:[EDI+ESI+0x1] + * 0042B690 66:0FB6C9 MOVZX CX,CL + * 0042B694 66:C1E1 08 SHL CX,0x8 + * 0042B698 0FB7D1 MOVZX EDX,CX + * 0042B69B 0BC2 OR EAX,EDX + * 0042B69D 50 PUSH EAX + * 0042B69E E8 D7B30500 CALL .00486A7A + * 0042B6A3 83C4 04 ADD ESP,0x4 + * 0042B6A6 85C0 TEST EAX,EAX + * 0042B6A8 74 1A JE SHORT .0042B6C4 + * 0042B6AA 66:8B043E MOV AX,WORD PTR DS:[ESI+EDI] + * 0042B6AE 834424 14 02 ADD DWORD PTR SS:[ESP+0x14],0x2 + * 0042B6B3 66:89441C 24 MOV WORD PTR SS:[ESP+EBX+0x24],AX + * 0042B6B8 83C3 02 ADD EBX,0x2 + * 0042B6BB 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX + * 0042B6BF 83C6 02 ADD ESI,0x2 + * 0042B6C2 ^EB BC JMP SHORT .0042B680 + * 0042B6C4 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] + * 0042B6C7 3C 3A CMP AL,0x3A + * 0042B6C9 74 10 JE SHORT .0042B6DB + * 0042B6CB FF4424 14 INC DWORD PTR SS:[ESP+0x14] + * 0042B6CF 88441C 24 MOV BYTE PTR SS:[ESP+EBX+0x24],AL + * 0042B6D3 43 INC EBX + * 0042B6D4 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX + * 0042B6D8 46 INC ESI + * 0042B6D9 ^EB A5 JMP SHORT .0042B680 + * 0042B6DB 896C24 18 MOV DWORD PTR SS:[ESP+0x18],EBP + * 0042B6DF 46 INC ESI + * 0042B6E0 8A0C3E MOV CL,BYTE PTR DS:[ESI+EDI] + * 0042B6E3 84C9 TEST CL,CL + * 0042B6E5 0F84 55010000 JE .0042B840 + * 0042B6EB 0FB64437 01 MOVZX EAX,BYTE PTR DS:[EDI+ESI+0x1] + * 0042B6F0 66:0FB6C9 MOVZX CX,CL + * 0042B6F4 66:C1E1 08 SHL CX,0x8 + * 0042B6F8 0FB7D1 MOVZX EDX,CX + * 0042B6FB 0BC2 OR EAX,EDX + * 0042B6FD 50 PUSH EAX + * 0042B6FE E8 77B30500 CALL .00486A7A + * 0042B703 83C4 04 ADD ESP,0x4 + * 0042B706 85C0 TEST EAX,EAX + * 0042B708 74 18 JE SHORT .0042B722 + * 0042B70A 66:8B043E MOV AX,WORD PTR DS:[ESI+EDI] + * 0042B70E FF4424 18 INC DWORD PTR SS:[ESP+0x18] + * 0042B712 66:89842C 240400>MOV WORD PTR SS:[ESP+EBP+0x424],AX + * 0042B71A 83C5 02 ADD EBP,0x2 + * 0042B71D 83C6 02 ADD ESI,0x2 + * 0042B720 ^EB BE JMP SHORT .0042B6E0 + * 0042B722 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] + * 0042B725 3C 7D CMP AL,0x7D + * 0042B727 74 0E JE SHORT .0042B737 + * 0042B729 FF4424 18 INC DWORD PTR SS:[ESP+0x18] + * 0042B72D 88842C 24040000 MOV BYTE PTR SS:[ESP+EBP+0x424],AL + * 0042B734 45 INC EBP + * 0042B735 ^EB A8 JMP SHORT .0042B6DF + * 0042B737 8D8424 24040000 LEA EAX,DWORD PTR SS:[ESP+0x424] + * 0042B73E 46 INC ESI + * 0042B73F C6842C 24040000 >MOV BYTE PTR SS:[ESP+EBP+0x424],0x0 + * 0042B747 8D50 01 LEA EDX,DWORD PTR DS:[EAX+0x1] + * 0042B74A 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX] + * 0042B750 8A08 MOV CL,BYTE PTR DS:[EAX] + * 0042B752 40 INC EAX + * 0042B753 84C9 TEST CL,CL + * 0042B755 ^75 F9 JNZ SHORT .0042B750 + * 0042B757 2BC2 SUB EAX,EDX + * 0042B759 83F8 1E CMP EAX,0x1E + * 0042B75C 0F87 DE000000 JA .0042B840 + * 0042B762 8B15 7CC44F00 MOV EDX,DWORD PTR DS:[0x4FC47C] + * 0042B768 83FA 14 CMP EDX,0x14 + * 0042B76B 0F8D AE000000 JGE .0042B81F + * 0042B771 6BD2 2C IMUL EDX,EDX,0x2C + * 0042B774 8D8C24 24040000 LEA ECX,DWORD PTR SS:[ESP+0x424] + * 0042B77B 81C2 8CC44F00 ADD EDX,.004FC48C + * 0042B781 8A01 MOV AL,BYTE PTR DS:[ECX] + * 0042B783 8802 MOV BYTE PTR DS:[EDX],AL + * 0042B785 41 INC ECX + * 0042B786 42 INC EDX + * 0042B787 84C0 TEST AL,AL + * 0042B789 ^75 F6 JNZ SHORT .0042B781 + * 0042B78B 8B0D 7CC44F00 MOV ECX,DWORD PTR DS:[0x4FC47C] + * 0042B791 8B5424 14 MOV EDX,DWORD PTR SS:[ESP+0x14] + * 0042B795 6BC9 2C IMUL ECX,ECX,0x2C + * 0042B798 8991 88C44F00 MOV DWORD PTR DS:[ECX+0x4FC488],EDX + * 0042B79E A1 7CC44F00 MOV EAX,DWORD PTR DS:[0x4FC47C] + * 0042B7A3 8B4C24 20 MOV ECX,DWORD PTR SS:[ESP+0x20] + * 0042B7A7 6BC0 2C IMUL EAX,EAX,0x2C + * 0042B7AA 8988 80C44F00 MOV DWORD PTR DS:[EAX+0x4FC480],ECX + * 0042B7B0 8B15 7CC44F00 MOV EDX,DWORD PTR DS:[0x4FC47C] + * 0042B7B6 8B4424 18 MOV EAX,DWORD PTR SS:[ESP+0x18] + * 0042B7BA 6BD2 2C IMUL EDX,EDX,0x2C + * 0042B7BD 8982 84C44F00 MOV DWORD PTR DS:[EDX+0x4FC484],EAX + * 0042B7C3 FF05 7CC44F00 INC DWORD PTR DS:[0x4FC47C] + * 0042B7C9 EB 54 JMP SHORT .0042B81F + * 0042B7CB 55 PUSH EBP + * 0042B7CC E8 7F000000 CALL .0042B850 + * 0042B7D1 8BD8 MOV EBX,EAX + * 0042B7D3 83C4 04 ADD ESP,0x4 + * 0042B7D6 85DB TEST EBX,EBX + * 0042B7D8 74 23 JE SHORT .0042B7FD + * 0042B7DA 53 PUSH EBX + * 0042B7DB 55 PUSH EBP + * 0042B7DC 8B6C24 18 MOV EBP,DWORD PTR SS:[ESP+0x18] + * 0042B7E0 8D4C2C 2C LEA ECX,DWORD PTR SS:[ESP+EBP+0x2C] + * 0042B7E4 51 PUSH ECX + * 0042B7E5 E8 A6E40500 CALL .00489C90 + * 0042B7EA 03EB ADD EBP,EBX + * 0042B7EC 03F3 ADD ESI,EBX + * 0042B7EE 83C4 0C ADD ESP,0xC + * 0042B7F1 015C24 1C ADD DWORD PTR SS:[ESP+0x1C],EBX + * 0042B7F5 896C24 10 MOV DWORD PTR SS:[ESP+0x10],EBP + * 0042B7F9 8BDD MOV EBX,EBP + * 0042B7FB EB 22 JMP SHORT .0042B81F + * 0042B7FD 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10] + * 0042B801 8A55 00 MOV DL,BYTE PTR SS:[EBP] + * 0042B804 40 INC EAX + * 0042B805 885404 23 MOV BYTE PTR SS:[ESP+EAX+0x23],DL + * 0042B809 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX + * 0042B80D 46 INC ESI + * 0042B80E 8BD8 MOV EBX,EAX + * 0042B810 EB 0D JMP SHORT .0042B81F + * 0042B812 8A043E MOV AL,BYTE PTR DS:[ESI+EDI] + * 0042B815 88441C 24 MOV BYTE PTR SS:[ESP+EBX+0x24],AL + * 0042B819 43 INC EBX + * 0042B81A 46 INC ESI + * 0042B81B 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX + * 0042B81F 803C3E 00 CMP BYTE PTR DS:[ESI+EDI],0x0 + * 0042B823 ^0F85 F7FDFFFF JNZ .0042B620 + * 0042B829 8D4424 24 LEA EAX,DWORD PTR SS:[ESP+0x24] + * 0042B82D 8BC8 MOV ECX,EAX + * 0042B82F C6441C 24 00 MOV BYTE PTR SS:[ESP+EBX+0x24],0x0 + * 0042B834 2BF9 SUB EDI,ECX + * 0042B836 8A08 MOV CL,BYTE PTR DS:[EAX] + * 0042B838 880C07 MOV BYTE PTR DS:[EDI+EAX],CL + * 0042B83B 40 INC EAX + * 0042B83C 84C9 TEST CL,CL + * 0042B83E ^75 F6 JNZ SHORT .0042B836 + * 0042B840 5F POP EDI + * 0042B841 5E POP ESI + * 0042B842 5D POP EBP + * 0042B843 5B POP EBX + * 0042B844 81C4 14080000 ADD ESP,0x814 + * 0042B84A C3 RETN + * 0042B84B CC INT3 + * 0042B84C CC INT3 + * 0042B84D CC INT3 + * 0042B84E CC INT3 + * + * Skip scenario text: + * 00438EF1 51 PUSH ECX + * 00438EF2 56 PUSH ESI + * 00438EF3 57 PUSH EDI + * 00438EF4 52 PUSH EDX + * 00438EF5 6A 03 PUSH 0x3 ; jichi: scenario arg1 is always 3 + * 00438EF7 E8 14F3FDFF CALL .00418210 ; jichi: text called here + * 00438EFC 894424 4C MOV DWORD PTR SS:[ESP+0x4C],EAX + * 00438F00 8D4424 78 LEA EAX,DWORD PTR SS:[ESP+0x78] + * 00438F04 83C4 30 ADD ESP,0x30 + * 00438F07 897C24 34 MOV DWORD PTR SS:[ESP+0x34],EDI + * 00438F0B 897424 38 MOV DWORD PTR SS:[ESP+0x38],ESI + * 00438F0F 8D48 01 LEA ECX,DWORD PTR DS:[EAX+0x1] + * 00438F12 8A10 MOV DL,BYTE PTR DS:[EAX] + * 00438F14 40 INC EAX + * 00438F15 84D2 TEST DL,DL + */ + bool attach(ULONG startAddress, ULONG stopAddress) + { + const uint8_t bytes[] = { + 0x81, 0xec, 0x14, 0x08, 0x00, 0x00 // 0042B5E0 81EC 14080000 SUB ESP,0x814 ; jichi: text in eax, name in eax - 1024, able to copy + }; + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); + if (addr == 0) + return false; + HookParam hp; + hp.address = addr; + + hp.hook_before = Private::hookBefore; + hp.type = EMBED_ABLE; + hp.newlineseperator = L"\\n"; + hp.hook_after = Private::hookafter; + hp.hook_font = F_GetGlyphOutlineA | F_TextOutA; + static ULONG paddr = (PatchA::patchEncoding(startAddress, stopAddress)); + ConsoleOutput("%p", paddr); + if (paddr) + { + hp.type |= EMBED_DYNA_SJIS; + hp.hook_font = F_GetGlyphOutlineA | F_TextOutA; + patch_fun = []() + { + PatchA::replace_near_call(paddr + 5, (ULONG)PatchA::Private::isLeadByteChar); + }; + } + return NewHook(hp, "EmbedWillplusA"); + } + + } // namespace ScenarioHookA + + namespace OtherHookA + { + + namespace Private + { + + bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role) + { + static std::string data_; + if (s->stack[1] == 3) // skip scenario hook where arg1 is 3 + return false; + auto text = (LPCSTR)s->stack[8]; // text in arg8 + if (!Engine::isAddressReadable(text) || !*text || ::strlen(text) <= 2) // do not translate single character + return false; + *role = Engine::OtherRole; + + return write_string_overwrite(data, len, text); + } + + } // namespace Private + + /** + * Sample games: Re:BIRTHDAY SONG~恋を唄う死神~(体験版) + * + * There are two GetGlyphOutlineA, that are called in the same functions. + * + * Caller of GetGlyphOutlineA, text in arg8. + */ + bool attach(ULONG startAddress, ULONG stopAddress) + { + ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)::GetGlyphOutlineA, startAddress, stopAddress); + if (addr == 0) + return false; + HookParam hp; + hp.address = addr; + hp.hook_before = Private::hookBefore; + hp.type = EMBED_ABLE | EMBED_DYNA_SJIS | EMBED_AFTER_OVERWRITE; + hp.offset = get_stack(8); + return NewHook(hp, "EmbedWillplus_other"); + } + + } // namespace OtherHookA } // unnamed namespace /** Public class */ -namespace WillPlusEngine{ -bool attach() +namespace WillPlusEngine { - ULONG startAddress=processStartAddress, stopAddress=processStopAddress; - + bool attach() + { + ULONG startAddress = processStartAddress, stopAddress = processStopAddress; - if (::attachScenarioHookW1(startAddress, stopAddress) || ::attachScenarioHookW2(startAddress, stopAddress)) { - - (::attachNameHookW(startAddress, stopAddress)) ; + if (::attachScenarioHookW1(startAddress, stopAddress) || ::attachScenarioHookW2(startAddress, stopAddress)) + { - (::attachOtherHookW(startAddress, stopAddress)); - - return true; + (::attachNameHookW(startAddress, stopAddress)); - } else if (ScenarioHookA::attach(startAddress, stopAddress)) { // try widechar pattern first, which is more unique - - (OtherHookA::attach(startAddress, stopAddress)) ; - // HijackManager::instance()->attachFunction((ULONG)::GetGlyphOutlineA); - // HijackManager::instance()->attachFunction((ULONG)::TextOutA); // not called. hijack in case it is used + (::attachOtherHookW(startAddress, stopAddress)); + + return true; + } + else if (ScenarioHookA::attach(startAddress, stopAddress)) + { // try widechar pattern first, which is more unique + + (OtherHookA::attach(startAddress, stopAddress)); + // HijackManager::instance()->attachFunction((ULONG)::GetGlyphOutlineA); + // HijackManager::instance()->attachFunction((ULONG)::TextOutA); // not called. hijack in case it is used + return true; + } + + return false; + } +} + +namespace +{ + + static bool InsertWillPlus4() + { + // by Blu3train + /* + * Sample games: + * https://vndb.org/r71235 + */ + const BYTE bytes[] = { + 0x33, 0xC9, // xor ecx,ecx <-- hook + 0x8B, 0xC7, // mov eax,edi + 0xC7, 0x84, 0x24, XX4, XX4, // mov [esp+000001E0],00000007 + 0x89, 0x9C, 0x24, XX4 // mov [esp+000001DC],ebx + }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) + { + ConsoleOutput("WillPlus4: pattern not found"); + return false; + } + + HookParam hp = {}; + hp.address = addr; + hp.offset = get_reg(regs::edi); + hp.type = CODEC_UTF16 | USING_STRING; + hp.filter_fun = WillPlus_extra_filter; + ConsoleOutput("INSERT WillPlus4"); + NewHook(hp, "WillPlus4"); return true; } - return false; -} -} - -namespace{ + static bool InsertWillPlus5() + { + // by Blu3train + /* + * Sample games: + * https://vndb.org/v29881 + */ + const BYTE bytes[] = { + 0xE8, XX4, // call AdvHD.exe+38550 <-- hook here + 0x8B, 0x4B, 0x08, // mov ecx,[ebx+08] + 0x89, 0x8F, XX4, // mov [edi+0000014C],ecx + 0x85, 0xC9, // test ecx,ecx + 0x74, 0x04 // je AdvHD.exe+396C6 + }; + ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); + ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); + if (!addr) + { + ConsoleOutput("WillPlus5: pattern not found"); + return false; + } -static bool InsertWillPlus4() -{ - //by Blu3train - /* - * Sample games: - * https://vndb.org/r71235 - */ - const BYTE bytes[] = { - 0x33, 0xC9, // xor ecx,ecx <-- hook - 0x8B, 0xC7, // mov eax,edi - 0xC7, 0x84, 0x24, XX4, XX4, // mov [esp+000001E0],00000007 - 0x89, 0x9C, 0x24, XX4 // mov [esp+000001DC],ebx - }; - ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); - ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); - if (!addr) { - ConsoleOutput("WillPlus4: pattern not found"); - return false; + HookParam hp = {}; + hp.address = addr; + hp.offset = get_reg(regs::esi); + hp.index = 0; + hp.split = get_reg(regs::ebx); + hp.split_index = 0; + hp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT | USING_SPLIT; + hp.filter_fun = WillPlus_extra_filter; + ConsoleOutput("INSERT WillPlus5"); + NewHook(hp, "WillPlus5"); + return true; } - HookParam hp = {}; - hp.address = addr; - hp.offset =get_reg(regs::edi); - hp.type = CODEC_UTF16 | USING_STRING; - hp.filter_fun = WillPlus_extra_filter; - ConsoleOutput("INSERT WillPlus4"); - NewHook(hp, "WillPlus4"); - return true; -} - -static bool InsertWillPlus5() -{ - //by Blu3train - /* - * Sample games: - * https://vndb.org/v29881 - */ - const BYTE bytes[] = { - 0xE8, XX4, // call AdvHD.exe+38550 <-- hook here - 0x8B, 0x4B, 0x08, // mov ecx,[ebx+08] - 0x89, 0x8F, XX4, // mov [edi+0000014C],ecx - 0x85, 0xC9, // test ecx,ecx - 0x74, 0x04 // je AdvHD.exe+396C6 - }; - ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); - ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); - if (!addr) { - ConsoleOutput("WillPlus5: pattern not found"); - return false; - } - - HookParam hp = {}; - hp.address = addr; - hp.offset = get_reg(regs::esi); - hp.index = 0; - hp.split = get_reg(regs::ebx); - hp.split_index = 0; - hp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT | USING_SPLIT; - hp.filter_fun = WillPlus_extra_filter; - ConsoleOutput("INSERT WillPlus5"); - NewHook(hp, "WillPlus5"); - return true; -} - -bool _xxx(){ -bool ok=false; + bool _xxx() + { + bool ok = false; ok = InsertWillPlus4() || ok; - ok = InsertWillPlus5() || ok; - return ok; -} + ok = InsertWillPlus5() || ok; + return ok; + } } -bool WillPlus::attach_function() { - bool succ=WillPlusEngine::attach(); - succ|=InsertWillPlusHook(); - succ|=InsertWillPlus4Hook(); - succ|=InsertWillPlus5Hook(); - succ|=insertwillplus6(); - succ|=willX(); - succ|=_xxx(); - - return succ; -} +bool WillPlus::attach_function() +{ + bool succ = WillPlusEngine::attach(); + succ |= InsertWillPlusHook(); + succ |= InsertWillPlus4Hook(); + succ |= InsertWillPlus5Hook(); + succ |= insertwillplus6(); + succ |= willX(); + succ |= _xxx(); + return succ; +} - -bool Willold::attach_function() { - //https://vndb.org/v17755 - //凌辱鬼 - auto addr=MemDbg::findLongJumpAddress((ULONG)TextOutA,processStartAddress,processStopAddress); - if(addr==0)return false; - addr=MemDbg::findNearCallAddress(addr,processStartAddress,processStopAddress); - if(addr==0)return false; - addr=findfuncstart(addr,0x200); - if(addr==0)return false; - HookParam hp; - hp.address=addr; - hp.type=USING_CHAR|CODEC_ANSI_BE; - hp.offset=get_stack(1); - return NewHook(hp,"will"); -} \ No newline at end of file +bool Willold::attach_function() +{ + // https://vndb.org/v17755 + // 凌辱鬼 + auto addr = MemDbg::findLongJumpAddress((ULONG)TextOutA, processStartAddress, processStopAddress); + if (addr == 0) + return false; + addr = MemDbg::findNearCallAddress(addr, processStartAddress, processStopAddress); + if (addr == 0) + return false; + addr = findfuncstart(addr, 0x200); + if (addr == 0) + return false; + HookParam hp; + hp.address = addr; + hp.type = USING_CHAR | CODEC_ANSI_BE; + hp.offset = get_stack(1); + return NewHook(hp, "will"); +} \ No newline at end of file