Skip to content

About

An ARM64 Android userspace function hooking kernel module using HWBP(hardware-breakpoint/perf) APIs

Resources

Stars

6 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Android HWBP Hook

An ARM64 Android userspace function hooking kernel module, with a small userspace controller. It uses the kernel hardware-breakpoint/perf APIs and does not patch the target process's text pages.

This project is for devices and processes you own or are explicitly authorized to instrument. It is an instrumentation primitive, not a general library injector or anti-detection package.

Thanks to Codex for assisting with this project.

The HWBP design and functionality in this project are based on and adapted from xiaojianbang8888/xiaojianbang-stealth-hook, the original APatch/KernelPatch KPM project. Credit for the original work goes to xiaojianbang8888 and that repository. This repository reworks the HWBP control path as a standalone ordinary kernel module with a /dev/ahwbp ioctl interface.

Features

  • Per-thread AArch64 execution breakpoints.
  • Capture X0–X7, LR, PC and optional X0 return value.
  • Replace selected arguments before the original code runs.
  • Skip a function and return a configured X0 value.
  • Skip initial hits or replace an integer return after the original runs.
  • Conditional bypass rules such as X0 == 42.
  • Enable, disable, query and unhook through /dev/ahwbp ioctl calls.
  • Live polling, new-thread coverage and optional pointer inspection.
  • No KernelPatch/KPM dependency.

Prerequisites

  • ARM64 Android device with hardware-breakpoint and perf support.
  • Root access to load the module and access /dev/ahwbp.
  • Matching kernel source, prepared output, and LLVM/Clang toolchain.
  • Android NDK AArch64 compiler for ahwbpctl.

Build

Kernel module

Set these to your prepared kernel source and output directories:

export KERNEL_SRC=/absolute/path/to/android-kernel-source
export KERNEL_OUT=/absolute/path/to/android-kernel-out
export LLVM_BIN=/absolute/path/to/matching-llvm/bin
export PATH="$LLVM_BIN:$PATH"
make module KERNEL_SRC="$KERNEL_SRC" KERNEL_OUT="$KERNEL_OUT" \
  ARCH=arm64 LLVM=1

This creates kernel/ahwbp.ko. The source, output (including Module.symvers when CONFIG_MODVERSIONS=y), and LLVM toolchain must match the target kernel.

Userspace controller

Build it with the Android NDK (or pass your cross-compiler as CC):

export NDK=/absolute/path/to/android-ndk
export NDK_BIN="$NDK/toolchains/llvm/prebuilt/<host>/bin"
"$NDK_BIN/aarch64-linux-android24-clang" \
  -O2 -Wall -Wextra -Iinclude/uapi \
  -o userspace/ahwbpctl userspace/ahwbpctl.c

Replace <host> with the NDK directory for your machine, such as darwin-x86_64 or linux-x86_64.

Load

adb push kernel/ahwbp.ko /data/local/tmp/
adb shell su -c 'insmod /data/local/tmp/ahwbp.ko'

Check load failures in dmesg:

adb shell su -c 'dmesg | tail -n 80'
adb shell su -c 'cat /proc/modules | grep ahwbp'

Usage

Resolve an ELF offset relative to a loaded library:

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340

Skip the original function and return zero:

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --replace-ret 0

Return a chosen value only when X0 equals 42; otherwise the original function runs:

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --replace-ret 0 --match-arg 0=42

Modify arguments and let the original function run:

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --modify-arg 0=0 --modify-arg 1=100

Query the most recent register snapshot:

ahwbpctl query --pid 1234 --so libtarget.so --offset 0x12340 \
  --listen-ret

Skip the first ten calls (or start applying the policy at call ten):

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --skip-hits 10 --listen-ret
ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --nth-hit 10 --replace-ret 0

Replace the integer return value after the original function runs:

ahwbpctl hook --pid 1234 --so libtarget.so --offset 0x12340 \
  --replace-ret-after 7

With return capture enabled, RET is the original X0 and RET_EFFECTIVE is the value returned to the caller.

Poll existing hooks until interrupted:

ahwbpctl query --pid 1234 --so libtarget.so --offset 0x12340 \
  --live --listen-ret

Install hooks, follow new threads, optionally inspect pointer arguments, and remove the nodes installed by this command when Ctrl-C is pressed:

ahwbpctl watch --pid 1234 --so libtarget.so --offset 0x12340 \
  --listen-ret --dump-arg 0=64

When running watch through ADB, allocate a remote PTY so Ctrl-C reaches the controller:

adb shell -t su -c '/data/local/tmp/ahwbpctl watch --pid 1234 \
  --so libtarget.so --offset 0x12340 --listen-ret'

Without -t, Ctrl-C may stop only the local ADB client and leave the remote hooks installed; use unhook manually in that case.

Change a policy without reinstalling the breakpoint:

ahwbpctl set --pid 1234 --so libtarget.so --offset 0x12340 \
  --replace-ret 7 --match-arg 0=99

hook reports how many per-thread nodes were installed. It does not stream calls; invoke the target function after installation, then use query to read the captured entry registers and (with --listen-ret) the return value. Use watch when you want live output and new-thread polling.

Remove, disable or enable all per-thread nodes for an address:

ahwbpctl unhook  --pid 1234 --so libtarget.so --offset 0x12340
ahwbpctl disable --pid 1234 --so libtarget.so --offset 0x12340
ahwbpctl enable  --pid 1234 --so libtarget.so --offset 0x12340

Absolute addresses are also accepted:

ahwbpctl hook --pid 1234 --address 0x7a0012340 --replace-ret 0

Limits and design notes

  • Hardware breakpoint slots are finite per thread; the controller currently accepts at most six addresses, matching common ARM64 Android hardware.
  • hook covers current threads. watch polls for new threads and removes the nodes it installed when interrupted.
  • X0–X7 are the supported integer/pointer argument registers. Floating point/SIMD arguments and returns are not decoded.
  • A bypass is safe only at a true function entry. Bypassing an instruction after a prologue may leave the stack/register state inconsistent.
  • Return capture moves the breakpoint to LR and therefore does not observe recursive calls on the same thread while the first call is active.
  • --dump-arg N=SIZE reads up to 256 bytes from the address in XN through /proc/<pid>/mem; it requires permission to read the target.
  • The breakpoint callback runs in an exception/atomic context, so it does not call access_process_vm() or other potentially sleeping memory APIs.
  • No /proc/maps hiding, ptrace spoofing, PTE manipulation or userspace library loading is included in this generic project.

Feature parity with the original KPM project

This repository intentionally keeps the ordinary-module HWBP core and leaves the KPM-specific stealth and code-rewriting layers out:

Capability This project
Per-thread ARM64 execution HWBP Implemented
Capture X0-X7, PC and LR Implemented
Return-value capture via LR state machine Implemented
Modify selected X0-X7 arguments Implemented
Skip original and return a chosen X0 Implemented
Conditional argument matching Implemented here as a kernel policy
Hook/query/enable/disable/unhook Implemented through /dev/ahwbp ioctls
Multiple offsets and existing-thread coverage Implemented by ahwbpctl
Automatic coverage for newly created threads Implemented by watch polling
Pointer memory dump and hex/ascii live output Optional userspace /proc/<pid>/mem reads
Nth-hit/skip-count mode Implemented with --nth-hit/--skip-hits
Continuous polling trace and Ctrl+C cleanup Implemented by query --live and watch
Post-return integer X0 override Implemented with --replace-ret-after
Inline callback/trampoline redirection Not implemented
Full hook, DBI decoder/recompiler and clone page Not implemented
PTE/UXN fault redirection Not implemented
Ghost memory allocation/write/free Not implemented
/proc/maps hiding Not implemented
ptrace hardware-register spoofing Not implemented
KPM syscall-285 bridge and supercall loading Not implemented; this uses a misc device

Layout

include/uapi/ahwbp.h   Shared ioctl ABI
kernel/ahwbp.c         Normal loadable kernel module
kernel/Kbuild          Kbuild fragment
Makefile               Optional local build wrapper
userspace/ahwbpctl.c   Android userspace controller
userspace/Makefile     Userspace build wrapper
LICENSE                GNU GPL v2 or later

About

An ARM64 Android userspace function hooking kernel module using HWBP(hardware-breakpoint/perf) APIs

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages