Current supported target scope:
- Flutter AOT Android ARM64 binaries
- static analysis only
Inputs:
- APK
libapp.so
If you want the shortest path:
- run without installing:
nix run - install persistently with Nix:
nix profile install - install a release binary
From GitHub:
nix run github:caverav/flutterdec -- --help
nix run github:caverav/flutterdec -- info ./sample.apk --json
nix run github:caverav/flutterdec -- decompile ./sample.apk -o ./outFrom a local checkout:
nix run . -- --help
nix run . -- info ./sample.apk --json
nix run . -- decompile ./sample.apk -o ./outInstall from GitHub:
nix profile install github:caverav/flutterdec
flutterdec --helpInstall from a local checkout:
nix profile install .
flutterdec --helpUpdate later:
nix profile upgrade flutterdecCurrent prerelease: v0.1.0-alpha.2
Linux x64:
curl -fLO https://github.com/caverav/flutterdec/releases/download/v0.1.0-alpha.2/flutterdec-v0.1.0-alpha.2-Linux-X64.tar.gz
tar -xzf flutterdec-v0.1.0-alpha.2-Linux-X64.tar.gz
sudo install -m 0755 flutterdec /usr/local/bin/flutterdec
flutterdec --helpmacOS arm64:
curl -fLO https://github.com/caverav/flutterdec/releases/download/v0.1.0-alpha.2/flutterdec-v0.1.0-alpha.2-macOS-ARM64.tar.gz
tar -xzf flutterdec-v0.1.0-alpha.2-macOS-ARM64.tar.gz
sudo install -m 0755 flutterdec /usr/local/bin/flutterdec
flutterdec --helpOther platforms and future tags:
Install into the user Cargo bin:
nix develop -c cargo install --path crates/flutterdec-cli
~/.cargo/bin/flutterdec --helpRun from source without installing:
nix develop -c cargo run -p flutterdec-cli -- --help
nix develop -c cargo run -p flutterdec-cli -- info ./sample.apk --json
nix develop -c cargo run -p flutterdec-cli -- decompile ./sample.apk -o ./outBuild a local release binary:
nix develop -c cargo build -p flutterdec-cli --release
./target/release/flutterdec --helpIf this is your first run, this is the shortest useful path.
- Inspect the target:
flutterdec info ./sample.apk --jsonFor APK inputs, info also reports Android startup summary fields:
android_startup_presentandroid_startup_confidenceandroid_startup_entrypoint_countandroid_startup_flutter_activity_count
If adapter metadata is available, info also reports:
app_package_counts_topadapter_kindmanifest_entry_presentadapter_snapshot_hash_matchcompatibility_warnings
- Install the adapter for the detected Dart hash:
flutterdec adapter install --dart-hash <HASH>
flutterdec adapter list- Decompile:
flutterdec decompile ./sample.apk -o ./out- Start with:
out/pseudocode/*.dartpseudoout/report.jsonout/quality.json
Inspect target metadata:
flutterdec info ./sample.apk --jsonDecompile with the default app-focused scope:
flutterdec decompile ./sample.apk -o ./outDecompile with extra artifacts:
flutterdec decompile ./sample.apk -o ./out --emit-asm --emit-irCompare two builds at recovered-function level:
flutterdec diff --old ./old.apk --new ./new.apk -o ./out-diff --jsonGenerate a Ghidra import script with recovered symbols:
flutterdec decompile ./sample.apk -o ./out --emit-ghidra-scriptGenerate an IDA import script with recovered symbols:
flutterdec decompile ./sample.apk -o ./out --emit-ida-scriptDefault scope is app-focused:
app-unknown(default): app (package:*) plus unknown ownership functionsapp: only app (package:*) functionsall: include Flutter, Dart runtime, and framework internals too
Include everything:
flutterdec decompile ./sample.apk -o ./out --function-scope allLimit output to selected app packages:
flutterdec decompile ./sample.apk -o ./out \
--function-scope app-unknown \
--app-package my_appTip: if you are not sure about package names, check report.json under function_scope.app_package_counts_top.
Decompile and disassemble one specific function:
flutterdec decompile ./sample.apk -o ./out \
--target id:42 \
--emit-asmTarget by entry address:
flutterdec decompile ./sample.apk -o ./out \
--target va:0x613468 \
--emit-asm--target accepts id:<N>, va:0x<ADDR>, 0x<ADDR>, or <N>.
When <N> matches multiple functions, the command fails and asks for explicit id: or va:.
Target mode emits only the matched function artifacts and reports selection details in report.json.target_selection.
If the match is outside current scope filters, target mode can override scope automatically for that function.
decompile profile controls analysis depth vs throughput.
Profile options:
balanced(default): best readability and semantic recoverylight: reduced analysis for faster large-scale runs
Examples:
flutterdec decompile ./sample.apk -o ./out --analysis-profile balanced
flutterdec decompile ./sample.apk -o ./out --analysis-profile lightAdapter backend options:
--adapter-backend auto(default): attempt Blutter bridge backend when configured, otherwise fallback to internal adapter--adapter-backend internal: force internal adapter only--adapter-backend blutter: require Blutter bridge backend with no fallback--require-snapshot-hash-match: fail if adapter snapshot hash does not match loader snapshot hash
Blutter bridge environment variables:
FLUTTERDEC_BLUTTER_CMD: full command used to launch Blutter, for examplepython3 /opt/blutter/blutter.pyFLUTTERDEC_BLUTTER_PY: direct path toblutter.py
Nix setup note:
- in
nix develop,FLUTTERDEC_BLUTTER_CMDis exported automatically to a Nix-managedflutterdec-blutterwrapper - direct wrapper invocation is available with
nix run .#blutter-bridge -- --help
Per-feature toggles:
--with-canonical-model-symbols/--no-canonical-model-symbols--with-pool-value-hints/--no-pool-value-hints--with-pool-semantic-hints/--no-pool-semantic-hints--with-semantic-reporting/--no-semantic-reporting--with-bootflow-category-seeds/--no-bootflow-category-seeds--with-apk-startup-analysis/--no-apk-startup-analysis
Decompile quality checks are controlled by:
--max-placeholder-ifs--max-unresolved-cf--max-indirect-call-ratio--min-disassembly-ratio
Example for exploratory runs:
flutterdec decompile ./sample.apk -o ./out \
--max-functions 250 \
--max-placeholder-ifs 999999 \
--max-unresolved-cf 999999 \
--max-indirect-call-ratio 1.0 \
--min-disassembly-ratio 0.0Generate direct-call target mapping from a stripped/unstripped engine pair:
flutterdec map-symbols \
--stripped ./libflutter.stripped.so \
--unstripped ./libflutter.unstripped.so \
-o ./out/symbol-map \
--register-local-cacheUse the cached mapping in later APK decompile runs:
flutterdec decompile ./sample.apk -o ./out \
--extra-symbol-elf ./libflutter.unstripped.soWhen the cached build id matches the APK's embedded libflutter.so, decompile auto-loads the registered target summary and reports the match under report.json.engine_symbol_ingestion.
Main output artifacts:
pseudocode/*.dartpseudoreport.jsonquality.jsonasm/*.sif--emit-asmir/*.jsonif--emit-irghidra_apply_symbols.pyif--emit-ghidra-scriptida_apply_symbols.pyif--emit-ida-scriptdiff_report.jsonif you runflutterdec diff
Most users should start by reading:
pseudocode/*.dartpseudoreport.json.android_startupquality.json