Torna agli aggiornamenti
New releaseAug 31, 2026

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.

Condividi

AOTopsy

CI Release License

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/unflutter non è più disponibile (rimosso dall'autore); esiste una continuazione della community su KristijanZic/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

OutputCosa è
Nomi di funzioniIl nome Dart originale per ogni funzione compilata
Strutture di classiNomi dei campi, offset in byte, catene di ereditarietà
Grafo di chiamataArchi di chiamata diretti (BL) e indiretti (BLR/dispatch) con provenienza
Riferimenti a stringheQuali funzioni caricano quali letterali stringa dal pool di oggetti
Segnali comportamentaliClassificazione per parole chiave: crittografia, rete, gioco d'azzardo, SIM, posizione, WebView, blockchain
PseudocodiceOutput decompilato neutro rispetto all'architettura da codice macchina ARM64 o x86_64
Esportazione sorgente DartFile .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:

MetricaValoreCosa significa
Accordo sul recupero dei nomi89,8% complessivo (81,3% nella banda peggiore, soglia minima ≥ 0,81) su 44 build di verità di baseNomi 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 decompilatore100% Dart validoOgni funzione in pseudocodice emessa viene parsata come Dart — TestDecompileQualityCorpus.
Tasso di fabbricazione0%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).

Diagnostica

Categorie