
Analizzatore statico per snapshot AOT Flutter/Dart — recupera i nomi delle funzioni, le gerarchie delle classi, i grafi di chiamata e i segnali comportamentali da libapp.so senza incorporare o eseguire la Dart VM. Supporta ARM64 e x86_64, Dart 2.10–3.12.
Un analizzatore di snapshot AOT per Dart. Trasforma libapp.so — il codice Dart compilato all'interno di un APK Flutter release — in nomi di funzioni, layout delle classi, grafi delle chiamate, segnali comportamentali e pseudocodice leggibile. Nessuna Dart VM, nessuna compilazione SDK, nessun fallback a runtime.
Avviso di fork: AOTopsy è un fork di unflutter, originariamente creato da Anthony Zboralski. Il repository originale
zboralski/unflutternon è più disponibile (rimosso dall'autore); esiste una continuazione della community pressoKristijanZic/unflutter. Tutto il merito per il parser originale degli snapshot, il deserializzatore dei cluster, la pipeline di disassemblaggio ARM64 e l'integrazione con Ghidra/IDA appartiene all'autore originale. AOTopsy lo estende con il supporto x86_64, un decompilatore nativo, l'inferenza dei tipi sull'intero programma, la generazione di script Frida e una documentazione completa.
| Output | Di cosa si tratta |
|---|---|
| Nomi delle funzioni | Il nome Dart originale per ogni funzione compilata |
| Strutture delle classi | Nomi dei campi, offset in byte, catene di ereditarietà |
| Grafo delle chiamate | Archi di chiamata diretti (BL) e indiretti (BLR/dispatch) con provenienza |
| Riferimenti alle stringhe | Quali funzioni caricano quali stringhe letterali dal pool di oggetti |
| Segnali comportamentali | Classificazione per parole chiave di crypto, rete, gioco d'azzardo, SIM, posizione, WebView, blockchain |
| Pseudocodice | Output decompilato indipendente dall'architettura da codice macchina ARM64 o x86_64 |
| Esportazione del sorgente Dart | File .dart modulari dell'intero progetto ricostruiti con classi, campi e metodi |
Supporta ARM64 e x86_64. Copre Dart dalla 2.10 alla 3.13 (la 3.13.2 è l'attuale frontiera stabile) — ogni versione modellata viene analizzata correttamente su entrambe le architetture, vedi COVERAGE.md (93 build di esempio, 0 fallimenti).
AOTopsy è misurato rispetto alla verità di riferimento, non affermato. Due proprietà sono verificate dalla suite di test a ogni modifica:
| Metrica | Valore | Cosa significa |
|---|---|---|
| Accordo nel recupero dei nomi | 90.2% complessivo (81.3% banda peggiore, soglia minima ≥ 0.81) su 44 build di riferimento | Nomi di funzioni recuperati confrontati con il .symtab ELF di ciascuna build, la verità di riferimento esterna — TestSymtabDifferential. Tabella di punteggio completa per build: BENCHMARK.md (make bench). |
| Validità sintattica del decompilatore | 100% Dart valido | Ogni funzione di pseudocodice emessa viene analizzata come Dart — TestDecompileQualityCorpus. |
| Tasso di fabbricazione | 0% | La regola §2: non emettere mai un nome, tipo o destinazione di chiamata indovinati come fatto. Gli elementi sconosciuti vengono resi onestamente (indirectCall, <unknown>, dynamic). |
I gemelli di riferimento sono build di produzione reali che non possiamo ridistribuire, quindi quelle
soglie differenziali vengono eseguite localmente; la CI pubblica convalida build, vet, gofmt,
staticcheck, test unitari (mescolati) e una soglia di copertura attraverso la matrice
delle piattaforme. samples/ non è ridistribuibile ed è escluso da git, quindi i test dipendenti dai campioni
vengono saltati lì — ma solo quando il corpus è del tutto assente; un corpus che è
presente ma manca di un campione registrato fallisce, perché queste due cose significano l'opposto.
Come viene misurato ogni numero qui (verità di riferimento, definizioni delle metriche, riproduzione): METHODOLOGY.md. Vedi
SECURITY.md per la verifica dei binari di rilascio e l'ambito onesto di seguito.
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
Consulta DEMO.md per una guida introduttiva di cinque minuti su uno snapshot reale, oppure WORKFLOW.md per la metodologia passo-passo quando hai un APK grezzo e non sai da dove iniziare.
AOTopsy tratta lo snapshot AOT di Dart come una grammatica binaria deterministica. Ogni byte ha esattamente una corretta interpretazione date le giuste condizioni (struttura ELF, magic dello snapshot, hash della versione, tabella CID, codifica dei cluster). Il parser applica vincoli finché non sopravvive una sola interpretazione — nessuna euristica, nessuna congettura.
La pipeline si articola in fasi, ciascuna una funzione pura da byte a dati strutturati:
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]
Due backend indipendenti condividono la stessa prima metà (da ELF fino al riempimento dei cluster), poi si dividono per architettura: internal/disasm per ARM64, internal/disasm/x86.go per x86_64. Il decompilatore (internal/decompiler) gestisce entrambe le architetture tramite un IR unificato.
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 incorpora la Dart VM per deserializzare lo snapshot tramite i propri percorsi di codice. Fedeltà perfetta, ma richiede la compilazione di un Dart SDK corrispondente per ogni versione target — ed è solo per ARM64, senza supporto statico per x86_64. AOTopsy è l'unico analizzatore statico e indipendente dalla versione con un decompilatore nativo di pseudocodice e un'accuratezza ground-truth pubblicata.