Skip to content

fix: handle XOM and W^X constraints for inline hooking on Android 10+ #24

@Bahtya

Description

@Bahtya

Problem

Android 10+ marks aarch64 system libraries (including libc) as Execute-Only Memory (XOM) — pages have PROT_EXEC but not PROT_READ. vproc's inline hooks need to:

  1. Read the function prologue to save original instructions → fails because PROT_READ is not set
  2. Write the trampoline → needs PROT_WRITE, but W^X policy prevents PROT_WRITE | PROT_EXEC simultaneously

Additionally, SELinux denies execmem permissions to untrusted app domains, blocking anonymous RWX mmap for trampoline memory allocation.

libc Path Changes

Android 10+ moved libc from /system/lib64/libc.so to /apex/com.android.runtime/lib64/bionic/libc.so, and linker namespaces block standard dlopen access.

Proposed Fix

1. XOM: Add PROT_READ before reading prologue

mprotect(page, page_size, PROT_READ | PROT_EXEC);
// read original instructions
// write trampoline
// flush I-cache
mprotect(page, page_size, PROT_EXEC); // restore XOM

2. W^X + SELinux: Alias mapping (dual mapping)

Map the same physical page at two virtual addresses with different permissions:

// Create anonymous mapping
let mem = mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Create alias via memfd_create + MAP_FIXED
let fd = memfd_create("vproc_trampoline", MFD_ALLOW_SEALING);
// Map writable alias
let writable = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Map executable alias
let executable = mmap(nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);

Write trampoline to the writable mapping, execute from the executable mapping. SELinux sees no PROT_WRITE | PROT_EXEC on any single mapping.

Reference: selinux-mem

3. libc Path Discovery

Use dlsym(RTLD_DEFAULT, "fork") to resolve the actual libc address at runtime rather than hardcoding paths.

Current Impact

vproc's inline hooks currently work in Termux (which has relaxed SELinux and no XOM on libc). They may fail on stock Android 10+ devices when loaded by a regular app (hermux on non-rooted devices).

Affected Code

  • src/vexec.rshook_libc_fork(), hook_libc_pipe(), hook_libc_wait() — all assume readable+executable code pages

References

  • Android XOM — execute-only memory for system libraries
  • selinux-mem — alias mapping technique
  • ShadowHook — production Android inline hook library handling XOM/W^X

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions