#include "LCScript.h" namespace { // unnamed namespace ScenarioHook { namespace Private { // Skip trailing 0203 LPCSTR trim(LPCSTR text, int *size) { auto length = *size; while (length && (UINT8)text[0] <= 127) { // remove all leading ASCII characters including zeros text++; length--; } while (length && (UINT8)text[length - 1] == 0) // remove all trailing zeros length--; // remove all trailing illegal double-characters enum { MinimumByte = 0x6 }; // the same as dynamicEncodingMinimumByte while (length >= 2 && (UINT8)text[length - 1] < MinimumByte && (UINT8)text[length - 2] < MinimumByte) length -= 2; *size = length; return text; } /** * Sample game: 春恋*乙女~乙女の園でごきげんよう。~ * * 067C73FA 8F CD 90 6D 01 81 75 96 7B 93 96 82 C9 82 B1 82 章仁「本当にこ・ * 067C740A F1 82 C8 82 C6 82 B1 82 EB 82 AA 82 A0 82 E9 82 ネところがある・ * 067C741A F1 82 BE 82 C8 82 9F 81 63 81 63 81 76 02 03 00 セなぁ……」. * 067C742A 38 00 00 00 01 81 40 96 DA 82 CC 91 4F 82 C9 8D 8... 目の前に・ * 067C743A 4C 82 AA 82 E9 8C F5 8C 69 82 F0 91 4F 82 C9 81 Lがる光景を前に・ * * Name/scenario splitter: 01 () * New line splitter: 0203 () */ // 0042FBE8 A1 E8234A00 MOV EAX,DWORD PTR DS:[0x4A23E8] ; jichi: text length here // // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] ; jichi: text length here // 0042FC09 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] ; jichi: count is here // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] // 0042FC10 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] ; jichi: offset // 0042FC1A 8BF8 MOV EDI,EAX // 0042FC1C 8BC1 MOV EAX,ECX // 0042FC1E 83C4 04 ADD ESP,0x4 // 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] ULONG textBaseAddress_, // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] textOffset_; // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] std::string data_; /** * Sample game: 姦獄学園 * Sample stack when hook1 is invoked: * 0012FE10 00000003 * 0012FE14 00000008 * 0012FE18 7FFDF000 * 0012FE1C 00000000 * 0012FE20 00000000 * 0012FE24 0012FEB0 Pointer to next SEH record * 0012FE28 00480918 SE handler * 0012FE2C 00000000 * 0012FE30 00419B16 RETURN to .00419B16 from .0040169F * 0012FE34 0012FE4C * 0012FE38 0012FE70 * 0012FE3C 00000040 * 0012FE40 77032EB2 user32.PeekMessageA * 0012FE44 00000000 * 0012FE48 00000039 * 0012FE4C 00000002 * 0012FE50 00000039 * 0012FE54 00000000 * 0012FE58 00000000 * * Scenario thread caller: * * 0041C27C E8 D65AFEFF CALL .00401D57 * 0041C281 8D5424 38 LEA EDX,DWORD PTR SS:[ESP+0x38] * 0041C285 68 00040000 PUSH 0x400 * 0041C28A 8D4424 34 LEA EAX,DWORD PTR SS:[ESP+0x34] * 0041C28E 52 PUSH EDX * 0041C28F 50 PUSH EAX * 0041C290 E8 2354FEFF CALL .004016B8 ; jichi: scenario caller here * 0041C295 83C4 0C ADD ESP,0xC * 0041C298 8D4C24 38 LEA ECX,DWORD PTR SS:[ESP+0x38] * 0041C29C 8B15 B44E4A00 MOV EDX,DWORD PTR DS:[0x4A4EB4] * 0041C2A2 51 PUSH ECX * 0041C2A3 8B0D 5C0A4A00 MOV ECX,DWORD PTR DS:[0x4A0A5C] * 0041C2A9 8BC1 MOV EAX,ECX * * Other thread callers: * * 00421298 8D8424 B0000000 LEA EAX,DWORD PTR SS:[ESP+0xB0] * 0042129F 50 PUSH EAX * 004212A0 51 PUSH ECX * 004212A1 895424 2C MOV DWORD PTR SS:[ESP+0x2C],EDX * 004212A5 E8 0E04FEFF CALL .004016B8 ; jichi: other caller * 004212AA 8D5424 38 LEA EDX,DWORD PTR SS:[ESP+0x38] * 004212AE 68 80000000 PUSH 0x80 * 004212B3 8D4424 24 LEA EAX,DWORD PTR SS:[ESP+0x24] * 004212B7 52 PUSH EDX * 004212B8 50 PUSH EAX * 004212B9 E8 FA03FEFF CALL .004016B8 ; jichi: other here * 004212BE 83C4 18 ADD ESP,0x18 * 004212C1 83FF 01 CMP EDI,0x1 * 004212C4 75 68 JNZ SHORT .0042132E * * * Sample game: 春恋*乙女~乙女の園でごきげんよう。~ * Sample scenario caller: * 0041C0C4 8D4424 38 LEA EAX,DWORD PTR SS:[ESP+0x38] * 0041C0C8 68 00040000 PUSH 0x400 * 0041C0CD 8D4C24 34 LEA ECX,DWORD PTR SS:[ESP+0x34] * 0041C0D1 50 PUSH EAX * 0041C0D2 51 PUSH ECX * 0041C0D3 E8 C755FEFF CALL .0040169F ; jichi: called here * 0041C0D8 8B0D 4CE94900 MOV ECX,DWORD PTR DS:[0x49E94C] * 0041C0DE 8B35 00244A00 MOV ESI,DWORD PTR DS:[0x4A2400] * 0041C0E4 8BC1 MOV EAX,ECX * 0041C0E6 83C4 0C ADD ESP,0xC * * 0012FA54 00000001 * 0012FA58 00000006 * 0012FA5C 7707EA71 user32.MessageBoxA * 0012FA60 00000000 * 0012FA64 00000000 * 0012FA68 0012FF78 Pointer to next SEH record * 0012FA6C 00480918 SE handler * 0012FA70 00000000 * 0012FA74 0041C0D8 RETURN to .0041C0D8 from .0040169F * 0012FA78 0012FAB4 * 0012FA7C 0012FABC * 0012FA80 00000400 ; jichi: used as split to identify scenario thread * 0012FA84 00000003 * 0012FA88 77032EB2 user32.PeekMessageA * 0012FA8C 77033569 user32.DispatchMessageA * 0012FA90 7FFDF000 * 0012FA94 00000000 * 0012FA98 00000000 * * Other thread caller: * 0012FD60 00000001 * 0012FD64 00000001 * 0012FD68 7FFDF000 * 0012FD6C 00000000 * 0012FD70 00000000 * 0012FD74 0012FF78 Pointer to next SEH record * 0012FD78 00480918 SE handler * 0012FD7C 00000000 * 0012FD80 0042113A RETURN to .0042113A from .0040169F * 0012FD84 0012FDAC * 0012FD88 0012FE3C * 0012FD8C 00000080 ; jichi: arg3 * 0012FD90 00000003 * 0012FD94 77032EB2 user32.PeekMessageA * 0012FD98 77033569 user32.DispatchMessageA * 0012FD9C 00000002 * 0012FDA0 00000034 * 0012FDA4 00000002 * 0012FDA8 0000006D * 0012FDAC 00000002 * 0012FDB0 00000034 * 0012FDB4 00000000 * 0012FDB8 00000001 * 0012FDBC 001907D0 * 0012FDC0 00000202 * * Sample game: 恋姫†無双 * ecx = 0x22 * Sample game text containing zeros * 01D6B13B 8E A9 8C 52 81 41 05 04 00 00 00 01 81 40 81 40 自軍、...   * 01D6B14B 81 40 91 CE 01 93 47 8C 52 81 41 05 05 00 00 00  対敵軍、... * 01D6B15B 02 00 14 00 00 00 5F 62 74 6C 5F 53 65 74 57 61 ...._btl_SetWa * 01D6B16B 7A 61 42 74 6E 53 72 63 59 00 0D 00 00 00 5F 62 zaBtnSrcY....._b * 01D6B17B 74 6C 5F 63 6D 64 63 68 69 70 00 0F 00 00 00 5F tl_cmdchip...._ * 01D6B18B 62 74 6C 5F 63 6D 64 63 68 69 70 5F 6D 00 0D 00 btl_cmdchip_m... * 01D6B19B 00 00 5F 62 74 6C 5F 6F 6E 6D 6F 75 73 65 00 0E .._btl_onmouse. * 01D6B1AB 00 00 00 5F 62 74 6C 5F 73 65 6C 65 63 74 65 64 ..._btl_selected * 01D6B1BB 00 0B 00 00 00 5F 62 74 6C 5F 52 65 74 72 79 00 . ..._btl_Retry. * 01D6B1CB 13 00 00 00 5F 62 74 6C 5F 43 6C 65 61 6E 75 70 ..._btl_Cleanup * * ecx = 0x19 * 01D6B317 81 40 04 6B 00 00 00 82 CC 91 B9 8A 51 82 F0 97  k...の損害を・ * 01D6B327 5E 82 A6 82 BD 81 42 02 00 10 00 00 00 5F 62 74 ^えた。...._bt * 01D6B337 6C 5F 57 61 7A 61 5F 43 68 6F 75 6E 00 17 00 00 l_Waza_Choun... * 01D6B347 00 5F 62 74 6C 5F 57 61 7A 61 45 6E 65 6D 79 5F ._btl_WazaEnemy_ * 01D6B357 42 75 66 66 41 54 4B 00 10 00 00 00 5F 62 74 6C BuffATK...._btl * 01D6B367 5F 57 61 7A 61 5F 4B 6F 63 68 75 00 1C 00 00 00 _Waza_Kochu.... */ bool hook1(hook_stack *s, void *data, size_t *len1, uintptr_t *role) { data_.clear(); int size = s->eax - 1; if (size <= 0) return false; // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] ; jichi: text here // 0042FC09 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] ; jichi: count is here // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] ; jichi: [arg1+4] // 0042FC10 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] ; jichi: base addr, [[0x4A23E8] + 0x2944] // 0042FC1A 8BF8 MOV EDI,EAX // 0042FC1C 8BC1 MOV EAX,ECX // 0042FC1E 83C4 04 ADD ESP,0x4 // // 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] ; jichi: hook2, text in esi ULONG edx, esi; { edx = *(DWORD *)textBaseAddress_; // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] edx = *(DWORD *)(edx + textOffset_); // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] esi = *(DWORD *)(s->esi + 0x4); // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] esi = edx + esi + 0x4; // 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] } auto text = (LPCSTR)esi; if (!*text //|| ::strlen(text) != size || text[size] // text length not verified since there could be trailing zeros || ::isalpha(text[0]) && ::isalpha(text[1]) // Sample system text in 恋姫無双: bcg_剣道場a || all_ascii(text)) return false; auto trimmedSize = size; auto trimmedText = trim(text, &trimmedSize); if (trimmedSize <= 0) return false; // auto size = s->ecx * 4; // auto dst = (LPSTR)s->edi; *role = Engine::OtherRole; auto retaddr = s->stack[8]; // if ((*(DWORD *)retaddr & 0xffffff) == 0x0cc483) // 0041C295 83C4 0C ADD ESP,0xC // role = Engine::ScenarioRole; auto arg3 = s->stack[8 + 3]; if (arg3 == 0x400) *role = Engine::ScenarioRole; // 8/7/2015: Here, I could also split choice and scenario from the retaddr. // But I didn't so that choice can also be display the same way asn scenario. // sig = retaddr; std::string oldData(trimmedText, trimmedSize); static const std::string zero_bytes(1, '\0'); const char *zero_str = LCSE_0; bool containsZeros = false; if (oldData.find('\0') != oldData.npos) { containsZeros = true; strReplace(oldData, zero_bytes, zero_str); // oldData.replace(zero_bytes, zero_str); *role = Engine::OtherRole; // FIXME: There could be individual ascii letters before zeros (such as "k" and "n") // They should be escaped here. // Escaping not implemented since I am lazy. } write_string_overwrite(data, len1, oldData); return true; } void hookafter(hook_stack *s, void *data, size_t len1) { int size = s->eax - 1; if (size <= 0) return; ULONG edx, esi; { edx = *(DWORD *)textBaseAddress_; // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] edx = *(DWORD *)(edx + textOffset_); // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] esi = *(DWORD *)(s->esi + 0x4); // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] esi = edx + esi + 0x4; // 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] } auto text = (LPCSTR)esi; if (!*text //|| ::strlen(text) != size || text[size] // text length not verified since there could be trailing zeros || ::isalpha(text[0]) && ::isalpha(text[1]) // Sample system text in 恋姫無双: bcg_剣道場a || all_ascii(text)) return; auto trimmedSize = size; auto trimmedText = trim(text, &trimmedSize); if (trimmedSize <= 0) return; auto retaddr = s->stack[8]; // if ((*(DWORD *)retaddr & 0xffffff) == 0x0cc483) // 0041C295 83C4 0C ADD ESP,0xC // role = Engine::ScenarioRole; auto arg3 = s->stack[8 + 3]; std::string oldData(trimmedText, trimmedSize); static const std::string zero_bytes(1, '\0'); const char *zero_str = LCSE_0; bool containsZeros = false; if (oldData.find('\0') != oldData.npos) { containsZeros = true; strReplace(oldData, zero_bytes, zero_str); // oldData.replace(zero_bytes, zero_str); // FIXME: There could be individual ascii letters before zeros (such as "k" and "n") // They should be escaped here. // Escaping not implemented since I am lazy. } std::string newData = std::string((char *)data, len1); if (newData.empty() || newData == oldData) return; if (containsZeros) strReplace(newData, zero_str, zero_bytes); // newData.replace(zero_str, zero_bytes); int prefixSize = trimmedText - text, suffixSize = size - prefixSize - trimmedSize; if (prefixSize) newData.insert(0, std::string(text, prefixSize)); if (suffixSize) newData.append(trimmedText + trimmedSize, suffixSize); data_ = newData; s->eax = data_.size() + 1; return; } bool hook2(hook_stack *s, void *data, size_t *len1, uintptr_t *role) { if (!data_.empty()) s->esi = (ULONG)data_.c_str(); return false; } } // namespace Private /** * Sample game: 春恋*乙女~乙女の園でごきげんよう。~ * * 0042FB1E CC INT3 * 0042FB1F CC INT3 * 0042FB20 6A FF PUSH -0x1 * 0042FB22 68 18094800 PUSH lcsebody.00480918 * 0042FB27 64:A1 00000000 MOV EAX,DWORD PTR FS:[0] * 0042FB2D 50 PUSH EAX * 0042FB2E 64:8925 00000000 MOV DWORD PTR FS:[0],ESP * 0042FB35 83EC 08 SUB ESP,0x8 * 0042FB38 53 PUSH EBX * 0042FB39 33DB XOR EBX,EBX * 0042FB3B 56 PUSH ESI * 0042FB3C 57 PUSH EDI * 0042FB3D 895C24 0C MOV DWORD PTR SS:[ESP+0xC],EBX * 0042FB41 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX * 0042FB45 8B7424 24 MOV ESI,DWORD PTR SS:[ESP+0x24] ; jichi; arg1 * 0042FB49 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX * 0042FB4D 8B06 MOV EAX,DWORD PTR DS:[ESI] * 0042FB4F 83F8 05 CMP EAX,0x5 * 0042FB52 75 2F JNZ SHORT lcsebody.0042FB83 * 0042FB54 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 0042FB57 8B3D E8234A00 MOV EDI,DWORD PTR DS:[0x4A23E8] * 0042FB5D 3BF3 CMP ESI,EBX * 0042FB5F 7C 08 JL SHORT lcsebody.0042FB69 * 0042FB61 39B7 54290000 CMP DWORD PTR DS:[EDI+0x2954],ESI * 0042FB67 7F 12 JG SHORT lcsebody.0042FB7B * 0042FB69 53 PUSH EBX * 0042FB6A 68 20F54800 PUSH lcsebody.0048F520 ; ASCII "err" * 0042FB6F 68 F4F44800 PUSH lcsebody.0048F4F4 * 0042FB74 53 PUSH EBX * 0042FB75 FF15 EC874A00 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; user32.MessageBoxA * 0042FB7B 8B87 74290000 MOV EAX,DWORD PTR DS:[EDI+0x2974] * 0042FB81 EB 32 JMP SHORT lcsebody.0042FBB5 * 0042FB83 83F8 08 CMP EAX,0x8 ; jichi: esi=arg1 jumped here * 0042FB86 75 57 JNZ SHORT lcsebody.0042FBDF * 0042FB88 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 0042FB8B 8B3D E8234A00 MOV EDI,DWORD PTR DS:[0x4A23E8] * 0042FB91 3BF3 CMP ESI,EBX * 0042FB93 7C 08 JL SHORT lcsebody.0042FB9D * 0042FB95 39B7 60290000 CMP DWORD PTR DS:[EDI+0x2960],ESI * 0042FB9B 7F 12 JG SHORT lcsebody.0042FBAF * 0042FB9D 53 PUSH EBX * 0042FB9E 68 20F54800 PUSH lcsebody.0048F520 ; ASCII "err" * 0042FBA3 68 F4F44800 PUSH lcsebody.0048F4F4 * 0042FBA8 53 PUSH EBX * 0042FBA9 FF15 EC874A00 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; user32.MessageBoxA * 0042FBAF 8B87 80290000 MOV EAX,DWORD PTR DS:[EDI+0x2980] * 0042FBB5 8D34F0 LEA ESI,DWORD PTR DS:[EAX+ESI*8] * 0042FBB8 8B06 MOV EAX,DWORD PTR DS:[ESI] * 0042FBBA 50 PUSH EAX * 0042FBBB 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX * 0042FBBF E8 5E840000 CALL lcsebody.00438022 * 0042FBC4 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] * 0042FBC8 83C4 04 ADD ESP,0x4 * 0042FBCB 8BD1 MOV EDX,ECX * 0042FBCD 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX * 0042FBD1 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 0042FBD4 8BF8 MOV EDI,EAX * 0042FBD6 C1E9 02 SHR ECX,0x2 * 0042FBD9 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> * 0042FBDB 8BCA MOV ECX,EDX * 0042FBDD EB 4D JMP SHORT lcsebody.0042FC2C * 0042FBDF 83F8 02 CMP EAX,0x2 ; jichi: esi=arg1 jumped here * 0042FBE2 0F85 A2000000 JNZ lcsebody.0042FC8A * 0042FBE8 A1 E8234A00 MOV EAX,DWORD PTR DS:[0x4A23E8] ; jichi: text length here * 0042FBED 8B56 04 MOV EDX,DWORD PTR DS:[ESI+0x4] * 0042FBF0 8B88 44290000 MOV ECX,DWORD PTR DS:[EAX+0x2944] * 0042FBF6 8B0411 MOV EAX,DWORD PTR DS:[ECX+EDX] * * 0042FBF9 50 PUSH EAX ; jichi: hook1, text length pushed, new function * 0042FBFA 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX ; jichi: text length, is this the memory allocation * 0042FBFE E8 1F840000 CALL lcsebody.00438022 * * 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] ; jichi: text here * 0042FC09 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] ; jichi: count is here * 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] ; jichi: [arg1+4] * 0042FC10 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX * 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] ; jichi: base addr, [[0x4A23E8] + 0x2944] * 0042FC1A 8BF8 MOV EDI,EAX * 0042FC1C 8BC1 MOV EAX,ECX * 0042FC1E 83C4 04 ADD ESP,0x4 * * 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] ; jichi: hook2, text in esi * 0042FC25 C1E9 02 SHR ECX,0x2 ; jichi: ecx is now the count, here, the rep function is blocked by 4 for performance * 0042FC28 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS[ESI] ; jichi: text accessed here from esi to edi * * 0042FC2A 8BC8 MOV ECX,EAX * 0042FC2C 8B5424 28 MOV EDX,DWORD PTR SS:[ESP+0x28] * 0042FC30 83E1 03 AND ECX,0x3 * 0042FC33 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] * 0042FC35 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+0x2C] * 0042FC39 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+0xC] * 0042FC3D 51 PUSH ECX * 0042FC3E 52 PUSH EDX * 0042FC3F 50 PUSH EAX * 0042FC40 E8 AB14FDFF CALL lcsebody.004010F0 * 0042FC45 83C4 0C ADD ESP,0xC * 0042FC48 C74424 1C FFFFFF>MOV DWORD PTR SS:[ESP+0x1C],-0x1 * 0042FC50 84C0 TEST AL,AL * 0042FC52 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10] * 0042FC56 895C24 0C MOV DWORD PTR SS:[ESP+0xC],EBX * 0042FC5A 74 21 JE SHORT lcsebody.0042FC7D * 0042FC5C 3BC3 CMP EAX,EBX * 0042FC5E 74 09 JE SHORT lcsebody.0042FC69 * 0042FC60 50 PUSH EAX * 0042FC61 E8 467E0000 CALL lcsebody.00437AAC * 0042FC66 83C4 04 ADD ESP,0x4 * 0042FC69 5F POP EDI * 0042FC6A 5E POP ESI * 0042FC6B B0 01 MOV AL,0x1 * 0042FC6D 5B POP EBX * 0042FC6E 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+0x8] * 0042FC72 64:890D 00000000 MOV DWORD PTR FS:[0],ECX * 0042FC79 83C4 14 ADD ESP,0x14 * 0042FC7C C3 RETN * 0042FC7D 3BC3 CMP EAX,EBX * 0042FC7F 74 09 JE SHORT lcsebody.0042FC8A * 0042FC81 50 PUSH EAX * 0042FC82 E8 257E0000 CALL lcsebody.00437AAC * 0042FC87 83C4 04 ADD ESP,0x4 * 0042FC8A 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+0x14] * 0042FC8E 5F POP EDI * 0042FC8F 5E POP ESI * 0042FC90 32C0 XOR AL,AL * 0042FC92 5B POP EBX * 0042FC93 64:890D 00000000 MOV DWORD PTR FS:[0],ECX * 0042FC9A 83C4 14 ADD ESP,0x14 * 0042FC9D C3 RETN * 0042FC9E 90 NOP * 0042FC9F 90 NOP * 0042FCA0 CC INT3 * 0042FCA1 CC INT3 * 0042FCA2 CC INT3 * 0042FCA3 CC INT3 * 0042FCA4 CC INT3 * 0042FCA5 CC INT3 * 0042FCA6 CC INT3 * * Sample game: 姦獄学園 * * 00430CAB CC INT3 * 00430CAC CC INT3 * 00430CAD CC INT3 * 00430CAE CC INT3 * 00430CAF CC INT3 * 00430CB0 6A FF PUSH -0x1 * 00430CB2 68 08204800 PUSH .00482008 * 00430CB7 64:A1 00000000 MOV EAX,DWORD PTR FS:[0] * 00430CBD 50 PUSH EAX * 00430CBE 64:8925 00000000 MOV DWORD PTR FS:[0],ESP * 00430CC5 83EC 08 SUB ESP,0x8 * 00430CC8 53 PUSH EBX * 00430CC9 33DB XOR EBX,EBX * 00430CCB 56 PUSH ESI * 00430CCC 57 PUSH EDI * 00430CCD 895C24 0C MOV DWORD PTR SS:[ESP+0xC],EBX * 00430CD1 895C24 10 MOV DWORD PTR SS:[ESP+0x10],EBX * 00430CD5 8B7424 24 MOV ESI,DWORD PTR SS:[ESP+0x24] * 00430CD9 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX * 00430CDD 8B06 MOV EAX,DWORD PTR DS:[ESI] * 00430CDF 83F8 05 CMP EAX,0x5 * 00430CE2 75 2F JNZ SHORT .00430D13 * 00430CE4 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 00430CE7 8B3D 9C4E4A00 MOV EDI,DWORD PTR DS:[0x4A4E9C] * 00430CED 3BF3 CMP ESI,EBX * 00430CEF 7C 08 JL SHORT .00430CF9 * 00430CF1 39B7 54310000 CMP DWORD PTR DS:[EDI+0x3154],ESI * 00430CF7 7F 12 JG SHORT .00430D0B * 00430CF9 53 PUSH EBX * 00430CFA 68 98154900 PUSH .00491598 ; ASCII "err" * 00430CFF 68 D8254900 PUSH .004925D8 * 00430D04 53 PUSH EBX * 00430D05 FF15 2CC84A00 CALL DWORD PTR DS:[0x4AC82C] ; user32.MessageBoxA * 00430D0B 8B87 74310000 MOV EAX,DWORD PTR DS:[EDI+0x3174] * 00430D11 EB 32 JMP SHORT .00430D45 * 00430D13 83F8 08 CMP EAX,0x8 * 00430D16 75 57 JNZ SHORT .00430D6F * 00430D18 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 00430D1B 8B3D 9C4E4A00 MOV EDI,DWORD PTR DS:[0x4A4E9C] * 00430D21 3BF3 CMP ESI,EBX * 00430D23 7C 08 JL SHORT .00430D2D * 00430D25 39B7 60310000 CMP DWORD PTR DS:[EDI+0x3160],ESI * 00430D2B 7F 12 JG SHORT .00430D3F * 00430D2D 53 PUSH EBX * 00430D2E 68 98154900 PUSH .00491598 ; ASCII "err" * 00430D33 68 AC254900 PUSH .004925AC * 00430D38 53 PUSH EBX * 00430D39 FF15 2CC84A00 CALL DWORD PTR DS:[0x4AC82C] ; user32.MessageBoxA * 00430D3F 8B87 80310000 MOV EAX,DWORD PTR DS:[EDI+0x3180] * 00430D45 8D34F0 LEA ESI,DWORD PTR DS:[EAX+ESI*8] * 00430D48 8B06 MOV EAX,DWORD PTR DS:[ESI] * 00430D4A 50 PUSH EAX * 00430D4B 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX * 00430D4F E8 BE890000 CALL .00439712 * 00430D54 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] * 00430D58 83C4 04 ADD ESP,0x4 * 00430D5B 8BD1 MOV EDX,ECX * 00430D5D 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX * 00430D61 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 00430D64 8BF8 MOV EDI,EAX * 00430D66 C1E9 02 SHR ECX,0x2 * 00430D69 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> * 00430D6B 8BCA MOV ECX,EDX * 00430D6D EB 4D JMP SHORT .00430DBC * 00430D6F 83F8 02 CMP EAX,0x2 * 00430D72 0F85 A2000000 JNZ .00430E1A * 00430D78 A1 9C4E4A00 MOV EAX,DWORD PTR DS:[0x4A4E9C] * 00430D7D 8B56 04 MOV EDX,DWORD PTR DS:[ESI+0x4] * 00430D80 8B88 44310000 MOV ECX,DWORD PTR DS:[EAX+0x3144] * 00430D86 8B0411 MOV EAX,DWORD PTR DS:[ECX+EDX] * 00430D89 50 PUSH EAX * 00430D8A 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX * 00430D8E E8 7F890000 CALL .00439712 * 00430D93 8B15 9C4E4A00 MOV EDX,DWORD PTR DS:[0x4A4E9C] * 00430D99 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] * 00430D9D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] * 00430DA0 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX * 00430DA4 8B92 44310000 MOV EDX,DWORD PTR DS:[EDX+0x3144] * 00430DAA 8BF8 MOV EDI,EAX * 00430DAC 8BC1 MOV EAX,ECX * 00430DAE 83C4 04 ADD ESP,0x4 * 00430DB1 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] ; jichi: the other game's access point * 00430DB5 C1E9 02 SHR ECX,0x2 * 00430DB8 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] * 00430DBA 8BC8 MOV ECX,EAX * 00430DBC 8B5424 28 MOV EDX,DWORD PTR SS:[ESP+0x28] * 00430DC0 83E1 03 AND ECX,0x3 * 00430DC3 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] * 00430DC5 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+0x2C] * 00430DC9 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+0xC] * 00430DCD 51 PUSH ECX * 00430DCE 52 PUSH EDX * 00430DCF 50 PUSH EAX * 00430DD0 E8 2503FDFF CALL .004010FA * 00430DD5 83C4 0C ADD ESP,0xC * 00430DD8 C74424 1C FFFFFF>MOV DWORD PTR SS:[ESP+0x1C],-0x1 * 00430DE0 84C0 TEST AL,AL * 00430DE2 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10] * 00430DE6 895C24 0C MOV DWORD PTR SS:[ESP+0xC],EBX * 00430DEA 74 21 JE SHORT .00430E0D * 00430DEC 3BC3 CMP EAX,EBX * 00430DEE 74 09 JE SHORT .00430DF9 * 00430DF0 50 PUSH EAX * 00430DF1 E8 A6830000 CALL .0043919C * 00430DF6 83C4 04 ADD ESP,0x4 * 00430DF9 5F POP EDI * 00430DFA 5E POP ESI * 00430DFB B0 01 MOV AL,0x1 * 00430DFD 5B POP EBX * 00430DFE 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+0x8] * 00430E02 64:890D 00000000 MOV DWORD PTR FS:[0],ECX * 00430E09 83C4 14 ADD ESP,0x14 * 00430E0C C3 RETN * 00430E0D 3BC3 CMP EAX,EBX * 00430E0F 74 09 JE SHORT .00430E1A * 00430E11 50 PUSH EAX * 00430E12 E8 85830000 CALL .0043919C * 00430E17 83C4 04 ADD ESP,0x4 * 00430E1A 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+0x14] * 00430E1E 5F POP EDI * 00430E1F 5E POP ESI * 00430E20 32C0 XOR AL,AL * 00430E22 5B POP EBX * 00430E23 64:890D 00000000 MOV DWORD PTR FS:[0],ECX * 00430E2A 83C4 14 ADD ESP,0x14 * 00430E2D C3 RETN * 00430E2E 90 NOP * 00430E2F 90 NOP * 00430E30 CC INT3 * 00430E31 CC INT3 * 00430E32 CC INT3 * 00430E33 CC INT3 * 00430E34 CC INT3 */ bool isLeadByteChar(const char *s) { return dynsjis::isleadstr(s); // return ::IsDBCSLeadByte(HIBYTE(testChar)); } bool attach(ULONG startAddress, ULONG stopAddress, ULONG dyna) { const uint8_t bytes[] = { 0x8d, 0x74, 0x32, 0x04, // 0042fc21 8d7432 04 lea esi,dword ptr ds:[edx+esi+0x4] 0xc1, 0xe9, 0x02, // 0042fc25 c1e9 02 shr ecx,0x2 0xf3, 0xa5 // 0042fc28 f3:a5 rep movs dword ptr es:[edi],dword ptr ds[esi] ; jichi: text accessed here from esi to edi }; ULONG addr2 = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); if (!addr2) return false; // 0042FBF9 50 PUSH EAX ; jichi: hook1, text length pushed, new function // 0042FBFA 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX ; jichi: text length, is this the memory allocation? // 0042FBFE E8 1F840000 CALL lcsebody.00438022 // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] ; jichi: text here // 0042FC09 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] ; jichi: count is here // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] ; jichi: [arg1+4] // 0042FC10 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] ; jichi: base addr, [[0x4A23E8] + 0x2944] // 0042FC1A 8BF8 MOV EDI,EAX // 0042FC1C 8BC1 MOV EAX,ECX // 0042FC1E 83C4 04 ADD ESP,0x4 // // 0042FC21 8D7432 04 LEA ESI,DWORD PTR DS:[EDX+ESI+0x4] ; jichi: hook2, text in esi // 0042FC25 C1E9 02 SHR ECX,0x2 ; jichi: ecx is now the count, here, the rep function is blocked by 4 for performance // 0042FC28 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS[ESI] ; jichi: text accessed here from esi to edi ULONG addr1 = addr2 + 0x0042fbf9 - 0x0042fc21; if (*(BYTE *)addr1 != 0x50) // push_eax return false; // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] ; jichi: text here // 0042FC09 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] ; jichi: count is here // 0042FC0D 8B76 04 MOV ESI,DWORD PTR DS:[ESI+0x4] ; jichi: [arg1+4] // 0042FC10 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] ; jichi: offset addr, [[0x4A23E8] + 0x2944] { ULONG addr = addr2 + 0x0042fc03 - 0x0042fc21; if (*(WORD *)addr != 0x158b) // 0042FC03 8B15 E8234A00 MOV EDX,DWORD PTR DS:[0x4A23E8] return false; addr += 2; Private::textBaseAddress_ = *(DWORD *)addr; } { ULONG addr = addr2 + 0x0042fc14 - 0x0042fc21; if (*(WORD *)addr != 0x928b) // 0042FC14 8B92 44290000 MOV EDX,DWORD PTR DS:[EDX+0x2944] return false; addr += 2; Private::textOffset_ = *(DWORD *)addr; } HookParam hp; hp.address = addr1; hp.hook_before = Private::hook1; hp.hook_after = Private::hookafter; hp.type = EMBED_ABLE; hp.newlineseperator = L"\x01"; hp.hook_font = F_GetGlyphOutlineA; if (dyna) { static ULONG dynas; dynas = dyna; hp.type |= EMBED_DYNA_SJIS; hp.hook_font = F_GetGlyphOutlineA; patch_fun = []() { ReplaceFunction((PVOID)dynas, (PVOID)(ULONG)isLeadByteChar); dynamiccodec->setMinimumSecondByte(6); //// skip 0x1,0x2,0x3 in case dynamic encoding could crash the game }; } auto succ = NewHook(hp, "EmbedLCSE"); hp.address = addr2 + 4; hp.hook_before = Private::hook2; hp.type = EMBED_ABLE | HOOK_EMPTY; succ |= NewHook(hp, "EmbedLCSE"); return succ; } } // namespace ScenarioHook namespace Patch { namespace Private { bool isLeadByteChar(const char *s) { return dynsjis::isleadstr(s); // return ::IsDBCSLeadByte(HIBYTE(testChar)); } } // namespace Private /** * Sample game: 春恋*乙女~乙女の園でごきげんよう。~ * * Debugging method: Find text in memory, and then insert hardware breakpoint. * It will be accessed only ONCE in the following function. * * This function can also be found by searching the following instruction: * 0040A389 3C 81 CMP AL,0x81 * * This function is very similar to that in CatSystem2. * * 0040A37E CC INT3 * 0040A37F CC INT3 * 0040A380 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+0x4] * 0040A384 8A01 MOV AL,BYTE PTR DS:[ECX] ; jichi: first byte * 0040A386 8A49 01 MOV CL,BYTE PTR DS:[ECX+0x1] ; jichi: second byte * 0040A389 3C 81 CMP AL,0x81 * 0040A38B 72 04 JB SHORT lcsebody.0040A391 * 0040A38D 3C 9F CMP AL,0x9F * 0040A38F 76 08 JBE SHORT lcsebody.0040A399 * 0040A391 3C E0 CMP AL,0xE0 * 0040A393 72 1B JB SHORT lcsebody.0040A3B0 * 0040A395 3C FC CMP AL,0xFC * 0040A397 77 17 JA SHORT lcsebody.0040A3B0 * 0040A399 80F9 40 CMP CL,0x40 * 0040A39C 72 05 JB SHORT lcsebody.0040A3A3 * 0040A39E 80F9 7E CMP CL,0x7E * 0040A3A1 76 0A JBE SHORT lcsebody.0040A3AD * 0040A3A3 80F9 80 CMP CL,0x80 * 0040A3A6 72 08 JB SHORT lcsebody.0040A3B0 * 0040A3A8 80F9 FC CMP CL,0xFC * 0040A3AB 77 03 JA SHORT lcsebody.0040A3B0 * 0040A3AD B0 01 MOV AL,0x1 * 0040A3AF C3 RETN * 0040A3B0 32C0 XOR AL,AL * 0040A3B2 C3 RETN * 0040A3B3 90 NOP * 0040A3B4 90 NOP * 0040A3B5 90 NOP * 0040A3B6 90 NOP * * This function is found by tracing the caller of GetGlyphOutlineA, as follows: * * 00416B6B CC INT3 * 00416B6C CC INT3 * 00416B6D CC INT3 * 00416B6E CC INT3 * 00416B6F CC INT3 * 00416B70 83EC 08 SUB ESP,0x8 * 00416B73 53 PUSH EBX * 00416B74 56 PUSH ESI * 00416B75 8BF1 MOV ESI,ECX * 00416B77 33DB XOR EBX,EBX ; jichi: zero ebx * 00416B79 57 PUSH EDI * 00416B7A 8B86 EC000000 MOV EAX,DWORD PTR DS:[ESI+0xEC] * 00416B80 8A9430 08010000 MOV DL,BYTE PTR DS:[EAX+ESI+0x108] ; jichi: byte accessed here * 00416B87 8D8C30 08010000 LEA ECX,DWORD PTR DS:[EAX+ESI+0x108] ; jichi: byte accessed here * 00416B8E 3AD3 CMP DL,BL ; jichi: bl is zero, dl is the current byte * 00416B90 75 0C JNZ SHORT lcsebody.00416B9E * 00416B92 B8 FF000000 MOV EAX,0xFF * 00416B97 5F POP EDI * 00416B98 5E POP ESI * 00416B99 5B POP EBX * 00416B9A 83C4 08 ADD ESP,0x8 * 00416B9D C3 RETN * 00416B9E 8B96 F0000000 MOV EDX,DWORD PTR DS:[ESI+0xF0] * 00416BA4 4A DEC EDX * 00416BA5 3BC2 CMP EAX,EDX * 00416BA7 0F8D 31010000 JGE lcsebody.00416CDE * 00416BAD 51 PUSH ECX * 00416BAE E8 31B1FEFF CALL lcsebody.00401CE4 ; jichi: ecx point to the current character, return 0 or 1 * 00416BB3 83C4 04 ADD ESP,0x4 * 00416BB6 84C0 TEST AL,AL * 00416BB8 0F84 20010000 JE lcsebody.00416CDE ; jichi: wrong here * 00416BBE 8B86 EC000000 MOV EAX,DWORD PTR DS:[ESI+0xEC] * 00416BC4 33C9 XOR ECX,ECX * 00416BC6 03C6 ADD EAX,ESI * 00416BC8 889E 20050000 MOV BYTE PTR DS:[ESI+0x520],BL * 00416BCE 8AA8 08010000 MOV CH,BYTE PTR DS:[EAX+0x108] ; jichi: high bits * 00416BD4 8A88 09010000 MOV CL,BYTE PTR DS:[EAX+0x109] * 00416BDA 8BF9 MOV EDI,ECX ; jichi: low bits, edi is now the full character * 00416BDC 8BCE MOV ECX,ESI ; jichi: recover ecx to esi * 00416BDE E8 13AEFEFF CALL lcsebody.004019F6 ; jichi: eax is zero when edi is legal * 00416BE3 3BC3 CMP EAX,EBX ; jichi: ebx is always zero as well * 00416BE5 74 4A JE SHORT lcsebody.00416C31 * 00416BE7 389E 2C050000 CMP BYTE PTR DS:[ESI+0x52C],BL * 00416BED 0F84 9A020000 JE lcsebody.00416E8D * 00416BF3 389E 20050000 CMP BYTE PTR DS:[ESI+0x520],BL * 00416BF9 74 1B JE SHORT lcsebody.00416C16 * 00416BFB B9 34F14800 MOV ECX,lcsebody.0048F134 * 00416C00 3B39 CMP EDI,DWORD PTR DS:[ECX] * 00416C02 74 2D JE SHORT lcsebody.00416C31 * 00416C04 83C1 04 ADD ECX,0x4 * 00416C07 81F9 50F14800 CMP ECX,lcsebody.0048F150 * 00416C0D ^7C F1 JL SHORT lcsebody.00416C00 * 00416C0F 5F POP EDI * 00416C10 5E POP ESI * 00416C11 5B POP EBX * 00416C12 83C4 08 ADD ESP,0x8 * 00416C15 C3 RETN * 00416C16 B9 00F14800 MOV ECX,lcsebody.0048F100 * 00416C1B 3B39 CMP EDI,DWORD PTR DS:[ECX] * 00416C1D 74 12 JE SHORT lcsebody.00416C31 * 00416C1F 83C1 04 ADD ECX,0x4 * 00416C22 81F9 34F14800 CMP ECX,lcsebody.0048F134 * 00416C28 ^7C F1 JL SHORT lcsebody.00416C1B * 00416C2A 5F POP EDI * 00416C2B 5E POP ESI * 00416C2C 5B POP EBX * 00416C2D 83C4 08 ADD ESP,0x8 * 00416C30 C3 RETN * 00416C31 8A8E 20050000 MOV CL,BYTE PTR DS:[ESI+0x520] * 00416C37 3ACB CMP CL,BL * 00416C39 74 15 JE SHORT lcsebody.00416C50 * 00416C3B B8 70F14800 MOV EAX,lcsebody.0048F170 * 00416C40 3B38 CMP EDI,DWORD PTR DS:[EAX] * 00416C42 74 21 JE SHORT lcsebody.00416C65 * 00416C44 83C0 04 ADD EAX,0x4 * 00416C47 3D 7CF14800 CMP EAX,lcsebody.0048F17C * 00416C4C ^7C F2 JL SHORT lcsebody.00416C40 * 00416C4E EB 1B JMP SHORT lcsebody.00416C6B * 00416C50 B8 50F14800 MOV EAX,lcsebody.0048F150 * 00416C55 3B38 CMP EDI,DWORD PTR DS:[EAX] ; jichi: compare current wide character with a threshold (0x8169 = "(") * 00416C57 74 0C JE SHORT lcsebody.00416C65 * 00416C59 83C0 04 ADD EAX,0x4 * 00416C5C 3D 70F14800 CMP EAX,lcsebody.0048F170 * 00416C61 ^7C F2 JL SHORT lcsebody.00416C55 * 00416C63 EB 06 JMP SHORT lcsebody.00416C6B * 00416C65 FF86 24050000 INC DWORD PTR DS:[ESI+0x524] * 00416C6B 3ACB CMP CL,BL * 00416C6D 74 15 JE SHORT lcsebody.00416C84 * 00416C6F B8 9CF14800 MOV EAX,lcsebody.0048F19C * 00416C74 3B38 CMP EDI,DWORD PTR DS:[EAX] * 00416C76 74 21 JE SHORT lcsebody.00416C99 * 00416C78 83C0 04 ADD EAX,0x4 * 00416C7B 3D A8F14800 CMP EAX,lcsebody.0048F1A8 * 00416C80 ^7C F2 JL SHORT lcsebody.00416C74 * 00416C82 EB 2A JMP SHORT lcsebody.00416CAE * 00416C84 B8 7CF14800 MOV EAX,lcsebody.0048F17C * 00416C89 3B38 CMP EDI,DWORD PTR DS:[EAX] * 00416C8B 74 0C JE SHORT lcsebody.00416C99 * 00416C8D 83C0 04 ADD EAX,0x4 * 00416C90 3D 9CF14800 CMP EAX,lcsebody.0048F19C * 00416C95 ^7C F2 JL SHORT lcsebody.00416C89 * 00416C97 EB 15 JMP SHORT lcsebody.00416CAE * 00416C99 8B86 24050000 MOV EAX,DWORD PTR DS:[ESI+0x524] * 00416C9F 48 DEC EAX * 00416CA0 8986 24050000 MOV DWORD PTR DS:[ESI+0x524],EAX * 00416CA6 79 06 JNS SHORT lcsebody.00416CAE * 00416CA8 899E 24050000 MOV DWORD PTR DS:[ESI+0x524],EBX * 00416CAE 57 PUSH EDI * 00416CAF 8BCE MOV ECX,ESI * 00416CB1 E8 20A5FEFF CALL lcsebody.004011D6 * 00416CB6 8B86 EC000000 MOV EAX,DWORD PTR DS:[ESI+0xEC] * 00416CBC 8A9430 08010000 MOV DL,BYTE PTR DS:[EAX+ESI+0x108] * 00416CC3 83C0 02 ADD EAX,0x2 * 00416CC6 885424 0C MOV BYTE PTR SS:[ESP+0xC],DL * 00416CCA 8A8C30 07010000 MOV CL,BYTE PTR DS:[EAX+ESI+0x107] * 00416CD1 884C24 0D MOV BYTE PTR SS:[ESP+0xD],CL * 00416CD5 885C24 0E MOV BYTE PTR SS:[ESP+0xE],BL * 00416CD9 E9 77010000 JMP lcsebody.00416E55 * 00416CDE 8B96 EC000000 MOV EDX,DWORD PTR DS:[ESI+0xEC] * 00416CE4 C686 20050000 01 MOV BYTE PTR DS:[ESI+0x520],0x1 * 00416CEB 8A8C16 08010000 MOV CL,BYTE PTR DS:[ESI+EDX+0x108] * 00416CF2 8D8416 08010000 LEA EAX,DWORD PTR DS:[ESI+EDX+0x108] * 00416CF9 80F9 1F CMP CL,0x1F * 00416CFC 77 54 JA SHORT lcsebody.00416D52 * 00416CFE 80F9 03 CMP CL,0x3 * 00416D01 75 06 JNZ SHORT lcsebody.00416D09 * 00416D03 899E 28050000 MOV DWORD PTR DS:[ESI+0x528],EBX * 00416D09 8A00 MOV AL,BYTE PTR DS:[EAX] * 00416D0B 83EC 0C SUB ESP,0xC * 00416D0E 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+0x18] * 00416D12 8BCC MOV ECX,ESP * 00416D14 896424 1C MOV DWORD PTR SS:[ESP+0x1C],ESP * 00416D18 8DBE FC000000 LEA EDI,DWORD PTR DS:[ESI+0xFC] * 00416D1E 52 PUSH EDX * 00416D1F 51 PUSH ECX * 00416D20 8BCF MOV ECX,EDI * 00416D22 884424 20 MOV BYTE PTR SS:[ESP+0x20],AL * 00416D26 885C24 21 MOV BYTE PTR SS:[ESP+0x21],BL * 00416D2A E8 D0A8FEFF CALL lcsebody.004015FF * 00416D2F 8BCF MOV ECX,EDI * 00416D31 E8 A1A8FEFF CALL lcsebody.004015D7 * 00416D36 8B8E EC000000 MOV ECX,DWORD PTR DS:[ESI+0xEC] * 00416D3C 0FBE8431 0801000> MOVSX EAX,BYTE PTR DS:[ECX+ESI+0x108] * 00416D44 41 INC ECX * 00416D45 898E EC000000 MOV DWORD PTR DS:[ESI+0xEC],ECX * 00416D4B 5F POP EDI * 00416D4C 5E POP ESI * 00416D4D 5B POP EBX * 00416D4E 83C4 08 ADD ESP,0x8 * 00416D51 C3 RETN * 00416D52 8BCE MOV ECX,ESI * 00416D54 E8 9DACFEFF CALL lcsebody.004019F6 * 00416D59 3BC3 CMP EAX,EBX * 00416D5B 74 4A JE SHORT lcsebody.00416DA7 * 00416D5D 389E 2C050000 CMP BYTE PTR DS:[ESI+0x52C],BL * 00416D63 0F84 24010000 JE lcsebody.00416E8D * 00416D69 389E 20050000 CMP BYTE PTR DS:[ESI+0x520],BL * 00416D6F 74 1B JE SHORT lcsebody.00416D8C * 00416D71 B9 34F14800 MOV ECX,lcsebody.0048F134 * 00416D76 3919 CMP DWORD PTR DS:[ECX],EBX * 00416D78 74 2D JE SHORT lcsebody.00416DA7 * 00416D7A 83C1 04 ADD ECX,0x4 * 00416D7D 81F9 50F14800 CMP ECX,lcsebody.0048F150 * 00416D83 ^7C F1 JL SHORT lcsebody.00416D76 * 00416D85 5F POP EDI * 00416D86 5E POP ESI * 00416D87 5B POP EBX * 00416D88 83C4 08 ADD ESP,0x8 * 00416D8B C3 RETN * 00416D8C B9 00F14800 MOV ECX,lcsebody.0048F100 * 00416D91 3919 CMP DWORD PTR DS:[ECX],EBX * 00416D93 74 12 JE SHORT lcsebody.00416DA7 * 00416D95 83C1 04 ADD ECX,0x4 * 00416D98 81F9 34F14800 CMP ECX,lcsebody.0048F134 * 00416D9E ^7C F1 JL SHORT lcsebody.00416D91 * 00416DA0 5F POP EDI * 00416DA1 5E POP ESI * 00416DA2 5B POP EBX * 00416DA3 83C4 08 ADD ESP,0x8 * 00416DA6 C3 RETN * 00416DA7 8B86 EC000000 MOV EAX,DWORD PTR DS:[ESI+0xEC] * 00416DAD 8A96 20050000 MOV DL,BYTE PTR DS:[ESI+0x520] * 00416DB3 0FBEBC06 08010000 MOVSX EDI,BYTE PTR DS:[ESI+EAX+0x108] ; jichi: edi get assigned to the illegal character * 00416DBB 8BCF MOV ECX,EDI * 00416DBD C1E1 08 SHL ECX,0x8 * 00416DC0 3AD3 CMP DL,BL * 00416DC2 74 15 JE SHORT lcsebody.00416DD9 * 00416DC4 B8 70F14800 MOV EAX,lcsebody.0048F170 * 00416DC9 3B08 CMP ECX,DWORD PTR DS:[EAX] * 00416DCB 74 21 JE SHORT lcsebody.00416DEE * 00416DCD 83C0 04 ADD EAX,0x4 * 00416DD0 3D 7CF14800 CMP EAX,lcsebody.0048F17C * 00416DD5 ^7C F2 JL SHORT lcsebody.00416DC9 * 00416DD7 EB 1B JMP SHORT lcsebody.00416DF4 * 00416DD9 B8 50F14800 MOV EAX,lcsebody.0048F150 * 00416DDE 3B08 CMP ECX,DWORD PTR DS:[EAX] * 00416DE0 74 0C JE SHORT lcsebody.00416DEE * 00416DE2 83C0 04 ADD EAX,0x4 * 00416DE5 3D 70F14800 CMP EAX,lcsebody.0048F170 * 00416DEA ^7C F2 JL SHORT lcsebody.00416DDE * 00416DEC EB 06 JMP SHORT lcsebody.00416DF4 * 00416DEE FF86 24050000 INC DWORD PTR DS:[ESI+0x524] * 00416DF4 3AD3 CMP DL,BL * 00416DF6 74 15 JE SHORT lcsebody.00416E0D * 00416DF8 B8 9CF14800 MOV EAX,lcsebody.0048F19C * 00416DFD 3B08 CMP ECX,DWORD PTR DS:[EAX] * 00416DFF 74 21 JE SHORT lcsebody.00416E22 * 00416E01 83C0 04 ADD EAX,0x4 * 00416E04 3D A8F14800 CMP EAX,lcsebody.0048F1A8 * 00416E09 ^7C F2 JL SHORT lcsebody.00416DFD * 00416E0B EB 2A JMP SHORT lcsebody.00416E37 * 00416E0D B8 7CF14800 MOV EAX,lcsebody.0048F17C * 00416E12 3B08 CMP ECX,DWORD PTR DS:[EAX] * 00416E14 74 0C JE SHORT lcsebody.00416E22 * 00416E16 83C0 04 ADD EAX,0x4 * 00416E19 3D 9CF14800 CMP EAX,lcsebody.0048F19C * 00416E1E ^7C F2 JL SHORT lcsebody.00416E12 * 00416E20 EB 15 JMP SHORT lcsebody.00416E37 * 00416E22 8B86 24050000 MOV EAX,DWORD PTR DS:[ESI+0x524] * 00416E28 48 DEC EAX * 00416E29 8986 24050000 MOV DWORD PTR DS:[ESI+0x524],EAX * 00416E2F 79 06 JNS SHORT lcsebody.00416E37 * 00416E31 899E 24050000 MOV DWORD PTR DS:[ESI+0x524],EBX * 00416E37 57 PUSH EDI ; jichi: invalid character * 00416E38 8BCE MOV ECX,ESI * 00416E3A E8 97A3FEFF CALL lcsebody.004011D6 ; jichi: char in arg1 * 00416E3F 8B86 EC000000 MOV EAX,DWORD PTR DS:[ESI+0xEC] */ ULONG patchEncoding(ULONG startAddress, ULONG stopAddress) { const uint8_t bytes[] = { 0x8b, 0x4c, 0x24, 0x04, // 0040a380 8b4c24 04 mov ecx,dword ptr ss:[esp+0x4] 0x8a, 0x01, // 0040a384 8a01 mov al,byte ptr ds:[ecx] 0x8a, 0x49, 0x01, // 0040a386 8a49 01 mov cl,byte ptr ds:[ecx+0x1] 0x3c, 0x81 // 0040a389 3c 81 cmp al,0x81 }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); return addr; // && winhook::replace_fun(addr, (ULONG)Private::isLeadByteChar); } } // namespace Patch } // unnamed namespace bool LCScript::attach_function() { if (!ScenarioHook::attach(processStartAddress, processStopAddress, Patch::patchEncoding(processStartAddress, processStopAddress))) return false; return true; }