2024-02-07 20:59:24 +08:00

636 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

#include"GXP.h"
/**
* jichi 5/11/2014: Hook to the beginning of a function
*
* Executable description shows "AVGEngineV2"
*
* Cached wrong text can also be found in GetGlyphOutlineW.
*
* 4/27/2015 old logic:
* 1. find the following location
* 00A78144 66:833C70 00 CMP WORD PTR DS:[EAX+ESI*2],0x0
* i.e. 0x66833C7000
* There are several matches, the first one is used.
* 2. find the first push operation after it
* 3. find the function call after push, and hook to it
* The text is in the arg1, which is character by character
*
* But in the new game since ウルスラグ<E383A9> there the function call is not immediately after 0x66833C7000 any more.
* My own way to find the function to hook is as follows:
* 1. find the following location
* 00A78144 66:833C70 00 CMP WORD PTR DS:[EAX+ESI*2],0x0
* i.e. 0x66833C7000
* There are several matches, the first one is used.
* 2. Use Ollydbg to debug step by step until the first function call is encountered
* Then, the text character is directly on the stack
*
* Here's an example of Demonion II (reladdr = 0x18c540):
* The text is displayed character by character.
* sub_58C540 proc near
* arg_0 = dword ptr 8 // LPCSTR with 1 character
*
* 0138C540 /$ 55 PUSH EBP
* 0138C541 |. 8BEC MOV EBP,ESP
* 0138C543 |. 83E4 F8 AND ESP,0xFFFFFFF8
* 0138C546 |. 8B43 0C MOV EAX,DWORD PTR DS:[EBX+0xC]
* 0138C549 |. 83EC 08 SUB ESP,0x8
* 0138C54C |. 56 PUSH ESI
* 0138C54D |. 57 PUSH EDI
* 0138C54E |. 85C0 TEST EAX,EAX
* 0138C550 |. 75 04 JNZ SHORT demonion.0138C556
* 0138C552 |. 33F6 XOR ESI,ESI
* 0138C554 |. EB 18 JMP SHORT demonion.0138C56E
* 0138C556 |> 8B4B 14 MOV ECX,DWORD PTR DS:[EBX+0x14]
* 0138C559 |. 2BC8 SUB ECX,EAX
* 0138C55B |. B8 93244992 MOV EAX,0x92492493
* 0138C560 |. F7E9 IMUL ECX
* 0138C562 |. 03D1 ADD EDX,ECX
* 0138C564 |. C1FA 04 SAR EDX,0x4
* 0138C567 |. 8BF2 MOV ESI,EDX
* 0138C569 |. C1EE 1F SHR ESI,0x1F
* 0138C56C |. 03F2 ADD ESI,EDX
* 0138C56E |> 8B7B 10 MOV EDI,DWORD PTR DS:[EBX+0x10]
* 0138C571 |. 8BCF MOV ECX,EDI
* 0138C573 |. 2B4B 0C SUB ECX,DWORD PTR DS:[EBX+0xC]
* 0138C576 |. B8 93244992 MOV EAX,0x92492493
* 0138C57B |. F7E9 IMUL ECX
* 0138C57D |. 03D1 ADD EDX,ECX
* 0138C57F |. C1FA 04 SAR EDX,0x4
* 0138C582 |. 8BC2 MOV EAX,EDX
* 0138C584 |. C1E8 1F SHR EAX,0x1F
* 0138C587 |. 03C2 ADD EAX,EDX
* 0138C589 |. 3BC6 CMP EAX,ESI
* 0138C58B |. 73 2F JNB SHORT demonion.0138C5BC
* 0138C58D |. C64424 08 00 MOV BYTE PTR SS:[ESP+0x8],0x0
* 0138C592 |. 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+0x8]
* 0138C596 |. 8B5424 08 MOV EDX,DWORD PTR SS:[ESP+0x8]
* 0138C59A |. 51 PUSH ECX
* 0138C59B |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+0x8]
* 0138C59E |. 52 PUSH EDX
* 0138C59F |. B8 01000000 MOV EAX,0x1
* 0138C5A4 |. 8BD7 MOV EDX,EDI
* 0138C5A6 |. E8 F50E0000 CALL demonion.0138D4A0
* 0138C5AB |. 83C4 08 ADD ESP,0x8
* 0138C5AE |. 83C7 1C ADD EDI,0x1C
* 0138C5B1 |. 897B 10 MOV DWORD PTR DS:[EBX+0x10],EDI
* 0138C5B4 |. 5F POP EDI
* 0138C5B5 |. 5E POP ESI
* 0138C5B6 |. 8BE5 MOV ESP,EBP
* 0138C5B8 |. 5D POP EBP
* 0138C5B9 |. C2 0400 RETN 0x4
* 0138C5BC |> 397B 0C CMP DWORD PTR DS:[EBX+0xC],EDI
* 0138C5BF |. 76 05 JBE SHORT demonion.0138C5C6
* 0138C5C1 |. E8 1B060D00 CALL demonion.0145CBE1
* 0138C5C6 |> 8B03 MOV EAX,DWORD PTR DS:[EBX]
* 0138C5C8 |. 57 PUSH EDI ; /Arg4
* 0138C5C9 |. 50 PUSH EAX ; |Arg3
* 0138C5CA |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] ; |
* 0138C5CD |. 50 PUSH EAX ; |Arg2
* 0138C5CE |. 8D4C24 14 LEA ECX,DWORD PTR SS:[ESP+0x14] ; |
* 0138C5D2 |. 51 PUSH ECX ; |Arg1
* 0138C5D3 |. 8BC3 MOV EAX,EBX ; |
* 0138C5D5 |. E8 D6010000 CALL demonion.0138C7B0 ; \demonion.0138C7B0
* 0138C5DA |. 5F POP EDI
* 0138C5DB |. 5E POP ESI
* 0138C5DC |. 8BE5 MOV ESP,EBP
* 0138C5DE |. 5D POP EBP
* 0138C5DF \. C2 0400 RETN 0x4
*
* 4/26/2015 ウルスラグ<E383A9> * base = 0xa30000, old hook addr = 0xbe6360
*
* 00A7813A EB 02 JMP SHORT .00A7813E
* 00A7813C 8BC7 MOV EAX,EDI
* 00A7813E 8BB3 E4020000 MOV ESI,DWORD PTR DS:[EBX+0x2E4]
* 00A78144 66:833C70 00 CMP WORD PTR DS:[EAX+ESI*2],0x0 ; jich: here's the first found segment
* 00A78149 74 36 JE SHORT .00A78181
* 00A7814B 837F 14 08 CMP DWORD PTR DS:[EDI+0x14],0x8
* 00A7814F 72 08 JB SHORT .00A78159
* 00A78151 8B07 MOV EAX,DWORD PTR DS:[EDI]
*
* 00A7883A 24 3C AND AL,0x3C
* 00A7883C 50 PUSH EAX
* 00A7883D C74424 4C 000000>MOV DWORD PTR SS:[ESP+0x4C],0x0
* 00A78845 0F5B ??? ; Unknown command
* 00A78847 C9 LEAVE
* 00A78848 F3:0F114424 44 MOVSS DWORD PTR SS:[ESP+0x44],XMM0
* 00A7884E F3:0F114C24 48 MOVSS DWORD PTR SS:[ESP+0x48],XMM1
* 00A78854 E8 37040000 CALL .00A78C90 ; jichi: here's the target function to hook to, text char on the stack[0]
* 00A78859 A1 888EDD00 MOV EAX,DWORD PTR DS:[0xDD8E88]
* 00A7885E A8 01 TEST AL,0x1
* 00A78860 75 30 JNZ SHORT .00A78892
* 00A78862 83C8 01 OR EAX,0x1
* 00A78865 A3 888EDD00 MOV DWORD PTR DS:[0xDD8E88],EAX
*
* Here's the new function call:
* 00A78C8A CC INT3
* 00A78C8B CC INT3
* 00A78C8C CC INT3
* 00A78C8D CC INT3
* 00A78C8E CC INT3
* 00A78C8F CC INT3
* 00A78C90 55 PUSH EBP
* 00A78C91 8BEC MOV EBP,ESP
* 00A78C93 56 PUSH ESI
* 00A78C94 8BF1 MOV ESI,ECX
* 00A78C96 57 PUSH EDI
* 00A78C97 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+0x8]
* 00A78C9A 8B4E 04 MOV ECX,DWORD PTR DS:[ESI+0x4]
* 00A78C9D 3BF9 CMP EDI,ECX
* 00A78C9F 73 76 JNB SHORT .00A78D17
* 00A78CA1 8B06 MOV EAX,DWORD PTR DS:[ESI]
* 00A78CA3 3BC7 CMP EAX,EDI
* 00A78CA5 77 70 JA SHORT .00A78D17
* 00A78CA7 2BF8 SUB EDI,EAX
* 00A78CA9 B8 93244992 MOV EAX,0x92492493
* 00A78CAE F7EF IMUL EDI
* 00A78CB0 03D7 ADD EDX,EDI
* 00A78CB2 C1FA 04 SAR EDX,0x4
* 00A78CB5 8BFA MOV EDI,EDX
* 00A78CB7 C1EF 1F SHR EDI,0x1F
* 00A78CBA 03FA ADD EDI,EDX
* 00A78CBC 3B4E 08 CMP ECX,DWORD PTR DS:[ESI+0x8]
* 00A78CBF 75 09 JNZ SHORT .00A78CCA
* 00A78CC1 6A 01 PUSH 0x1
* 00A78CC3 8BCE MOV ECX,ESI
* 00A78CC5 E8 36030000 CALL .00A79000
* 00A78CCA 8B56 04 MOV EDX,DWORD PTR DS:[ESI+0x4]
* 00A78CCD 8D0CFD 00000000 LEA ECX,DWORD PTR DS:[EDI*8]
* 00A78CD4 2BCF SUB ECX,EDI
* 00A78CD6 8B3E MOV EDI,DWORD PTR DS:[ESI]
* 00A78CD8 85D2 TEST EDX,EDX
* 00A78CDA 74 7B JE SHORT .00A78D57
* 00A78CDC 66:8B048F MOV AX,WORD PTR DS:[EDI+ECX*4]
* 00A78CE0 66:8902 MOV WORD PTR DS:[EDX],AX
* 00A78CE3 8B448F 04 MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x4]
* 00A78CE7 8942 04 MOV DWORD PTR DS:[EDX+0x4],EAX
* 00A78CEA 8B448F 08 MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x8]
* 00A78CEE 8942 08 MOV DWORD PTR DS:[EDX+0x8],EAX
* 00A78CF1 8B448F 0C MOV EAX,DWORD PTR DS:[EDI+ECX*4+0xC]
* 00A78CF5 8942 0C MOV DWORD PTR DS:[EDX+0xC],EAX
* 00A78CF8 C742 10 00000000 MOV DWORD PTR DS:[EDX+0x10],0x0
* 00A78CFF 8B448F 14 MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x14]
* 00A78D03 8942 14 MOV DWORD PTR DS:[EDX+0x14],EAX
* 00A78D06 8A448F 18 MOV AL,BYTE PTR DS:[EDI+ECX*4+0x18]
* 00A78D0A 8842 18 MOV BYTE PTR DS:[EDX+0x18],AL
* 00A78D0D 8346 04 1C ADD DWORD PTR DS:[ESI+0x4],0x1C
* 00A78D11 5F POP EDI
* 00A78D12 5E POP ESI
* 00A78D13 5D POP EBP
* 00A78D14 C2 0400 RETN 0x4
* 00A78D17 3B4E 08 CMP ECX,DWORD PTR DS:[ESI+0x8]
* 00A78D1A 75 09 JNZ SHORT .00A78D25
* 00A78D1C 6A 01 PUSH 0x1
* 00A78D1E 8BCE MOV ECX,ESI
* 00A78D20 E8 DB020000 CALL .00A79000
* 00A78D25 8B4E 04 MOV ECX,DWORD PTR DS:[ESI+0x4]
* 00A78D28 85C9 TEST ECX,ECX
* 00A78D2A 74 2B JE SHORT .00A78D57
* 00A78D2C 66:8B07 MOV AX,WORD PTR DS:[EDI]
* 00A78D2F 66:8901 MOV WORD PTR DS:[ECX],AX
* 00A78D32 8B47 04 MOV EAX,DWORD PTR DS:[EDI+0x4]
* 00A78D35 8941 04 MOV DWORD PTR DS:[ECX+0x4],EAX
* 00A78D38 8B47 08 MOV EAX,DWORD PTR DS:[EDI+0x8]
* 00A78D3B 8941 08 MOV DWORD PTR DS:[ECX+0x8],EAX
* 00A78D3E 8B47 0C MOV EAX,DWORD PTR DS:[EDI+0xC]
* 00A78D41 8941 0C MOV DWORD PTR DS:[ECX+0xC],EAX
* 00A78D44 C741 10 00000000 MOV DWORD PTR DS:[ECX+0x10],0x0
* 00A78D4B 8B47 14 MOV EAX,DWORD PTR DS:[EDI+0x14]
* 00A78D4E 8941 14 MOV DWORD PTR DS:[ECX+0x14],EAX
* 00A78D51 8A47 18 MOV AL,BYTE PTR DS:[EDI+0x18]
* 00A78D54 8841 18 MOV BYTE PTR DS:[ECX+0x18],AL
* 00A78D57 8346 04 1C ADD DWORD PTR DS:[ESI+0x4],0x1C
* 00A78D5B 5F POP EDI
* 00A78D5C 5E POP ESI
* 00A78D5D 5D POP EBP
* 00A78D5E C2 0400 RETN 0x4
* 00A78D61 CC INT3
* 00A78D62 CC INT3
* 00A78D63 CC INT3
* 00A78D64 CC INT3
* 00A78D65 CC INT3
*/
static bool InsertGXP1Hook()
{
union {
DWORD i;
DWORD *id;
BYTE *ib;
};
for (i = processStartAddress + 0x1000; i < processStopAddress - 4; i++) {
// jichi example:
// 00A78144 66:833C70 00 CMP WORD PTR DS:[EAX+ESI*2],0x0
//find cmp word ptr [esi*2+eax],0
if (*id != 0x703c8366)
continue;
i += 4;
if (*ib != 0)
continue;
i++;
DWORD j = i + 0x200;
j = j < (processStopAddress - 8) ? j : (processStopAddress - 8);
DWORD flag = false;
while (i < j) {
DWORD k = disasm(ib);
if (k == 0)
break;
if (k == 1 && (*ib & 0xf8) == 0x50) { // push reg
flag = true;
break;
}
i += k;
}
if (flag)
while (i < j) {
if (*ib == 0xe8) { // jichi: find first long call after the push operation
i++;
DWORD addr = *id + i + 4;
if (addr > processStartAddress && addr < processStopAddress) {
HookParam hp;
hp.address = addr;
//hp.type = CODEC_UTF16|DATA_INDIRECT;
hp.type = USING_STRING|CODEC_UTF16|DATA_INDIRECT|NO_CONTEXT|FIXING_SPLIT; // jichi 4/25/2015: Fixing split
hp.offset=get_stack(1);
//GROWL_DWORD3(hp.address, processStartAddress, hp.address - processStartAddress);
//DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec81); // zero
//DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec83); // zero
//DWORD call = Util::FindCallAndEntryAbs(hp.address, processStopAddress - processStartAddress, processStartAddress, 0xec8b55); // zero
//GROWL_DWORD3(call, processStartAddress, call - processStartAddress);
ConsoleOutput("INSERT GXP");
// jichi 5/13/2015: Disable hooking to GetGlyphOutlineW
// FIXME: GetGlyphOutlineW can extract name, but GXP cannot
ConsoleOutput("GXP: disable GDI hooks");
return NewHook(hp, "GXP");
}
}
i++;
}
}
//ConsoleOutput("Unknown GXP engine.");
ConsoleOutput("GXP: failed");
return false;
}
static bool InsertGXP2Hook()
{
// pattern = 0x0f5bc9f30f11442444f30f114c2448e8
const BYTE bytes[] = {
0x0f,0x5b, // 00A78845 0F5B ??? ; Unknown command
0xc9, // 00A78847 C9 LEAVE
0xf3,0x0f,0x11,0x44,0x24, 0x44, // 00A78848 F3:0F114424 44 MOVSS DWORD PTR SS:[ESP+0x44],XMM0
0xf3,0x0f,0x11,0x4c,0x24, 0x48, // 00A7884E F3:0F114C24 48 MOVSS DWORD PTR SS:[ESP+0x48],XMM1
0xe8 //37040000 // 00A78854 E8 37040000 CALL .00A78C90 ; jichi: here's the target function to hook to, text char on the stack[0]
};
enum { addr_offset = sizeof(bytes) - 1 }; // 0x00a78854 - 0x00a78845
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("GXP2: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.type = CODEC_UTF16|NO_CONTEXT|DATA_INDIRECT|FIXING_SPLIT|USING_STRING;
ConsoleOutput("INSERT GXP2");
ConsoleOutput("GXP: disable GDI hooks");
return NewHook(hp, "GXP2");
}
bool InsertGXPHook()
{
// GXP1 and GXP2 are harmless to each other
bool ok = InsertGXP1Hook();
ok = InsertGXP2Hook() || ok;
return ok;
}
#include"util/textunion.h"
namespace { // unnamed
ULONG moduleBaseAddress_; // saved only for debugging purposes
bool isBadText(LPCWSTR text)
{
return text[0] <= 127 || text[::wcslen(text) - 1] <= 127 // skip ascii text
|| ::wcschr(text, 0xff3f); // Skip system text containing: _
}
namespace ScenarioHook1 { // for old GXP1
namespace Private {
TextUnionW *arg_,
argValue_;
bool hookBefore(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
{
static std::wstring text_; // persistent storage, which makes this function not thread-safe
auto arg = (TextUnionW *)(s->stack[0] + 4); // arg1 + 0x4
if (!arg->isValid())
return 0;
auto text = arg->getText();
if (isBadText(text))
return 0;
std::wstring oldText = std::wstring(text);//,
wcscpy((LPWSTR)data1,oldText.c_str());*len=oldText.size()*2;
return 1;
// newText = EngineController::instance()->dispatchTextWSTD(oldText, role, reladdr);
// if (newText == oldText)
// return true;
// text_ = newText;
// arg_ = arg;
// argValue_ = *arg;
// arg->setText(text_);
// //if (arg->size)
// // hashes_.insert(Engine::hashWCharArray(arg->text, arg->size));
// return true;
}
void hook2a(hook_stack*s,void* data1, size_t len)
{
auto text_=new wchar_t[len/2+1];
auto n=std::wstring((LPWSTR)data1,len/2);
wcscpy(text_,n.c_str());
auto arg = (TextUnionW *)(s->stack[0] + 4); // arg1 + 0x4
arg_ = arg;
argValue_ = *arg;
arg->setText(text_);
//if (arg->size)
// hashes_.insert(Engine::hashWCharArray(arg->text, arg->size));
// return true;
}
bool hookAfter(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
{
if (arg_) {
*arg_ = argValue_;
arg_ = nullptr;
}
return 0;
}
} // namespace Private
/**
* Sample game: 塔の下のエクセルキトゥス体験版
* Executable description shows "AVGEngineV2"
*
* Debugging method: Find the fixed text address, and check when it is being modified
*
* Scenario caller, text in the struct of arg1 + 0x4.
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0xeb, 0x02, // 01313bb6 eb 02 jmp short trial.01313bba
0x8b,0xc5, // 01313bb8 8bc5 mov eax,ebp
0x8b,0x54,0x24, 0x18, // 01313bba 8b5424 18 mov edx,dword ptr ss:[esp+0x18]
0x8d,0x0c,0x51, // 01313bbe 8d0c51 lea ecx,dword ptr ds:[ecx+edx*2]
0x8d,0x1c,0x3f // 01313bc1 8d1c3f lea ebx,dword ptr ds:[edi+edi]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return addr;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr)
return addr;
//return winhook::hook_before(addr, Private::hookBefore);
int count = 0;
auto fun = [&count](ULONG addr) -> bool {
auto retaddr=addr+5;
if (*(DWORD *)retaddr!= 0x0c244c8a)
return true;
if (*(BYTE *)retaddr == 0x4f ||
(*(DWORD *)retaddr & 0x00ff00ff) == 0x0024008b) // skip truncated texts
return true;
HookParam hp;
hp.address=addr;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hook2a;
hp.type=EMBED_ABLE|CODEC_UTF16|USING_STRING;
hp.newlineseperator=L"%r";
hp.hook_font=F_GetGlyphOutlineW;
bool succ=NewHook(hp,"EmbedGXP");
hp.address=addr+5;
hp.hook_before=Private::hookAfter;
succ|=NewHook(hp,"EmbedGXP");
count+=1;
return succ; // replace all functions
};
MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
return count;
}
} // namespace ScenarioHook1
namespace ScenarioHook2 { // for new GXP2
namespace Private {
TextUnionW *arg_,
argValue_;
bool hookBefore(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
{
static std::wstring text_; // persistent storage, which makes this function not thread-safe
auto arg = (TextUnionW *)s->stack[0]; // arg1
if (!arg->isValid())
return 0;
auto text = arg->getText();
if (isBadText(text))
return 0;
std::wstring oldText = std::wstring(text);//,
wcscpy((LPWSTR)data1,oldText.c_str());*len=oldText.size()*2;
return 1;}
void hook2a(hook_stack*s,void* data1, size_t len)
{
auto text_=new wchar_t[len/2+1];
auto n=std::wstring((LPWSTR)data1,len/2);
wcscpy(text_,n.c_str());
auto arg = (TextUnionW *)s->stack[0]; // arg1 + 0x4
arg_ = arg;
argValue_ = *arg;
arg->setText(text_);
}
bool hookAfter(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
{
if (arg_) {
*arg_ = argValue_;
arg_ = nullptr;
}
return 0;
}
} // namespace Private
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x8d,0x04,0x3f, // 08159fd |. 8d043f lea eax,dword ptr ds:[edi+edi] ; jichi: edi *= 2 for wchar_t
0x50, // 0815a00 |. 50 push eax ; jichi: size
0x8d,0x04,0x4b, // 0815a01 |. 8d044b lea eax,dword ptr ds:[ebx+ecx*2]
0x50, // 0815a04 |. 50 push eax ; jichi: source text
0x52 // 0815a05 |. 52 push edx ; jichi: target text
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return addr;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr)
return addr;
//return winhook::hook_before(addr, Private::hookBefore);
int count = 0;
auto fun = [&count](ULONG addr) -> bool {
auto retaddr=addr+5;
if (*(WORD *)retaddr != 0x458a)
return true;
if (*(BYTE *)retaddr == 0xa1)
return true;
HookParam hp;
hp.address=addr;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hook2a;
hp.type=EMBED_ABLE|CODEC_UTF16|USING_STRING;
hp.newlineseperator=L"%r";
hp.hook_font=F_GetGlyphOutlineW;
bool succ=NewHook(hp,"EmbedGXP2");
hp.address=addr+5;
hp.hook_before=Private::hookAfter;
succ|=NewHook(hp,"EmbedGXP2");
count+=1;
return succ; // replace all functions
};
MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
return count;
}
} // namespace ScenarioHook2
/*
namespace PopupHook1 { // only for old GXP1 engine
namespace Private {
bool hookBefore(winhook::hook_stack *s)
{
static std::wstring text_; // persistent storage, which makes this function not thread-safe
auto arg = (TextUnionW *)(s->ecx + 0x1ec); // [ecx + 0x1ec]
if (!arg->isValid())
return true;
auto text = arg->getText();
if (isBadText(text))
return true;
auto retaddr = s->stack[0];
auto reladdr = retaddr - moduleBaseAddress_;
enum { role = Engine::OtherRole };
std::wstring oldText = std::wstring(text),
newText = EngineController::instance()->dispatchTextWSTD(oldText, role, reladdr);
if (newText == oldText)
return true;
text_ = newText;
arg->setText(text_);
return true;
}
} // Private
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x8b,0x86, 0xec,0x01,0x00,0x00, // 001092a9 8b86 ec010000 mov eax,dword ptr ds:[esi+0x1ec] ; jichi: text in eax
0xeb, 0x06, // 001092af eb 06 jmp short trial.001092b7
0x8d,0x86, 0xec,0x01,0x00,0x00, // 001092b1 8d86 ec010000 lea eax,dword ptr ds:[esi+0x1ec]
0x0f,0xb7,0x14,0x78, // 001092b7 0fb71478 movzx edx,word ptr ds:[eax+edi*2]
0x52 // 001092bb 52 push edx
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr)
return false;
return winhook::hook_before(addr, Private::hookBefore);
// Function called at runtime
//int count = 0;
//auto fun = [&count](ULONG addr) -> bool {
// auto before = std::bind(Private::hookBefore, addr + 5, std::placeholders::_1);
// count += winhook::hook_both(addr, before, Private::hookAfter);
// return true; // replace all functions
//};
//MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
//DOUT("call number =" << count);
//return count;
}
} // namespace PopupHook1
namespace OtherHook { // for all GXP engines
namespace Private {
bool hookBefore(winhook::hook_stack *s)
{
static std::wstring text_;
auto text = (LPCWSTR)s->stack[3]; // arg3
if (!text || !*text)
return true;
auto retaddr = s->stack[0];
auto reladdr = retaddr - moduleBaseAddress_;
enum { role = Engine::OtherRole };
std::wstring oldText = std::wstring(text),
newText = EngineController::instance()->dispatchTextWSTD(oldText, role, reladdr);
if (newText.empty() || oldText == newText)
return true;
strReplace(newText, L"%r", L"\n");
//newText.replace("%r", "\n");
text_ = newText;
s->stack[3] = (ULONG)text_.c_str();
return true;
}
} // Private
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x99, // 014d45ae 99 cdq
0x2b,0xc2, // 014d45af 2bc2 sub eax,edx
0xd1,0xf8, // 014d45b1 d1f8 sar eax,1
0x03 //,0xf0, // 014d45b3 03f0 add esi,eax
};
int count = 0;
auto fun = [&count](ULONG addr) -> bool {
count +=
(addr = MemDbg::findEnclosingAlignedFunction(addr))
&& winhook::hook_before(addr, Private::hookBefore);
return true;
};
MemDbg::iterFindBytes(fun, bytes, sizeof(bytes), startAddress, stopAddress);
DOUT("call number =" << count);
return count;
}
} // namespace OtherHook
*/
bool attach()
{
ULONG startAddress=processStartAddress, stopAddress=processStopAddress;
moduleBaseAddress_ = startAddress; // used to calculate reladdr for debug purposes
if (ScenarioHook2::attach(startAddress, stopAddress)) {
} else if (ScenarioHook1::attach(startAddress, stopAddress)) {
// (PopupHook1::attach(startAddress, stopAddress));
} else
return false;
// (OtherHook::attach(startAddress, stopAddress))
return true;
}
} // unnamed namespace
bool GXP::attach_function() {
auto _=InsertGXPHook();
return attach()||_;
}