mirror of
https://gitea.tendokyu.moe/TeamTofuShop/capnhook.git
synced 2026-09-22 22:38:12 +03:00
Fix GetProcAddress issue on ARM64 (#5)
See [TeamTofuShop/segatools/issues/20](https://gitea.tendokyu.moe/TeamTofuShop/segatools/issues/20). This PR adds `pe_is_arm64` for detecting ARM64 systems and `pe_get_arm64_real_proc` for retrieving the real thread address. I'm still getting familiar with the project, so I’d really appreciate any feedback, suggestions, or corrections. Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/capnhook/pulls/5 Co-authored-by: oldkingOK <oldkingok@qq.com> Co-committed-by: oldkingOK <oldkingok@qq.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user