LLVM-based code obfuscation and anti-tamper toolkit for iOS and Android native binaries.
Built on the New Pass Manager (LLVM 17+). Loaded as a pass plugin via -fpass-plugin — no LLVM source tree modification required.
kagura/
├── include/kagura/ # Public headers (Passes.h, Options.h, Utils.h, game_protect.h)
├── lib/Transforms/
│ ├── CFG/ # Control-flow obfuscation passes
│ ├── Data/ # String/constant/global/wide-string/memory-value encryption
│ ├── AntiAnalysis/ # Anti-debug, integrity, call indirection, honey values
│ ├── Platform/ # iOS (ObjC), Android (JNI), VM virtualization
│ ├── Infrastructure/ # DWARF control, config DSL, symbol map
│ ├── Options.cpp # Centralized CLI flag definitions
│ ├── Plugin.cpp # Pass registration & pipeline wiring
│ └── Utils.cpp # Shared IR helpers & PRNG
├── runtime/
│ ├── core/ # AES, VM interpreter, crash symbolication, device key
│ ├── anti_debug/ # Anti-debug / anti-Frida (cross-platform)
│ ├── android/ # Android + Linux: root detection, attestation, proc/syscall
│ ├── ios/ # iOS / macOS: jailbreak detection, Mach-O integrity
│ └── game/ # Anti-cheat, IL2CPP protection, telemetry
├── integration/ # Xcode, Gradle, Unity, Unreal, CMake, Bazel, CocoaPods, SPM
├── scripts/ # CLI tools, verification, differential testing, review risk assessment
└── tests/ # CTest + FileCheck lit-based regression tests
| Flag | Pass | Effect |
|---|---|---|
-kagura-fla |
ControlFlowFlattening | Converts CFG into a switch-based state machine |
-kagura-bcf |
BogusControlFlow | Injects dead blocks guarded by MBA opaque predicates |
-kagura-ibr |
IndirectBranch | Replaces direct calls with loads from function pointer globals |
-kagura-ci |
CallIndirection | Routes external calls through a runtime-resolved thunk table |
-kagura-lt |
LoopTransform | Adds bogus dead counters and opaque invariant branches |
-kagura-fsplit |
FunctionSplit | Extracts interior basic blocks into outlined helper functions |
-kagura-bbs |
BasicBlockSplitting | Splits large BBs at random points to inflate CFG complexity |
-kagura-bbr |
BasicBlockReordering | Shuffles BB layout to confuse linear disassemblers |
-kagura-dci |
DeadCodeInsertion | Inserts unreachable junk blocks to mislead static analysis |
-kagura-elt |
EncryptedLookupTable | Transforms switch statements into XOR-encrypted dispatch tables |
-kagura-vtp |
VTableProtection | Obfuscates C++ RTTI typeinfo names (_ZTS*); records vtable metadata |
| Flag | Pass | Effect |
|---|---|---|
-kagura-str |
StringEncryption | XOR-encrypts narrow string literals; lazy decrypt on first access |
-kagura-str-aes |
StringEncryptionAES | AES-128-CTR string encryption (requires runtime) |
-kagura-wstr |
WideStringEncryption | XOR-encrypts wide strings (wchar_t / char16_t / char32_t) and CFString buffers |
-kagura-co |
ConstantObfuscation | Replaces integer constants with MBA expressions |
-kagura-sub |
Substitution | Replaces arithmetic/bitwise ops with equivalent MBA |
-kagura-genc |
GlobalEncryption | Encrypts private integer globals; inline XOR at load sites |
-kagura-mvo |
MemoryValueObfuscation | XOR-encrypts alloca'd integer locals at every store/load site |
-kagura-pe |
PointerEncryption | XOR-encrypts alloca'd pointer variables to defeat memory-dump analysis |
| Flag | Pass | Effect |
|---|---|---|
-kagura-anti-debug |
AntiDebug | ptrace, Frida port, /proc/maps, hook, breakpoint, emulator checks |
-kagura-tamper |
AntiTamper | FNV-1a function checksums + jailbreak/root detection at startup |
-kagura-pac |
PointerAuth | Software CFI via XOR-tagged function pointer globals |
-kagura-sv |
SymbolVisibility | Sets non-public symbols to hidden; strips from dynamic symtab |
-kagura-honey |
HoneyValue | Injects decoy secret globals and fake security-stub functions |
-kagura-bbcheck |
BasicBlockChecksum | Injects per-BB opcode checksums; aborts on binary patch detection |
-kagura-telemetry |
Telemetry | Inserts behavioral event probes at function entry for cheat detection |
| Flag | Pass | Target |
|---|---|---|
-kagura-objc |
ObjCObfuscation | iOS — obfuscates ObjC selector and class names in IR metadata |
-kagura-jni |
JNIObfuscation | Android — converts static Java_* to dynamic RegisterNatives |
-kagura-vm |
VMObfuscation | Virtualizes function bodies into a custom stack-based VM bytecode |
| Flag | Pass | Effect |
|---|---|---|
-kagura-dwarf=strip|obfuscate |
DWARFControl | Strip or remap DWARF debug info after obfuscation |
-kagura-config=<file> |
ConfigLoader | Load JSON policy file; apply profile preset and per-pass overrides |
-kagura-symmap |
SymbolMap | Emit JSON symbol map (original → obfuscated name) for crash symbolication |
-kagura-audit |
AuditLog | Emit JSON audit log of every protected symbol and applied passes |
| Flag | Pass | Effect |
|---|---|---|
-kagura-metrics |
ObfuscationMetrics | Prints BB/instruction/cyclomatic complexity delta |
| Option | Default | Description |
|---|---|---|
-kagura-seed=<N> |
0 (entropy) |
PRNG seed for reproducible output |
-kagura-bcf-prob=<N> |
30 |
Bogus CF probability per BB [0-100] |
-kagura-bcf-iter=<N> |
1 |
Bogus CF iterations |
-kagura-sub-iter=<N> |
1 |
Substitution iterations |
-kagura-dci-prob=<N> |
40 |
Dead code insertion probability [0-100] |
| Option | Default | Description |
|---|---|---|
-kagura-lto-safe |
false |
Enable passes during LTO/ThinLTO pipeline phases |
-kagura-o0-protect |
false |
Enable lightweight protection (STR, AntiDebug) at -O0 |
-kagura-dwarf=<mode> |
keep |
DWARF handling: keep / strip / obfuscate |
-kagura-build-id=<id> |
— | Build identifier mixed into PRNG seed for per-build key rotation |
| Option | Default | Description |
|---|---|---|
-kagura-config=<path> |
— | Path to JSON policy file |
-kagura-symmap |
false |
Emit symbol map after obfuscation |
-kagura-symmap-out=<path> |
kagura_symbols.json |
Output file for symbol map |
-kagura-audit |
false |
Emit audit log of all protected symbols |
-kagura-audit-out=<path> |
kagura_audit.json |
Output file for audit log |
| Option | Default | Description |
|---|---|---|
-kagura-protect=<pattern> |
— | Force-protect matching symbols (comma-separated, * glob) |
-kagura-deny=<pattern> |
— | Exclude matching symbols from all obfuscation |
-kagura-allow=<pattern> |
— | Allowlist mode: only obfuscate matching symbols |
Kagura supports a JSON policy file (-kagura-config=<path>) to control all pass settings in one place.
{
"profile": "BALANCED",
"passes": {
"str": true,
"fla": true,
"bcf": true,
"honey": true,
"mvo": false
},
"tuning": {
"bcf_prob": 40,
"seed": 12345
}
}Built-in profiles:
| Profile | Passes | Intended use |
|---|---|---|
FAST |
STR only | Debug/CI builds with minimal overhead |
BALANCED |
STR + BCF + BBR + BBS + GENC + MVO | Standard Release builds |
STRONG |
All passes, BCF prob 60, 2 iterations | Security-critical shipping builds |
include/kagura/game_protect.h provides a C++17 header-only Protected<T> template for protecting game-critical values (HP, damage, currency, etc.) from memory scanners and freeze tools.
#include "kagura/game_protect.h"
kagura::Protected<int> hp(100);
kagura::Protected<float> speed(5.5f);
hp -= 30;
if (hp <= 0) die();
// Optional tamper callback (default: spin forever to deny clean crash point)
kagura::Protected<int>::setTamperCallback([]{ report_cheat(); });Protection strategy:
- Value is stored XOR-encrypted with a per-instance runtime key (ASLR + stack entropy)
- A shadow copy (different XOR mask) enables external-write detection
- Plaintext value is never resident in memory between reads and writes
Convenience aliases: ProtectedInt, ProtectedFloat, ProtectedDouble, ProtectedInt64, etc.
- LLVM 17 – 22 (tested on 17, 18, 19, 21, 22)
- CMake 3.20+
- C++17
Pre-built plugin binaries are published for each release on the GitHub Releases page.
kagura-<version>-macos-arm64-llvm21.tar.gz
kagura-<version>-macos-arm64-llvm22.tar.gz
kagura-<version>-linux-x86_64-llvm19.tar.gz
kagura-<version>-linux-x86_64-llvm21.tar.gz
kagura-<version>-linux-x86_64-llvm22.tar.gz
Each archive contains:
plugin/KaguraObfuscator.{dylib,so}runtime/libkagura_runtime.ainclude/kagura/*.h
# macOS (Homebrew LLVM)
brew install llvm
bash build.sh
# Custom LLVM
cmake -B build \
-DLLVM_DIR=/path/to/llvm/lib/cmake/llvm \
-DCMAKE_C_COMPILER=/path/to/clang \
-DCMAKE_CXX_COMPILER=/path/to/clang++ \
.
cmake --build build -j$(nproc)Output: build/lib/Transforms/KaguraObfuscator.dylib (or .so on Linux).
clang -fpass-plugin=build/lib/Transforms/KaguraObfuscator.dylib \
-mllvm -kagura-str \
-mllvm -kagura-fla \
-mllvm -kagura-bcf \
-mllvm -kagura-bcf-prob=50 \
-O1 your_file.c -o your_fileclang -fpass-plugin=build/lib/Transforms/KaguraObfuscator.dylib \
-mllvm -kagura-config=kagura.json \
-O1 your_file.c -o your_fileclang -O1 -emit-llvm -c your_file.c -o your_file.bc
opt --load-pass-plugin=build/lib/Transforms/KaguraObfuscator.dylib \
-passes="kagura-str,function(kagura-fla,kagura-bcf,kagura-sub)" \
your_file.bc -o your_file.opt.bc
clang your_file.opt.bc -o your_file// Force-enable a pass for this function
__attribute__((annotate("kagura_fla")))
void critical_function(void) { ... }
// Force-disable a pass for this function
__attribute__((annotate("kagura_nofla")))
void performance_sensitive(void) { ... }
// Virtualize with the VM pass
__attribute__((annotate("kagura_vm")))
int verify_license(const char *key) { ... }The plugin auto-applies this order via registerOptimizerLastEPCallback:
-O1 / -O2 (standard optimizations first)
1. kagura-config → load JSON policy (if -kagura-config is set)
2. kagura-ci → external call indirection
3. kagura-pac → pointer auth
4. kagura-str[-aes] → encrypt narrow strings
5. kagura-wstr → encrypt wide strings / CFString
6. kagura-tamper → integrity hash (before CFG changes)
7. kagura-objc → ObjC selector/class obfuscation
8. kagura-jni → JNI dynamic registration
9. kagura-anti-debug → anti-analysis checks
10. kagura-fsplit → function splitting
11. kagura-genc → encrypt globals
12. kagura-honey → inject honey values and fake stubs
13. kagura-sv → hide symbols
14. kagura-fla → CFG flattening ┐
15. kagura-bcf → bogus control flow │
16. kagura-bbs → BB splitting │
17. kagura-bbr → BB reordering │
18. kagura-dci → dead code insertion │
19. kagura-sub → instruction subst. │ function passes
20. kagura-co → constant obfuscation │
21. kagura-mvo → memory value XOR │
22. kagura-pe → pointer encryption │
23. kagura-telemetry → telemetry probes │
24. kagura-bbcheck → BB checksum guards │
25. kagura-elt → encrypted lookup tbl ┘
26. kagura-dwarf-control → DWARF strip/obfuscate (if -kagura-dwarf != keep)
27. kagura-vtp → RTTI/vtable protection
28. kagura-symmap → emit JSON symbol map (if -kagura-symmap)
29. kagura-audit → emit audit log (if -kagura-audit)
Some passes require linking libkagura_runtime.a:
| Pass | Required Symbols |
|---|---|
| StringEncryptionAES | kagura_aes128_ctr_decrypt |
| VMObfuscation | kagura_vm_execute |
| AntiDebug | kagura_anti_debug_init, kagura_check_hooks, kagura_check_breakpoints, kagura_check_emulator |
| AntiTamper | kagura_self_check, kagura_tamper_detected |
| CallIndirection | dlsym (system) |
| PointerAuth | kagura_random_u64 |
| StringEncryptionAES | kagura_zero_buf |
clang your_file.c build/runtime/libkagura_runtime.a -o your_fileThe runtime also provides anti-tamper checks callable directly:
#include "kagura/runtime.h"
kagura_self_check(); // Mach-O / ELF integrity + jailbreak/root
kagura_check_loaded_libraries(); // Suspicious dylib/so scan
kagura_run_review_risk_check(); // App Store / Play Store pre-submission scan| Platform | Quick setup | Guide |
|---|---|---|
| Xcode | Add integration/xcode/kagura.xcconfig + run script phase |
Xcode Integration Guide |
| Android (Gradle / NDK) | apply from: "kagura/integration/android/kagura.gradle" |
Android NDK Integration Guide |
| Unity (IL2CPP) | Copy Editor/KaguraPostBuildProcessor.cs to Assets/Editor/ |
Unity Integration Guide |
| Unreal Engine 5 | Copy KaguraToolchain.cs to UBT toolchain path |
Unreal Engine Integration Guide |
| CMake (Cocos2d-x, etc.) | -DCMAKE_TOOLCHAIN_FILE=kagura-toolchain.cmake |
Android NDK Integration Guide |
| Bazel | load("@kagura//integration/bazel:kagura.bzl", "kagura_cc_binary") |
Bazel Integration Guide |
| CocoaPods | pod 'KaguraRuntime' + xcconfig for plugin flag |
CocoaPods Integration Guide |
| Swift Package Manager | .product(name: "KaguraRuntime", package: "kagura") |
SPM Integration Guide |
cd build && ctest --output-on-failureVerify that a fixed seed produces byte-identical IR across two builds:
./scripts/verify-reproducible.sh
# [kagura-repro] PASS: Both builds produced identical IR.Verify that obfuscated binaries produce the same output as plain binaries:
./scripts/differential-test.sh
# [diff-test] arithmetic_test ... PASS
# [diff-test] combined_test ... PASS
# Results: 8 passed, 0 failed, 0 skippedScan a compiled binary for patterns that may trigger store review rejection:
./scripts/review-risk-assessment.sh path/to/MyApp.dylib --platform ios
# [HIGH ] [SEC-PIE] ...
# [INFO ] [ENC-DECL] No obvious encryption keyword references found.
# RESULT: No critical or high review risks detected.| Script | Purpose |
|---|---|
scripts/kagura-cli.py |
Config generator, audit log viewer, symbol map analyzer |
scripts/variant_generator.py |
Per-customer / per-app variant generation with custom keys |
scripts/attacker_cost_model.py |
Estimate attacker reverse-engineering cost (analyst-hours) |
scripts/battery_impact.py |
Model battery / CPU impact of runtime passes |
scripts/license_manager.py |
Generate, validate, and revoke time-limited license tokens |
# Symbolic execution resistance (angr)
cd tests/symbolic_exec && python3 run_angr_eval.py --binary /tmp/my_binary --timeout 30
# Decompiler resistance (Ghidra)
cd tests/decompiler_eval && python3 run_ghidra_eval.py --binary /tmp/my_binary --ghidra /path/to/ghidra
# Frida instrumentation resistance (probes F1-F8)
cd tests/frida_resistance && for s in probes/F*.js; do frida -l "$s" -f /tmp/my_binary; done
# Full red-team report
cd tests/redteam && python3 run_redteam.py --binary /tmp/my_binary --report report.jsonMIT — see LICENSE.