diff --git a/hook/pe.c b/hook/pe.c index 49b7e94..3dafff2 100644 --- a/hook/pe.c +++ b/hook/pe.c @@ -220,3 +220,65 @@ HRESULT pe_patch(void *dest, const void *src, size_t nbytes) return S_OK; } + +BOOL pe_is_arm64() +{ + typedef BOOL (WINAPI *LPFN_IsWow64Process2)(HANDLE, USHORT *, USHORT *); + LPFN_IsWow64Process2 is_wow64_process2; + USHORT process_machine; + USHORT native_machine; + HMODULE k32; + BOOL is_arm64; + BOOL ok; + + is_arm64 = FALSE; + k32 = GetModuleHandleW(L"kernel32.dll"); + if (k32 == NULL) { + goto end; + } + + is_wow64_process2 = + (LPFN_IsWow64Process2) GetProcAddress(k32, "IsWow64Process2"); + + if (is_wow64_process2 == NULL) { + goto end; + } + + ok = is_wow64_process2(GetCurrentProcess(), + &process_machine, + &native_machine); + + if (!ok) { + goto end; + } + + is_arm64 = native_machine == IMAGE_FILE_MACHINE_ARM64; +end: + return is_arm64; +} + +/* ARM64EC Fast-Forward Sequence thunks. + When x64 code calls GetProcAddress on an Arm64X hybrid PE, the returned + pointer is an x64 trampoline rather than the real ARM64EC function. Each + thunk is a canonical x64 prolog ending in a near JMP to the real target. + We read the JMP's displacement and skip past the thunk body. + + Canonical prolog as of Win11 26H2 aarch64 (0xe bytes): + +0x0 488bc4 mov rax, rsp + +0x3 48895820 mov [rax+0x20], rbx + +0x7 55 push rbp + +0x8 5d pop rbp + +0x9 e9xxxxxxxx jmp rel32 ; +0xa = displacement */ +#define X64_THUNK_JMP_DISP_OFFSET 0xa +#define X64_THUNK_SIZE 0xe + +void *pe_thunk_resolve(FARPROC proc) +{ + INT32 offset; + + assert(proc != NULL); + + memcpy(&offset, (BYTE *)proc + X64_THUNK_JMP_DISP_OFFSET, sizeof(offset)); + + return (BYTE *)proc + X64_THUNK_SIZE + offset; +} diff --git a/hook/pe.h b/hook/pe.h index 1a33f12..e56d3f0 100644 --- a/hook/pe.h +++ b/hook/pe.h @@ -25,3 +25,5 @@ HRESULT pe_iid_get_iat_entry( void *pe_get_export(HMODULE pe, const char *name, uint16_t ord); void *pe_get_entry_point(HMODULE pe); HRESULT pe_patch(void *dest, const void *src, size_t nbytes); +BOOL pe_is_arm64(); +void *pe_thunk_resolve(FARPROC proc); diff --git a/hook/process.c b/hook/process.c index 7d98cf2..c87da41 100644 --- a/hook/process.c +++ b/hook/process.c @@ -73,6 +73,9 @@ static HRESULT process_hijack_try_thread( exe_entry = pe_get_entry_point(exe); ntstart = GetProcAddress(ntdll, "RtlUserThreadStart"); + if (pe_is_arm64()) { + ntstart = pe_thunk_resolve(ntstart); + } if (ntstart == NULL) { /* TODO Deal with WinXP, for the poor souls still stuck on that OS.