731 lines
26 KiB
C++
Raw Normal View History

2024-11-02 15:49:09 +08:00
#include "5pb.h"
#include "mages/mages.h"
2024-02-07 20:59:24 +08:00
/** jichi 12/2/2014 5pb
*
* Sample game: [140924] CROSS<EFBFBD>CHANNEL FINAL COMPLETE<EFBFBD> * 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
*/
2024-11-02 15:49:09 +08:00
namespace
{ // unnamed
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
// 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
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
addr_offset = 0x0016d916 - 0x0016d90e
};
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
// GROWL_DWORD3(addr+addr_offset, processStartAddress,processStopAddress);
if (!addr)
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
ConsoleOutput("5pb1: pattern not found");
return false;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
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)
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
ConsoleOutput("5pb2: pattern not found");
return false;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
HookParam hp;
hp.address = addr;
hp.type = USING_STRING;
hp.text_fun = SpecialHook5pb2;
ConsoleOutput("INSERT 5pb2");
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
// GDI functions are not used by 5pb games anyway.
// ConsoleOutput("5pb: disable GDI hooks");
//
return NewHook(hp, "5pb2");
}
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
/** 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)
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
ConsoleOutput("5pb2: pattern not found");
return false;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
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");
}
2024-02-07 20:59:24 +08:00
} // unnamed namespace
bool Insert5pbHook()
{
2024-11-02 15:49:09 +08:00
bool ok = Insert5pbHook1();
ok = Insert5pbHook2() || ok;
ok = Insert5pbHook3() || ok;
return ok;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
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");
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
2024-02-07 20:59:24 +08:00
bool InsertStuffScriptHook()
{
// BOOL GetTextExtentPoint32(
// _In_ HDC hdc,
// _In_ LPCTSTR lpString,
// _In_ int c,
// _Out_ LPSIZE lpSize
// );
HookParam hp;
hp.address = (DWORD)::GetTextExtentPoint32A;
2024-11-02 15:49:09 +08:00
hp.offset = get_stack(2); // arg2 lpString
2024-02-07 20:59:24 +08:00
hp.split = get_reg(regs::esp);
hp.type = USING_STRING | USING_SPLIT;
ConsoleOutput("INSERT StuffScriptEngine");
return NewHook(hp, "StuffScriptEngine");
2024-11-02 15:49:09 +08:00
// RegisterEngine(ENGINE_STUFFSCRIPT);
2024-02-07 20:59:24 +08:00
}
bool StuffScript2Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
2024-11-02 15:49:09 +08:00
if (text[0] == '-')
{
2024-02-07 20:59:24 +08:00
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;
}
2024-11-02 15:49:09 +08:00
bool InsertStuffScript2Hook()
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
/*
* Sample games:
* https://vndb.org/r41537
* https://vndb.org/r41539
*/
2024-02-07 20:59:24 +08:00
const BYTE bytes[] = {
2024-11-02 15:49:09 +08:00
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
2024-02-07 20:59:24 +08:00
};
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;
2024-11-02 15:49:09 +08:00
hp.offset = get_reg(regs::eax);
2024-02-07 20:59:24 +08:00
hp.index = 0;
hp.type = USING_STRING | NO_CONTEXT;
hp.filter_fun = StuffScript2Filter;
ConsoleOutput("INSERT StuffScript2");
return NewHook(hp, "StuffScript2");
}
2024-03-15 16:08:16 +08:00
bool StuffScript3Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
2024-11-02 15:49:09 +08:00
if (text[0] == '\x81' && text[1] == '\x40')
{ // removes space at the beginning of the sentence
2024-03-15 16:08:16 +08:00
*len -= 2;
::memmove(text, text + 2, *len);
}
2024-11-02 15:49:09 +08:00
StringFilterBetween(text, len, "/\x81\x79", 3, "\x81\x7A", 2); // remove hidden name
StringFilterBetween(text, len, "[", 1, "]", 1); // garbage
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
// ruby
2024-03-15 16:08:16 +08:00
CharFilter(text, len, '<');
StringFilterBetween(text, len, ",", 1, ">", 1);
StringCharReplacer(text, len, "_r\x81\x40", 4, ' ');
StringCharReplacer(text, len, "_r", 2, ' ');
return true;
}
2024-11-02 15:49:09 +08:00
bool InsertStuffScript3Hook()
2024-03-15 16:08:16 +08:00
{
2024-11-02 15:49:09 +08:00
/*
* Sample games:
* https://vndb.org/v3111
*/
2024-03-15 16:08:16 +08:00
const BYTE bytes[] = {
2024-11-02 15:49:09 +08:00
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]
2024-03-15 16:08:16 +08:00
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
2024-11-02 15:49:09 +08:00
if (!addr)
return false;
2024-03-15 16:08:16 +08:00
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;
}
2024-11-02 15:49:09 +08:00
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;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
bool KaleidoFilter(LPVOID data, size_t *size, HookParam *)
2024-02-07 20:59:24 +08:00
{
2024-11-02 15:49:09 +08:00
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
// 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, '?');
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
return true;
2024-02-07 20:59:24 +08:00
}
bool InsertKaleidoHook()
{
2024-11-02 15:49:09 +08:00
/*
* 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
};
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
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");
2024-02-07 20:59:24 +08:00
}
namespace
2024-11-02 15:49:09 +08:00
{ // 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<uintptr_t, int> addr_hit;
for (auto sigsz : std::vector<std::pair<BYTE *, int>>{{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))
{
2024-02-07 20:59:24 +08:00
addr = MemDbg::findEnclosingAlignedFunction(addr);
2024-11-02 15:49:09 +08:00
if (addr == 0)
continue;
if (addr_hit.find(addr) == addr_hit.end())
{
addr_hit[addr] = 1;
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
else
addr_hit[addr] += 1;
}
}
DWORD addr = 0;
int m = 0;
for (auto _ : addr_hit)
{
if (_.second > m)
{
m = _.second;
addr = _.first;
}
2024-02-07 20:59:24 +08:00
}
2024-11-02 15:49:09 +08:00
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<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
StringCharReplacer(text, len, "\\n", 2, '\n');
return true;
};
return NewHook(hp, "5bp");
}
2024-02-07 20:59:24 +08:00
} // namespace name
2024-11-02 15:49:09 +08:00
namespace
{
2024-02-28 23:33:52 +08:00
bool __2()
{
2024-11-02 15:49:09 +08:00
// レヱル・ロマネスク 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};
2024-02-28 23:33:52 +08:00
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
2024-11-02 15:49:09 +08:00
if (!addr)
return false;
addr = MemDbg::findEnclosingAlignedFunction_strict(addr);
if (!addr)
return false;
2024-02-28 23:33:52 +08:00
HookParam hp;
hp.address = addr;
2024-11-02 15:49:09 +08:00
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<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(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);
2024-02-28 23:33:52 +08:00
};
2024-11-02 15:49:09 +08:00
hp.newlineseperator = L"\\n";
2024-02-28 23:33:52 +08:00
return NewHook(hp, "5bp");
}
2024-02-07 20:59:24 +08:00
2024-02-28 23:33:52 +08:00
}
2024-02-07 20:59:24 +08:00
2024-11-02 15:49:09 +08:00
bool _5pb_2::attach_function()
{
bool ___1 = __1() || __();
___1 |= __2();
return InsertKaleidoHook() || ___1;
2024-02-07 20:59:24 +08:00
}