LunaHook-mirror/LunaHook/engine32/BGI.cpp
恍兮惚兮 eb123790e6 refactor
2024-03-21 18:29:50 +08:00

1537 lines
69 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include"BGI.h"
#include"embed_util.h"
/********************************************************************************************
BGI hook:
Usually game folder contains BGI.*. After first run BGI.gdb appears.
BGI engine has font caching issue so the strategy is simple.
First find call to TextOutA or TextOutW then reverse to function entry point,
until full text is caught.
After 2 tries we will get to the right place. Use ESP value to split text since
it's likely to be different for different calls.
********************************************************************************************/
namespace { // unnamed
#if 0 // jichi 12/28/2013: dynamic BGI is not used
static bool FindBGIHook(DWORD fun, DWORD size, DWORD pt, WORD sig)
{
if (!fun) {
ConsoleOutput("BGI: cannot find BGI hook");
//swprintf(str, L"Can't find BGI hook: %.8X.",fun);
//ConsoleOutput(str);
return false;
}
//WCHAR str[0x40];
//i=FindCallBoth(fun,size,pt);
//swprintf(str, L"CALL addr: 0x%.8X",pt+i);
//ConsoleOutput(str);
for (DWORD i = fun, j = fun; j > i - 0x100; j--)
if ((*(WORD *)(pt + j)) == sig) { // Fun entry 1.
//swprintf(str, L"Entry 1: 0x%.8X",pt+j);
//ConsoleOutput(str);
for (DWORD k = i + 0x100; k < i+0x800; k++)
if (*(BYTE *)(pt + k) == 0xe8)
if (k + 5 + *(DWORD *)(pt + k + 1) == j) { // Find call to fun1.
//swprintf(str, L"CALL to entry 1: 0x%.8X",pt+k);
//ConsoleOutput(str);
for (DWORD l = k; l > k - 0x100;l--)
if ((*(WORD *)(pt + l)) == 0xec83) { // Fun entry 2.
//swprintf(str, L"Entry 2(final): 0x%.8X",pt+l);
//ConsoleOutput(str);
HookParam hp;
hp.address = (DWORD)pt + l;
hp.offset=get_stack(2);
hp.split =get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT;
ConsoleOutput("INSERT DynamicBGI");
return NewHook(hp, "BGI");
}
}
}
ConsoleOutput("DynamicBGI: failed");
return false;
}
bool InsertBGIDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
if (addr != TextOutA && addr != TextOutW) {
//ConsoleOutput("DynamicBGI: failed");
return false;
}
DWORD i = *(DWORD *)(stack + 4) - processStartAddress;
return FindBGIHook(i, processStopAddress - processStartAddress, processStartAddress, 0xec83);
}
#endif // 0
/** jichi 5/12/2014
* Sample game: FORTUNE ARTERIAL, case 2 at 0x41ebd0
*
* sub_41EBD0 proc near, seems to take 5 parameters
*
* 0041ebd0 /$ 83ec 28 sub esp,0x28 ; jichi: hook here, beginning of the function
* 0041ebd3 |. 55 push ebp
* 0041ebd4 |. 8b6c24 38 mov ebp,dword ptr ss:[esp+0x38]
* 0041ebd8 |. 81fd 00ff0000 cmp ebp,0xff00
* 0041ebde |. 0f82 e1000000 jb bgi.0041ecc5
* 0041ebe4 |. 81fd ffff0000 cmp ebp,0xffff
* 0041ebea |. 0f87 d5000000 ja bgi.0041ecc5
* 0041ebf0 |. a1 54634900 mov eax,dword ptr ds:[0x496354]
* 0041ebf5 |. 8bd5 mov edx,ebp
* 0041ebf7 |. 81e2 ff000000 and edx,0xff
* 0041ebfd |. 53 push ebx
* 0041ebfe |. 4a dec edx
* 0041ebff |. 33db xor ebx,ebx
* 0041ec01 |. 3bd0 cmp edx,eax
* 0041ec03 |. 56 push esi
* 0041ec04 |. 0f8d 8a000000 jge bgi.0041ec94
* 0041ec0a |. 57 push edi
* 0041ec0b |. b9 06000000 mov ecx,0x6
* 0041ec10 |. be 5c634900 mov esi,bgi.0049635c
* 0041ec15 |. 8d7c24 20 lea edi,dword ptr ss:[esp+0x20]
* 0041ec19 |. f3:a5 rep movs dword ptr es:[edi],dword ptr ds>
* 0041ec1b |. 8b0d 58634900 mov ecx,dword ptr ds:[0x496358]
* 0041ec21 |. 8b7424 3c mov esi,dword ptr ss:[esp+0x3c]
* 0041ec25 |. 8bc1 mov eax,ecx
* 0041ec27 |. 5f pop edi
* 0041ec28 |. 0fafc2 imul eax,edx
* 0041ec2b |. 8b56 08 mov edx,dword ptr ds:[esi+0x8]
* 0041ec2e |. 894424 0c mov dword ptr ss:[esp+0xc],eax
* 0041ec32 |. 3bca cmp ecx,edx
* 0041ec34 |. 7e 02 jle short bgi.0041ec38
* 0041ec36 |. 8bca mov ecx,edx
* 0041ec38 |> 8d4401 ff lea eax,dword ptr ds:[ecx+eax-0x1]
* 0041ec3c |. 8b4c24 28 mov ecx,dword ptr ss:[esp+0x28]
* 0041ec40 |. 894424 14 mov dword ptr ss:[esp+0x14],eax
* 0041ec44 |. 8b46 0c mov eax,dword ptr ds:[esi+0xc]
* 0041ec47 |. 3bc8 cmp ecx,eax
* 0041ec49 |. 895c24 10 mov dword ptr ss:[esp+0x10],ebx
* 0041ec4d |. 77 02 ja short bgi.0041ec51
* 0041ec4f |. 8bc1 mov eax,ecx
* 0041ec51 |> 8d4c24 0c lea ecx,dword ptr ss:[esp+0xc]
* 0041ec55 |. 8d5424 1c lea edx,dword ptr ss:[esp+0x1c]
* 0041ec59 |. 48 dec eax
* 0041ec5a |. 51 push ecx
* 0041ec5b |. 52 push edx
* 0041ec5c |. 894424 20 mov dword ptr ss:[esp+0x20],eax
* 0041ec60 |. e8 7b62feff call bgi.00404ee0
* 0041ec65 |. 8b4424 34 mov eax,dword ptr ss:[esp+0x34]
* 0041ec69 |. 83c4 08 add esp,0x8
* 0041ec6c |. 83f8 03 cmp eax,0x3
* 0041ec6f |. 75 15 jnz short bgi.0041ec86
* 0041ec71 |. 8b4424 48 mov eax,dword ptr ss:[esp+0x48]
* 0041ec75 |. 8d4c24 1c lea ecx,dword ptr ss:[esp+0x1c]
* 0041ec79 |. 50 push eax
* 0041ec7a |. 51 push ecx
* 0041ec7b |. 56 push esi
* 0041ec7c |. e8 1fa0feff call bgi.00408ca0
*/
bool InsertBGI1Hook()
{
union {
DWORD i;
DWORD *id;
BYTE *ib;
};
HookParam hp;
for (i = processStartAddress + 0x1000; i < processStopAddress; i++) {
if (ib[0] == 0x3d) {
i++;
if (id[0] == 0xffff) { //cmp eax,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) {
hp.offset=get_stack(3);
hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT;
ConsoleOutput("INSERT BGI#1");
//RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI");
}
}
}
if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8)) {
i += 2;
if (id[0] == 0xffff) { //cmp reg,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) {
hp.offset=get_stack(3);
hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT;
ConsoleOutput("INSERT BGI#2");
//RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI");
}
}
}
}
//ConsoleOutput("Unknown BGI engine.");
//ConsoleOutput("Probably BGI. Wait for text.");
//SwitchTrigger(true);
//trigger_fun=InsertBGIDynamicHook;
ConsoleOutput("BGI: failed");
return false;
}
/**
* jichi 2/5/2014: Add an alternative BGI hook
*
* Issue: This hook cannot extract character name for コトバの消えた日
*
* See: http://tieba.baidu.com/p/2845113296
* 世界と世界の真ん中で
* - /HSN4@349E0:sekachu.exe // Disabled BGI3, floating split char
* - /HS-1C:-4@68E56 // Not used, cannot detect character name
* - /HSC@34C80:sekachu.exe // BGI2, extract both scenario and character names
*
* [Lump of Sugar] 世界と世界の真ん中で
* /HSC@34C80:sekachu.exe
* - addr: 216192 = 0x34c80
* - module: 3599131534
* - off: 12 = 0xc
* - type: 65 = 0x41
*
* base: 0x11a0000
* hook_addr = base + addr = 0x11d4c80
*
* 011d4c7e cc int3
* 011d4c7f cc int3
* 011d4c80 /$ 55 push ebp ; jichi: hook here
* 011d4c81 |. 8bec mov ebp,esp
* 011d4c83 |. 6a ff push -0x1
* 011d4c85 |. 68 e6592601 push sekachu.012659e6
* 011d4c8a |. 64:a1 00000000 mov eax,dword ptr fs:[0]
* 011d4c90 |. 50 push eax
* 011d4c91 |. 81ec 300d0000 sub esp,0xd30
* 011d4c97 |. a1 d8c82801 mov eax,dword ptr ds:[0x128c8d8]
* 011d4c9c |. 33c5 xor eax,ebp
* 011d4c9e |. 8945 f0 mov dword ptr ss:[ebp-0x10],eax
* 011d4ca1 |. 53 push ebx
* 011d4ca2 |. 56 push esi
* 011d4ca3 |. 57 push edi
* 011d4ca4 |. 50 push eax
* 011d4ca5 |. 8d45 f4 lea eax,dword ptr ss:[ebp-0xc]
* 011d4ca8 |. 64:a3 00000000 mov dword ptr fs:[0],eax
* 011d4cae |. 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc]
* 011d4cb1 |. 8b55 18 mov edx,dword ptr ss:[ebp+0x18]
* 011d4cb4 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 011d4cb7 |. 8b5d 10 mov ebx,dword ptr ss:[ebp+0x10]
* 011d4cba |. 8b7d 38 mov edi,dword ptr ss:[ebp+0x38]
* 011d4cbd |. 898d d8f3ffff mov dword ptr ss:[ebp-0xc28],ecx
* 011d4cc3 |. 8b4d 28 mov ecx,dword ptr ss:[ebp+0x28]
* 011d4cc6 |. 8995 9cf3ffff mov dword ptr ss:[ebp-0xc64],edx
* 011d4ccc |. 51 push ecx
* 011d4ccd |. 8b0d 305c2901 mov ecx,dword ptr ds:[0x1295c30]
* 011d4cd3 |. 8985 e0f3ffff mov dword ptr ss:[ebp-0xc20],eax
* 011d4cd9 |. 8b45 1c mov eax,dword ptr ss:[ebp+0x1c]
* 011d4cdc |. 8d95 4cf4ffff lea edx,dword ptr ss:[ebp-0xbb4]
* 011d4ce2 |. 52 push edx
* 011d4ce3 |. 899d 40f4ffff mov dword ptr ss:[ebp-0xbc0],ebx
* 011d4ce9 |. 8985 1cf4ffff mov dword ptr ss:[ebp-0xbe4],eax
* 011d4cef |. 89bd f0f3ffff mov dword ptr ss:[ebp-0xc10],edi
* 011d4cf5 |. e8 862efdff call sekachu.011a7b80
* 011d4cfa |. 33c9 xor ecx,ecx
* 011d4cfc |. 8985 60f3ffff mov dword ptr ss:[ebp-0xca0],eax
* 011d4d02 |. 3bc1 cmp eax,ecx
* 011d4d04 |. 0f84 0f1c0000 je sekachu.011d6919
* 011d4d0a |. e8 31f6ffff call sekachu.011d4340
* 011d4d0f |. e8 6cf8ffff call sekachu.011d4580
* 011d4d14 |. 8985 64f3ffff mov dword ptr ss:[ebp-0xc9c],eax
* 011d4d1a |. 8a03 mov al,byte ptr ds:[ebx]
* 011d4d1c |. 898d 90f3ffff mov dword ptr ss:[ebp-0xc70],ecx
* 011d4d22 |. 898d 14f4ffff mov dword ptr ss:[ebp-0xbec],ecx
* 011d4d28 |. 898d 38f4ffff mov dword ptr ss:[ebp-0xbc8],ecx
* 011d4d2e |. 8d71 01 lea esi,dword ptr ds:[ecx+0x1]
* 011d4d31 |. 3c 20 cmp al,0x20 ; jichi: pattern starts
* 011d4d33 |. 7d 75 jge short sekachu.011d4daa
* 011d4d35 |. 0fbec0 movsx eax,al
* 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
* 011d4d3b |. 83f8 06 cmp eax,0x6
* 011d4d3e |. 77 6a ja short sekachu.011d4daa
* 011d4d40 |. ff2485 38691d0>jmp dword ptr ds:[eax*4+0x11d6938]
*
* 蒼の彼方 体験版 (8/6/2014)
* 01312cce cc int3 ; jichi: reladdr = 0x32cd0
* 01312ccf cc int3
* 01312cd0 $ 55 push ebp
* 01312cd1 . 8bec mov ebp,esp
* 01312cd3 . 83e4 f8 and esp,0xfffffff8
* 01312cd6 . 6a ff push -0x1
* 01312cd8 . 68 86583a01 push 蒼の彼方.013a5886
* 01312cdd . 64:a1 00000000 mov eax,dword ptr fs:[0]
* 01312ce3 . 50 push eax
* 01312ce4 . 81ec 38090000 sub esp,0x938
* 01312cea . a1 24673c01 mov eax,dword ptr ds:[0x13c6724]
* 01312cef . 33c4 xor eax,esp
* 01312cf1 . 898424 3009000>mov dword ptr ss:[esp+0x930],eax
* 01312cf8 . 53 push ebx
* 01312cf9 . 56 push esi
* 01312cfa . 57 push edi
* 01312cfb . a1 24673c01 mov eax,dword ptr ds:[0x13c6724]
* 01312d00 . 33c4 xor eax,esp
* 01312d02 . 50 push eax
* 01312d03 . 8d8424 4809000>lea eax,dword ptr ss:[esp+0x948]
* 01312d0a . 64:a3 00000000 mov dword ptr fs:[0],eax
* 01312d10 . 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 01312d13 . 8b7d 0c mov edi,dword ptr ss:[ebp+0xc]
* 01312d16 . 8b5d 30 mov ebx,dword ptr ss:[ebp+0x30]
* 01312d19 . 898424 8800000>mov dword ptr ss:[esp+0x88],eax
* 01312d20 . 8b45 14 mov eax,dword ptr ss:[ebp+0x14]
* 01312d23 . 898c24 8c00000>mov dword ptr ss:[esp+0x8c],ecx
* 01312d2a . 8b0d a8734a01 mov ecx,dword ptr ds:[0x14a73a8]
* 01312d30 . 894424 4c mov dword ptr ss:[esp+0x4c],eax
* 01312d34 . 899424 bc00000>mov dword ptr ss:[esp+0xbc],edx
* 01312d3b . 8b55 20 mov edx,dword ptr ss:[ebp+0x20]
* 01312d3e . 51 push ecx ; /arg1 => 00000000
* 01312d3f . 8d8424 0c02000>lea eax,dword ptr ss:[esp+0x20c] ; |
* 01312d46 . 897c24 34 mov dword ptr ss:[esp+0x34],edi ; |
* 01312d4a . 899c24 8800000>mov dword ptr ss:[esp+0x88],ebx ; |
* 01312d51 . e8 ca59fdff call 蒼の彼方.012e8720 ; \蒼の彼方.012e8720
* 01312d56 . 33c9 xor ecx,ecx
* 01312d58 . 898424 f400000>mov dword ptr ss:[esp+0xf4],eax
* 01312d5f . 3bc1 cmp eax,ecx
* 01312d61 . 0f84 391b0000 je 蒼の彼方.013148a0
* 01312d67 . e8 54280000 call 蒼の彼方.013155c0
* 01312d6c . e8 7f2a0000 call 蒼の彼方.013157f0
* 01312d71 . 898424 f800000>mov dword ptr ss:[esp+0xf8],eax
* 01312d78 . 8a07 mov al,byte ptr ds:[edi]
* 01312d7a . 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx
* 01312d81 . 894c24 2c mov dword ptr ss:[esp+0x2c],ecx
* 01312d85 . 894c24 1c mov dword ptr ss:[esp+0x1c],ecx
* 01312d89 . b9 01000000 mov ecx,0x1
* 01312d8e . 3c 20 cmp al,0x20 ; jichi: pattern starts
* 01312d90 . 7d 58 jge short 蒼の彼方.01312dea
* 01312d92 . 0fbec0 movsx eax,al
* 01312d95 . 83c0 fe add eax,-0x2 ; switch (cases 2..8)
* 01312d98 . 83f8 06 cmp eax,0x6
* 01312d9b . 77 4d ja short 蒼の彼方.01312dea
* 01312d9d . ff2485 c448310>jmp dword ptr ds:[eax*4+0x13148c4]
* 01312da4 > 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx ; case 2 of switch 01312d95
* 01312dab . 03f9 add edi,ecx
* 01312dad . eb 37 jmp short 蒼の彼方.01312de6
* 01312daf > 894c24 2c mov dword ptr ss:[esp+0x2c],ecx ; case 3 of switch 01312d95
* 01312db3 . 03f9 add edi,ecx
* 01312db5 . eb 2f jmp short 蒼の彼方.01312de6
* 01312db7 > ba e0103b01 mov edx,蒼の彼方.013b10e0 ; case 4 of switch 01312d95
* 01312dbc . eb 1a jmp short 蒼の彼方.01312dd8
* 01312dbe > ba e4103b01 mov edx,蒼の彼方.013b10e4 ; case 5 of switch 01312d95
* 01312dc3 . eb 13 jmp short 蒼の彼方.01312dd8
* 01312dc5 > ba e8103b01 mov edx,蒼の彼方.013b10e8 ; case 6 of switch 01312d95
* 01312dca . eb 0c jmp short 蒼の彼方.01312dd8
* 01312dcc > ba ec103b01 mov edx,蒼の彼方.013b10ec ; case 7 of switch 01312d95
* 01312dd1 . eb 05 jmp short 蒼の彼方.01312dd8
* 01312dd3 > ba f0103b01 mov edx,蒼の彼方.013b10f0 ; case 8 of switch 01312d95
* 01312dd8 > 8d7424 14 lea esi,dword ptr ss:[esp+0x14]
* 01312ddc . 894c24 1c mov dword ptr ss:[esp+0x1c],ecx
* 01312de0 . e8 1b8dffff call 蒼の彼方.0130bb00
* 01312de5 . 47 inc edi
* 01312de6 > 897c24 30 mov dword ptr ss:[esp+0x30],edi
* 01312dea > 8d8424 0802000>lea eax,dword ptr ss:[esp+0x208] ; default case of switch 01312d95
* 01312df1 . e8 ba1b0000 call 蒼の彼方.013149b0
* 01312df6 . 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 01312dfa . 8bb424 2802000>mov esi,dword ptr ss:[esp+0x228]
* 01312e01 . 894424 5c mov dword ptr ss:[esp+0x5c],eax
* 01312e05 . 74 12 je short 蒼の彼方.01312e19
* 01312e07 . 56 push esi ; /arg1
* 01312e08 . e8 c31b0000 call 蒼の彼方.013149d0 ; \蒼の彼方.013149d0
* 01312e0d . 83c4 04 add esp,0x4
* 01312e10 . 898424 c000000>mov dword ptr ss:[esp+0xc0],eax
* 01312e17 . eb 0b jmp short 蒼の彼方.01312e24
* 01312e19 > c78424 c000000>mov dword ptr ss:[esp+0xc0],0x0
* 01312e24 > 8b4b 04 mov ecx,dword ptr ds:[ebx+0x4]
* 01312e27 . 0fafce imul ecx,esi
* 01312e2a . b8 1f85eb51 mov eax,0x51eb851f
* 01312e2f . f7e9 imul ecx
* 01312e31 . c1fa 05 sar edx,0x5
* 01312e34 . 8bca mov ecx,edx
* 01312e36 . c1e9 1f shr ecx,0x1f
* 01312e39 . 03ca add ecx,edx
* 01312e3b . 894c24 70 mov dword ptr ss:[esp+0x70],ecx
* 01312e3f . 85c9 test ecx,ecx
* 01312e41 . 7f 09 jg short 蒼の彼方.01312e4c
* 01312e43 . b9 01000000 mov ecx,0x1
* 01312e48 . 894c24 70 mov dword ptr ss:[esp+0x70],ecx
* 01312e4c > 8b53 08 mov edx,dword ptr ds:[ebx+0x8]
* 01312e4f . 0fafd6 imul edx,esi
* 01312e52 . b8 1f85eb51 mov eax,0x51eb851f
* 01312e57 . f7ea imul edx
* 01312e59 . c1fa 05 sar edx,0x5
* 01312e5c . 8bc2 mov eax,edx
* 01312e5e . c1e8 1f shr eax,0x1f
* 01312e61 . 03c2 add eax,edx
* 01312e63 . 894424 78 mov dword ptr ss:[esp+0x78],eax
* 01312e67 . 85c0 test eax,eax
* 01312e69 . 7f 09 jg short 蒼の彼方.01312e74
* 01312e6b . b8 01000000 mov eax,0x1
* 01312e70 . 894424 78 mov dword ptr ss:[esp+0x78],eax
* 01312e74 > 33d2 xor edx,edx
* 01312e76 . 895424 64 mov dword ptr ss:[esp+0x64],edx
* 01312e7a . 895424 6c mov dword ptr ss:[esp+0x6c],edx
* 01312e7e . 8b13 mov edx,dword ptr ds:[ebx]
* 01312e80 . 4a dec edx ; switch (cases 1..2)
* 01312e81 . 74 0e je short 蒼の彼方.01312e91
* 01312e83 . 4a dec edx
* 01312e84 . 75 13 jnz short 蒼の彼方.01312e99
* 01312e86 . 8d1409 lea edx,dword ptr ds:[ecx+ecx] ; case 2 of switch 01312e80
* 01312e89 . 895424 64 mov dword ptr ss:[esp+0x64],edx
* 01312e8d . 03c0 add eax,eax
* 01312e8f . eb 04 jmp short 蒼の彼方.01312e95
* 01312e91 > 894c24 64 mov dword ptr ss:[esp+0x64],ecx ; case 1 of switch 01312e80
* 01312e95 > 894424 6c mov dword ptr ss:[esp+0x6c],eax
* 01312e99 > 8b9c24 3802000>mov ebx,dword ptr ss:[esp+0x238] ; default case of switch 01312e80
* 01312ea0 . 8bc3 mov eax,ebx
* 01312ea2 . e8 d98bffff call 蒼の彼方.0130ba80
* 01312ea7 . 8bc8 mov ecx,eax
* 01312ea9 . 8bc3 mov eax,ebx
* 01312eab . e8 e08bffff call 蒼の彼方.0130ba90
* 01312eb0 . 6a 01 push 0x1 ; /arg1 = 00000001
* 01312eb2 . 8bd0 mov edx,eax ; |
* 01312eb4 . 8db424 1c01000>lea esi,dword ptr ss:[esp+0x11c] ; |
* 01312ebb . e8 3056fdff call 蒼の彼方.012e84f0 ; \蒼の彼方.012e84f0
* 01312ec0 . 8bc7 mov eax,edi
* 01312ec2 . 83c4 04 add esp,0x4
* 01312ec5 . 8d70 01 lea esi,dword ptr ds:[eax+0x1]
* 01312ec8 > 8a08 mov cl,byte ptr ds:[eax]
* 01312eca . 40 inc eax
* 01312ecb . 84c9 test cl,cl
* 01312ecd .^75 f9 jnz short 蒼の彼方.01312ec8
* 01312ecf . 2bc6 sub eax,esi
* 01312ed1 . 40 inc eax
* 01312ed2 . 50 push eax
* 01312ed3 . e8 e74c0600 call 蒼の彼方.01377bbf
* 01312ed8 . 33f6 xor esi,esi
* 01312eda . 83c4 04 add esp,0x4
*
* 1/1/2016
* コドモノアソビ trial
*
* 00A64259 CC INT3
* 00A6425A CC INT3
* 00A6425B CC INT3
* 00A6425C CC INT3
* 00A6425D CC INT3
* 00A6425E CC INT3
* 00A6425F CC INT3
* 00A64260 55 PUSH EBP
* 00A64261 8BEC MOV EBP,ESP
* 00A64263 83E4 F8 AND ESP,0xFFFFFFF8
* 00A64266 6A FF PUSH -0x1
* 00A64268 68 D610B000 PUSH .00B010D6
* 00A6426D 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
* 00A64273 50 PUSH EAX
* 00A64274 81EC 40090000 SUB ESP,0x940
* 00A6427A A1 2417B200 MOV EAX,DWORD PTR DS:[0xB21724]
* 00A6427F 33C4 XOR EAX,ESP
* 00A64281 898424 38090000 MOV DWORD PTR SS:[ESP+0x938],EAX
* 00A64288 53 PUSH EBX
* 00A64289 56 PUSH ESI
* 00A6428A 57 PUSH EDI
* 00A6428B A1 2417B200 MOV EAX,DWORD PTR DS:[0xB21724]
* 00A64290 33C4 XOR EAX,ESP
* 00A64292 50 PUSH EAX
* 00A64293 8D8424 50090000 LEA EAX,DWORD PTR SS:[ESP+0x950]
* 00A6429A 64:A3 00000000 MOV DWORD PTR FS:[0],EAX
* 00A642A0 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8]
* 00A642A3 8B7D 0C MOV EDI,DWORD PTR SS:[EBP+0xC]
* 00A642A6 8B5D 30 MOV EBX,DWORD PTR SS:[EBP+0x30]
* 00A642A9 894424 50 MOV DWORD PTR SS:[ESP+0x50],EAX
* 00A642AD 8B45 14 MOV EAX,DWORD PTR SS:[EBP+0x14]
* 00A642B0 894C24 74 MOV DWORD PTR SS:[ESP+0x74],ECX
* 00A642B4 8B0D A024B800 MOV ECX,DWORD PTR DS:[0xB824A0]
* 00A642BA 894424 4C MOV DWORD PTR SS:[ESP+0x4C],EAX
* 00A642BE 899424 B8000000 MOV DWORD PTR SS:[ESP+0xB8],EDX
* 00A642C5 8B55 20 MOV EDX,DWORD PTR SS:[EBP+0x20]
* 00A642C8 51 PUSH ECX
* 00A642C9 8D8424 14020000 LEA EAX,DWORD PTR SS:[ESP+0x214]
* 00A642D0 897C24 2C MOV DWORD PTR SS:[ESP+0x2C],EDI
* 00A642D4 899C24 88000000 MOV DWORD PTR SS:[ESP+0x88],EBX
* 00A642DB E8 504CFDFF CALL .00A38F30
* 00A642E0 33C9 XOR ECX,ECX
* 00A642E2 898424 F8000000 MOV DWORD PTR SS:[ESP+0xF8],EAX
* 00A642E9 3BC1 CMP EAX,ECX
* 00A642EB 0F84 391C0000 JE .00A65F2A
* 00A642F1 E8 FA2A0000 CALL .00A66DF0
* 00A642F6 E8 252D0000 CALL .00A67020
* 00A642FB 898424 FC000000 MOV DWORD PTR SS:[ESP+0xFC],EAX
* 00A64302 8A07 MOV AL,BYTE PTR DS:[EDI]
* 00A64304 898C24 CC000000 MOV DWORD PTR SS:[ESP+0xCC],ECX
* 00A6430B 894C24 30 MOV DWORD PTR SS:[ESP+0x30],ECX
* 00A6430F 894C24 1C MOV DWORD PTR SS:[ESP+0x1C],ECX
* 00A64313 B9 01000000 MOV ECX,0x1
* 00A64318 3C 20 CMP AL,0x20 ; jichi: pattern found here
* 00A6431A 7D 58 JGE SHORT .00A64374
* 00A6431C 0FBEC0 MOVSX EAX,AL
* 00A6431F 83C0 FE ADD EAX,-0x2
* 00A64322 83F8 06 CMP EAX,0x6
* 00A64325 77 4D JA SHORT .00A64374
* 00A64327 FF2485 505FA600 JMP DWORD PTR DS:[EAX*4+0xA65F50]
* 00A6432E 898C24 CC000000 MOV DWORD PTR SS:[ESP+0xCC],ECX
* 00A64335 03F9 ADD EDI,ECX
* 00A64337 EB 37 JMP SHORT .00A64370
* 00A64339 894C24 30 MOV DWORD PTR SS:[ESP+0x30],ECX
* 00A6433D 03F9 ADD EDI,ECX
* 00A6433F EB 2F JMP SHORT .00A64370
* 00A64341 BA E0C1B000 MOV EDX,.00B0C1E0
* 00A64346 EB 1A JMP SHORT .00A64362
* 00A64348 BA E4C1B000 MOV EDX,.00B0C1E4
* 00A6434D EB 13 JMP SHORT .00A64362
* 00A6434F BA E8C1B000 MOV EDX,.00B0C1E8
* 00A64354 EB 0C JMP SHORT .00A64362
* 00A64356 BA ECC1B000 MOV EDX,.00B0C1EC
* 00A6435B EB 05 JMP SHORT .00A64362
* 00A6435D BA F0C1B000 MOV EDX,.00B0C1F0
* 00A64362 8D7424 14 LEA ESI,DWORD PTR SS:[ESP+0x14]
* 00A64366 894C24 1C MOV DWORD PTR SS:[ESP+0x1C],ECX
* 00A6436A E8 A196FFFF CALL .00A5DA10
* 00A6436F 47 INC EDI
* 00A64370 897C24 28 MOV DWORD PTR SS:[ESP+0x28],EDI
* 00A64374 8D8424 10020000 LEA EAX,DWORD PTR SS:[ESP+0x210]
* 00A6437B E8 C01C0000 CALL .00A66040
* 00A64380 837D 10 00 CMP DWORD PTR SS:[EBP+0x10],0x0
* 00A64384 8BB424 30020000 MOV ESI,DWORD PTR SS:[ESP+0x230]
* 00A6438B 894424 60 MOV DWORD PTR SS:[ESP+0x60],EAX
* 00A6438F 74 12 JE SHORT .00A643A3
* 00A64391 56 PUSH ESI
* 00A64392 E8 C91C0000 CALL .00A66060
* 00A64397 83C4 04 ADD ESP,0x4
* 00A6439A 898424 C4000000 MOV DWORD PTR SS:[ESP+0xC4],EAX
* 00A643A1 EB 0B JMP SHORT .00A643AE
* 00A643A3 C78424 C4000000 >MOV DWORD PTR SS:[ESP+0xC4],0x0
* 00A643AE 8B4B 04 MOV ECX,DWORD PTR DS:[EBX+0x4]
* 00A643B1 0FAFCE IMUL ECX,ESI
* 00A643B4 B8 1F85EB51 MOV EAX,0x51EB851F
* 00A643B9 F7E9 IMUL ECX
* 00A643BB C1FA 05 SAR EDX,0x5
* 00A643BE 8BCA MOV ECX,EDX
* 00A643C0 C1E9 1F SHR ECX,0x1F
* 00A643C3 03CA ADD ECX,EDX
* 00A643C5 898C24 94000000 MOV DWORD PTR SS:[ESP+0x94],ECX
* 00A643CC 85C9 TEST ECX,ECX
* 00A643D0 B9 01000000 MOV ECX,0x1
* ...
*/
//static inline size_t _bgistrlen(LPCSTR text)
//{
// size_t r = ::strlen(text);
// if (r >=2 && *(WORD *)(text + r - 2) == 0xa581) // remove trailing ▼ = \x81\xa5
// r -= 2;
// return r;
//}
//
//static void SpecialHookBGI2(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
//{
// LPCSTR text = (LPCSTR)*(DWORD *)(esp_base + hp->offset);
// if (text) {
// *data = (DWORD)text;
// *len = _bgistrlen(text);
// }
//}
namespace Private {
enum { Type1 = 1, Type2, Type3,Type_BGI3 } type_;
int textIndex_; // the i-th of argument on the stack holding the text
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
if (type_ == Type_BGI3) {
DWORD retaddr = s->stack[0]; // retaddr
* role = Engine::ScenarioRole;
return write_string_overwrite(data,len,(LPCSTR)s->stack[textIndex_]);
}
static std::string data_; // persistent storage, which makes this function not thread-safe
LPCSTR text = (LPCSTR)s->stack[textIndex_]; // arg2 or arg3
if (!text || !*text)
return false;
// In Type 1, split = arg8
// In Type 2, there is no arg8. However, arg8 seems to be a good split that can differenciate choice and character name
//DWORD split = stack->args[3]; // arg4
//DWORD split = s->stack[8]; // arg8
//auto sig = Engine::hashThreadSignature(s->stack[0], split);
//enum { role = Engine::UnknownRole };
//DWORD split = s->stack[8]; // this is a good split, but usually game-specific
DWORD retaddr = s->stack[0]; // retaddr
//* role = Engine::OtherRole;
switch (type_) {
case Type3:
switch (s->stack[textIndex_+1]) {
case 1:
if (*(WORD *)(retaddr + 8) == 0xcccc) // two int3
*role = Engine::ScenarioRole;
break;
case 0:
if (s->stack[10] == 0x00ffffff && s->stack[10 - 3] == 1 || // for old BGI2 games
s->stack[10] == 0 && s->stack[10 - 1] == 0 && s->stack[10 - 2] == 0) // for new BGI2 games
*role = Engine::NameRole;
break;
} break;
case Type2:
switch (s->stack[textIndex_+1]) {
case 1:
// Return address for history text
// 012B37BA 83C4 34 ADD ESP,0x34
// 012B37BD 837D 24 00 CMP DWORD PTR SS:[EBP+0x24],0x0
if (*(WORD *)(retaddr + 3) != 0x7d83)
*role = Engine::ScenarioRole;
break;
case 0:
if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 2)
*role = Engine::NameRole;
break;
} break;
case Type1:
switch (s->stack[textIndex_+1]) {
case 1: *role = Engine::ScenarioRole; break;
case 0:
if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 1)
*role = Engine::NameRole;
break;
} break;
}
return write_string_overwrite(data,len,(LPCSTR)s->stack[textIndex_]);
}
}
/**
* 5/12/2014
* This is the caller of the ITH BGI hook, which extract text by characters
* and cannot be used for substition.
*
* Sample game: 世界征服彼女
* ITH hooked function: BGI#2 0x425550, called by 0x427450
*
* 00427450 /$ 6a ff push -0x1 ; jichi: function starts
* 00427452 |. 68 78634900 push sekajyo_.00496378 ; se handler installation
* 00427457 |. 64:a1 00000000 mov eax,dword ptr fs:[0]
* 0042745d |. 50 push eax
* 0042745e |. 64:8925 000000>mov dword ptr fs:[0],esp
* 00427465 |. 81ec d80c0000 sub esp,0xcd8
* 0042746b |. 8b8424 080d000>mov eax,dword ptr ss:[esp+0xd08]
* 00427472 |. 56 push esi
* 00427473 |. 8d8c24 3801000>lea ecx,dword ptr ss:[esp+0x138]
* 0042747a |. 50 push eax
* 0042747b |. 51 push ecx
* 0042747c |. 8b0d e0464b00 mov ecx,dword ptr ds:[0x4b46e0]
* 00427482 |. e8 f9fdfdff call sekajyo_.00407280
* 00427487 |. 33f6 xor esi,esi
* 00427489 |. 898424 b800000>mov dword ptr ss:[esp+0xb8],eax
* 00427490 |. 3bc6 cmp eax,esi
* 00427492 |. 0f84 95140000 je sekajyo_.0042892d
* 00427498 |. 53 push ebx
* 00427499 |. 55 push ebp
* 0042749a |. 8bac24 fc0c000>mov ebp,dword ptr ss:[esp+0xcfc]
* 004274a1 |. 57 push edi
* 004274a2 |. 89b424 b400000>mov dword ptr ss:[esp+0xb4],esi
* 004274a9 |. 897424 10 mov dword ptr ss:[esp+0x10],esi
* 004274ad |. 8a45 00 mov al,byte ptr ss:[ebp]
* 004274b0 |. b9 01000000 mov ecx,0x1
* 004274b5 |. 3c 20 cmp al,0x20
* 004274b7 |. 7d 68 jge short sekajyo_.00427521
* 004274b9 |. 0fbec0 movsx eax,al
* 004274bc |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
*
* Sample game: FORTUNE ARTERIAL
* ITH hooked function: BGI#2 sub_41EBD0, called by 0x4207e0
*
* 0041ebcd 90 nop
* 0041ebce 90 nop
* 0041ebcf 90 nop
* 004207e0 /$ 81ec 30090000 sub esp,0x930 ; jichi: function starts
* 004207e6 |. 8b8424 5409000>mov eax,dword ptr ss:[esp+0x954]
* 004207ed |. 56 push esi
* 004207ee |. 8d8c24 0401000>lea ecx,dword ptr ss:[esp+0x104]
* 004207f5 |. 50 push eax
* 004207f6 |. 51 push ecx
* 004207f7 |. 8b0d 48634900 mov ecx,dword ptr ds:[0x496348]
* 004207fd |. e8 ee47feff call bgi.00404ff0
* 00420802 |. 33f6 xor esi,esi
* 00420804 |. 894424 54 mov dword ptr ss:[esp+0x54],eax
* 00420808 |. 3bc6 cmp eax,esi
* 0042080a |. 0f84 94080000 je bgi.004210a4
* 00420810 |. 53 push ebx
* 00420811 |. 55 push ebp
* 00420812 |. 8bac24 4809000>mov ebp,dword ptr ss:[esp+0x948]
* 00420819 |. 57 push edi
* 0042081a |. 897424 54 mov dword ptr ss:[esp+0x54],esi
* 0042081e |. 897424 10 mov dword ptr ss:[esp+0x10],esi
* 00420822 |. 8a45 00 mov al,byte ptr ss:[ebp]
* 00420825 |. 3c 20 cmp al,0x20
* 00420827 |. 7d 69 jge short bgi.00420892
* 00420829 |. 0fbec0 movsx eax,al
* 0042082c |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
* 0042082f |. 83f8 06 cmp eax,0x6
* 00420832 |. 77 5e ja short bgi.00420892
* 00420834 |. ff2485 ac10420>jmp dword ptr ds:[eax*4+0x4210ac]
* 0042083b |> c74424 54 0100>mov dword ptr ss:[esp+0x54],0x1 ; case 2 of switch 0042082c
* 00420843 |. eb 45 jmp short bgi.0042088a
* 00420845 |> 8d5424 1c lea edx,dword ptr ss:[esp+0x1c] ; case 4 of switch 0042082c
* 00420849 |. 68 0c424800 push bgi.0048420c
* 0042084e |. 52 push edx
* 0042084f |. eb 29 jmp short bgi.0042087a
* 00420851 |> 68 08424800 push bgi.00484208 ; case 5 of switch 0042082c
* 00420856 |. eb 1d jmp short bgi.00420875
* 00420858 |> 8d4c24 1c lea ecx,dword ptr ss:[esp+0x1c] ; case 6 of switch 0042082c
* 0042085c |. 68 04424800 push bgi.00484204
* 00420861 |. 51 push ecx
* 00420862 |. eb 16 jmp short bgi.0042087a
* 00420864 |> 8d5424 1c lea edx,dword ptr ss:[esp+0x1c] ; case 7 of switch 0042082c
* 00420868 |. 68 00424800 push bgi.00484200
* 0042086d |. 52 push edx
* 0042086e |. eb 0a jmp short bgi.0042087a
* 00420870 |> 68 fc414800 push bgi.004841fc ; case 8 of switch 0042082c
* 00420875 |> 8d4424 20 lea eax,dword ptr ss:[esp+0x20]
* 00420879 |. 50 push eax
* 0042087a |> c74424 18 0100>mov dword ptr ss:[esp+0x18],0x1
* 00420882 |. e8 b9a7ffff call bgi.0041b040
* 00420887 |. 83c4 08 add esp,0x8
* 0042088a |> 45 inc ebp
* 0042088b |. 89ac24 4c09000>mov dword ptr ss:[esp+0x94c],ebp
* 00420892 |> 8b9c24 3001000>mov ebx,dword ptr ss:[esp+0x130] ; default case of switch 0042082c
* 00420899 |. 8d8c24 1001000>lea ecx,dword ptr ss:[esp+0x110]
* 004208a0 |. 51 push ecx
* 004208a1 |. 895c24 70 mov dword ptr ss:[esp+0x70],ebx
* 004208a5 |. e8 76080000 call bgi.00421120
* 004208aa |. 894424 34 mov dword ptr ss:[esp+0x34],eax
* 004208ae |. 8b8424 5409000>mov eax,dword ptr ss:[esp+0x954]
* 004208b5 |. 83c4 04 add esp,0x4
* 004208b8 |. 3bc6 cmp eax,esi
* 004208ba |. 74 0f je short bgi.004208cb
* 004208bc |. 53 push ebx
* 004208bd |. e8 7e080000 call bgi.00421140
*/
ULONG search1(ULONG startAddress, ULONG stopAddress)
{
//return 0x4207e0; // FORTUNE ARTERIAL
//const BYTE bytes[] = {
// 0x8a,0x45, 0x00, // 00420822 |. 8a45 00 mov al,byte ptr ss:[ebp]
// 0x3c, 0x20, // 00420825 |. 3c 20 cmp al,0x20
// 0x7d, 0x69, // 00420827 |. 7d 69 jge short bgi.00420892
// 0x0f,0xbe,0xc0, // 00420829 |. 0fbec0 movsx eax,al
// 0x83,0xc0, 0xfe, // 0042082c |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
// 0x83,0xf8, 0x06, // 0042082f |. 83f8 06 cmp eax,0x6
// 0x77, 0x5e // 00420832 |. 77 5e ja short bgi.00420892
//};
//enum { hook_offset = 0x4207e0 - 0x420822 }; // distance to the beginning of the function
const uint8_t bytes[] = { // 0fafcbf7e9c1fa058bc2c1e81f03d08bfa85ff
0x0f,0xaf,0xcb, // 004208de |. 0fafcb imul ecx,ebx
0xf7,0xe9, // 004208e1 |. f7e9 imul ecx
0xc1,0xfa, 0x05, // 004208e3 |. c1fa 05 sar edx,0x5
0x8b,0xc2, // 004208e6 |. 8bc2 mov eax,edx
0xc1,0xe8, 0x1f, // 004208e8 |. c1e8 1f shr eax,0x1f
0x03,0xd0, // 004208eb |. 03d0 add edx,eax
0x8b,0xfa, // 004208ed |. 8bfa mov edi,edx
0x85,0xff, // 004208ef |. 85ff test edi,edi
};
//enum { hook_offset = 0x4207e0 - 0x4208de }; // distance to the beginning of the function
//ULONG range = qMin(stopAddress - startAddress, Engine::MaximumMemoryRange);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
//ConsoleOutput("BGI2: pattern not found");
return 0;
enum : WORD {
sub_esp = 0xec81 // 004207e0 /$ 81ec 30090000
, push_ff = 0xff6a // 00427450 /$ 6a ff push -0x1, seh handler
};
for (int i = 0; i < 300; i++, addr--)
if (*(WORD *)addr == sub_esp) { // beginning of the function without seh
// Sample game: 世界征服彼女 with SEH
// 00427450 /$ 6a ff push -0x1
// 00427452 |. 68 78634900 push sekajyo_.00496378 ; se handler installation
// 00427457 |. 64:a1 00000000 mov eax,dword ptr fs:[0]
// 0042745d |. 50 push eax
// 0042745e |. 64:8925 000000>mov dword ptr fs:[0],esp
// 00427465 |. 81ec d80c0000 sub esp,0xcd8
//
// 0x00427465 - 0x00427450 == 21
ULONG seh_addr = addr;
for (int j = 0; j < 40; j++, seh_addr--)
if (*(WORD *)seh_addr == push_ff) // beginning of the function with seh
return seh_addr;
return addr;
}
return 0;
}
/**
* jichi 2/5/2014: Add an alternative BGI hook
*
* Issue: This hook cannot extract character name for コトバの消えた日
*
* See: http://tieba.baidu.com/p/2845113296
* 世界と世界の真ん中で
* - /HSN4@349E0:sekachu.exe // Disabled BGI3, floating split char
* - /HS-1C:-4@68E56 // Not used, cannot detect character name
* - /HSC@34C80:sekachu.exe // BGI2, extract both scenario and character names
*
* [Lump of Sugar] 世界と世界の真ん中で
* /HSC@34C80:sekachu.exe
* - addr: 216192 = 0x34c80
* - module: 3599131534
* - off: 12 = 0xc
* - type: 65 = 0x41
*
* base: 0x11a0000
* hook_addr = base + addr = 0x11d4c80
*
* 011d4c7e cc int3
* 011d4c7f cc int3
* 011d4c80 /$ 55 push ebp ; jichi: hook here
* 011d4c81 |. 8bec mov ebp,esp
* 011d4c83 |. 6a ff push -0x1
* 011d4c85 |. 68 e6592601 push sekachu.012659e6
* 011d4c8a |. 64:a1 00000000 mov eax,dword ptr fs:[0]
* 011d4c90 |. 50 push eax
* 011d4c91 |. 81ec 300d0000 sub esp,0xd30
* 011d4c97 |. a1 d8c82801 mov eax,dword ptr ds:[0x128c8d8]
* 011d4c9c |. 33c5 xor eax,ebp
* 011d4c9e |. 8945 f0 mov dword ptr ss:[ebp-0x10],eax
* 011d4ca1 |. 53 push ebx
* 011d4ca2 |. 56 push esi
* 011d4ca3 |. 57 push edi
* 011d4ca4 |. 50 push eax
* 011d4ca5 |. 8d45 f4 lea eax,dword ptr ss:[ebp-0xc]
* 011d4ca8 |. 64:a3 00000000 mov dword ptr fs:[0],eax
* 011d4cae |. 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc]
* 011d4cb1 |. 8b55 18 mov edx,dword ptr ss:[ebp+0x18]
* 011d4cb4 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 011d4cb7 |. 8b5d 10 mov ebx,dword ptr ss:[ebp+0x10]
* 011d4cba |. 8b7d 38 mov edi,dword ptr ss:[ebp+0x38]
* 011d4cbd |. 898d d8f3ffff mov dword ptr ss:[ebp-0xc28],ecx
* 011d4cc3 |. 8b4d 28 mov ecx,dword ptr ss:[ebp+0x28]
* 011d4cc6 |. 8995 9cf3ffff mov dword ptr ss:[ebp-0xc64],edx
* 011d4ccc |. 51 push ecx
* 011d4ccd |. 8b0d 305c2901 mov ecx,dword ptr ds:[0x1295c30]
* 011d4cd3 |. 8985 e0f3ffff mov dword ptr ss:[ebp-0xc20],eax
* 011d4cd9 |. 8b45 1c mov eax,dword ptr ss:[ebp+0x1c]
* 011d4cdc |. 8d95 4cf4ffff lea edx,dword ptr ss:[ebp-0xbb4]
* 011d4ce2 |. 52 push edx
* 011d4ce3 |. 899d 40f4ffff mov dword ptr ss:[ebp-0xbc0],ebx
* 011d4ce9 |. 8985 1cf4ffff mov dword ptr ss:[ebp-0xbe4],eax
* 011d4cef |. 89bd f0f3ffff mov dword ptr ss:[ebp-0xc10],edi
* 011d4cf5 |. e8 862efdff call sekachu.011a7b80
* 011d4cfa |. 33c9 xor ecx,ecx
* 011d4cfc |. 8985 60f3ffff mov dword ptr ss:[ebp-0xca0],eax
* 011d4d02 |. 3bc1 cmp eax,ecx
* 011d4d04 |. 0f84 0f1c0000 je sekachu.011d6919
* 011d4d0a |. e8 31f6ffff call sekachu.011d4340
* 011d4d0f |. e8 6cf8ffff call sekachu.011d4580
* 011d4d14 |. 8985 64f3ffff mov dword ptr ss:[ebp-0xc9c],eax
* 011d4d1a |. 8a03 mov al,byte ptr ds:[ebx]
* 011d4d1c |. 898d 90f3ffff mov dword ptr ss:[ebp-0xc70],ecx
* 011d4d22 |. 898d 14f4ffff mov dword ptr ss:[ebp-0xbec],ecx
* 011d4d28 |. 898d 38f4ffff mov dword ptr ss:[ebp-0xbc8],ecx
* 011d4d2e |. 8d71 01 lea esi,dword ptr ds:[ecx+0x1]
* 011d4d31 |. 3c 20 cmp al,0x20
* 011d4d33 |. 7d 75 jge short sekachu.011d4daa
* 011d4d35 |. 0fbec0 movsx eax,al
* 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
* 011d4d3b |. 83f8 06 cmp eax,0x6
* 011d4d3e |. 77 6a ja short sekachu.011d4daa
* 011d4d40 |. ff2485 38691d0>jmp dword ptr ds:[eax*4+0x11d6938]
*/
ULONG search2(ULONG startAddress, ULONG stopAddress)
{
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d750fbec083c0fe83f806776a
0x3c, 0x20, // 011d4d31 |. 3c 20 cmp al,0x20
0x7d, 0x75, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa
0x0f,0xbe,0xc0, // 011d4d35 |. 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8, 0x06, // 011d4d3b |. 83f8 06 cmp eax,0x6
0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa
};
enum { hook_offset = 0x34c80 - 0x34d31 }; // distance to the beginning of the function
//ULONG range = qMin(stopAddress - startAddress, Engine::MaximumMemoryRange);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
//ConsoleOutput("BGI2: pattern not found");
return 0;
addr += hook_offset;
enum : uint8_t { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp
if (*(uint8_t *)addr != push_ebp)
//ConsoleOutput("BGI2: pattern found but the function offset is invalid");
return 0;
return addr;
}
/**
* Sample Game: type 3: 蒼の彼方 体験版 (8/6/2014)
* 01312cce cc int3 ; jichi: reladdr = 0x32cd0
* 01312ccf cc int3
* 01312cd0 $ 55 push ebp
* 01312cd1 . 8bec mov ebp,esp
* 01312cd3 . 83e4 f8 and esp,0xfffffff8
* 01312cd6 . 6a ff push -0x1
* 01312cd8 . 68 86583a01 push 蒼の彼方.013a5886
* 01312cdd . 64:a1 00000000 mov eax,dword ptr fs:[0]
* 01312ce3 . 50 push eax
* 01312ce4 . 81ec 38090000 sub esp,0x938
* 01312cea . a1 24673c01 mov eax,dword ptr ds:[0x13c6724]
* 01312cef . 33c4 xor eax,esp
* 01312cf1 . 898424 3009000>mov dword ptr ss:[esp+0x930],eax
* 01312cf8 . 53 push ebx
* 01312cf9 . 56 push esi
* 01312cfa . 57 push edi
* 01312cfb . a1 24673c01 mov eax,dword ptr ds:[0x13c6724]
* 01312d00 . 33c4 xor eax,esp
* 01312d02 . 50 push eax
* 01312d03 . 8d8424 4809000>lea eax,dword ptr ss:[esp+0x948]
* 01312d0a . 64:a3 00000000 mov dword ptr fs:[0],eax
* 01312d10 . 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 01312d13 . 8b7d 0c mov edi,dword ptr ss:[ebp+0xc]
* 01312d16 . 8b5d 30 mov ebx,dword ptr ss:[ebp+0x30]
* 01312d19 . 898424 8800000>mov dword ptr ss:[esp+0x88],eax
* 01312d20 . 8b45 14 mov eax,dword ptr ss:[ebp+0x14]
* 01312d23 . 898c24 8c00000>mov dword ptr ss:[esp+0x8c],ecx
* 01312d2a . 8b0d a8734a01 mov ecx,dword ptr ds:[0x14a73a8]
* 01312d30 . 894424 4c mov dword ptr ss:[esp+0x4c],eax
* 01312d34 . 899424 bc00000>mov dword ptr ss:[esp+0xbc],edx
* 01312d3b . 8b55 20 mov edx,dword ptr ss:[ebp+0x20]
* 01312d3e . 51 push ecx ; /arg1 => 00000000
* 01312d3f . 8d8424 0c02000>lea eax,dword ptr ss:[esp+0x20c] ; |
* 01312d46 . 897c24 34 mov dword ptr ss:[esp+0x34],edi ; |
* 01312d4a . 899c24 8800000>mov dword ptr ss:[esp+0x88],ebx ; |
* 01312d51 . e8 ca59fdff call 蒼の彼方.012e8720 ; \蒼の彼方.012e8720
* 01312d56 . 33c9 xor ecx,ecx
* 01312d58 . 898424 f400000>mov dword ptr ss:[esp+0xf4],eax
* 01312d5f . 3bc1 cmp eax,ecx
* 01312d61 . 0f84 391b0000 je 蒼の彼方.013148a0
* 01312d67 . e8 54280000 call 蒼の彼方.013155c0
* 01312d6c . e8 7f2a0000 call 蒼の彼方.013157f0
* 01312d71 . 898424 f800000>mov dword ptr ss:[esp+0xf8],eax
* 01312d78 . 8a07 mov al,byte ptr ds:[edi]
* 01312d7a . 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx
* 01312d81 . 894c24 2c mov dword ptr ss:[esp+0x2c],ecx
* 01312d85 . 894c24 1c mov dword ptr ss:[esp+0x1c],ecx
* 01312d89 . b9 01000000 mov ecx,0x1
* 01312d8e . 3c 20 cmp al,0x20 ; jichi: pattern starts
* 01312d90 . 7d 58 jge short 蒼の彼方.01312dea
* 01312d92 . 0fbec0 movsx eax,al
* 01312d95 . 83c0 fe add eax,-0x2 ; switch (cases 2..8)
* 01312d98 . 83f8 06 cmp eax,0x6
* 01312d9b . 77 4d ja short 蒼の彼方.01312dea
* 01312d9d . ff2485 c448310>jmp dword ptr ds:[eax*4+0x13148c4]
* 01312da4 > 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx ; case 2 of switch 01312d95
* 01312dab . 03f9 add edi,ecx
* 01312dad . eb 37 jmp short 蒼の彼方.01312de6
* 01312daf > 894c24 2c mov dword ptr ss:[esp+0x2c],ecx ; case 3 of switch 01312d95
* 01312db3 . 03f9 add edi,ecx
* 01312db5 . eb 2f jmp short 蒼の彼方.01312de6
* 01312db7 > ba e0103b01 mov edx,蒼の彼方.013b10e0 ; case 4 of switch 01312d95
* 01312dbc . eb 1a jmp short 蒼の彼方.01312dd8
* 01312dbe > ba e4103b01 mov edx,蒼の彼方.013b10e4 ; case 5 of switch 01312d95
* 01312dc3 . eb 13 jmp short 蒼の彼方.01312dd8
* 01312dc5 > ba e8103b01 mov edx,蒼の彼方.013b10e8 ; case 6 of switch 01312d95
* 01312dca . eb 0c jmp short 蒼の彼方.01312dd8
* 01312dcc > ba ec103b01 mov edx,蒼の彼方.013b10ec ; case 7 of switch 01312d95
* 01312dd1 . eb 05 jmp short 蒼の彼方.01312dd8
* 01312dd3 > ba f0103b01 mov edx,蒼の彼方.013b10f0 ; case 8 of switch 01312d95
* 01312dd8 > 8d7424 14 lea esi,dword ptr ss:[esp+0x14]
* 01312ddc . 894c24 1c mov dword ptr ss:[esp+0x1c],ecx
* 01312de0 . e8 1b8dffff call 蒼の彼方.0130bb00
* 01312de5 . 47 inc edi
* 01312de6 > 897c24 30 mov dword ptr ss:[esp+0x30],edi
* 01312dea > 8d8424 0802000>lea eax,dword ptr ss:[esp+0x208] ; default case of switch 01312d95
* 01312df1 . e8 ba1b0000 call 蒼の彼方.013149b0
* 01312df6 . 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 01312dfa . 8bb424 2802000>mov esi,dword ptr ss:[esp+0x228]
* 01312e01 . 894424 5c mov dword ptr ss:[esp+0x5c],eax
* 01312e05 . 74 12 je short 蒼の彼方.01312e19
* 01312e07 . 56 push esi ; /arg1
* 01312e08 . e8 c31b0000 call 蒼の彼方.013149d0 ; \蒼の彼方.013149d0
* 01312e0d . 83c4 04 add esp,0x4
* 01312e10 . 898424 c000000>mov dword ptr ss:[esp+0xc0],eax
* 01312e17 . eb 0b jmp short 蒼の彼方.01312e24
* 01312e19 > c78424 c000000>mov dword ptr ss:[esp+0xc0],0x0
* 01312e24 > 8b4b 04 mov ecx,dword ptr ds:[ebx+0x4]
* 01312e27 . 0fafce imul ecx,esi
* 01312e2a . b8 1f85eb51 mov eax,0x51eb851f
* 01312e2f . f7e9 imul ecx
* 01312e31 . c1fa 05 sar edx,0x5
* 01312e34 . 8bca mov ecx,edx
* 01312e36 . c1e9 1f shr ecx,0x1f
* 01312e39 . 03ca add ecx,edx
* 01312e3b . 894c24 70 mov dword ptr ss:[esp+0x70],ecx
* 01312e3f . 85c9 test ecx,ecx
* 01312e41 . 7f 09 jg short 蒼の彼方.01312e4c
* 01312e43 . b9 01000000 mov ecx,0x1
* 01312e48 . 894c24 70 mov dword ptr ss:[esp+0x70],ecx
* 01312e4c > 8b53 08 mov edx,dword ptr ds:[ebx+0x8]
* 01312e4f . 0fafd6 imul edx,esi
* 01312e52 . b8 1f85eb51 mov eax,0x51eb851f
* 01312e57 . f7ea imul edx
* 01312e59 . c1fa 05 sar edx,0x5
* 01312e5c . 8bc2 mov eax,edx
* 01312e5e . c1e8 1f shr eax,0x1f
* 01312e61 . 03c2 add eax,edx
* 01312e63 . 894424 78 mov dword ptr ss:[esp+0x78],eax
* 01312e67 . 85c0 test eax,eax
* 01312e69 . 7f 09 jg short 蒼の彼方.01312e74
* 01312e6b . b8 01000000 mov eax,0x1
* 01312e70 . 894424 78 mov dword ptr ss:[esp+0x78],eax
* 01312e74 > 33d2 xor edx,edx
* 01312e76 . 895424 64 mov dword ptr ss:[esp+0x64],edx
* 01312e7a . 895424 6c mov dword ptr ss:[esp+0x6c],edx
* 01312e7e . 8b13 mov edx,dword ptr ds:[ebx]
* 01312e80 . 4a dec edx ; switch (cases 1..2)
* 01312e81 . 74 0e je short 蒼の彼方.01312e91
* 01312e83 . 4a dec edx
* 01312e84 . 75 13 jnz short 蒼の彼方.01312e99
* 01312e86 . 8d1409 lea edx,dword ptr ds:[ecx+ecx] ; case 2 of switch 01312e80
* 01312e89 . 895424 64 mov dword ptr ss:[esp+0x64],edx
* 01312e8d . 03c0 add eax,eax
* 01312e8f . eb 04 jmp short 蒼の彼方.01312e95
* 01312e91 > 894c24 64 mov dword ptr ss:[esp+0x64],ecx ; case 1 of switch 01312e80
* 01312e95 > 894424 6c mov dword ptr ss:[esp+0x6c],eax
* 01312e99 > 8b9c24 3802000>mov ebx,dword ptr ss:[esp+0x238] ; default case of switch 01312e80
* 01312ea0 . 8bc3 mov eax,ebx
* 01312ea2 . e8 d98bffff call 蒼の彼方.0130ba80
* 01312ea7 . 8bc8 mov ecx,eax
* 01312ea9 . 8bc3 mov eax,ebx
* 01312eab . e8 e08bffff call 蒼の彼方.0130ba90
* 01312eb0 . 6a 01 push 0x1 ; /arg1 = 00000001
* 01312eb2 . 8bd0 mov edx,eax ; |
* 01312eb4 . 8db424 1c01000>lea esi,dword ptr ss:[esp+0x11c] ; |
* 01312ebb . e8 3056fdff call 蒼の彼方.012e84f0 ; \蒼の彼方.012e84f0
* 01312ec0 . 8bc7 mov eax,edi
* 01312ec2 . 83c4 04 add esp,0x4
* 01312ec5 . 8d70 01 lea esi,dword ptr ds:[eax+0x1]
* 01312ec8 > 8a08 mov cl,byte ptr ds:[eax]
* 01312eca . 40 inc eax
* 01312ecb . 84c9 test cl,cl
* 01312ecd .^75 f9 jnz short 蒼の彼方.01312ec8
* 01312ecf . 2bc6 sub eax,esi
* 01312ed1 . 40 inc eax
* 01312ed2 . 50 push eax
* 01312ed3 . e8 e74c0600 call 蒼の彼方.01377bbf
* 01312ed8 . 33f6 xor esi,esi
* 01312eda . 83c4 04 add esp,0x4
*/
ULONG search3(ULONG startAddress, ULONG stopAddress)
{
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d580fbec083c0fe83f806774d
0x3c, 0x20, // 01312d8e 3c 20 cmp al,0x20 ; jichi: pattern starts
0x7d, 0x58, // 01312d90 7d 58 jge short 蒼の彼方.01312dea
0x0f,0xbe,0xc0, // 01312d92 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 01312d95 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8, 0x06, // 01312d98 83f8 06 cmp eax,0x6
0x77, 0x4d // 01312d9b 77 4d ja short 蒼の彼方.01312dea
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return 0;
// distance to the beginning of the function
static const int hook_offsets[] = {
0x01312cd0 - 0x01312d8e // for new BGI2 game since 蒼の彼方 (2014/08), text is in arg2
, 0x00a64260 - 0x00a64318 // For newer BGI2 game since コドモノアソビ (2015/11)
};
enum { hook_offset_count = sizeof(hook_offsets) / sizeof(*hook_offsets) };
for (size_t i = 0; i < hook_offset_count; i++) {
int hook_offset = hook_offsets[i];
enum : uint8_t { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp
if (*(uint8_t *)(addr + hook_offset) == push_ebp)
return addr + hook_offset;
}
return 0; // failed
}
ULONG search_bgi3(ULONG startAddress, ULONG stopAddress )
{
//黄昏のフォルクローレ
/* .text:00C3A700 push ebp
.text : 00C3A701 mov ebp, esp
.text : 00C3A703 push[ebp + arg_30]
.text : 00C3A706 mov edx, [ebp + arg_4]
.text : 00C3A709 push[ebp + arg_2C]
.text : 00C3A70C mov ecx, [ebp + arg_0]
.text : 00C3A70F push[ebp + arg_28]
.text : 00C3A712 push[ebp + arg_24]
.text : 00C3A715 push[ebp + arg_20]
.text : 00C3A718 push[ebp + arg_1C]
.text : 00C3A71B push[ebp + arg_18]
.text : 00C3A71E push[ebp + arg_14]
.text : 00C3A721 push[ebp + arg_10]
.text : 00C3A724 push[ebp + arg_C]
.text : 00C3A727 push[ebp + arg_8]
.text : 00C3A72A call loc_C3A740
int __stdcall sub_C3A700(
int a1,
int a2,
int a3,
int a4,
int a5,
int a6,
int a7,
int a8,
int a9,
int a10,
int a11,
int a12,
int a13)
*/
const uint8_t bytes[] = {
0x55,
0x8b,0xec,
0xff,0x75,0x38,
0x8b,0x55,0x0c,
0xff,0x75,0x34,
0x8b,0x4d,0x08,
0xff,0x75,0x30
};
ULONG range = min(ULONG(stopAddress - startAddress), ULONG(0x00300000));
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, startAddress + range);
if (addr == 0)return 0;
return addr;
}
bool search_tayutama(DWORD *funaddr,DWORD *addr){
const BYTE bytes[] = {
// The following code does not exist in newer BGI games after BGI 1.633.0.0 (tayutama2_trial_EX)
//0x3c, 0x20, // 011d4d31 |. 3c 20 cmp al,0x20
//0x7d, XX, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa ; jichi: 0x75 or 0x58
0x0f,0xbe,0xc0, // 011d4d35 |. 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8//, 0x06 // 011d4d3b |. 83f8 06 cmp eax,0x6
// The following code does not exist in newer BGI games after 蒼の彼方
//0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
* addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(reladdr);
if (!*addr) {
return false;
}
* funaddr = MemDbg::findEnclosingAlignedFunction(*addr, 0x300); // range is around 177 ~ 190
enum : BYTE { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp
if (!*funaddr || *(BYTE *)*funaddr != push_ebp) {
return false;
}
return true;
}
bool InsertBGI2Hook()
{
/* Artikash 6/14/2019: Ugh, what a mess I've dug up...
At some point the beginning four bytes to search for were removed, but the difference below were not corrected? Or maybe they were?
I don't have all these games so no way to confirm which (if any) are wrong.
But the first difference (the important one since it's the one detecting offset=arg3, all others give new) seems to be four bytes off when hooking https://vndb.org/v8158
...but maybe it's not? Maybe I discovered a new difference?
I think the safest option is to just add the new? difference as a case that detects offset=arg3 since either way one case will detect offset=arg3 correctly.
And all the other cases fall through to offset=arg2.
*/
ULONG addr , funaddr;HookParam hp;
hp.hook_font=F_TextOutA|F_TextOutW;
if (addr=search_bgi3(processStartAddress, processStopAddress)){
//有乱码,无法处理。
Private::textIndex_ = 3;
hp.offset=get_stack(Private::textIndex_);
Private::type_ = Private::Type_BGI3;
hp.hook_font|=F_GetTextExtentPoint32W;
}
else if ( search_tayutama(&funaddr,&addr)) {
switch (funaddr - addr) {
// for old BGI2 game, text is arg3
case 0x34c80 - 0x34d31: // old offset
case 0x34c50 - 0x34d05: // correction as mentioned above
Private::textIndex_ = 3;
break;
// for new BGI2 game since 蒼の彼方 (2014/08), text is in arg2
case 0x01312cd0 - 0x01312D92:
// For newer BGI2 game since コドモノアソビ (2015/11)
case 0x00A64260 - 0x00A6431C:
// For latest BGI2 game since タユタマ2(2016/05) by @mireado
case 0x00E95290 - 0x00E95349:
// For latest BGI2 game since 千の刃濤、桃花染の皇姫 体験版 by @mireado
case 0x00AF5640 - 0x00AF56FF:
// For latest BGI2 game since by BGI 1.633.0.0 @mireado
case 0x00D8A660 - 0x00D8A73A:
Private::textIndex_ = 2;
break;
// Artikash 8/1/2018: Looks like it's basically always 4*2. Remove error from default case: breaks SubaHibi HD. Will figure out how to do this properly if it becomes an issue.
default:
ConsoleOutput("BGI2 WARN: function-code distance unknown");
Private::textIndex_ = 2;
break;
}
Private::type_ = Private::Type3;
addr = funaddr;
}
else if (addr =search3(processStartAddress, processStopAddress)) {
Private::type_ = Private::Type3;
Private::textIndex_ = 2; // use arg2, name = "BGI2";
}else if (addr = search2(processStartAddress, processStopAddress)) {
Private::type_ = Private::Type2;
Private::textIndex_ = 3; // use arg3, name = "BGI2";
} else if (addr =search1(processStartAddress, processStopAddress)) {
Private::type_ = Private::Type1;
Private::textIndex_ = 3; // use arg3, name = "BGI";
}
if(addr==0)return false;
hp.address = addr;
hp.offset=get_stack(Private::textIndex_);
// jichi 5/12/2014: Using split could distinguish name and choices. But the signature might become unstable
hp.type = USING_STRING|USING_SPLIT|EMBED_ABLE|EMBED_DYNA_SJIS|EMBED_AFTER_NEW;
hp.hook_before=Private::hookBefore;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
// It could be either <R..> or <r..>
static const std::regex rx("<r.+?>(.+?)</r>", std::regex_constants::icase);
std::string result = std::string((char*)data,*len);
result = std::regex_replace(result, rx, "$1");
return write_string_overwrite(data,len,result);
} ;
hp.split = get_stack(8); // pseudo arg8
//GROWL_DWORD2(hp.address, processStartAddress);
return NewHook(hp, "EmbedBGI");
}
bool InsertBGI3Hook()
{
/*
* Sample games:
* https://vndb.org/v28283
* https://vndb.org/v30456
* https://vndb.org/v33996
* https://vndb.org/v34532
* https://vndb.org/v36131
*/
bool found = false;
const BYTE pattern[] = {
0x55, // 55 push ebp
0x8b,0xec, // 8BEC mov ebp,esp
0x83,0xe4, 0xf8, // 83E4 F8 and esp,FFFFFFF8
0x81,0xec, 0x84,0x00,0x00,0x00 // 81EC 84000000 sub esp,0x84
};
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
{
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2);
hp.split =get_stack(1);
hp.type = CODEC_UTF16 | USING_SPLIT;
ConsoleOutput("INSERT BGI3");
found|=NewHook(hp, "BGI3");
}
if (!found) ConsoleOutput("BGI3: pattern not found");
return found;
}
#if 0
/**
* jichi 1/31/2014: Add a new BGI hook
* See: http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page702
* See: http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page716
*
* Issue: This hook has floating split char
*
* [ぷちけろ] コトバの消えた日 <20><EFBFBD>で裸にする純<E3828B>調教体験版
* /HS-1C:-4@68E56:BGI.exe
* - addr: 429654 (0x68e56)
* - module: 3927275266 (0xea157702)
* - off: 4294967264 = 0xffffffe0 = -0x20
* - split: 4294967288 = 0xfffffff8 = -0x8
* - type: 81 = 0x51
*
* 00e88e3d cc int3
* 00e88e3e cc int3
* 00e88e3f cc int3
* 00e88e40 /. 55 push ebp
* 00e88e41 |. 8bec mov ebp,esp
* 00e88e43 |. 56 push esi
* 00e88e44 |. 57 push edi
* 00e88e45 |. 8b7d 08 mov edi,dword ptr ss:[ebp+0x8]
* 00e88e48 |. 57 push edi
* 00e88e49 |. e8 c28a0100 call bgi.00ea1910
* 00e88e4e |. 57 push edi ; |arg1
* 00e88e4f |. 8bf0 mov esi,eax ; |
* 00e88e51 |. e8 ba8a0100 call bgi.00ea1910 ; \bgi.00ea1910
* 00e88e56 |. 83c4 08 add esp,0x8 ; jichi: hook here
* 00e88e59 |. 2bc6 sub eax,esi
* 00e88e5b |. eb 03 jmp short bgi.00e88e60
* 00e88e5d | 8d49 00 lea ecx,dword ptr ds:[ecx]
* 00e88e60 |> 8a0e /mov cl,byte ptr ds:[esi]
* 00e88e62 |. 880c30 |mov byte ptr ds:[eax+esi],cl
* 00e88e65 |. 46 |inc esi
* 00e88e66 |. 84c9 |test cl,cl
* 00e88e68 |.^75 f6 \jnz short bgi.00e88e60
* 00e88e6a |. 5f pop edi
* 00e88e6b |. 33c0 xor eax,eax
* 00e88e6d |. 5e pop esi
* 00e88e6e |. 5d pop ebp
* 00e88e6f \. c3 retn
*/
bool InsertBGI3Hook()
{
const BYTE bytes[] = {
0x83,0xc4, 0x08,// 00e88e56 |. 83c4 08 add esp,0x8 ; hook here
0x2b,0xc6, // 00e88e59 |. 2bc6 sub eax,esi
0xeb, 0x03, // 00e88e5b |. eb 03 jmp short bgi.00e88e60
0x8d,0x49, 0x00,// 00e88e5d | 8d49 00 lea ecx,dword ptr ds:[ecx]
0x8a,0x0e, // 00e88e60 |> 8a0e /mov cl,byte ptr ds:[esi]
0x88,0x0c,0x30, // 00e88e62 |. 880c30 |mov byte ptr ds:[eax+esi],cl
0x46, // 00e88e65 |. 46 |inc esi
0x84,0xc9, // 00e88e66 |. 84c9 |test cl,cl
0x75, 0xf6 // 00e88e68 |.^75 f6 \jnz short bgi.00e88e60
//0x5f, // 00e88e6a |. 5f pop edi
//0x33,0xc0, // 00e88e6b |. 33c0 xor eax,eax
//0x5e, // 00e88e6d |. 5e pop esi
//0x5d, // 00e88e6e |. 5d pop ebp
//0xc3 // 00e88e6f \. c3 retn
};
//enum { addr_offset = 0 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//reladdr = 0x68e56;
if (!addr) {
ConsoleOutput("BGI3: pattern not found");
return false;
}
HookParam hp;
hp.type = USING_STRING|USING_SPLIT;
hp.offset=get_reg(regs::esi);
hp.split = get_reg(regs::eax);
hp.address = addr;
//GROWL_DWORD2(hp.address, processStartAddress);
ConsoleOutput("INSERT BGI3");
return NewHook(hp, "BGI3");
}
#endif // 0
} // unnamed
// jichi 5/12/2014: BGI1 and BGI2 game can co-exist, such as 世界と世界の真ん中で
// BGI1 can exist in both old and new games
// BGI2 only exist in new games
// Insert BGI2 first.
// Artikash 6/12/2019: In newer games neither exists, but WideCharToMultiByte works, so insert that if BGI2 fails.
bool BGI7Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
CharFilter(text, len, L'\x0001');
CharFilter(text, len, L'\x0002');
CharFilter(text, len, L'\x0003');
CharFilter(text, len, L'\x0004');
CharFilter(text, len, L'\x0005');
CharFilter(text, len, L'\x000A');
if (text[0] == L'\x3000') {
*len -= 2;
::memmove(text, text+1, *len);
}
CharReplacer(text, len, L'\x3000', L' '); //IDSP
if (cpp_wcsnstr(text, L"<", *len/sizeof(wchar_t))) {
StringFilterBetween(text, len, L"<", 1, L">", 1);
}
return true;
}
bool InsertBGI7Hook()
{
/*
* Sample games:
* https://vndb.org/v26664
* https://vndb.org/v44105
*/
bool found = false;
const BYTE pattern[] = {
0x55, // 55 push ebp << hook here
0x8b,0xec, // 8BEC mov ebp,esp
0x53, // 53 push ebx
0x56, // 56 push esi
0x57, // 57 push edi
0x33, 0xFF, // 33 FF xor edi,edi
0xE8, XX4, // E8 23FDFFFF call saclet.exe+A0990
0x8B, 0xF0 // 8B F0 mov esi,eax
};
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
{
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.split =get_reg(regs::esp);
hp.type = CODEC_UTF16 | USING_STRING | USING_SPLIT | KNOWN_UNSTABLE;
hp.filter_fun = BGI7Filter;
ConsoleOutput("INSERT BGI4");
found|=NewHook(hp, "BGI4");
}
if (!found) ConsoleOutput("BGI4: pattern not found");
return found;
}
bool BGI56Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (text[0] == '@') {
*len -= 1;
::memmove(text, text + 1, *len);
}
return true;
}
bool InsertBGI5Hook()
{
/*
* Sample games:
* https://vndb.org/v473
*/
const BYTE bytes[] = {
0x90, // nop
0x81, 0xEC, XX4, // sub esp,00000920 << hook here
0x8B, 0x84, 0x24, XX4, // mov eax,[esp+00000944]
0x55, // push ebp
0x8D, 0x8C, 0x24, XX4 // lea ecx,[esp+000000F4]
};
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.padding = 1;
hp.type = USING_STRING;
hp.filter_fun = BGI56Filter;
ConsoleOutput("INSERT BGI5");
return NewHook(hp, "BGI5");
}
bool InsertBGI6Hook()
{
/*
* Sample games:
* https://vndb.org/r96578
*/
const BYTE bytes[] = {
0x90, // nop
0x6A, 0xFF, // push -01 << hook here
0x68, XX4, // push BGI.exe+87AF8
0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, // mov eax,fs:[00000000]
0x50, // push eax
0x64, 0x89, 0x25, 0x00, 0x00, 0x00, 0x00, // mov fs:[00000000],esp
0x81, 0xEC, XX4, // sub esp,000009B4
0x8B, 0x84, 0x24, 0xE4, 0x09, 0x00, 0x00 // mov eax,[esp+000009E4]
};
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.padding = 1;
hp.type = USING_STRING;
hp.filter_fun = BGI56Filter;
ConsoleOutput("INSERT BGI6");
return NewHook(hp, "BGI6");
}
bool InsertBGIHook()
{ return InsertBGI2Hook() || InsertBGI3Hook() || (PcHooks::hookOtherPcFunctions(), InsertBGI1Hook()); }
bool InsertBGI4Hook()
{
const BYTE bytes[] = {
0xBE,0xE9,0xFD,0x00,0x00, //cp=65001
XX2,
0xBE,0xA4,0x03,0x00,0x00 //cp=932
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.split = get_reg(regs::esp);
hp.type = CODEC_UTF16 | USING_STRING| USING_SPLIT ;
hp.filter_fun = BGI7Filter;
ConsoleOutput("BGI4");
return NewHook(hp, "BGI4");
}
namespace{
bool veryold(){
//紅月-くれないつき-
//あの街の恋の詩
auto entry=Util::FindImportEntry(processStartAddress,(DWORD)GetGlyphOutlineA);
if(entry==0)return false;
BYTE bytes[]={0xFF,0x15,XX4};
memcpy(bytes+2,&entry,4);
BYTE bytes2[]={0x8b,XX,XX4}; //mov ebp, ds:GetGlyphOutlineA
memcpy(bytes2+2,&entry,4); //銀行淫~堕ちゆく女達~
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)
addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
if (addr == 0)return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false;
auto xrefs=findxref_reverse_checkcallop(addr,addr-0x1000,addr+0x1000,0xe8);
if(xrefs.size()!=1)return false;
auto xrefaddr=xrefs[0];
auto funcstart = MemDbg::findEnclosingAlignedFunction(xrefaddr);
if (funcstart == 0)return false;
BYTE sig[]={0x81,XX,0x00,0x01,0x00,0x00};//cmp ebx, 100h
if(MemDbg::findBytes(sig, sizeof(sig), xrefaddr-0x40, xrefaddr)==0)return false;
HookParam hp;
hp.address = funcstart;
hp.offset=get_stack(2);
hp.split =get_stack(1);
hp.type = CODEC_ANSI_BE |USING_SPLIT;
return NewHook(hp, "BGI5");
}
}
bool BGI::attach_function() {
bool b1= InsertBGIHook();
bool b2=InsertBGI4Hook();
bool ok= b1||b2||veryold();
ok=InsertBGI7Hook()|| InsertBGI5Hook() || InsertBGI6Hook()||ok;
return ok;
}