#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 悪魔娘�看板料理 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] が、それ�人間相手�話�� */ 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 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 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 ; write_string_overwrite(data,len,text); 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; } write_string_overwrite(data,len,text); 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; } return write_string_overwrite(data,len,text); } } // 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(); }