
Flutter/Dart AOTスナップショット用の静的アナライザー — Dart VMを埋め込んだり実行したりせずに、libapp.soから関数名、クラス階層、コールグラフ、および挙動シグナルを復元します。ARM64およびx86_64、Dart 2.10–3.12をサポート。
Dart AOTスナップショットアナライザー。FlutterリリースAPK内にコンパイルされたDartコードであるlibapp.soを、関数名、クラスレイアウト、コールグラフ、行動シグナル、可読な擬似コードへと変換します。Dart VMもSDKコンパイルもランタイムフォールバックも不要です。
フォークに関する注意: AOTopsyは、Anthony Zboralskiによるunflutterのフォークです。元の
zboralski/unflutterリポジトリは現在利用できません(作者により削除済み)。コミュニティによる継続版がKristijanZic/unflutterに存在します。元のスナップショットパーサー、クラスタデシリアライザー、ARM64逆アセンブリパイプライン、Ghidra/IDA統合に関するすべての功績は原作者に帰属します。AOTopsyはこれにx86_64サポート、ネイティブデコンパイラ、プログラム全体の型推論、Fridaスクリプト生成、包括的なドキュメントを追加して拡張しています。
| 出力 | 内容 |
|---|---|
| 関数名 | コンパイルされた各関数の元のDart名 |
| クラス構造 | フィールド名、バイトオフセット、継承チェーン |
| コールグラフ | 出所情報付きの直接(BL)および間接(BLR/ディスパッチ)呼び出しエッジ |
| 文字列参照 | どの関数がオブジェクトプールからどの文字列リテラルをロードするか |
| 行動シグナル | 暗号、ネットワーク、ギャンブル、SIM、位置情報、WebView、ブロックチェーンキーワードの分類 |
| 擬似コード | ARM64またはx86_64マシンコードから生成されたアーキテクチャ非依存の逆コンパイル出力 |
| Dartソースエクスポート | クラス、フィールド、メソッドを再構築したプロジェクト全体のモジュール式.dartファイル |
ARM64とx86_64をサポート。Dart 2.10から3.13までをカバー(3.13.2が現在の安定版フロンティア)— モデル化されたすべてのバージョンが両アーキテクチャでクリーンにパースされます。COVERAGE.mdを参照(93サンプルビルド、失敗0件)。
AOTopsyは主張ではなく、グラウンドトゥルースに対して測定されます。2つの特性が すべての変更においてテストスイートによって強制されます:
| 指標 | 値 | 意味 |
|---|---|---|
| 名前復元一致率 | 44のグラウンドトゥルースビルド全体で90.2%(最悪バンド81.3%、≥ 0.81のゲート下限) | 復元された関数名を、各ビルド自身のELF .symtab(外部グラウンドトゥルース)と比較 — TestSymtabDifferential。ビルドごとの完全なスコアボード: BENCHMARK.md(make bench)。 |
| デコンパイラ構文妥当性 | 100% 有効なDart | 出力されるすべての擬似コード関数がDartとしてパースされる — TestDecompileQualityCorpus。 |
| 捏造率 | 0% | §2のルール: 推測された名前、型、呼び出しターゲットを事実として決して出力しない。不明なものは正直に表示される(indirectCall、<unknown>、dynamic)。 |
グラウンドトゥルースのツインは再配布できない実際のプロダクションビルドであるため、これらの
差分ゲートはローカルで実行されます。公開CIはビルド、vet、gofmt、
staticcheck、ユニットテスト(シャッフル)、およびプラットフォームマトリックス全体の
カバレッジ下限を検証します。samples/は再配布不可でgitignoreされているため、サンプル依存の
テストはそこでスキップされます — ただしコーパスが完全に存在しない場合に限ります。存在する
コーパスに登録済みサンプルが欠けている場合は失敗します。これらは正反対の意味を持つからです。
ここでのすべての数値の測定方法(グラウンドトゥルース、指標定義、再現方法): METHODOLOGY.md。リリースバイナリの検証と以下の正直なスコープについては
SECURITY.mdを参照してください。
make build
./aotopsy libapp.so # full pipeline
./aotopsy doctor libapp.so # quick diagnostic
./aotopsy export-dart --lib libapp.so --out ./lib # reconstruct entire Dart source project
./aotopsy _debug decompile-native --lib libapp.so --find MyClass # find and decompile a function
実際のスナップショットを使った5分間のウォークスルーについては DEMO.md を、生のAPKがありどこから始めればよいかわからない場合のステップバイステップの方法論については WORKFLOW.md を参照してください。
AOTopsyはDart AOTスナップショットを決定論的なバイナリ文法として扱います。適切な制約(ELF構造、スナップショットマジック、バージョンハッシュ、CIDテーブル、クラスタエンコーディング)が与えられれば、すべてのバイトはただ1つの正しい解釈を持ちます。パーサーは1つの解釈だけが残るまで制約を適用します — ヒューリスティクスも推測もありません。
パイプラインはステージごとに実行され、各ステージはバイトから構造化データへの純粋関数です:
flowchart TD
A[libapp.so] --> B[ELF parse]
B --> C[snapshot region extraction]
C --> D[version detection]
D --> E[cluster alloc<br/>object census]
E --> F[cluster fill<br/>field values, names, strings]
F --> G[instructions table<br/>code ranges, stub boundaries]
G --> H{architecture?}
H -->|ARM64| I[ARM64 disassembly]
H -->|x86_64| J[x86_64 disassembly]
I --> K[CFG + call edges<br/>register provenance]
J --> K
K --> L[type inference<br/>BLR receiver type resolution]
L --> M[signal classification<br/>behavioral keyword matching]
M --> N[JSONL + HTML + DOT<br/>pseudocode output]
2つの独立したバックエンドは同じ前半部分(ELFからクラスタフィルまで)を共有し、その後アーキテクチャごとに分岐します。ARM64用はinternal/disasm、x86_64用はinternal/disasm/x86.goです。デコンパイラ(internal/decompiler)は統一されたIRを通じて両方のアーキテクチャを処理します。
flowchart LR
subgraph "Shared front half"
A[elfx] --> B[snapshot]
B --> C[cluster]
end
subgraph "ARM64 backend"
C --> D1[disasm ARM64]
D1 --> E1[callgraph]
E1 --> F1[signal]
end
subgraph "x86_64 backend"
C --> D2[disasm x86_64]
D2 --> E2[callgraph]
E2 --> F2[signal]
end
subgraph "Decompiler (both archs)"
C --> G[decompiler IR]
G --> H[pseudocode]
end
flowchart LR
subgraph Blutter
direction TB
B1[libapp.so] --> B2[Compile matching<br/>Dart SDK]
B2 --> B3[Embed Dart VM]
B3 --> B4[Deserialize via<br/>VM internal APIs]
B4 --> B5[Perfect fidelity]
end
subgraph AOTopsy
direction TB
A1[libapp.so] --> A2[Parse binary format<br/>directly]
A2 --> A3[No VM, no SDK]
A3 --> A4[Version-specific<br/>format modeling]
A4 --> A5[Portability + speed]
end
Blutter は Dart VM を組み込み、独自のコードパスを通じてスナップショットをデシリアライズします。完全な忠実性を備えていますが、ターゲットバージョンごとに対応する Dart SDK をコンパイルする必要があり、ARM64 専用で静的な x86_64 サポートはありません。AOTopsy は、ネイティブの擬似コードデコンパイラと公開されたグラウンドトゥルース精度を備えた、唯一の静的かつバージョン非依存のアナライザーです。
AOTopsy はバイナリ形式を直接解析します。VM も SDK も不要です。トレードオフとして、Dart のバージョン間におけるあらゆる形式の変更を明示的にモデル化する必要があります。それを自動的に処理するランタイムは存在しません。
aotopsy libapp.so # disasm + call edges + signal + metadata (ARM64: + Ghidra/IDA)
aotopsy signal libapp.so # same, skip metadata
aotopsy libapp.so --graph # also build call graph DOT files
フラグ: --out <dir>(デフォルト: <basename>.aotopsy/)、--quiet、--strict、--max-steps <n>、--k <n>(シグナルコンテキストの深さ、デフォルト 2)、--decompile。
--decompile は関数ごとの Dart 疑似コードを <out>/dart/ に書き出します。出力ディレクトリがおよそ3倍になるため、デフォルトでは無効になっています。無効のまま実行するたびにその旨が表示されるので、この機能は単に存在するだけでなく発見可能になっています。
aotopsy doctor libapp.so # Dart version, pointer size, support status, build features
aotopsy find-libapp --apk app.apk # locate libapp.so inside an APK
すべてのクラス、フィールド、メソッド、getter、setter、コンストラクタを、元のライブラリ URI にマッピングされたモジュール式の .dart ファイルに再構築します:
aotopsy export-dart --lib libapp.so --out ./reconstructed_lib/ # full project export
aotopsy export-dart --lib libapp.so --out ./lib/ --app-only # filter out core/flutter framework
aotopsy export-dart --lib libapp.so --out ./lib/ --filter Auth # targeted export by name
バイナリの機械命令から、クリーンで慣用的な Dart コードを直接生成します:
for-in イテレータ、while、for、try-catch-finally。_SuspendState ステートマシンを展開し、線形な await future および await for に変換します。(item) => process(item) を直接合成します。String、int、UserModel) を SSA 値全体に伝播します。?.、??、??=)、カスケード (..)、Set/List/Map リテラル、文字列補間 ("${a}${b}")。aotopsy _debug decompile-native --lib libapp.so --find MyClass # locate by name
aotopsy _debug decompile-native --lib libapp.so --func 0x1a92728 # one function at a VA
aotopsy _debug decompile-native --lib libapp.so --from-main --out out/ # reachability from app entry
aotopsy _debug decompile-native --lib libapp.so --all --filter MyClass # bulk, filtered
警告: 小さな --max を指定せずに --all を使用すると、実際のアプリでは約64GBのRAMが必要になる場合があります。--find/--func/--from-main を推奨します。
aotopsy ghidra libapp.so # headless Ghidra with metadata injection
aotopsy ida libapp.so # headless IDA via idalib
どちらも x86_64 入力は拒否します。x86_64 の擬似コードには decompile-native を使用してください。