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:
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
it's likely to be different for different calls.
********************************************************************************************/
namespace { // unnamed
namespace
{ // unnamed
#if 0 // jichi 12/28/2013: dynamic BGI is not used
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
/** jichi 5/12/2014
/** jichi 5/12/2014
* Sample game: FORTUNE ARTERIAL, case 2 at 0x41ebd0
*
* 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
* 0041ec7c |. e8 1fa0feff call bgi.00408ca0
*/
bool InsertBGI1Hook()
{
union {
bool InsertBGI1Hook()
{
union
{
DWORD i;
DWORD *id;
BYTE *ib;
};
HookParam hp;
for (i = processStartAddress + 0x1000; i < processStopAddress; i++) {
if (ib[0] == 0x3d) {
for (i = processStartAddress + 0x1000; i < processStopAddress; i++)
{
if (ib[0] == 0x3d)
{
i++;
if (id[0] == 0xffff) { //cmp eax,0xffff
if (id[0] == 0xffff)
{ // cmp eax,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) {
hp.offset=get_stack(3);
if (hp.address)
{
hp.offset = get_stack(3);
hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT;
hp.type = CODEC_ANSI_BE | USING_SPLIT;
ConsoleOutput("INSERT BGI#1");
//RegisterEngineType(ENGINE_BGI);
// RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI");
}
}
}
if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8)) {
if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8))
{
i += 2;
if (id[0] == 0xffff) { //cmp reg,0xffff
if (id[0] == 0xffff)
{ // cmp reg,0xffff
hp.address = SafeFindEnclosingAlignedFunction(i, 0x40);
if (hp.address) {
hp.offset=get_stack(3);
if (hp.address)
{
hp.offset = get_stack(3);
hp.split = get_reg(regs::esp);
hp.type = CODEC_ANSI_BE|USING_SPLIT;
hp.type = CODEC_ANSI_BE | USING_SPLIT;
ConsoleOutput("INSERT BGI#2");
//RegisterEngineType(ENGINE_BGI);
// RegisterEngineType(ENGINE_BGI);
return NewHook(hp, "BGI");
}
}
}
}
//ConsoleOutput("Unknown BGI engine.");
// ConsoleOutput("Unknown BGI engine.");
//ConsoleOutput("Probably BGI. Wait for text.");
//SwitchTrigger(true);
//trigger_fun=InsertBGIDynamicHook;
// 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
@ -511,32 +520,40 @@ bool InsertBGI1Hook()
* 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)
// 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)
{
if (type_ == Type_BGI3) {
DWORD retaddr = s->stack[0]; // retaddr
* role = Engine::ScenarioRole;
return write_string_overwrite(data,len,(LPCSTR)s->stack[textIndex_]);
*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
@ -546,18 +563,20 @@ namespace Private {
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 = 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 split = s->stack[8]; // this is a good split, but usually game-specific
DWORD retaddr = s->stack[0]; // retaddr
//* role = Engine::OtherRole;
switch (type_) {
switch (type_)
{
case Type3:
switch (s->stack[textIndex_+1]) {
switch (s->stack[textIndex_ + 1])
{
case 1:
if (*(WORD *)(retaddr + 8) == 0xcccc) // two int3
*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
*role = Engine::NameRole;
break;
} break;
}
break;
case Type2:
switch (s->stack[textIndex_+1]) {
switch (s->stack[textIndex_ + 1])
{
case 1:
// Return address for history text
// 012B37BA 83C4 34 ADD ESP,0x34
@ -581,20 +602,25 @@ namespace Private {
if (s->stack[12] == 0x00ffffff && s->stack[12 - 3] == 2)
*role = Engine::NameRole;
break;
} break;
}
break;
case Type1:
switch (s->stack[textIndex_+1]) {
case 1: *role = Engine::ScenarioRole; break;
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_]);
break;
}
return write_string_overwrite(data, len, (LPCSTR)s->stack[textIndex_]);
}
}
}
/**
* 5/12/2014
@ -705,8 +731,8 @@ namespace Private {
*/
ULONG search1(ULONG startAddress, ULONG stopAddress)
{
//return 0x4207e0; // FORTUNE ARTERIAL
//const BYTE bytes[] = {
// 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
@ -714,31 +740,35 @@ namespace Private {
// 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
// };
// 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
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);
// 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");
// ConsoleOutput("BGI2: pattern not found");
return 0;
enum : WORD {
enum : WORD
{
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--)
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
// 00427450 /$ 6a ff push -0x1
@ -838,26 +868,33 @@ namespace Private {
*/
ULONG search2(ULONG startAddress, ULONG stopAddress)
{
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d750fbec083c0fe83f806776a
// 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
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);
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");
// ConsoleOutput("BGI2: pattern not found");
return 0;
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)
//ConsoleOutput("BGI2: pattern found but the function offset is invalid");
// ConsoleOutput("BGI2: pattern found but the function offset is invalid");
return 0;
return addr;
@ -1016,15 +1053,16 @@ namespace Private {
* 01312ed8 . 33f6 xor esi,esi
* 01312eda . 83c4 04 add esp,0x4
*/
ULONG search3(ULONG startAddress, ULONG stopAddress)
ULONG search3(ULONG startAddress, ULONG stopAddress)
{
//return startAddress + 0x31850; // 世界と世界の真ん中 体験版
const uint8_t bytes[] = { // 3c207d580fbec083c0fe83f806774d
// 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
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);
@ -1034,22 +1072,30 @@ ULONG search3(ULONG startAddress, ULONG stopAddress)
// 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)
,
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];
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)
return addr + hook_offset;
}
return 0; // failed
}
ULONG search_bgi3(ULONG startAddress, ULONG stopAddress )
ULONG search_bgi3(ULONG startAddress, ULONG stopAddress)
{
//黄昏のフォルクローレ
// 黄昏のフォルクローレ
/* .text:00C3A700 push ebp
.text : 00C3A701 mov ebp, esp
.text : 00C3A703 push[ebp + arg_30]
@ -1084,47 +1130,53 @@ ULONG search_bgi3(ULONG startAddress, ULONG stopAddress )
*/
const uint8_t bytes[] = {
0x55,
0x8b,0xec,
0xff,0x75,0x38,
0x8b,0x55,0x0c,
0xff,0x75,0x34,
0x8b,0x4d,0x08,
0xff,0x75,0x30
};
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;
if (addr == 0)
return 0;
return addr;
}
bool search_tayutama(DWORD *funaddr,DWORD *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
// 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
// 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) {
*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
*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) {
enum : BYTE
{
push_ebp = 0x55
}; // 011d4c80 /$ 55 push ebp
if (!*funaddr || *(BYTE *)*funaddr != push_ebp)
{
return false;
}
return true;
}
bool InsertBGI2Hook()
{
}
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?
@ -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.
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)){
//有乱码,无法处理。
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_);
hp.offset = get_stack(Private::textIndex_);
Private::type_ = Private::Type_BGI3;
hp.hook_font|=F_GetTextExtentPoint32W;
if(addr-processStartAddress==0x3B860)//[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-,无法处理的乱码,不知道怎么回事。
addr=0;
hp.hook_font |= F_GetTextExtentPoint32W;
if (addr - processStartAddress == 0x3B860) //[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-,无法处理的乱码,不知道怎么回事。
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
case 0x34c80 - 0x34d31: // old offset
case 0x34c50 - 0x34d05: // correction as mentioned above
@ -1174,44 +1230,48 @@ bool InsertBGI2Hook()
Private::type_ = Private::Type3;
addr = funaddr;
}
else if (addr =search3(processStartAddress, processStopAddress)) {
else if (addr = search3(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type3;
Private::textIndex_ = 2; // use arg2, name = "BGI2";
}else if (addr = search2(processStartAddress, processStopAddress)) {
}
else if (addr = search2(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type2;
Private::textIndex_ = 3; // use arg3, name = "BGI2";
} else if (addr =search1(processStartAddress, processStopAddress)) {
}
else if (addr = search1(processStartAddress, processStopAddress))
{
Private::type_ = Private::Type1;
Private::textIndex_ = 3; // use arg3, name = "BGI";
}
if(addr==0)return false;
if (addr == 0)
return false;
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
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.filter_fun=[](void* data, size_t* len, HookParam* hp){
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);
std::string result = std::string((char *)data, *len);
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
//GROWL_DWORD2(hp.address, processStartAddress);
// GROWL_DWORD2(hp.address, processStartAddress);
return NewHook(hp, "EmbedBGI");
}
}
bool InsertBGI3Hook()
{
bool InsertBGI3Hook()
{
/*
* Sample games:
* https://vndb.org/v28283
@ -1223,24 +1283,25 @@ bool InsertBGI3Hook()
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
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.offset = get_stack(2);
hp.split = get_stack(1);
hp.type = CODEC_UTF16 | USING_SPLIT;
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;
}
}
#if 0
/**
@ -1338,20 +1399,32 @@ bool BGI7Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
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'\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') {
if (text[0] == L'\x3000')
{
*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);
}
@ -1369,7 +1442,7 @@ bool InsertBGI7Hook()
bool found = false;
const BYTE pattern[] = {
0x55, // 55 push ebp << hook here
0x8b,0xec, // 8BEC mov ebp,esp
0x8b, 0xec, // 8BEC mov ebp,esp
0x53, // 53 push ebx
0x56, // 56 push esi
0x57, // 57 push edi
@ -1382,14 +1455,15 @@ bool InsertBGI7Hook()
{
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.split =get_reg(regs::esp);
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");
found |= NewHook(hp, "BGI4");
}
if (!found) ConsoleOutput("BGI4: pattern not found");
if (!found)
ConsoleOutput("BGI4: pattern not found");
return found;
}
@ -1398,7 +1472,8 @@ bool BGI56Filter(LPVOID data, size_t *size, HookParam *)
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (text[0] == '@') {
if (text[0] == '@')
{
*len -= 1;
::memmove(text, text + 1, *len);
}
@ -1428,7 +1503,7 @@ bool InsertBGI5Hook()
HookParam hp;
hp.address = addr + 1;
hp.offset=get_reg(regs::ecx);
hp.offset = get_reg(regs::ecx);
hp.padding = 1;
hp.type = USING_STRING;
hp.filter_fun = BGI56Filter;
@ -1462,7 +1537,7 @@ bool InsertBGI6Hook()
HookParam hp;
hp.address = addr + 1;
hp.offset=get_reg(regs::ecx);
hp.offset = get_reg(regs::ecx);
hp.padding = 1;
hp.type = USING_STRING;
hp.filter_fun = BGI56Filter;
@ -1471,8 +1546,9 @@ bool InsertBGI6Hook()
return NewHook(hp, "BGI6");
}
bool InsertBGIHook()
{ return InsertBGI2Hook() || InsertBGI3Hook() || (PcHooks::hookOtherPcFunctions(), InsertBGI1Hook()); }
{
return InsertBGI2Hook() || InsertBGI3Hook() || (PcHooks::hookOtherPcFunctions(), InsertBGI1Hook());
}
bool InsertBGI4Hook()
{
@ -1509,119 +1585,135 @@ bool InsertBGI4Hook()
return v3;
}*/
const BYTE bytes[] = {
0xBE,0xE9,0xFD,0x00,0x00, //cp=65001
0xBE, 0xE9, 0xFD, 0x00, 0x00, // cp=65001
XX2,
0xBE,0xA4,0x03,0x00,0x00 //cp=932
0xBE, 0xA4, 0x03, 0x00, 0x00 // cp=932
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false;
if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false;
if (addr == 0)
return false;
HookParam hp;
hp.address = addr;
// hp.offset=get_reg(regs::eax);
// hp.split = get_reg(regs::esp);
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];//不一定对
switch(*split){
case 0://name
case 1:
*len=2*wcslen((wchar_t*)*data);
break;
}
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]; // 不一定对
*len = 2 * wcslen((wchar_t *)*data);
// switch(*split){
// case 0://name
// 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.hook_font=F_TextOutW|F_GetTextExtentPoint32W;
hp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT | EMBED_ABLE | EMBED_BEFORE_SIMPLE | EMBED_AFTER_OVERWRITE;
hp.hook_font = F_TextOutW | F_GetTextExtentPoint32W;
hp.filter_fun = BGI7Filter;
ConsoleOutput("BGI4");
return NewHook(hp, "BGI4");
}
namespace{
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);
if (addr == 0)return false;
namespace
{
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);
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];
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;
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;
hp.offset = get_stack(2);
hp.split = get_stack(1);
hp.type = CODEC_ANSI_BE | USING_SPLIT;
return NewHook(hp, "BGI5");
}
}
namespace{
namespace
{
//[220729][1171051][きゃべつそふと] ジュエリー・ハーツ・アカデミア -We will wing wonder world-
//int __fastcall sub_438E90(int a1, int *a2, int a3, _DWORD *a4, int a5)
bool hook7(){
BYTE sig[]={
0x55,0x8b,0xec,
0x83,0xe4,0xf0,
0x83,0xec,XX,
// int __fastcall sub_438E90(int a1, int *a2, int a3, _DWORD *a4, int a5)
bool hook7()
{
BYTE sig[] = {
0x55, 0x8b, 0xec,
0x83, 0xe4, 0xf0,
0x83, 0xec, XX,
0x56,
0x57,
0x8b,XX,0x08,
0x8b,0xf2,
0x8b,0xd1,
0x81,0xcf,0x00,0x00,0x00,0x80,
0x8b,0xcf,
0x89,0x54,0x24,0x0c,
0xe8,XX4,
0x85,0xc0,
0x0f,0x84,XX4,
0x8b,0x45,0x08
0x8b, XX, 0x08,
0x8b, 0xf2,
0x8b, 0xd1,
0x81, 0xcf, 0x00, 0x00, 0x00, 0x80,
0x8b, 0xcf,
0x89, 0x54, 0x24, 0x0c,
0xe8, XX4,
0x85, 0xc0,
0x0f, 0x84, XX4,
0x8b, 0x45, 0x08
};
auto addr=MemDbg::findBytes(sig,sizeof(sig),processStartAddress,processStopAddress);
if(!addr)return false;
auto addr = MemDbg::findBytes(sig, sizeof(sig), processStartAddress, processStopAddress);
if (!addr)
return false;
HookParam hp;
hp.address=addr;
//hp.offset=get_stack(1);
//hp.split=get_stack(3);
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){
*data=(wchar_t)stack->stack[1];
switch(stack->stack[3]){
hp.address = addr;
// hp.offset=get_stack(1);
// hp.split=get_stack(3);
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)
{
*data = (wchar_t)stack->stack[1];
switch (stack->stack[3])
{
case 0xfefefe:
hp->user_value=stack->retaddr;
*len=2;
*split=1;
hp->user_value = stack->retaddr;
*len = 2;
*split = 1;
break;
case 0xffffff://名字&历史+零散的文字由于no_context他们被合并但是和名字和文本是同一个调用地址
case 0xffffff: // 名字&历史+零散的文字由于no_context他们被合并但是和名字和文本是同一个调用地址
if(hp->user_value==stack->retaddr){
*len=2;
*split=2;
if (hp->user_value == stack->retaddr)
{
*len = 2;
*split = 2;
}
break;
case 0xfcfcc0://历史
default:
;
case 0xfcfcc0: // 历史
default:;
}
};
return NewHook(hp,"bgi7");
return NewHook(hp, "bgi7");
}
}
bool BGI::attach_function() {
bool b1= InsertBGIHook();
bool b2=InsertBGI4Hook();
bool ok= b1||b2||veryold();
ok|=hook7();
ok=InsertBGI7Hook()|| InsertBGI5Hook() || InsertBGI6Hook()||ok;
bool BGI::attach_function()
{
bool b1 = InsertBGIHook();
bool b2 = InsertBGI4Hook();
bool ok = b1 || b2 || veryold();
ok |= hook7();
ok = InsertBGI7Hook() || InsertBGI5Hook() || InsertBGI6Hook() || ok;
return ok;
}