#include "5pb.h" #include "mages/mages.h" /** jichi 12/2/2014 5pb * * Sample game: [140924] CROSS�CHANNEL 〜FINAL COMPLETE� * See: http://sakuradite.com/topic/528 * * Debugging method: insert breakpoint. * The first matched function cannot extract prelude text. * The second matched function can extract anything but contains garbage. * * Function for scenario: * 0016d90e cc int3 * 0016d90f cc int3 * 0016d910 8b15 782b6e06 mov edx,dword ptr ds:[0x66e2b78] ; .00b43bfe * 0016d916 8a0a mov cl,byte ptr ds:[edx] ; jichi: hook here * 0016d918 33c0 xor eax,eax * 0016d91a 84c9 test cl,cl * 0016d91c 74 41 je short .0016d95f * 0016d91e 8bff mov edi,edi * 0016d920 80f9 25 cmp cl,0x25 * 0016d923 75 11 jnz short .0016d936 * 0016d925 8a4a 01 mov cl,byte ptr ds:[edx+0x1] * 0016d928 42 inc edx * 0016d929 80f9 4e cmp cl,0x4e * 0016d92c 74 05 je short .0016d933 * 0016d92e 80f9 6e cmp cl,0x6e * 0016d931 75 26 jnz short .0016d959 * 0016d933 42 inc edx * 0016d934 eb 23 jmp short .0016d959 * 0016d936 80f9 81 cmp cl,0x81 * 0016d939 72 05 jb short .0016d940 * 0016d93b 80f9 9f cmp cl,0x9f * 0016d93e 76 0a jbe short .0016d94a * 0016d940 80f9 e0 cmp cl,0xe0 * 0016d943 72 0c jb short .0016d951 * 0016d945 80f9 fc cmp cl,0xfc * 0016d948 77 07 ja short .0016d951 * 0016d94a b9 02000000 mov ecx,0x2 * 0016d94f eb 05 jmp short .0016d956 * 0016d951 b9 01000000 mov ecx,0x1 * 0016d956 40 inc eax * 0016d957 03d1 add edx,ecx * 0016d959 8a0a mov cl,byte ptr ds:[edx] * 0016d95b 84c9 test cl,cl * 0016d95d ^75 c1 jnz short .0016d920 * 0016d95f c3 retn * * Function for everything: * 001e9a76 8bff mov edi,edi * 001e9a78 55 push ebp * 001e9a79 8bec mov ebp,esp * 001e9a7b 51 push ecx * 001e9a7c 8365 fc 00 and dword ptr ss:[ebp-0x4],0x0 * 001e9a80 53 push ebx * 001e9a81 8b5d 10 mov ebx,dword ptr ss:[ebp+0x10] * 001e9a84 85db test ebx,ebx * 001e9a86 75 07 jnz short .001e9a8f * 001e9a88 33c0 xor eax,eax * 001e9a8a e9 9a000000 jmp .001e9b29 * 001e9a8f 56 push esi * 001e9a90 83fb 04 cmp ebx,0x4 * 001e9a93 72 75 jb short .001e9b0a * 001e9a95 8d73 fc lea esi,dword ptr ds:[ebx-0x4] * 001e9a98 85f6 test esi,esi * 001e9a9a 74 6e je short .001e9b0a * 001e9a9c 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc] * 001e9a9f 8b45 08 mov eax,dword ptr ss:[ebp+0x8] * 001e9aa2 8a10 mov dl,byte ptr ds:[eax] * 001e9aa4 83c0 04 add eax,0x4 * 001e9aa7 83c1 04 add ecx,0x4 * 001e9aaa 84d2 test dl,dl * 001e9aac 74 52 je short .001e9b00 * 001e9aae 3a51 fc cmp dl,byte ptr ds:[ecx-0x4] * 001e9ab1 75 4d jnz short .001e9b00 * 001e9ab3 8a50 fd mov dl,byte ptr ds:[eax-0x3] * 001e9ab6 84d2 test dl,dl * 001e9ab8 74 3c je short .001e9af6 * 001e9aba 3a51 fd cmp dl,byte ptr ds:[ecx-0x3] * 001e9abd 75 37 jnz short .001e9af6 * 001e9abf 8a50 fe mov dl,byte ptr ds:[eax-0x2] * 001e9ac2 84d2 test dl,dl * 001e9ac4 74 26 je short .001e9aec * 001e9ac6 3a51 fe cmp dl,byte ptr ds:[ecx-0x2] * 001e9ac9 75 21 jnz short .001e9aec * 001e9acb 8a50 ff mov dl,byte ptr ds:[eax-0x1] * 001e9ace 84d2 test dl,dl * 001e9ad0 74 10 je short .001e9ae2 * 001e9ad2 3a51 ff cmp dl,byte ptr ds:[ecx-0x1] * 001e9ad5 75 0b jnz short .001e9ae2 * 001e9ad7 8345 fc 04 add dword ptr ss:[ebp-0x4],0x4 * 001e9adb 3975 fc cmp dword ptr ss:[ebp-0x4],esi * 001e9ade ^72 c2 jb short .001e9aa2 * 001e9ae0 eb 2e jmp short .001e9b10 * 001e9ae2 0fb640 ff movzx eax,byte ptr ds:[eax-0x1] * 001e9ae6 0fb649 ff movzx ecx,byte ptr ds:[ecx-0x1] * 001e9aea eb 46 jmp short .001e9b32 * 001e9aec 0fb640 fe movzx eax,byte ptr ds:[eax-0x2] * 001e9af0 0fb649 fe movzx ecx,byte ptr ds:[ecx-0x2] * 001e9af4 eb 3c jmp short .001e9b32 * 001e9af6 0fb640 fd movzx eax,byte ptr ds:[eax-0x3] * 001e9afa 0fb649 fd movzx ecx,byte ptr ds:[ecx-0x3] * 001e9afe eb 32 jmp short .001e9b32 * 001e9b00 0fb640 fc movzx eax,byte ptr ds:[eax-0x4] * 001e9b04 0fb649 fc movzx ecx,byte ptr ds:[ecx-0x4] * 001e9b08 eb 28 jmp short .001e9b32 * 001e9b0a 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc] * 001e9b0d 8b45 08 mov eax,dword ptr ss:[ebp+0x8] * 001e9b10 8b75 fc mov esi,dword ptr ss:[ebp-0x4] * 001e9b13 eb 0d jmp short .001e9b22 * 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word * 001e9b17 84d2 test dl,dl * 001e9b19 74 11 je short .001e9b2c * 001e9b1b 3a11 cmp dl,byte ptr ds:[ecx] * 001e9b1d 75 0d jnz short .001e9b2c * 001e9b1f 40 inc eax * 001e9b20 46 inc esi * 001e9b21 41 inc ecx * 001e9b22 3bf3 cmp esi,ebx * 001e9b24 ^72 ef jb short .001e9b15 * 001e9b26 33c0 xor eax,eax * 001e9b28 5e pop esi * 001e9b29 5b pop ebx * 001e9b2a c9 leave * 001e9b2b c3 retn */ namespace { // unnamed // Characters to ignore: [%0-9A-Z] bool Insert5pbHook1() { const BYTE bytes[] = { 0xcc, // 0016d90e cc int3 0xcc, // 0016d90f cc int3 0x8b, 0x15, XX4, // 0016d910 8b15 782b6e06 mov edx,dword ptr ds:[0x66e2b78] ; .00b43bfe 0x8a, 0x0a, // 0016d916 8a0a mov cl,byte ptr ds:[edx] ; jichi: hook here 0x33, 0xc0, // 0016d918 33c0 xor eax,eax 0x84, 0xc9 // 0016d91a 84c9 test cl,cl }; enum { addr_offset = 0x0016d916 - 0x0016d90e }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); // GROWL_DWORD3(addr+addr_offset, processStartAddress,processStopAddress); if (!addr) { ConsoleOutput("5pb1: pattern not found"); return false; } HookParam hp; hp.address = addr + addr_offset; hp.offset = get_reg(regs::edx); hp.type = USING_STRING; ConsoleOutput("INSERT 5pb1"); // GDI functions are not used by 5pb games anyway. // ConsoleOutput("5pb: disable GDI hooks"); // return NewHook(hp, "5pb1"); } // Characters to ignore: [%@A-z] inline bool _5pb2garbage_ch(char c) { return c == '%' || c == '@' || c >= 'A' && c <= 'z'; } // 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word void SpecialHook5pb2(hook_stack *stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len) { static DWORD lasttext; DWORD text = stack->eax; if (lasttext == text) return; BYTE c = *(BYTE *)text; if (!c) return; BYTE size = ::LeadByteTable[c]; // 1, 2, or 3 if (size == 1 && _5pb2garbage_ch(*(LPCSTR)text)) return; lasttext = text; *data = text; *len = size; } bool Insert5pbHook2() { const BYTE bytes[] = { 0x8a, 0x10, // 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word 0x84, 0xd2, // 001e9b17 84d2 test dl,dl 0x74, 0x11 // 001e9b19 74 11 je short .001e9b2c }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); // GROWL_DWORD3(addr, processStartAddress,processStopAddress); if (!addr) { ConsoleOutput("5pb2: pattern not found"); return false; } HookParam hp; hp.address = addr; hp.type = USING_STRING; hp.text_fun = SpecialHook5pb2; ConsoleOutput("INSERT 5pb2"); // GDI functions are not used by 5pb games anyway. // ConsoleOutput("5pb: disable GDI hooks"); // return NewHook(hp, "5pb2"); } /** jichi 2/2/2015: New 5pb hook * Sample game: Hyperdimension.Neptunia.ReBirth1 * * Debugging method: hardware breakpoint and find function in msvc110 * Then, backtrack the function stack to find proper function. * * Hooked function: 558BEC56FF750C8BF1FF75088D460850 * * 0025A12E CC INT3 * 0025A12F CC INT3 * 0025A130 55 PUSH EBP * 0025A131 8BEC MOV EBP,ESP * 0025A133 56 PUSH ESI * 0025A134 FF75 0C PUSH DWORD PTR SS:[EBP+0xC] * 0025A137 8BF1 MOV ESI,ECX * 0025A139 FF75 08 PUSH DWORD PTR SS:[EBP+0x8] * 0025A13C 8D46 08 LEA EAX,DWORD PTR DS:[ESI+0x8] * 0025A13F 50 PUSH EAX * 0025A140 E8 DB100100 CALL .0026B220 * 0025A145 8B8E 988D0000 MOV ECX,DWORD PTR DS:[ESI+0x8D98] * 0025A14B 8988 80020000 MOV DWORD PTR DS:[EAX+0x280],ECX * 0025A151 8B8E A08D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DA0] * 0025A157 8988 88020000 MOV DWORD PTR DS:[EAX+0x288],ECX * 0025A15D 8B8E A88D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DA8] * 0025A163 8988 90020000 MOV DWORD PTR DS:[EAX+0x290],ECX * 0025A169 8B8E B08D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DB0] * 0025A16F 8988 98020000 MOV DWORD PTR DS:[EAX+0x298],ECX * 0025A175 83C4 0C ADD ESP,0xC * 0025A178 8D8E 188B0000 LEA ECX,DWORD PTR DS:[ESI+0x8B18] * 0025A17E E8 DDD8FEFF CALL .00247A60 * 0025A183 5E POP ESI * 0025A184 5D POP EBP * 0025A185 C2 0800 RETN 0x8 * 0025A188 CC INT3 * 0025A189 CC INT3 * * Runtime stack, text in arg1, and name in arg2: * * 0015F93C 00252330 RETURN to .00252330 from .0025A130 * 0015F940 181D0D4C ASCII "That's my line! I won't let any of you * take the title of True Goddess!" * 0015F944 0B8B4D20 ASCII " White Heart " * 0015F948 0B8B5528 * 0015F94C 0B8B5524 * 0015F950 /0015F980 * 0015F954 |0026000F RETURN to .0026000F from .002521D0 * * * Another candidate funciton for backup usage. * Previous text in arg1. * Current text in arg2. * Current name in arg3. * * 0026B21C CC INT3 * 0026B21D CC INT3 * 0026B21E CC INT3 * 0026B21F CC INT3 * 0026B220 55 PUSH EBP * 0026B221 8BEC MOV EBP,ESP * 0026B223 81EC A0020000 SUB ESP,0x2A0 * 0026B229 BA A0020000 MOV EDX,0x2A0 * 0026B22E 53 PUSH EBX * 0026B22F 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+0x8] * 0026B232 56 PUSH ESI * 0026B233 57 PUSH EDI * 0026B234 8D041A LEA EAX,DWORD PTR DS:[EDX+EBX] * 0026B237 B9 A8000000 MOV ECX,0xA8 * 0026B23C 8BF3 MOV ESI,EBX * 0026B23E 8DBD 60FDFFFF LEA EDI,DWORD PTR SS:[EBP-0x2A0] * 0026B244 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> * 0026B246 B9 A8000000 MOV ECX,0xA8 * 0026B24B 8BF0 MOV ESI,EAX * 0026B24D 8BFB MOV EDI,EBX * 0026B24F F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> * 0026B251 81C2 A0020000 ADD EDX,0x2A0 * 0026B257 B9 A8000000 MOV ECX,0xA8 * 0026B25C 8DB5 60FDFFFF LEA ESI,DWORD PTR SS:[EBP-0x2A0] * 0026B262 8BF8 MOV EDI,EAX * 0026B264 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> * 0026B266 81FA 40830000 CMP EDX,0x8340 * 0026B26C ^7C C6 JL SHORT .0026B234 * 0026B26E 8BCB MOV ECX,EBX * 0026B270 E8 EBC7FDFF CALL .00247A60 * 0026B275 FF75 0C PUSH DWORD PTR SS:[EBP+0xC] * 0026B278 8B35 D8525000 MOV ESI,DWORD PTR DS:[0x5052D8] ; msvcr110.sprintf * 0026B27E 68 805C5000 PUSH .00505C80 ; ASCII "%s" * 0026B283 53 PUSH EBX * 0026B284 FFD6 CALL ESI * 0026B286 FF75 10 PUSH DWORD PTR SS:[EBP+0x10] * 0026B289 8D83 00020000 LEA EAX,DWORD PTR DS:[EBX+0x200] * 0026B28F 68 805C5000 PUSH .00505C80 ; ASCII "%s" * 0026B294 50 PUSH EAX * 0026B295 FFD6 CALL ESI * 0026B297 83C4 18 ADD ESP,0x18 * 0026B29A 8BC3 MOV EAX,EBX * 0026B29C 5F POP EDI * 0026B29D 5E POP ESI * 0026B29E 5B POP EBX * 0026B29F 8BE5 MOV ESP,EBP * 0026B2A1 5D POP EBP * 0026B2A2 C3 RETN * 0026B2A3 CC INT3 * 0026B2A4 CC INT3 * 0026B2A5 CC INT3 * 0026B2A6 CC INT3 */ void SpecialHook5pb3(hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len) { int index = 0; // Text in arg1, name in arg2 if (LPCSTR text = (LPCSTR)stack->stack[index + 1]) if (*text) { if (index) // trim spaces in character name while (*text == ' ') text++; size_t sz = ::strlen(text); if (index) while (sz && text[sz - 1] == ' ') sz--; *data = (DWORD)text; *len = sz; *split = FIXED_SPLIT_VALUE << index; } } bool Insert5pbHook3() { const BYTE bytes[] = { // function starts 0x55, // 0025A130 55 PUSH EBP 0x8b, 0xec, // 0025A131 8BEC MOV EBP,ESP 0x56, // 0025A133 56 PUSH ESI 0xff, 0x75, 0x0c, // 0025A134 FF75 0C PUSH DWORD PTR SS:[EBP+0xC] 0x8b, 0xf1, // 0025A137 8BF1 MOV ESI,ECX 0xff, 0x75, 0x08, // 0025A139 FF75 08 PUSH DWORD PTR SS:[EBP+0x8] 0x8d, 0x46, 0x08, // 0025A13C 8D46 08 LEA EAX,DWORD PTR DS:[ESI+0x8] 0x50, // 0025A13F 50 PUSH EAX 0xe8 // 0025A140 E8 DB100100 CALL .0026B220 }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); // GROWL_DWORD3(addr, processStartAddress,processStopAddress); if (!addr) { ConsoleOutput("5pb2: pattern not found"); return false; } HookParam hp; hp.address = addr; hp.type = USING_STRING | NO_CONTEXT; hp.text_fun = SpecialHook5pb3; hp.filter_fun = NewLineCharToSpaceFilterA; // replace '\n' by ' ' ConsoleOutput("INSERT 5pb3"); // GDI functions are not used by 5pb games anyway. // ConsoleOutput("5pb: disable GDI hooks"); // return NewHook(hp, "5pb3"); } } // unnamed namespace bool Insert5pbHook() { bool ok = Insert5pbHook1(); ok = Insert5pbHook2() || ok; ok = Insert5pbHook3() || ok; return ok; } bool Insert5pbHookex() { // 祝姬 const BYTE bytes[] = { 0x0F, 0xB6, 0xC2, 0x35, 0xC5, 0x9D, 0x1C, 0x81}; auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); if (addr == 0) return false; const BYTE start[] = { 0x55, 0x8b, 0xec, 0x83, 0xe4}; addr = reverseFindBytes(start, sizeof(start), addr - 0x40, addr); if (addr == 0) return false; HookParam hp; hp.address = addr; hp.offset = get_reg(regs::ecx); hp.type = CODEC_UTF16; return NewHook(hp, "5pb"); } bool InsertStuffScriptHook() { // BOOL GetTextExtentPoint32( // _In_ HDC hdc, // _In_ LPCTSTR lpString, // _In_ int c, // _Out_ LPSIZE lpSize // ); HookParam hp; hp.address = (DWORD)::GetTextExtentPoint32A; hp.offset = get_stack(2); // arg2 lpString hp.split = get_reg(regs::esp); hp.type = USING_STRING | USING_SPLIT; ConsoleOutput("INSERT StuffScriptEngine"); return NewHook(hp, "StuffScriptEngine"); // RegisterEngine(ENGINE_STUFFSCRIPT); } bool StuffScript2Filter(LPVOID data, size_t *size, HookParam *) { auto text = reinterpret_cast(data); auto len = reinterpret_cast(size); if (text[0] == '-') { StringFilter(text, len, "-/-", 3); StringFilterBetween(text, len, "-", 1, "-", 1); } StringCharReplacer(text, len, "_n_r", 4, '\n'); StringCharReplacer(text, len, "_r", 2, ' '); StringFilter(text, len, "\\n", 2); StringFilter(text, len, "_n", 2); return true; } bool InsertStuffScript2Hook() { /* * Sample games: * https://vndb.org/r41537 * https://vndb.org/r41539 */ const BYTE bytes[] = { 0x0F, XX, XX4, // jne tokyobabel.exe+3D4E8 0xB9, XX4, // mov ecx,tokyobabel.exe+54EAC 0x8D, 0x85, XX4, // lea eax,[ebp+tokyobabel.exe+59B968] 0x8A, 0x10, // mov dl,[eax] <-- hook here 0x3A, 0x11, // cmp dl,[ecx] 0x75, 0x1A, // jne tokyobabel.exe+3D1D7 0x84, 0xD2, // test dl,dl 0x74, 0x12, // je tokyobabel.exe+3D1D3 0x8A, 0x50, 0x01, // mov dl,[eax+01] 0x3A, 0x51, 0x01, // cmp dl,[ecx+01] 0x75, 0x0E, // jne tokyobabel.exe+3D1D7 0x83, 0xC0, 0x02, // add eax,02 0x83, 0xC1, 0x02, // add ecx,02 0x84, 0xD2, // test dl,dl 0x75, 0xE4, // jne Agreement.exe+4F538 0x33, 0xC0, // xor eax,eax 0xEB, 0x05, // jmp Agreement.exe+4F55D 0x1B, 0xC0, // sbb eax,eax 0x83, 0xD8, 0xFF, // sbb eax,-01 XX2, // cmp eax,edi 0x0F, 0x84, XX4 // je tokyobabel.exe+3D4E8 }; ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); if (!addr) return false; HookParam hp; hp.address = addr + 0x11; hp.offset = get_reg(regs::eax); hp.index = 0; hp.type = USING_STRING | NO_CONTEXT; hp.filter_fun = StuffScript2Filter; ConsoleOutput("INSERT StuffScript2"); return NewHook(hp, "StuffScript2"); } bool StuffScript3Filter(LPVOID data, size_t *size, HookParam *) { auto text = reinterpret_cast(data); auto len = reinterpret_cast(size); if (text[0] == '\x81' && text[1] == '\x40') { // removes space at the beginning of the sentence *len -= 2; ::memmove(text, text + 2, *len); } StringFilterBetween(text, len, "/\x81\x79", 3, "\x81\x7A", 2); // remove hidden name StringFilterBetween(text, len, "[", 1, "]", 1); // garbage // ruby CharFilter(text, len, '<'); StringFilterBetween(text, len, ",", 1, ">", 1); StringCharReplacer(text, len, "_r\x81\x40", 4, ' '); StringCharReplacer(text, len, "_r", 2, ' '); return true; } bool InsertStuffScript3Hook() { /* * Sample games: * https://vndb.org/v3111 */ const BYTE bytes[] = { 0xCC, // int 3 0x81, 0xEC, XX4, // sub esp,00000140 <-- hook here 0xA1, XX4, // mov eax,[EVOLIMIT.exe+8C1F0] 0x33, 0xC4, // xor eax,esp 0x89, 0x84, 0x24, XX4, // mov [esp+0000013C],eax 0x53, // push ebx 0x55, // push ebp 0x8B, 0xAC, 0x24, XX4, // mov ebp,[esp+0000014C] 0x8B, 0x45, 0x2C // mov eax,[ebp+2C] }; ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); if (!addr) return false; HookParam hp = {}; hp.address = addr + 1; hp.offset = get_reg(regs::ecx); hp.type = USING_STRING | NO_CONTEXT; hp.filter_fun = StuffScript3Filter; NewHook(hp, "StuffScript3"); return true; } bool StuffScript_attach_function() { auto _ = InsertStuffScriptHook(); _ |= InsertStuffScript2Hook(); _ |= InsertStuffScript3Hook(); return _; } bool _5pb::attach_function() { bool b1 = Insert5pbHook(); bool b2 = Insert5pbHookex(); bool b3 = hookmages::MAGES(); bool sf = StuffScript_attach_function(); return b1 || b2 || b3 || sf; } bool KaleidoFilter(LPVOID data, size_t *size, HookParam *) { auto text = reinterpret_cast(data); auto len = reinterpret_cast(size); // Unofficial eng TL with garbage newline spaces StringCharReplacer(text, len, " \\n ", 4, ' '); StringCharReplacer(text, len, " \\n", 3, ' '); StringCharReplacer(text, len, "\\n", 2, ' '); StringCharReplacer(text, len, "\xEF\xBC\x9F", 3, '?'); return true; } bool InsertKaleidoHook() { /* * Sample games: * https://vndb.org/v29889 */ const BYTE bytes[] = { 0xFF, 0x75, 0xD4, // push [ebp-2C] 0xE8, XX4, // call 5toubun.exe+1DD0 0x83, 0xC4, 0x0C, // add esp,0C 0x8A, 0xC3, // mov al,bl 0x8B, 0x4D, 0xF4, // mov ecx,[ebp-0C] 0x64, 0x89, 0x0D, XX4, // mov fs:[00000000],ecx 0x59 // pop ecx << hook here }; enum { addr_offset = sizeof(bytes) - 1 }; ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); if (!addr) return false; HookParam hp; hp.address = addr + addr_offset; hp.offset = get_reg(regs::esi); hp.index = 0; hp.split = get_stack(3); hp.split_index = 0; hp.type = USING_STRING | USING_SPLIT; hp.filter_fun = KaleidoFilter; ConsoleOutput(" INSERT Kaleido"); return NewHook(hp, "Kaleido"); } namespace { // ANONYMOUS;CODE 官中 bool __1() { BYTE bytes[] = { 0x8d, 0x45, 0xf4, 0x64, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8b, 0xf1, 0x8a, 0x46, 0x2c, 0x8b, 0x55, 0x08, 0x84, 0xc0, 0x74, 0x04, 0x32, 0xc0}; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); if (!addr) return false; addr = MemDbg::findEnclosingAlignedFunction(addr); if (addr == 0) return false; HookParam hp; hp.address = addr; hp.offset = get_stack(1); hp.type = USING_STRING | CODEC_UTF8 | EMBED_ABLE | EMBED_BEFORE_SIMPLE | EMBED_AFTER_NEW; hp.newlineseperator = L"\\n"; return NewHook(hp, "5bp"); } bool __() { BYTE sig1[] = { 0x81, 0xFE, 0xF0, 0x00, 0x00, 0x00}; BYTE sig2[] = { 0x81, 0xFE, 0xF8, 0x00, 0x00, 0x00}; BYTE sig3[] = { 0x81, 0xFE, 0xFC, 0x00, 0x00, 0x00}; BYTE sig4[] = { 0x81, 0xFE, 0xFE, 0x00, 0x00, 0x00}; BYTE sig5[] = { 0x81, 0xFE, 0x80, 0x00, 0x00, 0x00}; BYTE sig6[] = { 0x81, 0xFE, 0xE0, 0x00, 0x00, 0x00}; std::unordered_map addr_hit; for (auto sigsz : std::vector>{{sig1, sizeof(sig1)}, {sig2, sizeof(sig2)}, {sig3, sizeof(sig3)}, {sig4, sizeof(sig4)}, {sig5, sizeof(sig5)}, {sig6, sizeof(sig6)}}) { for (auto addr : Util::SearchMemory(sigsz.first, sigsz.second, PAGE_EXECUTE, processStartAddress, processStopAddress)) { addr = MemDbg::findEnclosingAlignedFunction(addr); if (addr == 0) continue; if (addr_hit.find(addr) == addr_hit.end()) { addr_hit[addr] = 1; } else addr_hit[addr] += 1; } } DWORD addr = 0; int m = 0; for (auto _ : addr_hit) { if (_.second > m) { m = _.second; addr = _.first; } } if (!addr) return false; HookParam hp; hp.address = addr; hp.offset = get_stack(1); hp.type = USING_STRING | CODEC_UTF8; hp.filter_fun = [](LPVOID data, size_t *size, HookParam *) { auto text = reinterpret_cast(data); auto len = reinterpret_cast(size); StringCharReplacer(text, len, "\\n", 2, '\n'); return true; }; return NewHook(hp, "5bp"); } } // namespace name namespace { bool __2() { // レヱル・ロマネスク origin 多国語版 // https://vndb.org/r119877 // char __thiscall sub_426B70(float *this, int a2, int a3, int a4, int a5, char a6, char a7) BYTE bytes[] = { 0x0f, 0xb7, 0x04, 0x72, 0x46, 0x89, 0x85, XX4, 0x0f, 0xb7, 0xc0, 0x83, 0xc0, 0xf6, 0x83, 0xf8, 0x52, 0x0f, 0x87}; auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); if (!addr) return false; addr = MemDbg::findEnclosingAlignedFunction_strict(addr); if (!addr) return false; HookParam hp; hp.address = addr; hp.offset = get_stack(1); hp.split = get_stack(2); hp.type = USING_SPLIT | USING_STRING | FULL_STRING | CODEC_UTF16 | EMBED_ABLE | EMBED_BEFORE_SIMPLE | EMBED_AFTER_NEW; // 中文显示不出来 hp.filter_fun = [](LPVOID data, size_t *size, HookParam *) { // そうして、[おひとよ,2]御一夜――\n眼下に広がるこの町も、僕を間違いなく救ってくれた。 // 「行政に関しての最大の変化は、市長です。\n現在の市長には[ひない,1]雛衣・ポーレットが就任しています」 // 「なるほど。それゆえ、御一夜は衰退し、\n\x%lエアクラ;#00ffc040;エアクラ%l;#;工場の誘致話が持ち上がったわけか?」 // 「ナビ。お前も\x%lエアクラ;#00ffc040;エアクラ%l;#;の仲間だったな。\n気を悪くしたか?」 auto text = reinterpret_cast(data); auto len = reinterpret_cast(size); auto xx = std::wstring(text, *len / 2); xx = std::regex_replace(xx, std::wregex(L"\\[(.*?),\\d\\]"), L"$1"); xx = std::regex_replace(xx, std::wregex(L"\\\\x%l(.*?);(.*?);(.*?);#;"), L"$1"); return write_string_overwrite(data, size, xx); }; hp.newlineseperator = L"\\n"; return NewHook(hp, "5bp"); } } bool _5pb_2::attach_function() { bool ___1 = __1() || __(); ___1 |= __2(); return InsertKaleidoHook() || ___1; }