Skip to content

Repository files navigation

CyberArmor — Android APK Hardening & Reinforcement Toolkit

License: MIT Kotlin Spring Boot Android NDK Verified on Android 14 Release PRs welcome

CyberArmor

An in-house Android APK hardening / reinforcement toolkit — the same class of tool as 360 Jiagu. It takes a built APK, pulls out and encrypts its DEX behind a native stub loader, repoints the manifest at that loader, applies runtime self-protection (RASP), and re-signs the result — so the shipped APK is resistant to decompilation, repackaging, and runtime tampering.

Runtime-validated on a real device (Redmi, Android 14, arm64): a hardened app boots through the stub, decrypts its DEX in memory, and runs identically to the original.

CyberArmor hardening a real APK, then running it on-device with data encrypted at rest


Table of contents

  1. Architecture
  2. Prerequisites
  3. Installation & setup
  4. Quick start
  5. CLI reference
  6. Protection profiles
  7. REST service
  8. How it works
  9. Verifying on a device
  10. Roadmap
  11. Limitations & caveats
  12. Legal

Architecture

The build-time packer is JVM (Kotlin). The on-device protection shell must be native — it runs before the JVM app code, does anti-debug, decrypts the DEX in memory, and hooks the runtime. So the project is polyglot by necessity.

Module Language Runs Role
packer-core Kotlin server the hardening pipeline (library) — DEX crypto, binary-AXML manifest patch, zip repackage, sign
packer-service Kotlin + Spring Boot 4 server REST API: upload → harden → download
packer-cli Kotlin + Clikt server command-line packer
shell-native C++ / NDK phone libcyberarmor.so — anti-debug, anti-ptrace, RASP detectors, PLT hooker, transparent asset decryption
shell-stub Java → DEX phone StubApp bootstrap loader + CyberArmorAssets API
flowchart LR
  A([Input APK]) --> B[Extract DEX]
  B --> C[AES-256-GCM<br/>encrypt]
  C --> D[Patch manifest<br/>&rarr; StubApp]
  D --> E[Inject stub +<br/>native shell + payload]
  E --> F[zipalign]
  F --> G[apksigner<br/>v1+v2+v3]
  G --> H([Hardened APK])

  subgraph RT [on-device at launch]
    direction LR
    I[StubApp] --> J[RASP guards]
    J --> K[Decrypt DEX<br/>in memory]
    K --> L[Inject into<br/>classloader]
    L --> M[Run real app]
  end
  H -.-> I
Loading

Prerequisites

Tool Version used Notes
JDK 21 Spring Boot 4 baseline; JAVA_HOME set
Android SDK build-tools 36.1.0 provides zipalign, apksigner, d8, aapt2
Android NDK 27.x builds the native shell
CMake + Ninja 3.22 (bundled in SDK) native build
Gradle 8.14.3 (via wrapper) ./gradlew — do not use a system Gradle < 8.14

The Gradle wrapper pins Gradle 8.14.3 (Spring Boot 4 requires ≥ 8.14). No global Gradle needed. The pipeline shells out only to SDK zipalign/apksigner/d8; apktool is not required (manifest editing is done with a built-in binary-AXML editor, resources are never re-encoded).


Installation & setup

1. Clone & point at your SDK

Create local.properties in the repo root:

sdk.dir=/home/<you>/Android/Sdk
cyberarmor.buildToolsVersion=36.1.0
cyberarmor.ndkVersion=27.1.12297006

(Alternatively export ANDROID_HOME. The build-tools/NDK versions auto-detect to the newest installed if omitted.)

2. Build the on-device artifacts (native shell + stub)

./shell-native/build-native.sh     # -> shell-native/build/<abi>/libcyberarmor.so  (4 ABIs)
./shell-stub/build-stub.sh         # -> shell-stub/build/classes.dex

3. Build the JVM modules

./gradlew build                    # compiles + tests packer-core / service / cli
./gradlew :packer-cli:installDist  # produces the CLI launcher

4. Create a signing keystore (once)

A hardened APK is always re-signed, so you need a key you control:

keytool -genkeypair -v -keystore keys/cyberarmor.jks -storepass changeit \
  -alias cyberarmor -keypass changeit -keyalg RSA -keysize 2048 -validity 10000 \
  -dname "CN=CyberArmor"

Use your real release key for production. Re-signing changes the app's signing identity — coordinate the key with whoever publishes the app.


Quick start

Harden an APK with the recommended default protection profile:

./packer-cli/build/install/cyberarmor/bin/cyberarmor \
  -i app-release.apk \
  -o app-hardened.apk \
  --stub-dex   shell-stub/build/classes.dex \
  --shell-arm64 shell-native/build/arm64-v8a/libcyberarmor.so \
  --shell-arm32 shell-native/build/armeabi-v7a/libcyberarmor.so \
  --ks keys/cyberarmor.jks --ks-pass changeit --ks-alias cyberarmor

Output is a signed (v1+v2+v3), aligned, hardened APK ready to install.


CLI reference

cyberarmor -i <input.apk> -o <output.apk> [options]
Option Description
-i, --input <file> (required) raw APK to harden
-o, --output <file> (required) destination for the hardened APK
--stub-dex <file> (required) compiled StubApp dex (shell-stub/build/classes.dex)
--shell-arm64 <file> libcyberarmor.so for arm64-v8a
--shell-arm32 <file> libcyberarmor.so for armeabi-v7a
--ks <file> signing keystore (omit to leave unsigned — debug only)
--ks-pass <pw> keystore password
--ks-alias <alias> key alias
--protection <profile> default (recommended) · strict · none. See below
--protect-asset <glob> encrypt matching assets, transparently decrypted at runtime. Repeatable. e.g. --protect-asset "assets/config/keys.json"
--protect-so encrypt the app's own native libraries; transparently decrypted + loaded in memory, key bound to the signing cert
--anti-ptrace opt-in: block/detect ptrace (ART-safe; disables native crash tombstones)
--root-detection opt-in: refuse to run on rooted/Magisk devices
--emulator-detection opt-in: refuse to run on emulators
--double-open opt-in: detect app-clone / dual-instance containers
--accessibility opt-in: detect enabled accessibility-service automation
--log-suppress opt-in: silence the app's logcat output (PLT hook)
--keep-work keep intermediate build files under work/cli

Examples

Strictest profile + asset protection:

cyberarmor -i app.apk -o app-hardened.apk \
  --stub-dex shell-stub/build/classes.dex \
  --shell-arm64 shell-native/build/arm64-v8a/libcyberarmor.so \
  --shell-arm32 shell-native/build/armeabi-v7a/libcyberarmor.so \
  --protection strict \
  --protect-asset "assets/*.json" \
  --ks keys/cyberarmor.jks --ks-pass changeit --ks-alias cyberarmor

Packing only (no runtime guards), useful for debugging a launch:

cyberarmor -i app.apk -o app-packed.apk --protection none \
  --stub-dex shell-stub/build/classes.dex \
  --shell-arm64 shell-native/build/arm64-v8a/libcyberarmor.so \
  --ks keys/cyberarmor.jks --ks-pass changeit --ks-alias cyberarmor

Protection profiles

Guard none default strict
DEX encryption + in-memory load
Re-signing (v1+v2+v3)
Signature verification (anti-repackage)
File-integrity verification
Anti-debug (TracerPid)
Anti-Frida / Xposed
Screen-capture prevention (FLAG_SECURE)
Anti-hijacking (obscured-touch)
Anti-ptrace
Root / emulator detection
Double-open / accessibility detection
Log suppression

Any strict-only guard can be added to default with its individual flag. Asset protection is independent — add --protect-asset to any profile.


REST service

The Spring Boot 4 service exposes the same pipeline over HTTP. Configure server-side artifact paths in packer-service/src/main/resources/application.yml (cyberarmor.stub-dex, cyberarmor.native-shells, cyberarmor.signing), then:

./gradlew :packer-service:bootRun
Method Endpoint Purpose
POST /api/harden (multipart apk) submit an APK → returns a job id (202)
GET /api/harden/{id} job status + step report
GET /api/harden/{id}/download download the hardened APK
curl -F apk=@app-release.apk http://localhost:8080/api/harden       # -> {"id": "...", ...}
curl http://localhost:8080/api/harden/<id>                          # status
curl -OJ http://localhost:8080/api/harden/<id>/download             # hardened apk

How it works

Build time (packer-core): read the original classes*.dex and binary AndroidManifest.xml straight from the APK zip → AES-256-GCM encrypt the DEX into assets/cyberarmor.payload (data key wrapped by a KEK the native shell holds) → surgically patch the manifest's application android:name to com.cyberarmor.stub.StubApp (resource-id-sorted binary-AXML edit; resources are never touched) → repackage the zip with the stub dex, native shells, payload and guard metadata → zipalignapksigner.

Runtime (shell-stub + shell-native): StubApp.attachBaseContext loads libcyberarmor.so, runs the enabled guards, verifies the signing cert and payload hash, decrypts the DEX with the KEK from native, and injects it into the app's classloader (InMemoryDexClassLoader merged into the base PathClassLoader) so the original code — including manifest-declared components — resolves normally. Protected assets are decrypted and served transparently through a native hook of AssetManager.nativeOpenAsset.


Benchmarks

Size impact of hardening (default profile), measured on real sample APKs. The DEX is compressed before encryption, so the encrypted payload stays close to the original packaged size; the overhead is essentially the injected native shells (~1.5 MB across arm64 + arm32). Large apps can even end up smaller than the original when our max-compression beats the app's original packaging.

App Original Hardened Overhead
powrantv 8.88 MB 9.76 MB +0.88 MB (+10.0%)
Thogaitv BoxApp 10.98 MB 12.03 MB +1.05 MB (+9.6%)
CSM CHANNEL 28.12 MB 26.97 MB −1.15 MB (−4.1%)

Runtime guards (signature/integrity/anti-debug/anti-Frida/FLAG_SECURE/anti-hijack), SO protection, and asset protection add negligible size — guards are metadata flags, and encrypted .so/asset copies replace their originals in place. Reproduce with --protection none|default|strict, --protect-so, --protect-asset.

Without pre-compression the same apps ballooned +74% to +177% (AES ciphertext is incompressible); compress-then-encrypt is what keeps hardened APKs distributable.

Verifying on a device

adb install -r -d app-hardened.apk
adb shell monkey -p <your.package> -c android.intent.category.LAUNCHER 1
adb logcat | grep -i CyberArmor        # guard/init messages (unless --log-suppress)

With --protection default/strict, FLAG_SECURE is on, so screenshots of the app appear black — that is screen-capture prevention working, not a bug. Build with --protection none if you need to screenshot the UI.


Roadmap

Detailed status per feature lives in FEATURES.md; each shipped feature has a page under docs/. The program is organized in phases:

✅ Phase 1 — Packer MVP (done)

DEX AES-256-GCM encryption + in-memory loading, surgical binary-AXML manifest repoint, raw-zip repackaging (resources never re-encoded), zipalign + apksigner (v1+v2+v3).

✅ Phase 2 — Integrity + core RASP (done)

Signature verification (anti-repackaging), file-integrity check, unique per-build fingerprint, anti-debug, anti-Frida/Xposed, FLAG_SECURE screen-capture prevention, anti-hijacking.

✅ Phase 3 — Assets, runtime guards & hooking (done)

Transparent asset encryption (native AssetManager hook), root/emulator/double-open/accessibility detection (opt-in), ART-safe anti-ptrace (opt-in), PLT/GOT hooker, log-leak prevention.

🟡 Phase 3b — Heavy obfuscation (in progress)

  • SO protection / anti-theft (B1/B2) — encrypt the app's own .so; native in-memory loader (memfd + android_dlopen_ext hook), bound to the signing cert. Device-verified (--protect-so). Also landed: a robust custom-Application swap (Hilt/Dagger apps).
  • Local files protection (D2)CyberArmorStorage API: AES-256-GCM encrypted files + preferences under the app's private dir, key bound to the per-build DEX key. Device-verified.
  • Per-build KEK + native master-key crypto — unique KEK baked into the shell per build, DEK unwrapped in native (ca_crypto.h); the master key never enters the DEX/Java.
  • String encryption (A2) — DEX-instruction rewrite: const-string → decrypt call.
  • Transparent asset/SO hook hardening — multi-Android-version validation of the libandroidfw ABI coupling.

🧊 Phase 4 — VMP tier (long-horizon R&D)

DEX VMP, lifecycle/registered-class VMP, Dex2C, QDEX, Dex Shadow, full/custom VMP, and memory-tamper detection (code-section checksums). These require a custom bytecode VM and/or native code generation — see docs/roadmap.md.


Limitations & caveats

  • Transparent asset protection is ABI-coupled to libandroidfw symbols (Asset::createFromFd, AssetManager2::Open) and validated on Android 14. It resolves symbols by name and no-ops safely if they're absent, but re-validate per major Android version.
  • --anti-ptrace disables native crash tombstones (via PR_SET_DUMPABLE(0)). Leave it off if the app ships an NDK crash reporter.
  • Master key (KEK): unique per build, patched into the native shell at hardening time and used only in native (it never appears in the DEX/Java). The per-build DEX key is unwrapped in native; the bulk DEX decrypt stays on Java's hardware-accelerated AES to avoid a main-thread stall on large apps.
  • The advanced VMP / Dex2C / string-encryption tier is not implemented — see FEATURES.md.

Legal

Only harden APKs your organization owns or is explicitly authorized to repackage and re-sign. Re-signing changes the app's identity and invalidates the original signature.

About

Android APK hardening & reinforcement toolkit (360-Jiagu-style): DEX / native-lib / asset encryption with in-memory loading, RASP self-protection, anti-debug / anti-tamper / anti-repackaging, transparent AssetManager & dlopen hooks, and re-signing. Kotlin · Spring Boot 4 · C++/NDK.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages