在 android 上实现 inline hook 的一个简单 demo,只支持 arm64 架构
// 只支持 arm64 架构
// 1. 覆盖掉 target_func 头部的 16 bytes,将其修改为跳转到 hook_func 地址的指令
// 2. 原本 target_func 头部的 16 bytes 被存到 trampoline 里面,再接一条跳转到原 target_func 剩余部分的指令
hook_t hook(void *target_func, void *hook_func) {
auto target_address = reinterpret_cast<uint64_t>(target_func);
auto hook_address = reinterpret_cast<uint64_t>(hook_func);
// 16 byte 指令块
uint32_t hook_instructions[4];
hook_t hook {
.origin_func = static_cast<uint32_t *>(malloc(32))
};
generate_hook_instructions(target_address, hook_address, hook_instructions);
// trampoline 跳转指令,16 byte
auto trampoline_address = reinterpret_cast<uint64_t>(hook.origin_func);
uint32_t return_instructions[4];
generate_hook_instructions(trampoline_address + 16, target_address + 16, return_instructions);
// 前 16 byte 可写就行
make_rwx(target_func, 16);
// trampoline 需要可执行
make_rwx(hook.origin_func, 32);
memcpy(hook.origin_func, target_func, 16);
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(hook.origin_func) + 16), return_instructions, 16);
memcpy(target_func, hook_instructions, 16);
return hook;
}
// 复原
// 将 trampoline 前 16 个字节写回 target_func,并释放 trampoline
void unhook(void *target_func, hook_t *hook) {
memcpy(target_func, hook->origin_func, 16);
free(hook->origin_func);
}
void generate_hook_instructions(uint64_t current_addr, uint64_t hook_function_addr, uint32_t* instructions) {
// 计算页面基址(4KB 对齐)
uint64_t current_page = current_addr & ~0xFFFULL;
uint64_t target_page = hook_function_addr & ~0xFFFULL;
// 计算页面差值(以页面为单位)
int64_t page_diff = (int64_t)(target_page - current_page) >> 12;
// 计算页面内偏移
uint32_t page_offset = hook_function_addr & 0xFFF;
// 检查是否在 ADRP 范围内
if (page_diff <= 0x1FFFFF && page_diff >= -0x200000) {
// 使用 ADRP + ADD + BR(原方案)
instructions[0] = encode_adrp(16, page_diff); // ADRP X16, hook_function
instructions[1] = encode_add_imm(16, 16, page_offset); // ADD X16, X16, #:lo12:hook_function
instructions[2] = 0xD61F0200; // BR X16
instructions[3] = 0xD503201F; // NOP
LOGI("Using ADRP method (page_diff: %ld)", page_diff);
} else {
// 使用 LDR + BR 方法(无距离限制)
LOGI("Using LDR method (page_diff: %ld, out of ADRP range)", page_diff);
// LDR X16, #8 ; 从 PC+8 位置加载 64 位地址到 X16
// BR X16 ; 跳转到 X16
// .quad hook_function_addr ; 64 位目标地址
instructions[0] = 0x58000050; // LDR X16, #8 (PC + 8)
instructions[1] = 0xD61F0200; // BR X16
// 将 64 位地址拆分为两个 32 位指令位置
instructions[2] = (uint32_t)(hook_function_addr & 0xFFFFFFFF); // 低32位
instructions[3] = (uint32_t)((hook_function_addr >> 32) & 0xFFFFFFFF); // 高32位
}
}