
aotopsy v1.1.0
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.
AOTopsy
Un analizzatore di snapshot AOT per Dart. Trasforma libapp.so — il codice Dart compilato all'interno di un APK Flutter in versione release — in nomi di funzioni, layout di classi, grafi di chiamata, segnali comportamentali e pseudocodice leggibile. Nessuna Dart VM, nessuna compilazione SDK, nessun fallback a runtime.
Nota sul 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 suKristijanZic/unflutter. Tutto il merito per il parser di snapshot originale, il deserializzatore di cluster, la pipeline di disassemblaggio ARM64 e l'integrazione con Ghidra/IDA appartiene all'autore originale. AOTopsy lo estende con supporto x86_64, un decompilatore nativo, inferenza di tipi a livello di intero programma, generazione di script Frida e documentazione completa.
Cosa Recupera
| Output | Cosa è |
|---|---|
| Nomi di funzioni | Il nome Dart originale per ogni funzione compilata |
| Strutture di classi | Nomi dei campi, offset in byte, catene di ereditarietà |
| Grafo di chiamata | Archi di chiamata diretti (BL) e indiretti (BLR/dispatch) con provenienza |
| Riferimenti a stringhe | Quali funzioni caricano quali letterali stringa dal pool di oggetti |
| Segnali comportamentali | Classificazione per parole chiave: crittografia, rete, gioco d'azzardo, SIM, posizione, WebView, blockchain |
| Pseudocodice | Output decompilato neutro rispetto all'architettura da codice macchina ARM64 o x86_64 |
| Esportazione sorgente Dart | File .dart modulari a livello di intero progetto ricostruiti con classi, campi e metodi |
Supporta ARM64 e x86_64. Copre Dart 2.10 fino a 3.13 (3.13.2 è l'attuale frontiera stabile).
Accuratezza e Onestà
AOTopsy è misurato rispetto alla verità di base, non semplicemente dichiarato. Due proprietà sono imposte dalla suite di test a ogni modifica:
| Metrica | Valore | Cosa significa |
|---|---|---|
| Accordo sul recupero dei nomi | 89,8% complessivo (81,3% nella banda peggiore, soglia minima ≥ 0,81) su 44 build di verità di base | Nomi di funzioni recuperati confrontati con il .symtab ELF di ogni build, la verità di base esterna — TestSymtabDifferential. Classifica completa per build: BENCHMARK.md (make bench). |
| Validità sintattica del decompilatore | 100% Dart valido | Ogni funzione in pseudocodice emessa viene parsata come Dart — TestDecompileQualityCorpus. |
| Tasso di fabbricazione | 0% | La regola §2: non emettere mai un nome, tipo o target di chiamata indovinato come fatto. Gli sconosciuti vengono resi onestamente (indirectCall, <unknown>, dynamic). |
I gemelli di verità di base sono build di produzione reali che non possiamo ridistribuire, quindi quelle porte differenziali vengono eseguite localmente; la CI pubblica valida build + test unitari sulla matrice di piattaforme (i test dipendenti da campioni vengono saltati correttamente quando il binario è assente). Vedi SECURITY.md per la verifica dei binari di release e l'ambito onesto di seguito.
Avvio Rapido
make build
./aotopsy libapp.so # pipeline completa
./aotopsy doctor libapp.so # diagnostica rapida
./aotopsy export-dart --lib libapp.so --out ./lib # ricostruisce l'intero progetto sorgente Dart
./aotopsy _debug decompile-native --lib libapp.so --find MyClass # trova e decompila una funzione
Vedi WORKFLOW.md per la metodologia passo-passo quando hai un APK grezzo e non sai da dove iniziare.
Come Funziona
AOTopsy tratta lo snapshot AOT di Dart come una grammatica binaria deterministica. Ogni byte ha esattamente un'interpretazione corretta dati i vincoli giusti (struttura ELF, magic dello snapshot, hash di versione, tabella CID, codifica dei cluster). Il parser applica i vincoli finché sopravvive una sola interpretazione — nessuna euristica, nessuna supposizione.
La pipeline procede per fasi, ciascuna una funzione pura da byte a dati strutturati:
flowchart TD
A[libapp.so] --> B[Parsing ELF]
B --> C[Estrazione regione snapshot]
C --> D[Rilevamento versione]
D --> E[Allocazione cluster<br/>censimento oggetti]
E --> F[Riempimento cluster<br/>valori campi, nomi, stringhe]
F --> G[Tabella istruzioni<br/>intervalli codice, confini stub]
G --> H{Architettura?}
H -->|ARM64| I[Disassemblaggio ARM64]
H -->|x86_64| J[Disassemblaggio x86_64]
I --> K[CFG + archi di chiamata<br/>provenienza registri]
J --> K
K --> L[Inferenza tipi<br/>risoluzione tipo ricevente BLR]
L --> M[Classificazione segnali<br/>corrispondenza parole chiave comportamentali]
M --> N[Output JSONL + HTML + DOT<br/>pseudocodice]
Due backend indipendenti condividono la stessa metà anteriore (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 "Metà anteriore condivisa"
A[elfx] --> B[snapshot]
B --> C[cluster]
end
subgraph "Backend ARM64"
C --> D1[disasm ARM64]
D1 --> E1[callgraph]
E1 --> F1[signal]
end
subgraph "Backend x86_64"
C --> D2[disasm x86_64]
D2 --> E2[callgraph]
E2 --> F2[signal]
end
subgraph "Decompilatore (entrambe le architetture)"
C --> G[IR decompilatore]
G --> H[pseudocodice]
end
Confronto con Blutter
flowchart LR
subgraph Blutter
direction TB
B1[libapp.so] --> B2[Compila SDK Dart<br/>corrispondente]
B2 --> B3[Integra Dart VM]
B3 --> B4[Deserializza tramite<br/>API interne VM]
B4 --> B5[Fedeltà perfetta]
end
subgraph AOTopsy
direction TB
A1[libapp.so] --> A2[Parsing formato binario<br/>direttamente]
A2 --> A3[Nessuna VM, nessun SDK]
A3 --> A4[Modellazione formato<br/>specifico per versione]
A4 --> A5[Portabilità + velocità]
end
Blutter integra la Dart VM per deserializzare lo snapshot attraverso i propri percorsi di codice. Fedeltà perfetta, ma richiede la compilazione di un SDK Dart corrispondente per ogni versione target — ed è solo ARM64, senza supporto statico x86_64. AOTopsy è l'unico analizzatore statico e indipendente dalla versione con un decompilatore nativo in pseudocodice e accuratezza pubblicata rispetto alla verità di base.
AOTopsy analizza direttamente il formato binario. Nessuna VM, nessun SDK. Il compromesso: ogni modifica al formato tra le versioni di Dart deve essere modellata esplicitamente. Non c'è un runtime che la gestisca automaticamente.
Comandi
Pipeline completa
aotopsy libapp.so # disasm + archi di chiamata + segnali + metadati (ARM64: + Ghidra/IDA)
aotopsy signal libapp.so # come sopra, salta i metadati
aotopsy libapp.so --graph # crea anche file DOT del grafo di chiamata
Flag: --out <dir> (default: <basename>.aotopsy/), --quiet, --strict, --max-steps <n>, --k <n> (profondità contesto segnali, default 2).