A quick-start Android/LSPosed module template for authorized testing and reusable private native modding research.
Use this only on apps/systems you own or are authorized to test.
- Modern
libxposedAPI 101 entry point (io.github.libxposed:api:101.0.1,compileOnly). - Both
onPackageLoadedandonPackageReadycallbacks for broad LSPosed/Vector compatibility. - Process-level filters in
TemplateConfigso the module stays out of push, crash, telemetry, sandbox, and anti-cheat satellite processes unless you explicitly opt in. - Modern LSPosed metadata under
META-INF/xposed/:java_init.listscope.listmodule.propwithexceptionMode=protective
- A safe Java hook smoke test using the API 101 interceptor-chain style.
FeatureRegistryruntime bool/float settings with best-effort target-sandbox persistence.- A movable dark/lavender floating menu with one toggle row per bool feature.
EngineDetectorfor Unity, Unreal, Cocos2d-x, Godot, Flutter, React-Native, and Xamarin hints.NativeUtilsJNI helpers for/proc/self/mapsmodule lookup, IDA-style pattern scan,dlsym, read-memory, andmprotect-guarded write-memory.- A self-contained ARM64 native framework, with no bundled third-party native hook library or PLT/GOT dependency.
- Debug/release split with
BuildConfig.VERBOSE_LOGS; release builds disable verbose Java/native logs by default. - A configure script that can rename the package, app name, scope, metadata, and packaged native library name.
The reusable native part lives in:
app/src/main/cpp/arm64_guest_framework.happ/src/main/cpp/arm64_guest_framework.cppapp/src/main/cpp/template_native.cpp
An optional, reusable inline-hook engine that calls the original back lives in:
app/src/main/cpp/houdini_inline_hook.happ/src/main/cpp/houdini_inline_hook.cpp
It provides:
/proc/self/mapsparser with module range and module base helpers.- Bounded waiting for game libraries that load after
Application.attach. - IDA-style pattern parser and scanner for ARM64 byte signatures.
- File-backed code reading, useful for checking original ARM64 bytes from mapped APK/SO pages.
- Houdini/native-bridge alias discovery for addresses that represent the same ARM64 file offset.
- Alias-aware writes for byte patches and absolute jump patches.
- Export lookup through
dlopen/dlsym,RTLD_DEFAULT, and manual ELF symbol-table fallback. - Live memory read/write with page permission changes and instruction-cache flush.
- 16-byte ARM64 absolute branch patch:
ldr x17, #8br x17- 8-byte replacement address
- Patch records and framework status returned through
NativeBridge.getNativeRecords(). HoudiniInlineHook(hih::): the call-original half the primitives above do not provide - a prologue relocator, a 4-byte branch into a near veneer (safe for the 8-byte IL2CPP leaf functions that a 16-byte jump corrupts), and memfd-backed trampolines. Seedocs/HOUDINI_INLINE_HOOK.md.
The default template installs no game-specific native hooks. That is intentional. Each target game branch owns offsets, signatures, object layouts, feature logic, replacement functions, and any manual trampoline/prologue replay. The reusable part is the loader, scanner, verifier, and patcher.
On real ARM64 devices, the arm64-v8a native library runs directly.
On x86_64 Android emulators with Houdini/native bridge, the same arm64-v8a library may load as
guest ARM64 code and the bridge translates it for the x86_64 host. In that setup:
- Patch ARM64 guest addresses from ARM64 guest mappings.
- Do not try to patch x86_64 translated cache code.
- Do not reject the process just because the host kernel or emulator is x86_64.
System.loadLibrary("template_native")is the real compatibility test.- Pattern bytes and expected prologues must be ARM64 bytes from the target
lib/arm64-v8a/*.so. - Prefer readable module scans for ARM64 signatures; executable-only file-backed ranges can be empty or misleading under native bridge.
- Write every alias for the same file offset. A patch can verify at one guest address while gameplay still uses another mapping that still contains original bytes.
- Generic Android inline-hook libraries that depend on their own supported ABI matrix may fail even when manual ARM64 guest patching can still work.
This template ships only arm64-v8a native code. Java LSPosed hooks can still work without native
loading; set TemplateConfig.ENABLE_NATIVE_ARM64_FRAMEWORK = false for Java-only modules.
See docs/ARM64_HOUDINI_FRAMEWORK.md.
From this directory:
./tools/configure-template.py \
--package com.yourname.yourmodule \
--name "Your Module" \
--target com.example.target \
--author "YourName" \
--native-lib audio_utilThen edit:
app/src/main/java/com/template/lsposed/TemplateConfig.javaapp/src/main/resources/META-INF/xposed/scope.listapp/src/main/resources/META-INF/xposed/module.propapp/src/main/res/values/arrays.xmlapp/src/main/res/values/strings.xml
For native game work, set TemplateConfig.NATIVE_TARGET_LIBRARIES on a game branch, for example:
public static final String[] NATIVE_TARGET_LIBRARIES = {"libil2cpp.so"};Build:
./gradlew :app:assembleReleaseInstall and run:
adb install -r app/build/outputs/apk/release/app-release.apk
adb shell am force-stop com.example.target
adb shell monkey -p com.example.target 1
adb logcat -c
adb logcat -s AppRuntimeThe entry class extends io.github.libxposed.api.XposedModule and is listed in:
app/src/main/resources/META-INF/xposed/java_init.list
Hooking is interceptor-chain based:
hook(method)
.setExceptionMode(XposedInterface.ExceptionMode.PROTECTIVE)
.intercept(chain -> {
Object result = chain.proceed();
return result;
});Important points:
- Keep
libxposedascompileOnly; never package the framework API into the APK. - Keep scope tight. Do not hook every app unless you are building a framework/system module and know why.
- Use
PROTECTIVEexception mode so hook failures do not crash the target app. - Use
deoptimize(executable)only when needed.
Use Frida, IDA/Ghidra, NDK tools, and logcat first to answer:
- Which process and ABI are active?
- Which native libraries load and when?
- Is the target code exported, pattern-only, or offset-only?
- Are your bytes from the ARM64 file-backed library or from a modified runtime page?
- Does the first 16 bytes of the target still match your expected prologue?
- Does your replacement need to call original, replay instructions, or only redirect/patch?
For workflow notes, see:
docs/ARM64_HOUDINI_FRAMEWORK.mddocs/ENGINE_NATIVE_WORKFLOWS.mddocs/FRIDA_EMULATOR_QUICKSTART.md
libxposedAPI:101.0.1.- LSPosed/Vector modern API level:
101. - Android Gradle Plugin:
8.7.2. - NDK:
25.2.9519653.
There is intentionally no native hook-library version here because the template no longer packages or depends on a bundled third-party hook library.
LICENSE- CC BY-NC-ND 4.0 license notice and official license links.SECURITY.md- safe issue-reporting expectations.CONTRIBUTING.md- contribution and validation expectations.docs/ARM64_HOUDINI_FRAMEWORK.md- native bridge restrictions and patcher usage.docs/HOUDINI_INLINE_HOOK.md- the optional call-original inline-hook engine and the non-obvious Houdini facts it has to respect (r-- guest mappings, short-function corruption, anonymous-return faults, memfd trampolines).docs/ENGINE_NATIVE_WORKFLOWS.md- Unity IL2CPP and native-heavy target notes.docs/FRIDA_EMULATOR_QUICKSTART.md- emulator reconnaissance workflow.
This project is licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
International (CC-BY-NC-ND-4.0). You may share the unmodified template with attribution for
non-commercial use. Do not distribute modified versions without separate permission.