DEV Community

Jack Sparrow
Jack Sparrow

Posted on

Stealth Inline Hook Detection via LR Return Address

1. Background: Why Traditional Inline Hook Detection Fails

Inline hook detection on Android commonly relies on:

  1. CRC or code hash verification

  2. 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 RET to 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 .text code 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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.");
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Advanced Protection: Stack Corruption & Anti-Analysis

To make debugging nearly impossible:

  1. detect hook

  2. overwrite stack (FP → FP+2048)

  3. 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)();
}
Enter fullscreen mode Exit fullscreen mode

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

Image description style=

Top comments (0)