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;
|
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_export(HMODULE pe, const char *name, uint16_t ord);
|
||||||
void *pe_get_entry_point(HMODULE pe);
|
void *pe_get_entry_point(HMODULE pe);
|
||||||
HRESULT pe_patch(void *dest, const void *src, size_t nbytes);
|
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);
|
exe_entry = pe_get_entry_point(exe);
|
||||||
ntstart = GetProcAddress(ntdll, "RtlUserThreadStart");
|
ntstart = GetProcAddress(ntdll, "RtlUserThreadStart");
|
||||||
|
if (pe_is_arm64()) {
|
||||||
|
ntstart = pe_thunk_resolve(ntstart);
|
||||||
|
}
|
||||||
|
|
||||||
if (ntstart == NULL) {
|
if (ntstart == NULL) {
|
||||||
/* TODO Deal with WinXP, for the poor souls still stuck on that OS.
|
/* TODO Deal with WinXP, for the poor souls still stuck on that OS.
|
||||||
|
|||||||
Reference in New Issue
Block a user