2024-11-02 15:49:09 +08:00
|
|
|
|
#include "ApricoT.h"
|
2024-02-07 20:59:24 +08:00
|
|
|
|
|
|
|
|
|
/********************************************************************************************
|
|
|
|
|
Apricot hook:
|
|
|
|
|
Game folder contains arc.a*.
|
|
|
|
|
This engine is heavily based on new DirectX interfaces.
|
|
|
|
|
I can't find a good place where text is clean and not repeating.
|
|
|
|
|
The game processes script encoded in UTF32-like format.
|
|
|
|
|
I reversed the parsing algorithm of the game and implemented it partially.
|
|
|
|
|
Only name and text data is needed.
|
|
|
|
|
|
|
|
|
|
********************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/** jichi 2/15/2015: ApricoT
|
|
|
|
|
*
|
|
|
|
|
* Sample game: イセカイ・ラヴァーズ<EFBFBD>体験版
|
|
|
|
|
* Issue of the old game is that it uses esp as split, and hence has relative address
|
|
|
|
|
*
|
|
|
|
|
* 00978100 5b pop ebx
|
|
|
|
|
* 00978101 83c4 2c add esp,0x2c
|
|
|
|
|
* 00978104 c2 0400 retn 0x4
|
|
|
|
|
* 00978107 33c0 xor eax,eax ; jichi: hook here
|
|
|
|
|
* 00978109 bb 03000000 mov ebx,0x3
|
|
|
|
|
* 0097810e 895c24 30 mov dword ptr ss:[esp+0x30],ebx
|
|
|
|
|
* 00978112 894424 2c mov dword ptr ss:[esp+0x2c],eax
|
|
|
|
|
* 00978116 894424 1c mov dword ptr ss:[esp+0x1c],eax
|
|
|
|
|
* 0097811a 8b4e 34 mov ecx,dword ptr ds:[esi+0x34]
|
|
|
|
|
* 0097811d 3b4e 3c cmp ecx,dword ptr ds:[esi+0x3c]
|
|
|
|
|
* 00978120 894424 3c mov dword ptr ss:[esp+0x3c],eax
|
|
|
|
|
* 00978124 7e 3b jle short .00978161
|
|
|
|
|
* 00978126 8b7e 3c mov edi,dword ptr ds:[esi+0x3c]
|
|
|
|
|
* 00978129 3b7e 34 cmp edi,dword ptr ds:[esi+0x34]
|
|
|
|
|
* 0097812c 76 05 jbe short .00978133
|
|
|
|
|
* 0097812e e8 01db1500 call .00ad5c34
|
|
|
|
|
* 00978133 837e 38 04 cmp dword ptr ds:[esi+0x38],0x4
|
|
|
|
|
* 00978137 72 05 jb short .0097813e
|
|
|
|
|
* 00978139 8b46 24 mov eax,dword ptr ds:[esi+0x24]
|
|
|
|
|
* 0097813c eb 03 jmp short .00978141
|
|
|
|
|
* 0097813e 8d46 24 lea eax,dword ptr ds:[esi+0x24]
|
|
|
|
|
* 00978141 8b3cb8 mov edi,dword ptr ds:[eax+edi*4]
|
|
|
|
|
* 00978144 016e 3c add dword ptr ds:[esi+0x3c],ebp
|
|
|
|
|
* 00978147 57 push edi
|
|
|
|
|
* 00978148 55 push ebp
|
|
|
|
|
* 00978149 8d4c24 20 lea ecx,dword ptr ss:[esp+0x20]
|
|
|
|
|
* 0097814d e8 de05feff call .00958730
|
|
|
|
|
*
|
|
|
|
|
* Sample stack: baseaddr = 0c90000
|
|
|
|
|
* 001aec2c ede50fbb
|
|
|
|
|
* 001aec30 0886064c
|
|
|
|
|
* 001aec34 08860bd0
|
|
|
|
|
* 001aec38 08860620
|
|
|
|
|
* 001aec3c 00000000
|
|
|
|
|
* 001aec40 00000000
|
|
|
|
|
* 001aec44 08860bd0
|
|
|
|
|
* 001aec48 001aee18
|
|
|
|
|
* 001aec4c 08860620
|
|
|
|
|
* 001aec50 00000000
|
|
|
|
|
* 001aec54 00cb4408 return to .00cb4408 from .00c973e0
|
|
|
|
|
* 001aec58 08860bd8
|
|
|
|
|
* 001aec5c 00000000
|
|
|
|
|
* 001aec60 001aefd8 pointer to next seh record
|
|
|
|
|
* 001aec64 00e47d88 se handler
|
|
|
|
|
* 001aec68 ffffffff
|
|
|
|
|
* 001aec6c 00cb9f40 return to .00cb9f40 from .00cc8030 ; jichi: split here
|
|
|
|
|
*/
|
2024-11-02 15:49:09 +08:00
|
|
|
|
static void SpecialHookApricoT(hook_stack *stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
|
2024-02-07 20:59:24 +08:00
|
|
|
|
{
|
|
|
|
|
DWORD reg_esi = stack->esi;
|
|
|
|
|
DWORD base = *(DWORD *)(reg_esi + 0x24);
|
|
|
|
|
DWORD index = *(DWORD *)(reg_esi + 0x3c);
|
|
|
|
|
DWORD *script = (DWORD *)(base + index * 4);
|
|
|
|
|
// jichi 2/14/2015
|
|
|
|
|
// Change reg_esp to the return address
|
2024-11-02 15:49:09 +08:00
|
|
|
|
// DWORD reg_esp = regof(esp, esp_base);
|
2024-02-07 20:59:24 +08:00
|
|
|
|
//*split = reg_esp;
|
|
|
|
|
//*split = regof(esp, esp_base);
|
2024-11-02 15:49:09 +08:00
|
|
|
|
DWORD arg = stack->stack[16]; // return address
|
2024-02-07 20:59:24 +08:00
|
|
|
|
*split = arg > processStartAddress ? arg - processStartAddress : arg; // use relative split value
|
|
|
|
|
//*split = argof(1, esp_base);
|
2024-11-02 15:49:09 +08:00
|
|
|
|
if (script[0] == L'<')
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
DWORD *end;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
for (end = script; *end != L'>'; end++)
|
|
|
|
|
; // jichi 2/14/2015: i.e. = ::wcschr(script) or script
|
|
|
|
|
switch (script[1])
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
case L'N':
|
2024-11-02 15:49:09 +08:00
|
|
|
|
if (script[2] == L'a' && script[3] == L'm' && script[4] == L'e')
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
buffer_index = 0;
|
|
|
|
|
for (script += 5; script < end; script++)
|
|
|
|
|
if (*script > 0x20)
|
|
|
|
|
wc_buffer[buffer_index++] = *script & 0xFFFF;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
*len = buffer_index << 1;
|
2024-02-07 20:59:24 +08:00
|
|
|
|
*data = (DWORD)wc_buffer;
|
|
|
|
|
// jichi 1/4/2014: The way I save subconext is not able to distinguish the split value
|
|
|
|
|
// Change to shift 16
|
|
|
|
|
//*split |= 1 << 31;
|
|
|
|
|
*split |= 1 << 16; // jichi: differentiate name and text script
|
2024-11-02 15:49:09 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2024-02-07 20:59:24 +08:00
|
|
|
|
case L'T':
|
2024-11-02 15:49:09 +08:00
|
|
|
|
if (script[2] == L'e' && script[3] == L'x' && script[4] == L't')
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
buffer_index = 0;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
for (script += 5; script < end; script++)
|
|
|
|
|
{
|
|
|
|
|
if (*script > 0x40)
|
|
|
|
|
{
|
|
|
|
|
while (*script == L'{')
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
script++;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
while (*script != L'\\')
|
|
|
|
|
{
|
2024-02-07 20:59:24 +08:00
|
|
|
|
wc_buffer[buffer_index++] = *script & 0xffff;
|
|
|
|
|
script++;
|
|
|
|
|
}
|
2024-11-02 15:49:09 +08:00
|
|
|
|
while (*script++ != L'}')
|
|
|
|
|
;
|
2024-02-07 20:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
wc_buffer[buffer_index++] = *script & 0xffff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*len = buffer_index << 1;
|
|
|
|
|
*data = (DWORD)wc_buffer;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2024-02-07 20:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool InsertApricoTHook()
|
|
|
|
|
{
|
|
|
|
|
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
|
2024-11-02 15:49:09 +08:00
|
|
|
|
if ((*(DWORD *)i & 0xfff8fc) == 0x3cf880) // cmp reg,0x3c
|
2024-02-07 20:59:24 +08:00
|
|
|
|
for (DWORD j = i + 3, k = i + 0x100; j < k; j++)
|
2024-11-02 15:49:09 +08:00
|
|
|
|
if ((*(DWORD *)j & 0xffffff) == 0x4c2)
|
|
|
|
|
{ // retn 4
|
2024-02-07 20:59:24 +08:00
|
|
|
|
HookParam hp;
|
|
|
|
|
hp.address = j + 3;
|
|
|
|
|
hp.text_fun = SpecialHookApricoT;
|
2024-11-02 15:49:09 +08:00
|
|
|
|
hp.type = USING_STRING | NO_CONTEXT | CODEC_UTF16;
|
2024-02-07 20:59:24 +08:00
|
|
|
|
ConsoleOutput("INSERT ApricoT");
|
2024-11-02 15:49:09 +08:00
|
|
|
|
// GROWL_DWORD3(hp.address, processStartAddress, processStopAddress);
|
|
|
|
|
|
|
|
|
|
// RegisterEngineType(ENGINE_APRICOT);
|
|
|
|
|
// jichi 2/14/2015: disable cached GDI functions
|
2024-02-07 20:59:24 +08:00
|
|
|
|
ConsoleOutput("ApRicoT: disable GDI hooks");
|
2024-11-02 15:49:09 +08:00
|
|
|
|
|
2024-02-07 20:59:24 +08:00
|
|
|
|
return NewHook(hp, "ApRicoT");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ConsoleOutput("ApricoT: failed");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-02 15:49:09 +08:00
|
|
|
|
bool ApricoT::attach_function()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return InsertApricoTHook();
|
|
|
|
|
}
|