1. Background: Why Traditional Inline Hook Detection Fails
Inline hook detection on Android commonly relies on:
-
CRC or code hash verification
-
Comparing function prologue bytes (e.g., detecting LDR/BR trampolines)
However, these methods have major weaknesses:
-
They read code pages, which makes them easily traceable with memory breakpoints.
-
Attackers can hook the detection function itself.
-
Their “signatures” are well known and easy to bypass.
Thus they are visible and high-risk in real offensive–defensive scenarios.
2. Core Idea: Detecting Hooks via the LR (Return Address)
This article introduces a stealth and cross-platform method that doesn’t read any instructions, doesn’t scan memory, and is extremely difficult for Frida to bypass.
✔ Why LR works
Inline hook frameworks (Frida, Dobby, xhook, etc.) must:
-
overwrite the LR register
-
redirect
RETto a trampoline stored in a custom allocated memory page (not inside the original module)
Thus:
➜ If a function’s LR is outside its module’s memory range → it is inline-hooked.
This approach:
-
Does not read
.textcode section -
Cannot be traced via hardware breakpoints
-
Works on ARM64 and x86_64
3. Detection Pipeline
Step 1 — Retrieve current module range
static uint64_t g_begin = 0;
static uint64_t g_end = 0;
__attribute__((constructor))
static void init_module_range() {
Dl_info info;
if (dladdr((void*)init_module_range, &info)) {
g_begin = (uint64_t)info.dli_fbase;
g_end = g_begin + get_module_size(info.dli_fname);
}
}
Step 2 — Read LR via ARM64 inline assembly
__attribute__((alwaysinline))
uint64_t get_lr() {
uint64_t lr;
asm volatile(
"mov x10, x29 n"
"ldr %0, [x10, #8]n"
: "=r"(lr)
:
: "x10"
);
return lr;
}
Step 3 — Detect inline hook
void check_inline_hook() {
uint64_t lr = get_lr();
if (lr < g_begin || lr > g_end) {
LOGD("[!] Inline-hook detected. lr = %llx", lr);
} else {
LOGD("[+] Function not hooked.");
}
}
4. Advanced Protection: Stack Corruption & Anti-Analysis
To make debugging nearly impossible:
-
detect hook
-
overwrite stack (FP → FP+2048)
-
jump to an invalid address
void anti_debug_crash() {
uint64_t fp;
asm volatile("mov %0, x29" : "=r"(fp));
memset((void*)fp, 0xCC, 2048);
((void(*)())0x12345678)();
}
This destroys stack traces and prevents reverse engineering of your protection logic.
5. Conclusion
This LR-based inline hook detection method:
-
does not read code pages
-
is nearly breakpoint-proof
-
detects Frida, Dobby, xhook, and other trampoline mechanisms
-
works on Android and Windows
It offers a new direction for mobile security, anti-cheat systems, and runtime protection.
I’m H.
Six years deep in Android reversing.
Is this sword sharp today?
— H
