Update BGI.cpp

This commit is contained in:
恍兮惚兮 2024-07-26 13:49:01 +08:00
parent 7b61703dcb
commit ed299f7b20

View File

@ -1,4 +1,4 @@
#include"BGI.h" #include "BGI.h"
/******************************************************************************************** /********************************************************************************************
BGI hook: BGI hook:
Usually game folder contains BGI.*. After first run BGI.gdb appears. Usually game folder contains BGI.*. After first run BGI.gdb appears.
@ -9,7 +9,8 @@ BGI hook:
After 2 tries we will get to the right place. Use ESP value to split text since 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. it's likely to be different for different calls.
********************************************************************************************/ ********************************************************************************************/
namespace { // unnamed namespace
{ // unnamed
#if 0 // jichi 12/28/2013: dynamic BGI is not used #if 0 // jichi 12/28/2013: dynamic BGI is not used
static bool FindBGIHook(DWORD fun, DWORD size, DWORD pt, WORD sig) static bool FindBGIHook(DWORD fun, DWORD size, DWORD pt, WORD sig)
{ {
@ -63,7 +64,7 @@ bool InsertBGIDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
} }
#endif // 0 #endif // 0
/** jichi 5/12/2014 /** jichi 5/12/2014
* Sample game: FORTUNE ARTERIAL, case 2 at 0x41ebd0 * Sample game: FORTUNE ARTERIAL, case 2 at 0x41ebd0
* *
* sub_41EBD0 proc near, seems to take 5 parameters * sub_41EBD0 proc near, seems to take 5 parameters
@ -125,56 +126,64 @@ bool InsertBGIDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
* 0041ec7b |. 56 push esi * 0041ec7b |. 56 push esi
* 0041ec7c |. e8 1fa0feff call bgi.00408ca0 * 0041ec7c |. e8 1fa0feff call bgi.00408ca0
*/ */
bool InsertBGI1Hook() bool InsertBGI1Hook()
{ {
union { union
{
DWORD i; DWORD i;
DWORD *id; DWORD *id;
BYTE *ib; BYTE *ib;
}; };
HookParam hp; HookParam hp;
for (i = processStartAddress + 0x1000; i < processStopAddress; i++) { for (i = processStartAddress + 0x1000; i < processStopAddress; i++)
if (ib[0] == 0x3d) { {
if (ib[0] == 0x3d)
{
i++; i++;
if (id[0] == 0xffff) { //cmp eax,0xffff if (id[0] == 0xffff)
{ // cmp eax,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40); hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) { if (hp.address)
hp.offset=get_stack(3); {
hp.offset = get_stack(3);
hp.split = get_reg(regs::esp); hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT; hp.type = CODEC_ANSI_BE | USING_SPLIT;
ConsoleOutput("INSERT BGI#1"); ConsoleOutput("INSERT BGI#1");
//RegisterEngineType(ENGINE_BGI); // RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI"); return NewHook(hp, "BGI");
} }
} }
} }
if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8)) { if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8))
{
i += 2; i += 2;
if (id[0] == 0xffff) { //cmp reg,0xffff if (id[0] == 0xffff)
{ // cmp reg,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40); hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) { if (hp.address)
hp.offset=get_stack(3); {
hp.offset = get_stack(3);
hp.split = get_reg(regs::esp); hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT; hp.type = CODEC_ANSI_BE | USING_SPLIT;
ConsoleOutput("INSERT BGI#2"); ConsoleOutput("INSERT BGI#2");
//RegisterEngineType(ENGINE_BGI); // RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI"); return NewHook(hp, "BGI");
} }
} }
} }
} }
//ConsoleOutput("Unknown BGI engine."); // ConsoleOutput("Unknown BGI engine.");
//ConsoleOutput("Probably BGI. Wait for text."); // ConsoleOutput("Probably BGI. Wait for text.");
//SwitchTrigger(true); // SwitchTrigger(true);
//trigger_fun=InsertBGIDynamicHook; // trigger_fun=InsertBGIDynamicHook;
ConsoleOutput("BGI: failed"); ConsoleOutput("BGI: failed");
return false; return false;
} }
/** /**
* jichi 2/5/2014: Add an alternative BGI hook * jichi 2/5/2014: Add an alternative BGI hook
* *
* Issue: This hook cannot extract character name for * Issue: This hook cannot extract character name for
@ -511,32 +520,40 @@ bool InsertBGI1Hook()
* 00A643D0 B9 01000000 MOV ECX,0x1 * 00A643D0 B9 01000000 MOV ECX,0x1
* ... * ...
*/ */
//static inline size_t _bgistrlen(LPCSTR text) // static inline size_t _bgistrlen(LPCSTR text)
//{ //{
// size_t r = ::strlen(text); // size_t r = ::strlen(text);
// if (r >=2 && *(WORD *)(text + r - 2) == 0xa581) // remove trailing ▼ = \x81\xa5 // if (r >=2 && *(WORD *)(text + r - 2) == 0xa581) // remove trailing ▼ = \x81\xa5
// r -= 2; // r -= 2;
// return r; // return r;
//} // }
// //
//static void SpecialHookBGI2(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len) // 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); // LPCSTR text = (LPCSTR)*(DWORD *)(esp_base + hp->offset);
// if (text) { // if (text) {
// *data = (DWORD)text; // *data = (DWORD)text;
// *len = _bgistrlen(text); // *len = _bgistrlen(text);
// } // }
//} // }
namespace Private { namespace Private
enum { Type1 = 1, Type2, Type3,Type_BGI3 } type_; {
int textIndex_; // the i-th of argument on the stack holding the text enum
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role) {
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)
{ {
if (type_ == Type_BGI3) {
DWORD retaddr = s->stack[0]; // retaddr DWORD retaddr = s->stack[0]; // retaddr
* role = Engine::ScenarioRole; *role = Engine::ScenarioRole;
return write_string_overwrite(data,len,(LPCSTR)s->stack[textIndex_]); return write_string_overwrite(data, len, (LPCSTR)s->stack[textIndex_]);
} }
static std::string data_; // persistent storage, which makes this function not thread-safe static std::string data_; // persistent storage, which makes this function not thread-safe
@ -546,18 +563,20 @@ namespace Private {
return false; return false;
// In Type 1, split = arg8 // 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 // 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 = stack->args[3]; // arg4
//DWORD split = s->stack[8]; // arg8 // DWORD split = s->stack[8]; // arg8
//auto sig = Engine::hashThreadSignature(s->stack[0], split); // auto sig = Engine::hashThreadSignature(s->stack[0], split);
//enum { role = Engine::UnknownRole }; // enum { role = Engine::UnknownRole };
//DWORD split = s->stack[8]; // this is a good split, but usually game-specific // DWORD split = s->stack[8]; // this is a good split, but usually game-specific
DWORD retaddr = s->stack[0]; // retaddr DWORD retaddr = s->stack[0]; // retaddr
//* role = Engine::OtherRole; //* role = Engine::OtherRole;
switch (type_) { switch (type_)
{
case Type3: case Type3:
switch (s->stack[textIndex_+1]) { switch (s->stack[textIndex_ + 1])
{
case 1: case 1:
if (*(WORD *)(retaddr + 8) == 0xcccc) // two int3 if (*(WORD *)(retaddr + 8) == 0xcccc) // two int3
*role = Engine::ScenarioRole; *role = Engine::ScenarioRole;
@ -567,9 +586,11 @@ namespace Private {
s->stack[10] == 0 && s->stack[10 - 1] == 0 && s->stack[10 - 2] == 0) // for new BGI2 games s->stack[10] == 0 && s->stack[10 - 1] == 0 && s->stack[10 - 2] == 0) // for new BGI2 games
*role = Engine::NameRole; *role = Engine::NameRole;
break; break;
} break; }
break;
case Type2: case Type2:
switch (s->stack[textIndex_+1]) { switch (s->stack[textIndex_ + 1])
{
case 1: case 1:
// Return address for history text // Return address for history text
// 012B37BA 83C4 34 ADD ESP,0x34 // 012B37BA 83C4 34 ADD ESP,0x34
@ -581,20 +602,25 @@ namespace Private {
if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 2) if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 2)
*role = Engine::NameRole; *role = Engine::NameRole;
break; break;
} break; }
break;
case Type1: case Type1:
switch (s->stack[textIndex_+1]) { switch (s->stack[textIndex_ + 1])
case 1: *role = Engine::ScenarioRole; break; {
case 1:
*role = Engine::ScenarioRole;
break;
case 0: case 0:
if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 1) if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 1)
*role = Engine::NameRole; *role = Engine::NameRole;
break; break;
} break;
} }
return write_string_overwrite(data,len,(LPCSTR)s->stack[textIndex_]); break;
}
return write_string_overwrite(data, len, (LPCSTR)s->stack[textIndex_]);
} }
} }
/** /**
* 5/12/2014 * 5/12/2014
@ -705,8 +731,8 @@ namespace Private {
*/ */
ULONG search1(ULONG startAddress, ULONG stopAddress) ULONG search1(ULONG startAddress, ULONG stopAddress)
{ {
//return 0x4207e0; // FORTUNE ARTERIAL // return 0x4207e0; // FORTUNE ARTERIAL
//const BYTE bytes[] = { // const BYTE bytes[] = {
// 0x8a,0x45, 0x00, // 00420822 |. 8a45 00 mov al,byte ptr ss:[ebp] // 0x8a,0x45, 0x00, // 00420822 |. 8a45 00 mov al,byte ptr ss:[ebp]
// 0x3c, 0x20, // 00420825 |. 3c 20 cmp al,0x20 // 0x3c, 0x20, // 00420825 |. 3c 20 cmp al,0x20
// 0x7d, 0x69, // 00420827 |. 7d 69 jge short bgi.00420892 // 0x7d, 0x69, // 00420827 |. 7d 69 jge short bgi.00420892
@ -714,31 +740,35 @@ namespace Private {
// 0x83,0xc0, 0xfe, // 0042082c |. 83c0 fe add eax,-0x2 ; switch (cases 2..8) // 0x83,0xc0, 0xfe, // 0042082c |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
// 0x83,0xf8, 0x06, // 0042082f |. 83f8 06 cmp eax,0x6 // 0x83,0xf8, 0x06, // 0042082f |. 83f8 06 cmp eax,0x6
// 0x77, 0x5e // 00420832 |. 77 5e ja short bgi.00420892 // 0x77, 0x5e // 00420832 |. 77 5e ja short bgi.00420892
//}; // };
//enum { hook_offset = 0x4207e0 - 0x420822 }; // distance to the beginning of the function // enum { hook_offset = 0x4207e0 - 0x420822 }; // distance to the beginning of the function
const uint8_t bytes[] = { // 0fafcbf7e9c1fa058bc2c1e81f03d08bfa85ff const uint8_t bytes[] = {
0x0f,0xaf,0xcb, // 004208de |. 0fafcb imul ecx,ebx // 0fafcbf7e9c1fa058bc2c1e81f03d08bfa85ff
0xf7,0xe9, // 004208e1 |. f7e9 imul ecx 0x0f, 0xaf, 0xcb, // 004208de |. 0fafcb imul ecx,ebx
0xc1,0xfa, 0x05, // 004208e3 |. c1fa 05 sar edx,0x5 0xf7, 0xe9, // 004208e1 |. f7e9 imul ecx
0x8b,0xc2, // 004208e6 |. 8bc2 mov eax,edx 0xc1, 0xfa, 0x05, // 004208e3 |. c1fa 05 sar edx,0x5
0xc1,0xe8, 0x1f, // 004208e8 |. c1e8 1f shr eax,0x1f 0x8b, 0xc2, // 004208e6 |. 8bc2 mov eax,edx
0x03,0xd0, // 004208eb |. 03d0 add edx,eax 0xc1, 0xe8, 0x1f, // 004208e8 |. c1e8 1f shr eax,0x1f
0x8b,0xfa, // 004208ed |. 8bfa mov edi,edx 0x03, 0xd0, // 004208eb |. 03d0 add edx,eax
0x85,0xff, // 004208ef |. 85ff test edi,edi 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 // enum { hook_offset = 0x4207e0 - 0x4208de }; // distance to the beginning of the function
//ULONG range = qMin(stopAddress - startAddress, Engine::MaximumMemoryRange); // ULONG range = qMin(stopAddress - startAddress, Engine::MaximumMemoryRange);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr) if (!addr)
//ConsoleOutput("BGI2: pattern not found"); // ConsoleOutput("BGI2: pattern not found");
return 0; return 0;
enum : WORD { enum : WORD
{
sub_esp = 0xec81 // 004207e0 /$ 81ec 30090000 sub_esp = 0xec81 // 004207e0 /$ 81ec 30090000
, push_ff = 0xff6a // 00427450 /$ 6a ff push -0x1, seh handler ,
push_ff = 0xff6a // 00427450 /$ 6a ff push -0x1, seh handler
}; };
for (int i = 0; i < 300; i++, addr--) for (int i = 0; i < 300; i++, addr--)
if (*(WORD *)addr == sub_esp) { // beginning of the function without seh if (*(WORD *)addr == sub_esp)
{ // beginning of the function without seh
// Sample game: 世界征服彼女 with SEH // Sample game: 世界征服彼女 with SEH
// 00427450 /$ 6a ff push -0x1 // 00427450 /$ 6a ff push -0x1
@ -838,26 +868,33 @@ namespace Private {
*/ */
ULONG search2(ULONG startAddress, ULONG stopAddress) ULONG search2(ULONG startAddress, ULONG stopAddress)
{ {
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版 // return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d750fbec083c0fe83f806776a const uint8_t bytes[] = {
// 3c207d750fbec083c0fe83f806776a
0x3c, 0x20, // 011d4d31 |. 3c 20 cmp al,0x20 0x3c, 0x20, // 011d4d31 |. 3c 20 cmp al,0x20
0x7d, 0x75, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa 0x7d, 0x75, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa
0x0f,0xbe,0xc0, // 011d4d35 |. 0fbec0 movsx eax,al 0x0f, 0xbe, 0xc0, // 011d4d35 |. 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8) 0x83, 0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8, 0x06, // 011d4d3b |. 83f8 06 cmp eax,0x6 0x83, 0xf8, 0x06, // 011d4d3b |. 83f8 06 cmp eax,0x6
0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa 0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa
}; };
enum { hook_offset = 0x34c80 - 0x34d31 }; // distance to the beginning of the function enum
//ULONG range = qMin(stopAddress - startAddress, Engine::MaximumMemoryRange); {
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); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr) if (!addr)
//ConsoleOutput("BGI2: pattern not found"); // ConsoleOutput("BGI2: pattern not found");
return 0; return 0;
addr += hook_offset; addr += hook_offset;
enum : uint8_t { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp enum : uint8_t
{
push_ebp = 0x55
}; // 011d4c80 /$ 55 push ebp
if (*(uint8_t *)addr != push_ebp) if (*(uint8_t *)addr != push_ebp)
//ConsoleOutput("BGI2: pattern found but the function offset is invalid"); // ConsoleOutput("BGI2: pattern found but the function offset is invalid");
return 0; return 0;
return addr; return addr;
@ -1016,15 +1053,16 @@ namespace Private {
* 01312ed8 . 33f6 xor esi,esi * 01312ed8 . 33f6 xor esi,esi
* 01312eda . 83c4 04 add esp,0x4 * 01312eda . 83c4 04 add esp,0x4
*/ */
ULONG search3(ULONG startAddress, ULONG stopAddress) ULONG search3(ULONG startAddress, ULONG stopAddress)
{ {
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版 // return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d580fbec083c0fe83f806774d const uint8_t bytes[] = {
// 3c207d580fbec083c0fe83f806774d
0x3c, 0x20, // 01312d8e 3c 20 cmp al,0x20 ; jichi: pattern starts 0x3c, 0x20, // 01312d8e 3c 20 cmp al,0x20 ; jichi: pattern starts
0x7d, 0x58, // 01312d90 7d 58 jge short 蒼の彼方.01312dea 0x7d, 0x58, // 01312d90 7d 58 jge short 蒼の彼方.01312dea
0x0f,0xbe,0xc0, // 01312d92 0fbec0 movsx eax,al 0x0f, 0xbe, 0xc0, // 01312d92 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 01312d95 83c0 fe add eax,-0x2 ; switch (cases 2..8) 0x83, 0xc0, 0xfe, // 01312d95 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8, 0x06, // 01312d98 83f8 06 cmp eax,0x6 0x83, 0xf8, 0x06, // 01312d98 83f8 06 cmp eax,0x6
0x77, 0x4d // 01312d9b 77 4d ja short 蒼の彼方.01312dea 0x77, 0x4d // 01312d9b 77 4d ja short 蒼の彼方.01312dea
}; };
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
@ -1034,22 +1072,30 @@ ULONG search3(ULONG startAddress, ULONG stopAddress)
// distance to the beginning of the function // distance to the beginning of the function
static const int hook_offsets[] = { static const int hook_offsets[] = {
0x01312cd0 - 0x01312d8e // for new BGI2 game since 蒼の彼方 (2014/08), text is in arg2 0x01312cd0 - 0x01312d8e // for new BGI2 game since 蒼の彼方 (2014/08), text is in arg2
, 0x00a64260 - 0x00a64318 // For newer BGI2 game since コドモノアソビ (2015/11) ,
0x00a64260 - 0x00a64318 // For newer BGI2 game since コドモノアソビ (2015/11)
};
enum
{
hook_offset_count = sizeof(hook_offsets) / sizeof(*hook_offsets)
}; };
enum { hook_offset_count = sizeof(hook_offsets) / sizeof(*hook_offsets) };
for (size_t i = 0; i < hook_offset_count; i++) { for (size_t i = 0; i < hook_offset_count; i++)
{
int hook_offset = hook_offsets[i]; int hook_offset = hook_offsets[i];
enum : uint8_t { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp enum : uint8_t
{
push_ebp = 0x55
}; // 011d4c80 /$ 55 push ebp
if (*(uint8_t *)(addr + hook_offset) == push_ebp) if (*(uint8_t *)(addr + hook_offset) == push_ebp)
return addr + hook_offset; return addr + hook_offset;
} }
return 0; // failed return 0; // failed
} }
ULONG search_bgi3(ULONG startAddress, ULONG stopAddress ) ULONG search_bgi3(ULONG startAddress, ULONG stopAddress)
{ {
//黄昏のフォルクローレ // 黄昏のフォルクローレ
/* .text:00C3A700 push ebp /* .text:00C3A700 push ebp
.text : 00C3A701 mov ebp, esp .text : 00C3A701 mov ebp, esp
.text : 00C3A703 push[ebp + arg_30] .text : 00C3A703 push[ebp + arg_30]
@ -1084,47 +1130,53 @@ ULONG search_bgi3(ULONG startAddress, ULONG stopAddress )
*/ */
const uint8_t bytes[] = { const uint8_t bytes[] = {
0x55, 0x55,
0x8b,0xec, 0x8b, 0xec,
0xff,0x75,0x38, 0xff, 0x75, 0x38,
0x8b,0x55,0x0c, 0x8b, 0x55, 0x0c,
0xff,0x75,0x34, 0xff, 0x75, 0x34,
0x8b,0x4d,0x08, 0x8b, 0x4d, 0x08,
0xff,0x75,0x30 0xff, 0x75, 0x30};
};
ULONG range = min(ULONG(stopAddress - startAddress), ULONG(0x00300000)); ULONG range = min(ULONG(stopAddress - startAddress), ULONG(0x00300000));
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, startAddress + range); ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, startAddress + range);
if (addr == 0)return 0; if (addr == 0)
return 0;
return addr; return addr;
} }
bool search_tayutama(DWORD *funaddr,DWORD *addr){ bool search_tayutama(DWORD *funaddr, DWORD *addr)
{
const BYTE bytes[] = { const BYTE bytes[] = {
// The following code does not exist in newer BGI games after BGI 1.633.0.0 (tayutama2_trial_EX) // 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 // 0x3c, 0x20, // 011d4d31 |. 3c 20 cmp al,0x20
//0x7d, XX, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa ; jichi: 0x75 or 0x58 // 0x7d, XX, // 011d4d33 |. 7d 75 jge short sekachu.011d4daa ; jichi: 0x75 or 0x58
0x0f,0xbe,0xc0, // 011d4d35 |. 0fbec0 movsx eax,al 0x0f, 0xbe, 0xc0, // 011d4d35 |. 0fbec0 movsx eax,al
0x83,0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8) 0x83, 0xc0, 0xfe, // 011d4d38 |. 83c0 fe add eax,-0x2 ; switch (cases 2..8)
0x83,0xf8//, 0x06 // 011d4d3b |. 83f8 06 cmp eax,0x6 0x83, 0xf8 //, 0x06 // 011d4d3b |. 83f8 06 cmp eax,0x6
// The following code does not exist in newer BGI games after 蒼の彼方 // The following code does not exist in newer BGI games after 蒼の彼方
//0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa // 0x77, 0x6a // 011d4d3e |. 77 6a ja short sekachu.011d4daa
}; };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR); ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
* addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range); *addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(reladdr); // GROWL_DWORD(reladdr);
if (!*addr) { if (!*addr)
{
return false; return false;
} }
* funaddr = MemDbg::findEnclosingAlignedFunction(*addr, 0x300); // range is around 177 ~ 190 *funaddr = MemDbg::findEnclosingAlignedFunction(*addr, 0x300); // range is around 177 ~ 190
enum : BYTE { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp enum : BYTE
if (!*funaddr || *(BYTE *)*funaddr != push_ebp) { {
push_ebp = 0x55
}; // 011d4c80 /$ 55 push ebp
if (!*funaddr || *(BYTE *)*funaddr != push_ebp)
{
return false; return false;
} }
return true; return true;
} }
bool InsertBGI2Hook() bool InsertBGI2Hook()
{ {
/* Artikash 6/14/2019: Ugh, what a mess I've dug up... /* 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? At some point the beginning four bytes to search for were removed, but the difference below were not corrected? Or maybe they were?
@ -1134,20 +1186,24 @@ bool InsertBGI2Hook()
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. 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. And all the other cases fall through to offset=arg2.
*/ */
ULONG addr , funaddr;HookParam hp; ULONG addr, funaddr;
hp.hook_font=F_TextOutA|F_TextOutW; HookParam hp;
if (addr=search_bgi3(processStartAddress, processStopAddress)){ hp.hook_font = F_TextOutA | F_TextOutW;
//有乱码,无法处理。 if (addr = search_bgi3(processStartAddress, processStopAddress))
{
// 有乱码,无法处理。
Private::textIndex_ = 3; Private::textIndex_ = 3;
hp.offset=get_stack(Private::textIndex_); hp.offset = get_stack(Private::textIndex_);
Private::type_ = Private::Type_BGI3; Private::type_ = Private::Type_BGI3;
hp.hook_font|=F_GetTextExtentPoint32W; hp.hook_font |= F_GetTextExtentPoint32W;
if(addr-processStartAddress==0x3B860)//[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-,无法处理的乱码,不知道怎么回事。 if (addr - processStartAddress == 0x3B860) //[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-,无法处理的乱码,不知道怎么回事。
addr=0; addr = 0;
} }
else if ( search_tayutama(&funaddr,&addr)) { else if (search_tayutama(&funaddr, &addr))
{
switch (funaddr - addr) { switch (funaddr - addr)
{
// for old BGI2 game, text is arg3 // for old BGI2 game, text is arg3
case 0x34c80 - 0x34d31: // old offset case 0x34c80 - 0x34d31: // old offset
case 0x34c50 - 0x34d05: // correction as mentioned above case 0x34c50 - 0x34d05: // correction as mentioned above
@ -1174,44 +1230,48 @@ bool InsertBGI2Hook()
Private::type_ = Private::Type3; Private::type_ = Private::Type3;
addr = funaddr; addr = funaddr;
} }
else if (addr =search3(processStartAddress, processStopAddress)) { else if (addr = search3(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type3; Private::type_ = Private::Type3;
Private::textIndex_ = 2; // use arg2, name = "BGI2"; Private::textIndex_ = 2; // use arg2, name = "BGI2";
}else if (addr = search2(processStartAddress, processStopAddress)) { }
else if (addr = search2(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type2; Private::type_ = Private::Type2;
Private::textIndex_ = 3; // use arg3, name = "BGI2"; Private::textIndex_ = 3; // use arg3, name = "BGI2";
} else if (addr =search1(processStartAddress, processStopAddress)) { }
else if (addr = search1(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type1; Private::type_ = Private::Type1;
Private::textIndex_ = 3; // use arg3, name = "BGI"; Private::textIndex_ = 3; // use arg3, name = "BGI";
} }
if(addr==0)return false; if (addr == 0)
return false;
hp.address = addr; hp.address = addr;
hp.offset=get_stack(Private::textIndex_); hp.offset = get_stack(Private::textIndex_);
// jichi 5/12/2014: Using split could distinguish name and choices. But the signature might become unstable // 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.type = USING_STRING | USING_SPLIT | EMBED_ABLE | EMBED_DYNA_SJIS | EMBED_AFTER_NEW;
hp.hook_before=Private::hookBefore; hp.hook_before = Private::hookBefore;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){ hp.filter_fun = [](void *data, size_t *len, HookParam *hp)
{
// It could be either <R..> or <r..> // It could be either <R..> or <r..>
static const std::regex rx("<r.+?>(.+?)</r>", std::regex_constants::icase); static const std::regex rx("<r.+?>(.+?)</r>", std::regex_constants::icase);
std::string result = std::string((char*)data,*len); std::string result = std::string((char *)data, *len);
result = std::regex_replace(result, rx, "$1"); result = std::regex_replace(result, rx, "$1");
return write_string_overwrite(data,len,result); return write_string_overwrite(data, len, result);
} ; };
hp.split = get_stack(8); // pseudo arg8 hp.split = get_stack(8); // pseudo arg8
//GROWL_DWORD2(hp.address, processStartAddress); // GROWL_DWORD2(hp.address, processStartAddress);
return NewHook(hp, "EmbedBGI"); return NewHook(hp, "EmbedBGI");
} }
bool InsertBGI3Hook() bool InsertBGI3Hook()
{ {
/* /*
* Sample games: * Sample games:
* https://vndb.org/v28283 * https://vndb.org/v28283
@ -1223,24 +1283,25 @@ bool InsertBGI3Hook()
bool found = false; bool found = false;
const BYTE pattern[] = { const BYTE pattern[] = {
0x55, // 55 push ebp 0x55, // 55 push ebp
0x8b,0xec, // 8BEC mov ebp,esp 0x8b, 0xec, // 8BEC mov ebp,esp
0x83,0xe4, 0xf8, // 83E4 F8 and esp,FFFFFFF8 0x83, 0xe4, 0xf8, // 83E4 F8 and esp,FFFFFFF8
0x81,0xec, 0x84,0x00,0x00,0x00 // 81EC 84000000 sub esp,0x84 0x81, 0xec, 0x84, 0x00, 0x00, 0x00 // 81EC 84000000 sub esp,0x84
}; };
for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress)) for (auto addr : Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, processStartAddress, processStopAddress))
{ {
HookParam hp; HookParam hp;
hp.address = addr; hp.address = addr;
hp.offset=get_stack(2); hp.offset = get_stack(2);
hp.split =get_stack(1); hp.split = get_stack(1);
hp.type = CODEC_UTF16 | USING_SPLIT; hp.type = CODEC_UTF16 | USING_SPLIT;
ConsoleOutput("INSERT BGI3"); ConsoleOutput("INSERT BGI3");
found|=NewHook(hp, "BGI3"); found |= NewHook(hp, "BGI3");
} }
if (!found) ConsoleOutput("BGI3: pattern not found"); if (!found)
ConsoleOutput("BGI3: pattern not found");
return found; return found;
} }
#if 0 #if 0
/** /**
@ -1338,20 +1399,32 @@ bool BGI7Filter(LPVOID data, size_t *size, HookParam *)
{ {
auto text = reinterpret_cast<LPWSTR>(data); auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size); auto len = reinterpret_cast<size_t *>(size);
// BGI4 split不管用所以只好手动过滤掉了
auto ws = std::wstring(text, *len / 2);
if (endWith(ws, L".bs5"))
return false;
if (endWith(ws, L".arc"))
return false;
if (all_ascii(ws.c_str()) && (ws.find(L"-") != ws.npos) && (ws.find(L"_") != ws.npos))
return false;
if (ws.find(L"[ 0 ]") != ws.npos)
return false; // 個別アニメーション [ 0 ] の透明度
//
CharFilter(text, len, L'\x0001'); CharFilter(text, len, L'\x0001');
CharFilter(text, len, L'\x0002'); CharFilter(text, len, L'\x0002');
CharFilter(text, len, L'\x0003'); CharFilter(text, len, L'\x0003');
CharFilter(text, len, L'\x0004'); CharFilter(text, len, L'\x0004');
CharFilter(text, len, L'\x0005'); CharFilter(text, len, L'\x0005');
CharFilter(text, len, L'\x000A'); CharFilter(text, len, L'\x000A');
if (text[0] == L'\x3000') { if (text[0] == L'\x3000')
{
*len -= 2; *len -= 2;
::memmove(text, text+1, *len); ::memmove(text, text + 1, *len);
} }
CharReplacer(text, len, L'\x3000', L' '); //IDSP CharReplacer(text, len, L'\x3000', L' '); // IDSP
if (cpp_wcsnstr(text, L"<", *len/sizeof(wchar_t))) { if (cpp_wcsnstr(text, L"<", *len / sizeof(wchar_t)))
{
StringFilterBetween(text, len, L"<", 1, L">", 1); StringFilterBetween(text, len, L"<", 1, L">", 1);
} }
@ -1369,7 +1442,7 @@ bool InsertBGI7Hook()
bool found = false; bool found = false;
const BYTE pattern[] = { const BYTE pattern[] = {
0x55, // 55 push ebp << hook here 0x55, // 55 push ebp << hook here
0x8b,0xec, // 8BEC mov ebp,esp 0x8b, 0xec, // 8BEC mov ebp,esp
0x53, // 53 push ebx 0x53, // 53 push ebx
0x56, // 56 push esi 0x56, // 56 push esi
0x57, // 57 push edi 0x57, // 57 push edi
@ -1382,14 +1455,15 @@ bool InsertBGI7Hook()
{ {
HookParam hp; HookParam hp;
hp.address = addr; hp.address = addr;
hp.offset=get_reg(regs::eax); hp.offset = get_reg(regs::eax);
hp.split =get_reg(regs::esp); hp.split = get_reg(regs::esp);
hp.type = CODEC_UTF16 | USING_STRING | USING_SPLIT | KNOWN_UNSTABLE; hp.type = CODEC_UTF16 | USING_STRING | USING_SPLIT | KNOWN_UNSTABLE;
hp.filter_fun = BGI7Filter; hp.filter_fun = BGI7Filter;
ConsoleOutput("INSERT BGI4"); ConsoleOutput("INSERT BGI4");
found|=NewHook(hp, "BGI4"); found |= NewHook(hp, "BGI4");
} }
if (!found) ConsoleOutput("BGI4: pattern not found"); if (!found)
ConsoleOutput("BGI4: pattern not found");
return found; return found;
} }
@ -1398,7 +1472,8 @@ bool BGI56Filter(LPVOID data, size_t *size, HookParam *)
auto text = reinterpret_cast<LPSTR>(data); auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size); auto len = reinterpret_cast<size_t *>(size);
if (text[0] == '@') { if (text[0] == '@')
{
*len -= 1; *len -= 1;
::memmove(text, text + 1, *len); ::memmove(text, text + 1, *len);
} }
@ -1428,7 +1503,7 @@ bool InsertBGI5Hook()
HookParam hp; HookParam hp;
hp.address = addr + 1; hp.address = addr + 1;
hp.offset=get_reg(regs::ecx); hp.offset = get_reg(regs::ecx);
hp.padding = 1; hp.padding = 1;
hp.type = USING_STRING; hp.type = USING_STRING;
hp.filter_fun = BGI56Filter; hp.filter_fun = BGI56Filter;
@ -1462,7 +1537,7 @@ bool InsertBGI6Hook()
HookParam hp; HookParam hp;
hp.address = addr + 1; hp.address = addr + 1;
hp.offset=get_reg(regs::ecx); hp.offset = get_reg(regs::ecx);
hp.padding = 1; hp.padding = 1;
hp.type = USING_STRING; hp.type = USING_STRING;
hp.filter_fun = BGI56Filter; hp.filter_fun = BGI56Filter;
@ -1471,8 +1546,9 @@ bool InsertBGI6Hook()
return NewHook(hp, "BGI6"); return NewHook(hp, "BGI6");
} }
bool InsertBGIHook() bool InsertBGIHook()
{ return InsertBGI2Hook() || InsertBGI3Hook() || (PcHooks::hookOtherPcFunctions(), InsertBGI1Hook()); } {
return InsertBGI2Hook() || InsertBGI3Hook() || (PcHooks::hookOtherPcFunctions(), InsertBGI1Hook());
}
bool InsertBGI4Hook() bool InsertBGI4Hook()
{ {
@ -1509,119 +1585,135 @@ bool InsertBGI4Hook()
return v3; return v3;
}*/ }*/
const BYTE bytes[] = { const BYTE bytes[] = {
0xBE,0xE9,0xFD,0x00,0x00, //cp=65001 0xBE, 0xE9, 0xFD, 0x00, 0x00, // cp=65001
XX2, XX2,
0xBE,0xA4,0x03,0x00,0x00 //cp=932 0xBE, 0xA4, 0x03, 0x00, 0x00 // cp=932
}; };
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress); auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false; if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr); addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false; if (addr == 0)
return false;
HookParam hp; HookParam hp;
hp.address = addr; hp.address = addr;
// hp.offset=get_reg(regs::eax); // hp.offset=get_reg(regs::eax);
// hp.split = get_reg(regs::esp); // hp.split = get_reg(regs::esp);
hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ hp.text_fun = [](hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len)
*data=stack->stack[2]; {
*split=stack->stack[6];//不一定对 *data = stack->stack[2];
switch(*split){ *split = stack->stack[6]; // 不一定对
case 0://name *len = 2 * wcslen((wchar_t *)*data);
case 1: // switch(*split){
*len=2*wcslen((wchar_t*)*data); // case 0://name
break; // case 1:
} // *len=2*wcslen((wchar_t*)*data);
// break;
// }
}; };
hp.type = CODEC_UTF16 | USING_STRING|NO_CONTEXT |EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_OVERWRITE; hp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT | EMBED_ABLE | EMBED_BEFORE_SIMPLE | EMBED_AFTER_OVERWRITE;
hp.hook_font=F_TextOutW|F_GetTextExtentPoint32W; hp.hook_font = F_TextOutW | F_GetTextExtentPoint32W;
hp.filter_fun = BGI7Filter; hp.filter_fun = BGI7Filter;
ConsoleOutput("BGI4"); ConsoleOutput("BGI4");
return NewHook(hp, "BGI4"); return NewHook(hp, "BGI4");
} }
namespace{ namespace
bool veryold(){ {
//紅月-くれないつき- bool veryold()
//あの街の恋の詩 {
auto addr = findiatcallormov((DWORD)GetGlyphOutlineA,processStartAddress,processStartAddress,processStopAddress); // 紅月-くれないつき-
if (addr == 0)//銀行淫~堕ちゆく女達~ //mov ebp, ds:GetGlyphOutlineA // あの街の恋の詩
addr = findiatcallormov((DWORD)GetGlyphOutlineA,processStartAddress,processStartAddress,processStopAddress,false,XX); auto addr = findiatcallormov((DWORD)GetGlyphOutlineA, processStartAddress, processStartAddress, processStopAddress);
if (addr == 0)return false; if (addr == 0) // 銀行淫~堕ちゆく女達~ //mov ebp, ds:GetGlyphOutlineA
addr = findiatcallormov((DWORD)GetGlyphOutlineA, processStartAddress, processStartAddress, processStopAddress, false, XX);
if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr); addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false; if (addr == 0)
auto xrefs=findxref_reverse_checkcallop(addr,addr-0x1000,addr+0x1000,0xe8); return false;
if(xrefs.size()!=1)return false; auto xrefs = findxref_reverse_checkcallop(addr, addr - 0x1000, addr + 0x1000, 0xe8);
auto xrefaddr=xrefs[0]; if (xrefs.size() != 1)
return false;
auto xrefaddr = xrefs[0];
auto funcstart = MemDbg::findEnclosingAlignedFunction(xrefaddr); auto funcstart = MemDbg::findEnclosingAlignedFunction(xrefaddr);
if (funcstart == 0)return false; if (funcstart == 0)
BYTE sig[]={0x81,XX,0x00,0x01,0x00,0x00};//cmp ebx, 100h return false;
if(MemDbg::findBytes(sig, sizeof(sig), xrefaddr-0x40, xrefaddr)==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; HookParam hp;
hp.address = funcstart; hp.address = funcstart;
hp.offset=get_stack(2); hp.offset = get_stack(2);
hp.split =get_stack(1); hp.split = get_stack(1);
hp.type = CODEC_ANSI_BE |USING_SPLIT; hp.type = CODEC_ANSI_BE | USING_SPLIT;
return NewHook(hp, "BGI5"); return NewHook(hp, "BGI5");
} }
} }
namespace{ namespace
{
//[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world- //[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-
//int __fastcall sub_438E90(int a1, int *a2, int a3, _DWORD *a4, int a5) // int __fastcall sub_438E90(int a1, int *a2, int a3, _DWORD *a4, int a5)
bool hook7(){ bool hook7()
BYTE sig[]={ {
0x55,0x8b,0xec, BYTE sig[] = {
0x83,0xe4,0xf0, 0x55, 0x8b, 0xec,
0x83,0xec,XX, 0x83, 0xe4, 0xf0,
0x83, 0xec, XX,
0x56, 0x56,
0x57, 0x57,
0x8b,XX,0x08, 0x8b, XX, 0x08,
0x8b,0xf2, 0x8b, 0xf2,
0x8b,0xd1, 0x8b, 0xd1,
0x81,0xcf,0x00,0x00,0x00,0x80, 0x81, 0xcf, 0x00, 0x00, 0x00, 0x80,
0x8b,0xcf, 0x8b, 0xcf,
0x89,0x54,0x24,0x0c, 0x89, 0x54, 0x24, 0x0c,
0xe8,XX4, 0xe8, XX4,
0x85,0xc0, 0x85, 0xc0,
0x0f,0x84,XX4, 0x0f, 0x84, XX4,
0x8b,0x45,0x08 0x8b, 0x45, 0x08
}; };
auto addr=MemDbg::findBytes(sig,sizeof(sig),processStartAddress,processStopAddress); auto addr = MemDbg::findBytes(sig, sizeof(sig), processStartAddress, processStopAddress);
if(!addr)return false; if (!addr)
return false;
HookParam hp; HookParam hp;
hp.address=addr; hp.address = addr;
//hp.offset=get_stack(1); // hp.offset=get_stack(1);
//hp.split=get_stack(3); // hp.split=get_stack(3);
hp.type=USING_CHAR|CODEC_UTF16|NO_CONTEXT;//|USING_SPLIT; hp.type = USING_CHAR | CODEC_UTF16 | NO_CONTEXT; //|USING_SPLIT;
hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ hp.text_fun = [](hook_stack *stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len)
*data=(wchar_t)stack->stack[1]; {
switch(stack->stack[3]){ *data = (wchar_t)stack->stack[1];
switch (stack->stack[3])
{
case 0xfefefe: case 0xfefefe:
hp->user_value=stack->retaddr; hp->user_value = stack->retaddr;
*len=2; *len = 2;
*split=1; *split = 1;
break; break;
case 0xffffff://名字&历史+零散的文字由于no_context他们被合并但是和名字和文本是同一个调用地址 case 0xffffff: // 名字&历史+零散的文字由于no_context他们被合并但是和名字和文本是同一个调用地址
if(hp->user_value==stack->retaddr){ if (hp->user_value == stack->retaddr)
*len=2; {
*split=2; *len = 2;
*split = 2;
} }
break; break;
case 0xfcfcc0://历史 case 0xfcfcc0: // 历史
default: default:;
;
} }
}; };
return NewHook(hp,"bgi7"); return NewHook(hp, "bgi7");
} }
} }
bool BGI::attach_function() { bool BGI::attach_function()
bool b1= InsertBGIHook(); {
bool b2=InsertBGI4Hook(); bool b1 = InsertBGIHook();
bool ok= b1||b2||veryold(); bool b2 = InsertBGI4Hook();
ok|=hook7(); bool ok = b1 || b2 || veryold();
ok=InsertBGI7Hook()|| InsertBGI5Hook() || InsertBGI6Hook()||ok; ok |= hook7();
ok = InsertBGI7Hook() || InsertBGI5Hook() || InsertBGI6Hook() || ok;
return ok; return ok;
} }