kagura — Android NDK Integration¶
This directory contains the Android NDK CMake integration for the kagura LLVM obfuscator. It provides helper functions that wire the obfuscation plugin and its companion runtime library into an Android Studio / Gradle project with minimal boilerplate.
Files¶
| File | Purpose |
|---|---|
kagura-android-ndk.cmake |
CMake helper — defines kagura_android_target(), kagura_android_config(), kagura_android_runtime_target() |
kagura-cmake.cmake |
Lean single-function include for projects that only need kagura_target() |
kagura.gradle |
Groovy Gradle script that injects flags into externalNativeBuild |
Android Studio Integration¶
Step 1 — Build the plugin¶
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
# produces build/lib/Transforms/KaguraObfuscator.so
Step 2 — Add to app/build.gradle¶
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DKAGURA_PLUGIN_PATH=${rootDir}/../kagura/build/lib/Transforms/KaguraObfuscator.so",
"-DKAGURA_PROFILE=BALANCED"
}
}
}
externalNativeBuild {
cmake { path "src/main/cpp/CMakeLists.txt" }
}
}
Step 3 — Include in CMakeLists.txt¶
cmake_minimum_required(VERSION 3.22)
project(mygame)
include(${CMAKE_SOURCE_DIR}/../kagura/integration/android/kagura-android-ndk.cmake)
kagura_android_config()
add_library(mynativelib SHARED src/native.cpp)
kagura_android_target(mynativelib)
To also compile the kagura runtime (anti-debug, AES decrypt stubs, Android
integrity checks) into your build, add one line before kagura_android_target:
kagura_android_runtime_target(kagura_runtime)
kagura_android_target(mynativelib) # auto-links kagura_runtime
The runtime target globs runtime/core/, runtime/anti_debug/ and
runtime/android/ by directory (plus runtime/game/ when
KAGURA_ENABLE_IL2CPP=ON), so files moving inside runtime/ do not break it.
runtime/ios/ and runtime/windows/ are excluded.
CMake Flags Reference¶
| Variable | Type | Default | Description |
|---|---|---|---|
KAGURA_PLUGIN_PATH |
FILEPATH | auto | Absolute path to KaguraObfuscator.{dylib,so,dll}. Auto-discovered under <kagura>/build/lib/Transforms; may also come from the environment variable of the same name. All three extensions are probed, because the plugin runs in the host clang |
KAGURA_PROFILE |
STRING | BALANCED |
FAST / BALANCED / STRONG / CUSTOM, or a path to your own JSON policy file |
KAGURA_RUNTIME_DIR |
PATH | auto | The kagura runtime/ directory |
KAGURA_ENABLE_JNI |
BOOL | ON | JNI dynamic registration (Android-only; in no shared profile) |
KAGURA_ENABLE_IL2CPP |
BOOL | OFF | Also compile runtime/game/ into kagura_runtime |
KAGURA_METRICS |
BOOL | OFF | Print obfuscation metrics to stdout |
Per-pass overrides¶
These have no default on purpose — leaving one undefined means "the
profile decides". Define one on the CMake command line and it is applied after
the profile (later flag wins), and -kagura-config is dropped so the
kagura-config pass cannot clobber it.
| Variable | Description |
|---|---|
KAGURA_ENABLE_STR |
String encryption |
KAGURA_ENABLE_FLA |
CFG flattening |
KAGURA_ENABLE_BCF |
Bogus control flow |
KAGURA_ENABLE_SUB |
Instruction substitution |
KAGURA_ENABLE_CO |
Constant obfuscation (MBA) |
KAGURA_ENABLE_ANTIDEBUG |
Anti-debug / Anti-Frida |
KAGURA_BCF_PROB |
Bogus CF probability 0–100 |
KAGURA_BCF_ITER |
Bogus CF iterations |
KAGURA_SUB_ITER |
Instruction substitution iterations |
KAGURA_SEED |
PRNG seed (0 = system entropy) |
Profile Presets¶
The pass set for each profile is not defined in this integration. It is
read at configure time from the shared policy files in
integration/profiles, which is what keeps the
Android, Xcode, CMake, Unity, Unreal and Bazel integrations from drifting
apart:
| Profile | Definition | Intended use |
|---|---|---|
FAST |
integration/profiles/fast.json |
Hot paths, CI builds, debug variants |
BALANCED |
integration/profiles/balanced.json |
Release builds (default) |
STRONG |
integration/profiles/strong.json |
Security-critical shipping builds |
CUSTOM |
none — only KAGURA_ENABLE_* |
Fine-grained control |
| path | your own JSON policy file | See Configuration |
Gradle Plugin Usage¶
For projects that prefer to configure everything from Gradle, apply the companion script instead of using CMake arguments directly:
Override individual settings before the apply from line:
ext.kagura = [
root : "/opt/kagura", // plugin + profiles are found from here
profile : "strong", // fast | balanced | strong | off | <path.json>
enableBcf : true, // override just this pass
bcfProb : 40,
]
apply from: "${rootDir}/../kagura/integration/android/kagura.gradle"
Every enable* / tuning key defaults to null, meaning "use the profile".
Setting one switches to the explicit-flag path.
Settings can also be placed in local.properties (not committed to VCS):
kagura.root=/Users/me/kagura
kagura.profile=balanced
kagura.pluginPath=/Users/me/kagura/build/lib/Transforms/KaguraObfuscator.so
ABI Notes¶
| ABI | Notes |
|---|---|
arm64-v8a |
Fully supported; all passes tested. Recommended primary target. |
armeabi-v7a |
Supported. BCF iterations automatically capped at 1 to limit code-size growth on Thumb-2. |
x86_64 |
Supported (emulator / Chrome OS). BCF adds branch-prediction overhead; keep KAGURA_BCF_PROB at or below 20. |
x86 |
Compiles but BCF is discouraged — 32-bit x86 Android is effectively end-of-life. |
Performance Impact (Rough Estimates)¶
These figures are measured on a mid-range Arm Cortex-A55 device running Android 12. Actual impact depends heavily on code structure.
| Profile | Binary size increase | CPU overhead (hot loop) | Build time increase |
|---|---|---|---|
| FAST | +5 – 10 % | < 2 % | +10 – 20 % |
| BALANCED | +15 – 25 % | 3 – 8 % | +25 – 40 % |
| STRONG | +40 – 70 % | 10 – 20 % | +60 – 100 % |
Startup time is unaffected by the obfuscation passes themselves; any
measurable startup delta comes from the runtime self-check
(kagura_self_check) which typically completes in under 5 ms.
String decryption stubs add a one-time per-string decryption cost on first
use. Subsequent accesses hit the decrypted copy in .data without overhead.