636 lines
24 KiB
C++
Raw Normal View History

2024-02-07 20:59:24 +08:00
#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 <EFBFBD> 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 <EFBFBD> * 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()||_;
}