A Frida-based Android security research toolkit for extracting and analyzing runtime artifacts — DEX files, IL2CPP binaries, Unity assets, crypto keys, network traffic, and more.
Intended use: penetration testing, CTF challenges, security research, and reverse engineering of apps you own or have explicit permission to analyze.
- Requirements
- Installation
- Quick Start
- Subcommands
- Agent Reference
- Output Files
- Project Layout
- Development
| Dependency | Notes |
|---|---|
| Python 3.12+ | |
| uv | Package manager |
| ADB | In PATH |
| Rooted device or emulator | AVD / Genymotion |
| frida-server | Auto-deployed by enma setup |
Optional (for analyze):
| Tool | Purpose |
|---|---|
| jadx | DEX / Mono DLL decompilation |
| apktool | APK decoding |
| Il2CppDumper | IL2CPP symbol extraction |
strings |
Native .so string extraction |
git clone https://github.com/ykus4/enma
cd enma
uv syncActivate the virtual environment (optional — prefix commands with uv run instead):
source .venv/bin/activateFor Unity asset extraction, install the optional extra:
uv pip install "enma[unity]"# 1. Push frida-server to device
uv run enma setup
# 2. Find the target package name
uv run enma list
# 3. Dump all artifacts
uv run enma dump com.example.game -o ./dump
# 4. Run post-dump analysis
uv run enma analyze ./dump
# 5. Open the HTML report
uv run enma report ./dump
open ./dump/report.htmluv run enma setup
uv run enma setup -s R3CN30XXXXX # specify device serial
uv run enma setup --force # force re-pushDetects device ABI, downloads the matching frida-server from GitHub releases,
pushes it to /data/local/tmp/frida-server, and starts it via su.
Downloaded binaries are cached in ~/.cache/enma/.
uv run enma list
uv run enma list -s <serial> com.example.game My Game [PID 12345]
com.example.another Another App
uv run enma dump <target> [options]| Option | Description |
|---|---|
-t TYPE [TYPE …] |
Run specific agents only (default: all) |
-o DIR |
Output directory (default: ./dump) |
-s SERIAL |
ADB device serial |
--spawn |
Spawn the app instead of attaching |
--watch |
Wait for the process to start, then attach |
--retry |
Re-attach automatically when the process dies |
--timeout N |
Auto-detach after N seconds |
# All agents
uv run enma dump com.example.game
# Selected agents
uv run enma dump com.example.game -t dex il2cpp jni crypto
# Bypass agents only (stealth mode)
uv run enma dump com.example.game -t ssl anti_detect anti_tamper safetynet
# Spawn + custom output + timeout
uv run enma dump com.example.game --spawn -o ./out --timeout 60Available agent types:
| Category | Agents |
|---|---|
| Dump | dex il2cpp assets mono heap |
| Analysis | coverage tracer jni |
| Bypass | ssl crypto anti_detect anti_tamper safetynet |
| Network | http websocket protobuf binder |
| Storage | sqlite fileio dlopen |
uv run enma analyze <dump_dir>
uv run enma analyze ./dump -o custom_report.jsonReads all artifact files in the dump directory, runs optional external tools, and writes report.json.
What it covers:
- DEX obfuscation ratio + jadx decompilation
- IL2CPP method/field counts + security-relevant method flagging
- JNI mappings by native module
- Crypto key and heap key candidate summary
- Native
.soURL / JWT string extraction - TLS, HTTP, WebSocket, SQLite, Binder, File I/O, Protobuf summaries
- dlopen load log, code coverage, anti-tamper bypass log
uv run enma report <dump_dir>
uv run enma report ./dump --json custom.json -o out.htmlConverts report.json into a self-contained dark-theme HTML report with:
- Summary stat cards
- Collapsible sections per category
- Color-coded severity badges (high / med / low / info)
uv run enma repack app.apk
uv run enma repack app.apk -o app-gadget.apk --arch arm64| Option | Description |
|---|---|
-o PATH |
Output APK path |
--arch |
arm64 (default) / arm / x86_64 / x86 |
--keep-workdir |
Keep the intermediate smali directory |
Requires on PATH: apktool, zipalign, and apksigner (Android build-tools) or jarsigner (JDK).
Pipeline:
- Download
frida-gadget-{ver}-android-{arch}.so.xz→ cached in~/.cache/enma/ apktool d— decode APK- Copy gadget
.sotolib/{arch}/ - Inject a smali
Applicationsubclass that callsSystem.loadLibrary("frida-gadget") apktool b→ zipalign → sign with debug keystore
After installing the repacked APK, frida-gadget listens on port 27042 and acts as a standard frida-server.
uv run enma unity <dump_dir>
uv run enma unity ./dump -o ./extractedRequires the enma[unity] extra (see Installation).
Extracts from every .unity3d bundle in the dump directory:
| Asset type | Output |
|---|---|
Texture2D |
PNG |
AudioClip |
WAV / OGG |
TextAsset |
.txt / .bytes |
Sprite |
PNG |
MonoBehaviour |
JSON (via typetree) |
Interactive CheatEngine-style REPL for locating values in process memory.
uv run enma memscan com.example.game
uv run enma memscan com.example.game --spawnmemscan> scan 9999 int32 # first scan — find all addresses = 9999
[*] Scan #1: 48,302 results
# change the value in-game (e.g. spend coins, take damage)
memscan> filter 9990 eq # narrow to addresses now = 9990
[*] Scan #2: 3 results remaining
memscan> results
0x7ff1a2b4 = 9990
0x7ff3c100 = 9990
0x7ff3c104 = 9990
memscan> read 0x7ff1a2b4 int32 # confirm the value
0x7ff1a2b4 = 9990
Commands:
| Command | Description |
|---|---|
scan <value> [type] |
First scan — search all readable memory |
filter <value> [op] |
Narrow results by comparison operator |
results [max] |
Show current result list |
read <addr> <type> |
Read a single value |
reset |
Clear all results and start over |
info |
Show scan state (type, scan count, result count) |
help |
Show command reference |
quit / exit |
Detach and exit |
Supported types: int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double bytes
Filter operators: eq ne gt lt gte lte changed unchanged
uv run enma mempatch com.example.game <addr> [options]| Option | Description |
|---|---|
-t TYPE |
Value type (default: int32) |
-v VALUE |
Value to write |
--nop N |
Write N NOP instructions (arch-aware: arm64 / arm / x86) |
--bytes HEX … |
Write raw bytes, e.g. --bytes DE AD BE EF |
--freeze |
Continuously overwrite the value (Ctrl+C to stop) |
--interval MS |
Freeze interval in ms (default: 100) |
# Write a value once
uv run enma mempatch com.example.game 0x7ff1a2b4 -t int32 -v 99999
# Freeze a float value (e.g. player speed)
uv run enma mempatch com.example.game 0x7ff3c100 -t float -v 9999.0 --freeze
# NOP out 4 instructions (e.g. disable a check)
uv run enma mempatch com.example.game 0x7ff00100 --nop 4
# Raw byte patch
uv run enma mempatch com.example.game 0x7ff00200 --bytes 00 00 A0 E3Tip: use
memscanto locate the address first, thenmempatchto write to it.
Three-stage strategy:
InMemoryDexClassLoaderhook — captures bytes before runtime patchinglibart OpenMemoryhook — intercepts at the ART level- Memory scan — searches for
dex\n035/dex\n039magic
Output: classes_N_*.dex, dex_obfuscation_report.json
- Dumps raw
libil2cpp.soELF - Extracts
global-metadata.datvia/proc/self/maps - Builds an Il2CppDumper-compatible
il2cpp_dump.jsonby walkingil2cpp_domain_get_assemblies
LoadFromMemoryhook (IL2CPP managed)libc open / mmaphook — tracks.unity3d/.bundlefile descriptors- Memory scan for
UnityFS/UnityRaw/UnityWebmagic - APK
AssetManager.list()enumeration
mono_assembly_foreachenumerationmono_assembly_load/mono_image_openhooks for late-loaded DLLs- MZ/PE header scan fallback
| What | File |
|---|---|
SecretKeySpec, PrivateKey |
heap_keys.json |
High-entropy byte[] (entropy ≥ 7.2) |
heap_bytes.json |
SharedPreferences key-value pairs |
heap_prefs.json |
OkHttp3 Request / Response |
heap_http.json |
android.net.Uri |
heap_uris.json |
| Strings matching credential keywords | heap_strings.json |
Uses Frida Stalker to record every unique basic block executed. Skips system libraries. Flushes after 30 s.
Output: coverage.json — {blocks: [{module, rva, size}], summary: {module: count}}
Hooks all exports matching a regex pattern (default: .*).
Records timestamp, thread ID, call depth, arguments (hex), and return value.
Output: trace.json (up to 5,000 records)
Hooks RegisterNatives (or JNI vtable slot 215 as fallback).
Parses JNI type descriptors into human-readable signatures.
Output: jni_map.json — {javaClass, javaMethod, returnType, params, module, rva}
| Target | Technique |
|---|---|
X509TrustManager |
Registers a permissive TrustManager |
OkHttp3 CertificatePinner |
Replaces check() with a no-op |
| TrustKit | Nullifies PinningTrustManager.checkServerTrusted |
| BoringSSL / libssl | SSL_CTX_set_verify → VERIFY_NONE |
| Xamarin | Clears ServicePointManager.ServerCertificateValidationCallback |
Also logs TLS SNI hostnames → tls_connections.json
Java: SecretKeySpec, Cipher.init, KeyGenerator, KeyPairGenerator, MessageDigest
Native: AES_set_*_key, EVP_*Init_ex, d2i_RSAPrivateKey
Memory: 32-byte sliding window, Shannon entropy ≥ 7.4
Output: crypto_keys.json, rsa_private_key.der
java.io.File.exists()spoofing for su / Magisk pathsSystemProperties.get()— returns safe build fingerprint values/proc/self/statusTracerPidpatchingDebug.isDebuggerConnected()→falseBuild.TAGS/FINGERPRINT/MODELfield spoofing
- Logs
PackageInfo.GET_SIGNATUREScalls - Patches
ptrace(PTRACE_TRACEME)→ 0 - Scrubs Frida tokens from
/proc/mapsreads - Logs CRC32 / ZipFile self-checks
Output: anti_tamper_log.json
- Logs
SafetyNetClient.attest()and JWS result tokens - Logs
IntegrityManagercalls - Logs
KeyStore.getCertificateChain(attestation chain) - Patches
LicenseChecker.checkAccess→ always allow
Output: safetynet_log.json
- OkHttp3: injects a
NetworkInterceptorthat peeks request/response bodies HttpURLConnection: logs URL, method, status- Volley: hooks
parseNetworkResponse
Output: http_traffic.json
- OkHttp3: wraps
WebSocketListenerandRealWebSocket.send javax.websocket(Tyrus): hookssendText
Output: websocket_log.json — direction SEND / RECV / OPEN / CLOSE
Hooks protobuf-java (MessageLite.toByteArray, AbstractParser.parseFrom),
Wire (ProtoAdapter), and OkHttp ResponseBody for application/x-protobuf.
Includes a minimal schema-less field decoder (varint, length-delimited, 32/64-bit).
Output: protobuf_log.json
- Java:
startActivity,sendBroadcast,startService(Intent inspection) - ContentProvider:
ContentResolver.query,insert - Native
libbinder:IBinder::transact(transaction code logging)
Output: binder_log.json
Hooks SQLiteDatabase.execSQL, rawQuery, insert, update, delete,
SQLiteStatement, and Jetpack Room SupportSQLiteDatabase.
Output: sqlite_log.json
Java: FileOutputStream / FileInputStream, SharedPreferences.commit,
java.nio.file.Files.write
Native: libc open / write / unlink for paths under /data/data/, /sdcard/, etc.
Output: fileio_log.json
Hooks dlopen, android_dlopen_ext, dlsym, System.loadLibrary.
Immediately dumps newly loaded .so ELF binaries.
Output: dlopen_log.json, dlsym_log.json, dlopen_*.so binaries
| File | Description |
|---|---|
classes_N_*.dex |
Dumped DEX files |
dex_obfuscation_report.json |
Obfuscation ratio (ProGuard / R8 detection) |
libil2cpp.so |
IL2CPP native ELF |
global-metadata.dat |
Unity IL2CPP metadata |
il2cpp_dump.json |
Il2CppDumper-compatible symbol map |
*.dll |
Mono managed assemblies |
asset_N_*.unity3d |
Unity AssetBundles |
tls_connections.json |
TLS SNI hostnames |
crypto_keys.json |
Harvested cryptographic keys |
rsa_private_key.der |
DER-encoded RSA private key |
jni_map.json |
JNI native method registration map |
heap_keys.json |
Live SecretKey / PrivateKey from heap |
heap_bytes.json |
High-entropy byte[] from heap |
heap_prefs.json |
SharedPreferences key-value pairs |
heap_http.json |
OkHttp3 Request / Response objects |
heap_uris.json |
android.net.Uri objects |
heap_strings.json |
Credential-related String objects |
http_traffic.json |
HTTP requests and responses |
websocket_log.json |
WebSocket frames |
protobuf_log.json |
Decoded Protobuf messages |
binder_log.json |
Binder / IPC transactions |
sqlite_log.json |
SQLite operations |
fileio_log.json |
File I/O operations |
dlopen_log.json |
Dynamic library load log |
dlopen_*.so |
Dumped dynamically loaded libraries |
coverage.json |
Frida Stalker code coverage |
trace.json |
Function call trace |
anti_tamper_log.json |
Anti-tamper bypass events |
safetynet_log.json |
SafetyNet / Play Integrity events |
report.json |
Consolidated analysis report |
report.html |
Self-contained HTML report |
enma/
├── pyproject.toml
├── docs/
│ └── architecture.md # System architecture and data-flow diagrams
└── src/enma/
├── cli.py # CLI entry point, argument parser, agent runner
├── device.py # frida-server auto-deploy
├── repack.py # frida-gadget APK injection
├── analyze.py # Post-dump analysis pipeline
├── report.py # HTML report generation
├── unity.py # Unity AssetBundle extraction
├── mem.py # Memory scanner / patcher REPL
├── _util.py # Shared download / cache utilities
├── __init__.py
└── agents/
├── dump/ # dex, il2cpp, assets, mono, heap
├── analysis/ # coverage, tracer, jni
├── bypass/ # ssl, crypto, anti_detect, anti_tamper, safetynet
├── network/ # http, websocket, protobuf, binder
├── storage/ # sqlite, fileio, dlopen
├── ue4/ # ue4_sdk, ue4_pak, ue4_blueprint
└── mem/ # memscan, mempatch
uv run ruff check src/
uv run ruff check src/ --fix
uv run ruff format src/uv run pre-commit install # install hooks (once)
uv run pre-commit run --all-files # run manually- Create
src/enma/agents/{category}/{name}_agent.js - Add the agent name and category to
AGENT_NAMESand_AGENT_DIRin cli.py - Add an analyzer function
_analyze_{name}to analyze.py - Add a renderer
_render_{name}and entry to_RENDERERSin report.py
Agent message protocol (JavaScript → Python):
send({ event: "log", message: "something happened" });
send({ event: "file", name: "output.bin" }, arrayBuffer);
send({ event: "json", name: "result.json", data: { key: "value" } });Use only against apps you own or have explicit written authorization to analyze. Dumped files may contain sensitive cryptographic material — handle with care.