LunaHook-mirror/LunaHook/engine32/Exp.cpp
2024-02-07 20:59:24 +08:00

229 lines
10 KiB
C++
Raw Blame History

#include"Exp.h"
/** jichi 9/8/2014 EXP, http://www.exp-inc.jp
* Maker: EXP, 5pb
* Sample game: 剣の街<E381AE>異邦人
*
* There are three matched memory addresses with SHIFT-JIS.
* The middle one is used as it is aligned with zeros.
* The memory address is fixed.
*
* There are three functions found using hardware breakpoints.
* The last one is used as the first two are looped.
*
* reladdr = 0x138020
*
* baseaddr = 0x00120000
*
* 0025801d cc int3
* 0025801e cc int3
* 0025801f cc int3
* 00258020 55 push ebp ; jichi: hook here
* 00258021 8bec mov ebp,esp
* 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 00258026 83ec 08 sub esp,0x8
* 00258029 85c0 test eax,eax
* 0025802b 0f84 d8000000 je .00258109
* 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 00258035 0f84 ce000000 je .00258109
* 0025803b 8b10 mov edx,dword ptr ds:[eax] ; jichi: edx is the text
* 0025803d 8b45 0c mov eax,dword ptr ss:[ebp+0xc]
* 00258040 53 push ebx
* 00258041 56 push esi
* 00258042 c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
* 00258049 8945 fc mov dword ptr ss:[ebp-0x4],eax
* 0025804c 57 push edi
* 0025804d 8d49 00 lea ecx,dword ptr ds:[ecx]
* 00258050 8a0a mov cl,byte ptr ds:[edx] jichi: text in accessed in edx
* 00258052 8a45 14 mov al,byte ptr ss:[ebp+0x14]
* 00258055 3ac1 cmp al,cl
* 00258057 74 7a je short .002580d3
* 00258059 8b7d 10 mov edi,dword ptr ss:[ebp+0x10]
* 0025805c 8b5d fc mov ebx,dword ptr ss:[ebp-0x4]
* 0025805f 33f6 xor esi,esi
* 00258061 8bc2 mov eax,edx
* 00258063 80f9 81 cmp cl,0x81
* 00258066 72 05 jb short .0025806d
* 00258068 80f9 9f cmp cl,0x9f
* 0025806b 76 0a jbe short .00258077
* 0025806d 80f9 e0 cmp cl,0xe0
* 00258070 72 1d jb short .0025808f
* 00258072 80f9 fc cmp cl,0xfc
* 00258075 77 18 ja short .0025808f
* 00258077 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 0025807a 85c0 test eax,eax
* 0025807c 74 05 je short .00258083
* 0025807e 8808 mov byte ptr ds:[eax],cl
* 00258080 8d58 01 lea ebx,dword ptr ds:[eax+0x1]
* 00258083 8b7d 10 mov edi,dword ptr ss:[ebp+0x10]
* 00258086 8d42 01 lea eax,dword ptr ds:[edx+0x1]
* 00258089 be 01000000 mov esi,0x1
* 0025808e 4f dec edi
* 0025808f 85ff test edi,edi
* 00258091 74 36 je short .002580c9
* 00258093 85db test ebx,ebx
* 00258095 74 04 je short .0025809b
* 00258097 8a08 mov cl,byte ptr ds:[eax]
* 00258099 880b mov byte ptr ds:[ebx],cl
* 0025809b 46 inc esi
* 0025809c 33c0 xor eax,eax
* 0025809e 66:3bc6 cmp ax,si
* 002580a1 7f 47 jg short .002580ea
* 002580a3 0fbfce movsx ecx,si
* 002580a6 03d1 add edx,ecx
* 002580a8 3945 fc cmp dword ptr ss:[ebp-0x4],eax
* 002580ab 74 03 je short .002580b0
* 002580ad 014d fc add dword ptr ss:[ebp-0x4],ecx
* 002580b0 294d 10 sub dword ptr ss:[ebp+0x10],ecx
* 002580b3 014d f8 add dword ptr ss:[ebp-0x8],ecx
* 002580b6 8a0a mov cl,byte ptr ds:[edx]
* 002580b8 80f9 0a cmp cl,0xa
* 002580bb 74 20 je short .002580dd
* 002580bd 80f9 0d cmp cl,0xd
* 002580c0 74 1b je short .002580dd
* 002580c2 3945 10 cmp dword ptr ss:[ebp+0x10],eax
* 002580c5 ^75 89 jnz short .00258050
* 002580c7 eb 21 jmp short .002580ea
* 002580c9 85db test ebx,ebx
* 002580cb 74 1d je short .002580ea
* 002580cd c643 ff 00 mov byte ptr ds:[ebx-0x1],0x0
* 002580d1 eb 17 jmp short .002580ea
* 002580d3 84c0 test al,al
* 002580d5 74 13 je short .002580ea
* 002580d7 42 inc edx
* 002580d8 ff45 f8 inc dword ptr ss:[ebp-0x8]
* 002580db eb 0d jmp short .002580ea
* 002580dd 8a42 01 mov al,byte ptr ds:[edx+0x1]
* 002580e0 42 inc edx
* 002580e1 3c 0a cmp al,0xa
* 002580e3 74 04 je short .002580e9
* 002580e5 3c 0d cmp al,0xd
* 002580e7 75 01 jnz short .002580ea
* 002580e9 42 inc edx
* 002580ea 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 002580ed 5f pop edi
* 002580ee 5e pop esi
* 002580ef 5b pop ebx
* 002580f0 85c0 test eax,eax
* 002580f2 74 09 je short .002580fd
* 002580f4 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 002580f8 74 03 je short .002580fd
* 002580fa c600 00 mov byte ptr ds:[eax],0x0
* 002580fd 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 00258100 8b45 f8 mov eax,dword ptr ss:[ebp-0x8]
* 00258103 8911 mov dword ptr ds:[ecx],edx
* 00258105 8be5 mov esp,ebp
* 00258107 5d pop ebp
* 00258108 c3 retn
* 00258109 33c0 xor eax,eax
* 0025810b 8be5 mov esp,ebp
* 0025810d 5d pop ebp
* 0025810e c3 retn
* 0025810f cc int3
*
* Stack:
* 0f14f87c 00279177 return to .00279177 from .00258020
* 0f14f880 0f14f8b0 ; arg1 address of the text's pointer
* 0f14f884 0f14f8c0 ; arg2 pointed to zero, maybe a buffer
* 0f14f888 00000047 ; arg3 it is zero if no text, this value might be text size + 1
* 0f14f88c ffffff80 ; constant, used as split
* 0f14f890 005768c8 .005768c8
* 0f14f894 02924340 ; text is at 02924350
* 0f14f898 00000001 ; this might also be a good split
* 0f14f89c 1b520020
* 0f14f8a0 00000000
* 0f14f8a4 00000000
* 0f14f8a8 029245fc
* 0f14f8ac 0004bfd3
* 0f14f8b0 0f14fae0
* 0f14f8b4 00000000
* 0f14f8b8 00000000
* 0f14f8bc 02924340
* 0f14f8c0 00000000
*
* Registers:
* eax 0f14f8c0 ; floating at runtime
* ecx 0f14f8b0; floating at runtime
* edx 00000000
* ebx 0f14fae0; floating at runtime
* esp 0f14f87c; floating at runtime
* ebp 0f14facc; floating at runtime
* esi 00000047
* edi 02924340 ; text is in 02924350
* eip 00258020 .00258020
*
* Memory access pattern:
* For long sentences, it first render the first line, then the second line, and so on.
* So, the second line is a subtext of the entire dialog.
*/
static void SpecialHookExp(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
{
static DWORD lasttext;
// 00258020 55 push ebp ; jichi: hook here
// 00258021 8bec mov ebp,esp
// 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; jichi: move arg1 to eax
// 00258029 85c0 test eax,eax ; check if text is null
// 0025802b 0f84 d8000000 je .00258109
// 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0 ; jichi: compare 0 with arg3, which is size+1
// 00258035 0f84 ce000000 je .00258109
// 0025803b 8b10 mov edx,dword ptr ds:[eax] ; move text address to edx
DWORD arg1 = stack->stack[1], // mov eax,dword ptr ss:[ebp+0x8]
arg3 = stack->stack[3]; // size - 1
if (arg1 && arg3)
if (DWORD text = *(DWORD *)arg1)
if (!(text > lasttext && text < lasttext + VNR_TEXT_CAPACITY)) { // text is not a subtext of lastText
*data = lasttext = text; // mov edx,dword ptr ds:[eax]
//*len = arg3 - 1; // the last char is the '\0', so -1, but this value is not reliable
*len = ::strlen((LPCSTR)text);
// Registers are not used as split as all of them are floating at runtime
//*split = argof(4, esp_base); // arg4, always -8, this will merge all threads and result in repetition
*split = stack->stack[7]; // reduce repetition, but still have sub-text repeat
}
}
bool InsertExpHook()
{
const BYTE bytes[] = {
0x55, // 00258020 55 push ebp ; jichi: hook here, function starts, text in [arg1], size+1 in arg3
0x8b,0xec, // 00258021 8bec mov ebp,esp
0x8b,0x45, 0x08, // 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x83,0xec, 0x08, // 00258026 83ec 08 sub esp,0x8
0x85,0xc0, // 00258029 85c0 test eax,eax
0x0f,0x84, XX4, // 0025802b 0f84 d8000000 je .00258109
0x83,0x7d, 0x10, 0x00, // 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
0x0f,0x84, XX4, // 00258035 0f84 ce000000 je .00258109
0x8b,0x10, // 0025803b 8b10 mov edx,dword ptr ds:[eax] ; jichi: edx is the text
0x8b,0x45, 0x0c, // 0025803d 8b45 0c mov eax,dword ptr ss:[ebp+0xc]
0x53, // 00258040 53 push ebx
0x56, // 00258041 56 push esi
0xc7,0x45, 0xf8, 0x00,0x00,0x00,0x00, // 00258042 c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
0x89,0x45, 0xfc, // 00258049 8945 fc mov dword ptr ss:[ebp-0x4],eax
0x57, // 0025804c 57 push edi
0x8d,0x49, 0x00, // 0025804d 8d49 00 lea ecx,dword ptr ds:[ecx]
0x8a,0x0a // 00258050 8a0a mov cl,byte ptr ds:[edx] ; jichi: text accessed in edx
};
enum { addr_offset = 0 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(addr);
if (!addr) {
ConsoleOutput("EXP: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.type = NO_CONTEXT|USING_STRING; // NO_CONTEXT to get rid of floating address
hp.text_fun = SpecialHookExp;
ConsoleOutput("INSERT EXP");
ConsoleOutput("EXP: disable GDI hooks"); // There are no GDI functions hooked though
return NewHook(hp, "EXP"); // FIXME: text displayed line by line
}
bool Exp::attach_function() {
return InsertExpHook();
}