mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-24 22:35:36 +08:00
636 lines
24 KiB
C++
636 lines
24 KiB
C++
#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()||_;
|
||
}
|