790 lines
33 KiB
C++
Raw Normal View History

2024-02-07 20:59:24 +08:00
#include"SystemAoi.h"
#include"embed_util.h"
/* 7/8/2014: The engine name is supposed to be: AoiGameSystem Engine
* See: http://capita.tistory.com/m/post/205
*
* BUNNYBLACK Trial2 (SystemAoi4)
* baseaddr: 0x01d0000
*
* 1002472e cc int3
* 1002472f cc int3
* 10024730 55 push ebp ; jichi: hook here
* 10024731 8bec mov ebp,esp
* 10024733 51 push ecx
* 10024734 c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
* 1002473b 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 1002473e 0fb708 movzx ecx,word ptr ds:[eax]
* 10024741 85c9 test ecx,ecx
* 10024743 74 34 je short _8.10024779
* 10024745 6a 00 push 0x0
* 10024747 6a 00 push 0x0
* 10024749 6a 01 push 0x1
* 1002474b 8b55 14 mov edx,dword ptr ss:[ebp+0x14]
* 1002474e 52 push edx
* 1002474f 0fb645 10 movzx eax,byte ptr ss:[ebp+0x10]
* 10024753 50 push eax
* 10024754 0fb74d 0c movzx ecx,word ptr ss:[ebp+0xc]
* 10024758 51 push ecx
* 10024759 8b55 08 mov edx,dword ptr ss:[ebp+0x8]
* 1002475c 52 push edx
* 1002475d e8 8eddffff call _8.100224f0
* 10024762 83c4 1c add esp,0x1c
* 10024765 8945 fc mov dword ptr ss:[ebp-0x4],eax
* 10024768 8b45 1c mov eax,dword ptr ss:[ebp+0x1c]
* 1002476b 50 push eax
* 1002476c 8b4d 18 mov ecx,dword ptr ss:[ebp+0x18]
* 1002476f 51 push ecx
* 10024770 8b55 fc mov edx,dword ptr ss:[ebp-0x4]
* 10024773 52 push edx
* 10024774 e8 77c6ffff call _8.10020df0
* 10024779 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 1002477c 8be5 mov esp,ebp
* 1002477e 5d pop ebp
* 1002477f c2 1800 retn 0x18
* 10024782 cc int3
* 10024783 cc int3
* 10024784 cc int3
*
* 2/12/2015 jichi: SystemAoi5
*
* Note that BUNNYBLACK 3 also has SystemAoi5 version 4.1
*
* Hooked to PgsvTd.dll for all SystemAoi engine, which contains GDI functions.
* - Old: AoiLib.dll from DrawTextExA
* - SystemAoi4: Aoi4.dll from DrawTextExW
* - SystemAoi5: Aoi5.dll from GetGlyphOutlineW
*
* Logic:
* - Find GDI function (DrawTextExW, etc.) used to paint text in PgsvTd.dll
* - Then search the function call stack, to find where the exe module invoke PgsvTd
* - Finally insert to the call address, and text is on the top of the stack.
*
* Sample hooked call in <EFBFBD> Aoi5
*
* 00B6D085 56 PUSH ESI
* 00B6D086 52 PUSH EDX
* 00B6D087 51 PUSH ECX
* 00B6D088 68 9E630000 PUSH 0x639E
* 00B6D08D 50 PUSH EAX
* 00B6D08E FF15 54D0BC00 CALL DWORD PTR DS:[0xBCD054] ; _12.0039E890, jichi: hook here
* 00B6D094 8B57 20 MOV EDX,DWORD PTR DS:[EDI+0x20]
* 00B6D097 89049A MOV DWORD PTR DS:[EDX+EBX*4],EAX
* 00B6D09A 8B4F 20 MOV ECX,DWORD PTR DS:[EDI+0x20]
* 00B6D09D 8B1499 MOV EDX,DWORD PTR DS:[ECX+EBX*4]
* 00B6D0A0 8D85 50FDFFFF LEA EAX,DWORD PTR SS:[EBP-0x2B0]
* 00B6D0A6 50 PUSH EAX
* 00B6D0A7 52 PUSH EDX
* 00B6D0A8 FF15 18D0BC00 CALL DWORD PTR DS:[0xBCD018] ; _12.003A14A0
*
* Special hook is needed, since the utf16 text is like this:
* [f9S30e0u] <EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
namespace { // unnamed
void SpecialHookSystemAoi(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
{
*split = 0; // 8/3/2014 jichi: split is zero, so return address is used as split
if (hp->type & CODEC_UTF16) {
LPCWSTR wcs = (LPWSTR)stack->stack[1]; // jichi: text on the top of the stack
size_t size = ::wcslen(wcs);
for (DWORD i = 0; i < size; i++)
if (wcs[i] == L'>' || wcs[i] == L']') { // skip leading ] for scenario and > for name threads
i++;
if (wcs[i] == 0x3000) // \u3000
i++;
*data = (DWORD)(wcs + i);
size -= i;
*len = size * 2; // * 2 for wstring
return;
}
} else {
LPCSTR cs = (LPCSTR)stack->stack[1]; // jichi: text on the top of the stack
size_t size = ::strlen(cs);
for (DWORD i = 0; i < size; i++)
if (cs[i] == '>' || cs[i] == ']') {
i++;
if ((unsigned char)cs[i] == 0x81 && cs[i+1] == 0x40) // \u3000
i += 2;
*data = (DWORD)(cs + i);
size -= i;
*len = size;
return;
}
}
}
int GetSystemAoiVersion() // return result is cached
{
static int ret = 0;
if (!ret) {
if (Util::CheckFile(L"Aoi4.dll"))
ret = 4;
else if (Util::CheckFile(L"Aoi5.dll"))
ret = 5;
else if (Util::CheckFile(L"Aoi6.dll")) // not exist yet, for future version
ret = 6;
else if (Util::CheckFile(L"Aoi7.dll")) // not exist yet, for future version
ret = 7;
else // AoiLib.dll, etc
ret = 3;
}
return ret;
}
bool InsertSystemAoiDynamicHook(LPVOID addr, uintptr_t frame, uintptr_t stack)
{
int version = GetSystemAoiVersion();
bool utf16 = true;
if (addr == ::DrawTextExA) // < 4
utf16 = false;
if (addr == ::DrawTextExW) // 4~5
; // pass
else if (addr == ::GetGlyphOutlineW && version >= 5)
; // pass
else
return false;
DWORD high, low;
Util::GetCodeRange(processStartAddress, &low, &high);
// jichi 2/15/2015: Traverse the stack to dynamically find the ancestor call from the main module
const DWORD stop = (stack & 0xffff0000) + 0x10000; // range to traverse the stack
for (DWORD i = stack; i < stop; i += 4) {
DWORD k = *(DWORD *)i;
if (k > low && k < high && // jichi: if the stack address falls into the code region of the main exe module
((*(WORD *)(k - 6) == 0x15ff) || *(BYTE *)(k - 5) == 0xe8)) { // jichi 10/20/2014: call dword ptr ds
HookParam hp;
hp.offset=get_stack(1);
hp.text_fun = SpecialHookSystemAoi; // need to remove garbage
hp.type = utf16 ? (USING_STRING|CODEC_UTF16) : USING_STRING;
i = *(DWORD *)(k - 4); // get function call address
if (*(DWORD *)(k - 5) == 0xe8) // short jump
hp.address = i + k;
else
hp.address = *(DWORD *)i; // jichi: long jump, this is what is happening in Aoi5
//NewHook(hp, "SofthouseChara");
//GROWL_DWORD(hp.address); // BUNNYBLACK: 0x10024730, base 0x01d0000
auto succ=false;
if (hp.address) {
ConsoleOutput("INSERT SystemAoi");
if (addr == ::GetGlyphOutlineW)
succ|=NewHook(hp, "SystemAoi2"); // jichi 2/12/2015
else
succ|=NewHook(hp, "SystemAoi"); // jichi 7/8/2014: renamed, see: ja.wikipedia.org/wiki/ソフトハウスキャラ
ConsoleOutput("SystemAoi: disable GDI hooks");
} else
ConsoleOutput("failed to detect SystemAoi");
//RegisterEngineType(ENGINE_SOFTHOUSE);
return succ;
}
}
ConsoleOutput("SystemAoi: failed");
return true; // jichi 12/25/2013: return true
}
bool InsertSystemAoiDynamic()
{
ConsoleOutput("DYNAMIC SystemAoi");
//ConsoleOutput("Probably SoftHouseChara. Wait for text.");
trigger_fun = InsertSystemAoiDynamicHook;
return true;
}
ULONG findAoiProc(HMODULE hModule, LPCSTR functionName, int minParamNum = 0, int maxParamNum = 10)
{
for (int i = minParamNum; i < maxParamNum; i++) {
std::string sig; // function signature name, such as _AgsSpriteCreateText@20
sig.push_back('_');
sig += functionName;
sig.push_back('@');
sig += std::to_string(4ll * i);
if (auto proc = ::GetProcAddress(hModule, sig.c_str()))
return (ULONG)proc;
}
return 0;
}
namespace{
template<typename wstrT>
wstrT ltrimA(wstrT text)
{
static const char *quotes[] = { "<>", "[]" }; // skip leading quotes
for each (const char *q in quotes)
while (text[0] == q[0]) {
if (auto p = ::strchr(text, q[1])) {
text = p + 1;
if ((UINT8)text[0] == 0x81 && (UINT8)text[1] == 0x40) // skip \u3000 leading space, assuming sjis encoding
text += 2;
} else
break;
}
return text;
}
template<typename wstrT>
wstrT ltrimW(wstrT text)
{
static const char *quotes[] = { "<>", "[]" }; // skip leading quotes
for each (const char *q in quotes)
while (text[0] == q[0]) {
if (auto p = ::wcschr(text, q[1])) {
text = p + 1;
if (*text == 0x3000) // skip \u3000 leading space
text++;
} else
break;
}
return text;
}
bool beforeAgsSpriteCreateTextExW(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
auto text = (LPWSTR)s->stack[2]; // arg2
if (!text || !*text || !Engine::isAddressWritable(text))
return false;
text = ltrimW(text);
if (!*text)
return false;
*role = Engine::OtherRole ;
2024-03-21 17:57:04 +08:00
write_string_overwrite(data,len,text);
2024-02-07 20:59:24 +08:00
return true;
}
void afterAgsSpriteCreateTextExW(hook_stack*s,void* data1, size_t len)
{
auto text = (LPWSTR)s->stack[2];
text = ltrimW(text);
std::wstring _=std::wstring((LPWSTR)data1,len);
wcscpy((LPWSTR)text,_.c_str());
}
bool beforeAgsSpriteCreateTextW(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
// All threads including character names are linked together
auto text = (LPWSTR)s->stack[1]; // arg1
if (!text || !*text || !Engine::isAddressWritable(text)) // skip modifying readonly text in code region
return false;
bool containsTags = ::wcsstr(text, L"[u]");
text = ltrimW(text);
if (!*text)
return false;
* role = Engine::OtherRole;
//ULONG split = s->stack[0]; // retaddr
ULONG split = s->stack[2]; // arg2
if (!containsTags)
switch (split) {
case 0x63a1:
*role = Engine::NameRole;
break;
case 0x639e:
*role = Engine::ScenarioRole;
break;
}
2024-03-21 17:57:04 +08:00
write_string_overwrite(data,len,text);
2024-02-07 20:59:24 +08:00
return true;
}
void afterAgsSpriteCreateTextW(hook_stack*s,void* data1, size_t len)
{
auto text = (LPWSTR)s->stack[1];
text = ltrimW(text);
std::wstring _=std::wstring((LPWSTR)data1,len);
wcscpy((LPWSTR)text,_.c_str());
}
void afterAgsSpriteCreateTextA(hook_stack*s,void* data1, size_t len)
{
auto text = (LPSTR)s->stack[1]; // arg1
text = ltrimA(text);
std::string _=std::string((char*)data1,len);
strcpy((char*)text,_.c_str());
}
bool beforeAgsSpriteCreateTextA(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
// All threads including character names are linked together
auto text = (LPSTR)s->stack[1]; // arg1
if (!text || !*text || !Engine::isAddressWritable(text)) // skip modifying readonly text in code region
return false;
bool containsTags = ::strstr(text, "[u]");
text = ltrimA(text);
if (!*text)
return false;
* role = Engine::OtherRole;
//ULONG split = s->stack[0]; // retaddr
ULONG split = s->stack[2]; // arg2
if (!containsTags)
switch (split) {
case 0x639d:
*role = Engine::NameRole;
break;
case 0x639c:
*role = Engine::ScenarioRole;
break;
}
2024-03-21 17:57:04 +08:00
return write_string_overwrite(data,len,text);
2024-02-07 20:59:24 +08:00
}
}
// jichi 7/26/2015: Backport logic in vnragent to vnrhook
namespace AgsPatchA {
namespace Private {
struct HookArgument {
ULONG unknown[13]; // + 0x34
LPCSTR text;
};
HookArgument *arg_;
LPCSTR text_;
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
LPCSTR src = (LPCSTR)s->stack[6]; // original text in arg7
//LPSTR dest = *(LPSTR *)(s->stack[0] + 0x34); // bad text in arg1+0x34
arg_ = (HookArgument *)s->stack[0];
text_ = arg_->text;
arg_->text = src;
return false;
}
bool hookAfter(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
if (arg_) {
arg_->text = text_;
arg_ = nullptr;
}
return false;
}
} // namespace Private
/**
* Sample game:
*
* Prevent Aoi engine from modifying illegal characters.
*
* Function found by hijack DrawTextExA.
*
* 100173BD CC INT3
* 100173BE CC INT3
* 100173BF CC INT3
* 100173C0 83EC 28 SUB ESP,0x28
* 100173C3 53 PUSH EBX
* 100173C4 33DB XOR EBX,EBX
* 100173C6 55 PUSH EBP
* 100173C7 8B6C24 34 MOV EBP,DWORD PTR SS:[ESP+0x34]
* 100173CB 56 PUSH ESI
* 100173CC 57 PUSH EDI
* 100173CD 8BF8 MOV EDI,EAX
* 100173CF C745 30 18000000 MOV DWORD PTR SS:[EBP+0x30],0x18
* 100173D6 381F CMP BYTE PTR DS:[EDI],BL
* 100173D8 895C24 28 MOV DWORD PTR SS:[ESP+0x28],EBX
* 100173DC C74424 2C FFFFFF>MOV DWORD PTR SS:[ESP+0x2C],0x7FFFFFFF
* 100173E4 895C24 1C MOV DWORD PTR SS:[ESP+0x1C],EBX
* 100173E8 895C24 20 MOV DWORD PTR SS:[ESP+0x20],EBX
* 100173EC 895C24 30 MOV DWORD PTR SS:[ESP+0x30],EBX
* 100173F0 895C24 34 MOV DWORD PTR SS:[ESP+0x34],EBX
* 100173F4 895C24 24 MOV DWORD PTR SS:[ESP+0x24],EBX
* 100173F8 895C24 14 MOV DWORD PTR SS:[ESP+0x14],EBX
* 100173FC 895C24 18 MOV DWORD PTR SS:[ESP+0x18],EBX
* 10017400 8BF7 MOV ESI,EDI
* 10017402 74 12 JE SHORT Ags.10017416
* 10017404 56 PUSH ESI
* 10017405 FF15 90A00210 CALL DWORD PTR DS:[<&AoiLib._AoiString2B>; AoiLib._AoiString2ByteIs@4
* 1001740B 85C0 TEST EAX,EAX
* 1001740D 74 7D JE SHORT Ags.1001748C
* 1001740F 83C6 02 ADD ESI,0x2
* 10017412 381E CMP BYTE PTR DS:[ESI],BL
* 10017414 ^75 EE JNZ SHORT Ags.10017404
* 10017416 57 PUSH EDI
* 10017417 FF15 94A00210 CALL DWORD PTR DS:[<&AoiLib._AoiStrlen@4>; AoiLib._AoiStrlen@4
* 1001741D 8BC8 MOV ECX,EAX
* 1001741F 83C1 02 ADD ECX,0x2
* 10017422 395C24 1C CMP DWORD PTR SS:[ESP+0x1C],EBX
* 10017426 74 0C JE SHORT Ags.10017434
* 10017428 8BC1 MOV EAX,ECX
* 1001742A 33D2 XOR EDX,EDX
* 1001742C F77424 2C DIV DWORD PTR SS:[ESP+0x2C]
* 10017430 8D4C01 01 LEA ECX,DWORD PTR DS:[ECX+EAX+0x1]
* 10017434 395C24 28 CMP DWORD PTR SS:[ESP+0x28],EBX
* 10017438 74 07 JE SHORT Ags.10017441
* 1001743A 8B4424 24 MOV EAX,DWORD PTR SS:[ESP+0x24]
* 1001743E 8D0C41 LEA ECX,DWORD PTR DS:[ECX+EAX*2]
* 10017441 51 PUSH ECX
* 10017442 FF15 18A00210 CALL DWORD PTR DS:[<&AoiLib._AoiMemoryAl>; AoiLib._AoiMemoryAlloc@4
* 10017448 8945 34 MOV DWORD PTR SS:[EBP+0x34],EAX
* 1001744B 381F CMP BYTE PTR DS:[EDI],BL
* 1001744D 8BF0 MOV ESI,EAX
* 1001744F 0F84 6C020000 JE Ags.100176C1
* 10017455 8B2D 50A10210 MOV EBP,DWORD PTR DS:[<&AoiLib._AoiStrin>; AoiLib._AoiString1to2Byte@8
* 1001745B EB 03 JMP SHORT Ags.10017460
* 1001745D 8D49 00 LEA ECX,DWORD PTR DS:[ECX]
* 10017460 57 PUSH EDI
* 10017461 FF15 90A00210 CALL DWORD PTR DS:[<&AoiLib._AoiString2B>; AoiLib._AoiString2ByteIs@4
* 10017467 85C0 TEST EAX,EAX
* 10017469 0F84 99010000 JE Ags.10017608
* 1001746F 8A0F MOV CL,BYTE PTR DS:[EDI]
* 10017471 880E MOV BYTE PTR DS:[ESI],CL
* 10017473 8A57 01 MOV DL,BYTE PTR DS:[EDI+0x1]
* 10017476 83C7 01 ADD EDI,0x1
* 10017479 83C6 01 ADD ESI,0x1
* 1001747C 8816 MOV BYTE PTR DS:[ESI],DL
* 1001747E 83C6 01 ADD ESI,0x1
* 10017481 83C7 01 ADD EDI,0x1
* 10017484 83C3 02 ADD EBX,0x2
* 10017487 E9 F8010000 JMP Ags.10017684
* 1001748C 803E 3C CMP BYTE PTR DS:[ESI],0x3C
* 1001748F 74 0D JE SHORT Ags.1001749E
* 10017491 83C6 01 ADD ESI,0x1
* 10017494 834424 24 01 ADD DWORD PTR SS:[ESP+0x24],0x1
* 10017499 ^E9 74FFFFFF JMP Ags.10017412
* 1001749E 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 100174A1 83C6 01 ADD ESI,0x1
* 100174A4 84C0 TEST AL,AL
* 100174A6 ^0F84 6AFFFFFF JE Ags.10017416
* 100174AC 8D6424 00 LEA ESP,DWORD PTR SS:[ESP]
* 100174B0 3C 3E CMP AL,0x3E
* 100174B2 ^0F84 5AFFFFFF JE Ags.10017412
* 100174B8 0FBEC0 MOVSX EAX,AL
* 100174BB 83C0 B5 ADD EAX,-0x4B
* 100174BE 83F8 2A CMP EAX,0x2A
* 100174C1 77 52 JA SHORT Ags.10017515
* 100174C3 0FB680 70770110 MOVZX EAX,BYTE PTR DS:[EAX+0x10017770]
* 100174CA FF2485 50770110 JMP DWORD PTR DS:[EAX*4+0x10017750]
* 100174D1 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 100174D4 83C6 01 ADD ESI,0x1
* 100174D7 33C9 XOR ECX,ECX
* 100174D9 3C 30 CMP AL,0x30
* 100174DB 7C 1A JL SHORT Ags.100174F7
* 100174DD 8D49 00 LEA ECX,DWORD PTR DS:[ECX]
* 100174E0 3C 39 CMP AL,0x39
* 100174E2 7F 13 JG SHORT Ags.100174F7
* 100174E4 83C6 01 ADD ESI,0x1
* 100174E7 0FBED0 MOVSX EDX,AL
* 100174EA 8A06 MOV AL,BYTE PTR DS:[ESI]
* 100174EC 3C 30 CMP AL,0x30
* 100174EE 8D0C89 LEA ECX,DWORD PTR DS:[ECX+ECX*4]
* 100174F1 8D4C4A D0 LEA ECX,DWORD PTR DS:[EDX+ECX*2-0x30]
* 100174F5 ^7D E9 JGE SHORT Ags.100174E0
* 100174F7 6A 0A PUSH 0xA
* 100174F9 53 PUSH EBX
* 100174FA 51 PUSH ECX
* 100174FB FF15 88A00210 CALL DWORD PTR DS:[<&AoiLib._AoiMathLimi>; AoiLib._AoiMathLimit@12
* 10017501 8B0485 08CB0210 MOV EAX,DWORD PTR DS:[EAX*4+0x1002CB08]
* 10017508 8945 30 MOV DWORD PTR SS:[EBP+0x30],EAX
* 1001750B EB 0B JMP SHORT Ags.10017518
* 1001750D C74424 28 010000>MOV DWORD PTR SS:[ESP+0x28],0x1
* 10017515 83C6 01 ADD ESI,0x1
* 10017518 8A06 MOV AL,BYTE PTR DS:[ESI]
* 1001751A 84C0 TEST AL,AL
* 1001751C ^75 92 JNZ SHORT Ags.100174B0
* 1001751E ^E9 F3FEFFFF JMP Ags.10017416
* 10017523 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 10017526 83C6 01 ADD ESI,0x1
* 10017529 33C9 XOR ECX,ECX
* 1001752B 3C 30 CMP AL,0x30
* 1001752D C74424 1C 010000>MOV DWORD PTR SS:[ESP+0x1C],0x1
* 10017535 ^7C E1 JL SHORT Ags.10017518
* 10017537 3C 39 CMP AL,0x39
* 10017539 7F 13 JG SHORT Ags.1001754E
* 1001753B 83C6 01 ADD ESI,0x1
* 1001753E 0FBED0 MOVSX EDX,AL
* 10017541 8A06 MOV AL,BYTE PTR DS:[ESI]
* 10017543 3C 30 CMP AL,0x30
* 10017545 8D0C89 LEA ECX,DWORD PTR DS:[ECX+ECX*4]
* 10017548 8D4C4A D0 LEA ECX,DWORD PTR DS:[EDX+ECX*2-0x30]
* 1001754C ^7D E9 JGE SHORT Ags.10017537
* 1001754E 3BCB CMP ECX,EBX
* 10017550 ^74 C6 JE SHORT Ags.10017518
* 10017552 894C24 2C MOV DWORD PTR SS:[ESP+0x2C],ECX
* 10017556 ^EB C0 JMP SHORT Ags.10017518
* 10017558 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 1001755B 83C6 01 ADD ESI,0x1
* 1001755E 3C 30 CMP AL,0x30
* 10017560 ^7C B6 JL SHORT Ags.10017518
* 10017562 3C 39 CMP AL,0x39
* 10017564 ^7F B2 JG SHORT Ags.10017518
* 10017566 0FBEC0 MOVSX EAX,AL
* 10017569 66:8B0C45 94CA02>MOV CX,WORD PTR DS:[EAX*2+0x1002CA94]
* 10017571 66:81C9 0080 OR CX,0x8000
* 10017576 0FB7D1 MOVZX EDX,CX
* 10017579 895424 20 MOV DWORD PTR SS:[ESP+0x20],EDX
* 1001757D ^EB 96 JMP SHORT Ags.10017515
* 1001757F 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 10017582 83C6 01 ADD ESI,0x1
* 10017585 3C 30 CMP AL,0x30
* 10017587 ^7C 8F JL SHORT Ags.10017518
* 10017589 3C 39 CMP AL,0x39
* 1001758B ^7F 8B JG SHORT Ags.10017518
* 1001758D 0FBEC0 MOVSX EAX,AL
* 10017590 0FB70C45 94CA021>MOVZX ECX,WORD PTR DS:[EAX*2+0x1002CA94]
* 10017598 894C24 20 MOV DWORD PTR SS:[ESP+0x20],ECX
* 1001759C ^E9 74FFFFFF JMP Ags.10017515
* 100175A1 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 100175A4 83C6 01 ADD ESI,0x1
* 100175A7 3C 30 CMP AL,0x30
* 100175A9 ^0F8C 69FFFFFF JL Ags.10017518
* 100175AF 3C 39 CMP AL,0x39
* 100175B1 ^0F8F 61FFFFFF JG Ags.10017518
* 100175B7 0FBED0 MOVSX EDX,AL
* 100175BA 0FB70455 94CA021>MOVZX EAX,WORD PTR DS:[EDX*2+0x1002CA94]
* 100175C2 894424 30 MOV DWORD PTR SS:[ESP+0x30],EAX
* 100175C6 ^E9 4AFFFFFF JMP Ags.10017515
* 100175CB 8A46 01 MOV AL,BYTE PTR DS:[ESI+0x1]
* 100175CE 83C6 01 ADD ESI,0x1
* 100175D1 33C9 XOR ECX,ECX
* 100175D3 3C 30 CMP AL,0x30
* 100175D5 ^0F8C 3DFFFFFF JL Ags.10017518
* 100175DB EB 03 JMP SHORT Ags.100175E0
* 100175DD 8D49 00 LEA ECX,DWORD PTR DS:[ECX]
* 100175E0 3C 39 CMP AL,0x39
* 100175E2 7F 13 JG SHORT Ags.100175F7
* 100175E4 83C6 01 ADD ESI,0x1
* 100175E7 0FBED0 MOVSX EDX,AL
* 100175EA 8A06 MOV AL,BYTE PTR DS:[ESI]
* 100175EC 3C 30 CMP AL,0x30
* 100175EE 8D0C89 LEA ECX,DWORD PTR DS:[ECX+ECX*4]
* 100175F1 8D4C4A D0 LEA ECX,DWORD PTR DS:[EDX+ECX*2-0x30]
* 100175F5 ^7D E9 JGE SHORT Ags.100175E0
* 100175F7 3BCB CMP ECX,EBX
* 100175F9 ^0F84 19FFFFFF JE Ags.10017518
* 100175FF 894C24 34 MOV DWORD PTR SS:[ESP+0x34],ECX
* 10017603 ^E9 10FFFFFF JMP Ags.10017518
* 10017608 8A07 MOV AL,BYTE PTR DS:[EDI]
* 1001760A 3C 3C CMP AL,0x3C
* 1001760C 75 2A JNZ SHORT Ags.10017638
* 1001760E 83C7 01 ADD EDI,0x1
* 10017611 8806 MOV BYTE PTR DS:[ESI],AL
* 10017613 8A07 MOV AL,BYTE PTR DS:[EDI]
* 10017615 83C6 01 ADD ESI,0x1
* 10017618 84C0 TEST AL,AL
* 1001761A 74 16 JE SHORT Ags.10017632
* 1001761C 8D6424 00 LEA ESP,DWORD PTR SS:[ESP]
* 10017620 3C 3E CMP AL,0x3E
* 10017622 74 0E JE SHORT Ags.10017632
* 10017624 83C7 01 ADD EDI,0x1
* 10017627 8806 MOV BYTE PTR DS:[ESI],AL
* 10017629 8A07 MOV AL,BYTE PTR DS:[EDI]
* 1001762B 83C6 01 ADD ESI,0x1
* 1001762E 84C0 TEST AL,AL
* 10017630 ^75 EE JNZ SHORT Ags.10017620
* 10017632 8A07 MOV AL,BYTE PTR DS:[EDI]
* 10017634 8806 MOV BYTE PTR DS:[ESI],AL
* 10017636 EB 46 JMP SHORT Ags.1001767E
* 10017638 3C 0A CMP AL,0xA
* 1001763A 74 27 JE SHORT Ags.10017663
* 1001763C 3C 7C CMP AL,0x7C
* 1001763E 74 23 JE SHORT Ags.10017663
* 10017640 837C24 28 00 CMP DWORD PTR SS:[ESP+0x28],0x0
* 10017645 74 0F JE SHORT Ags.10017656
* 10017647 50 PUSH EAX
* 10017648 56 PUSH ESI
* 10017649 FFD5 CALL EBP
* 1001764B 83C6 02 ADD ESI,0x2
* 1001764E 83C7 01 ADD EDI,0x1
* 10017651 83C3 02 ADD EBX,0x2
* 10017654 EB 2E JMP SHORT Ags.10017684
* 10017656 8806 MOV BYTE PTR DS:[ESI],AL
* 10017658 83C6 01 ADD ESI,0x1
* 1001765B 83C7 01 ADD EDI,0x1
* 1001765E 83C3 01 ADD EBX,0x1
* 10017661 EB 21 JMP SHORT Ags.10017684
* 10017663 395C24 14 CMP DWORD PTR SS:[ESP+0x14],EBX
* 10017667 73 04 JNB SHORT Ags.1001766D
* 10017669 895C24 14 MOV DWORD PTR SS:[ESP+0x14],EBX
* 1001766D 837C24 1C 00 CMP DWORD PTR SS:[ESP+0x1C],0x0
* 10017672 74 3D JE SHORT Ags.100176B1
* 10017674 33DB XOR EBX,EBX
* 10017676 834424 18 01 ADD DWORD PTR SS:[ESP+0x18],0x1
* 1001767B C606 0A MOV BYTE PTR DS:[ESI],0xA
* 1001767E 83C6 01 ADD ESI,0x1
* 10017681 83C7 01 ADD EDI,0x1
* 10017684 3B5C24 2C CMP EBX,DWORD PTR SS:[ESP+0x2C]
* 10017688 72 1E JB SHORT Ags.100176A8
* 1001768A 395C24 14 CMP DWORD PTR SS:[ESP+0x14],EBX
* 1001768E 73 04 JNB SHORT Ags.10017694
* 10017690 895C24 14 MOV DWORD PTR SS:[ESP+0x14],EBX
* 10017694 837C24 1C 00 CMP DWORD PTR SS:[ESP+0x1C],0x0
* 10017699 74 16 JE SHORT Ags.100176B1
* 1001769B 834424 18 01 ADD DWORD PTR SS:[ESP+0x18],0x1
* 100176A0 33DB XOR EBX,EBX
* 100176A2 C606 0A MOV BYTE PTR DS:[ESI],0xA
* 100176A5 83C6 01 ADD ESI,0x1
* 100176A8 803F 00 CMP BYTE PTR DS:[EDI],0x0
* 100176AB ^0F85 AFFDFFFF JNZ Ags.10017460
* 100176B1 395C24 14 CMP DWORD PTR SS:[ESP+0x14],EBX
* 100176B5 8B6C24 3C MOV EBP,DWORD PTR SS:[ESP+0x3C]
* 100176B9 73 04 JNB SHORT Ags.100176BF
* 100176BB 895C24 14 MOV DWORD PTR SS:[ESP+0x14],EBX
* 100176BF 33DB XOR EBX,EBX
* 100176C1 8B4C24 18 MOV ECX,DWORD PTR SS:[ESP+0x18]
* 100176C5 83C1 01 ADD ECX,0x1
* 100176C8 807E FF 0A CMP BYTE PTR DS:[ESI-0x1],0xA
* 100176CC 75 03 JNZ SHORT Ags.100176D1
* 100176CE 83C6 FF ADD ESI,-0x1
* 100176D1 C606 00 MOV BYTE PTR DS:[ESI],0x0
* 100176D4 8B45 30 MOV EAX,DWORD PTR SS:[EBP+0x30]
* 100176D7 8BD0 MOV EDX,EAX
* 100176D9 0FAFC1 IMUL EAX,ECX
* 100176DC 0FAF5424 14 IMUL EDX,DWORD PTR SS:[ESP+0x14]
* 100176E1 8945 10 MOV DWORD PTR SS:[EBP+0x10],EAX
* 100176E4 A1 BC3F0310 MOV EAX,DWORD PTR DS:[0x10033FBC]
* 100176E9 D1EA SHR EDX,1
* 100176EB 8955 0C MOV DWORD PTR SS:[EBP+0xC],EDX
* 100176EE 8B88 44010000 MOV ECX,DWORD PTR DS:[EAX+0x144]
* 100176F4 3999 28010000 CMP DWORD PTR DS:[ECX+0x128],EBX
* 100176FA 74 19 JE SHORT Ags.10017715
* 100176FC 8B5424 30 MOV EDX,DWORD PTR SS:[ESP+0x30]
* 10017700 8B4424 20 MOV EAX,DWORD PTR SS:[ESP+0x20]
* 10017704 52 PUSH EDX
* 10017705 50 PUSH EAX
* 10017706 8B4424 3C MOV EAX,DWORD PTR SS:[ESP+0x3C]
* 1001770A 55 PUSH EBP
* 1001770B E8 90F5FFFF CALL Ags.10016CA0 ; jichi: the paint function, bad text address in arg1 + 0x34, good text in arg7
* 10017710 83C4 0C ADD ESP,0xC
* 10017713 EB 1B JMP SHORT Ags.10017730
* 10017715 8B4C24 30 MOV ECX,DWORD PTR SS:[ESP+0x30]
* 10017719 8B5424 20 MOV EDX,DWORD PTR SS:[ESP+0x20]
* 1001771D 8B45 34 MOV EAX,DWORD PTR SS:[EBP+0x34]
* 10017720 51 PUSH ECX
* 10017721 8B4C24 38 MOV ECX,DWORD PTR SS:[ESP+0x38]
* 10017725 52 PUSH EDX
* 10017726 50 PUSH EAX
* 10017727 55 PUSH EBP
* 10017728 E8 33F9FFFF CALL Ags.10017060
* 1001772D 83C4 10 ADD ESP,0x10
* 10017730 8B4D 30 MOV ECX,DWORD PTR SS:[EBP+0x30]
* 10017733 8BC1 MOV EAX,ECX
* 10017735 99 CDQ
* 10017736 2BC2 SUB EAX,EDX
* 10017738 5F POP EDI
* 10017739 D1F8 SAR EAX,1
* 1001773B 5E POP ESI
* 1001773C 8945 1C MOV DWORD PTR SS:[EBP+0x1C],EAX
* 1001773F 894D 20 MOV DWORD PTR SS:[EBP+0x20],ECX
* 10017742 5D POP EBP
* 10017743 B8 01000000 MOV EAX,0x1
* 10017748 5B POP EBX
* 10017749 83C4 28 ADD ESP,0x28
* 1001774C C3 RETN
* 1001774D 8D49 00 LEA ECX,DWORD PTR DS:[ECX]
* 10017750 7F 75 JG SHORT Ags.100177C7
* 10017752 0110 ADD DWORD PTR DS:[EAX],EDX
* 10017754 CB RETF ; Far return
* 10017755 75 01 JNZ SHORT Ags.10017758
* 10017757 1058 75 ADC BYTE PTR DS:[EAX+0x75],BL
* 1001775A 0110 ADD DWORD PTR DS:[EAX],EDX
* 1001775C A1 75011023 MOV EAX,DWORD PTR DS:[0x23100175]
* 10017761 75 01 JNZ SHORT Ags.10017764
* 10017763 10D1 ADC CL,DL
* 10017765 74 01 JE SHORT Ags.10017768
* 10017767 100D 75011015 ADC BYTE PTR DS:[0x15100175],CL
* 1001776D 75 01 JNZ SHORT Ags.10017770
* 1001776F 1000 ADC BYTE PTR DS:[EAX],AL
* 10017771 0107 ADD DWORD PTR DS:[EDI],EAX
* 10017773 07 POP ES ; Modification of segment register
* 10017774 07 POP ES ; Modification of segment register
* 10017775 07 POP ES ; Modification of segment register
* 10017776 07 POP ES ; Modification of segment register
* 10017777 07 POP ES ; Modification of segment register
* 10017778 07 POP ES ; Modification of segment register
* 10017779 07 POP ES ; Modification of segment register
* 1001777A 07 POP ES ; Modification of segment register
* 1001777B 07 POP ES ; Modification of segment register
* 1001777C 07 POP ES ; Modification of segment register
* 1001777D 07 POP ES ; Modification of segment register
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x8b,0x44,0x24, 0x3c, // 10017706 8b4424 3c mov eax,dword ptr ss:[esp+0x3c]
0x55, // 1001770a 55 push ebp
0xe8, XX4, // 1001770b e8 90f5ffff call ags.10016ca0 ; jichi: the paint function, bad text address in arg1 + 0x34, good text in arg7
0x83,0xc4, 0x0c, // 10017710 83c4 0c add esp,0xc
0xeb, 0x1b // 10017713 eb 1b jmp short ags.10017730
};
enum { addr_offset = 0x1001770b - 0x10017706 }; // == 5
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return false;
HookParam hp;
hp.address=addr;
hp.type=EMBED_ABLE|HOOK_EMPTY;
hp.hook_before=Private::hookBefore;
auto succ=NewHook(hp,"AgsPatchA");
hp.address+=5;
hp.hook_before=Private::hookAfter;
succ|=NewHook(hp,"AgsPatchA");
return succ;
}
} // namespace AgsPatchA
bool InsertSystemAoiStatic(HMODULE hModule, bool wideChar) // attach scenario
{
ULONG addr = findAoiProc(hModule, "AgsSpriteCreateText", 1);
if (!addr) {
ConsoleOutput("SystemAoiStatic: function found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.text_fun = SpecialHookSystemAoi; //其实已无效在before的lstrim里有一样的功能。但保留。
hp.type=EMBED_ABLE|USING_STRING;//|EMBED_AFTER_OVERWRITE;
//hp.type |= NO_CONTEXT|USING_SPLIT|SPLIT_INDIRECT;
ConsoleOutput("INSERT static SystemAoi");
auto succ=false;
if (wideChar){
hp.type |=CODEC_UTF16 ;
hp.hook_before=beforeAgsSpriteCreateTextW;
hp.hook_after=afterAgsSpriteCreateTextW;
succ|=NewHook(hp, "SystemAoiW");
ULONG addr = findAoiProc(hModule, "AgsSpriteCreateTextEx", 1);
if (addr) {
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2);
hp.type=CODEC_UTF16|EMBED_ABLE;//|EMBED_AFTER_OVERWRITE;
hp.hook_before=beforeAgsSpriteCreateTextExW;
hp.hook_after=afterAgsSpriteCreateTextExW;
succ|=NewHook(hp, "SystemAoiExW");
}
return succ;
}
else{
hp.hook_before=beforeAgsSpriteCreateTextA;
hp.hook_after=afterAgsSpriteCreateTextA;
hp.hook_font=F_DrawTextExA;
if(AgsPatchA::attach(processStartAddress,processStopAddress)==false)
hp.type|=EMBED_DYNA_SJIS;
succ|=NewHook(hp, "SystemAoiA");
}
return succ;
}
} // unnamed namespace
bool InsertSystemAoiHook() // this function always returns true
{
HMODULE hModule = ::GetModuleHandleA("Ags.dll");
bool wideChar = true;
if (hModule) // Aoi <= 3
wideChar = false;
else { // Aoi >= 4
hModule = ::GetModuleHandleA("Ags5.dll");
if (!hModule)
hModule = ::GetModuleHandleA("Ags4.dll");
}
return hModule && InsertSystemAoiStatic(hModule, wideChar)
|| InsertSystemAoiDynamic();
}
bool SystemAoi::attach_function() {
return InsertSystemAoiHook();
}