
Analizador estático para instantáneas AOT de Flutter/Dart — recupera nombres de funciones, jerarquías de clases, grafos de llamadas y señales de comportamiento de libapp.so sin incrustar ni ejecutar la VM de Dart. Compatible con ARM64 y x86_64, Dart 2.10–3.12.
Un analizador de snapshots AOT de Dart. Convierte libapp.so —el código Dart compilado dentro de un APK de release de Flutter— en nombres de funciones, diseños de clases, grafos de llamadas, señales de comportamiento y pseudocódigo legible. Sin Dart VM, sin compilación del SDK, sin fallback en tiempo de ejecución.
Aviso de fork: AOTopsy es un fork de unflutter, originalmente creado por Anthony Zboralski. El repositorio original
zboralski/unflutterya no está disponible (eliminado por el autor); existe una continuación de la comunidad enKristijanZic/unflutter. Todo el crédito por el parser de snapshots original, el deserializador de clústeres, la pipeline de desensamblado ARM64 y la integración con Ghidra/IDA pertenece al autor original. AOTopsy lo amplía con soporte para x86_64, un descompilador nativo, inferencia de tipos de programa completo, generación de scripts de Frida y documentación exhaustiva.
| Salida | Qué es |
|---|---|
| Nombres de funciones | El nombre Dart original de cada función compilada |
| Estructuras de clases | Nombres de campos, desplazamientos en bytes, cadenas de herencia |
| Grafo de llamadas | Aristas de llamada directas (BL) e indirectas (BLR/dispatch) con procedencia |
| Referencias a cadenas | Qué funciones cargan qué literales de cadena desde el pool de objetos |
| Señales de comportamiento | Clasificación por palabras clave de criptografía, red, apuestas, SIM, ubicación, WebView, blockchain |
| Pseudocódigo | Salida descompilada neutral respecto a la arquitectura a partir de código máquina ARM64 o x86_64 |
| Exportación de código fuente Dart | Archivos .dart modulares de todo el proyecto reconstruidos con clases, campos y métodos |
Compatible con ARM64 y x86_64. Cubre Dart 2.10 hasta 3.13 (3.13.2 es la frontera estable actual) — cada versión modelada se parsea limpiamente en ambas arquitecturas, véase COVERAGE.md (93 compilaciones de muestra, 0 fallos).
AOTopsy se mide contra la verdad de referencia, no se afirma sin más. Dos propiedades se verifican mediante la suite de pruebas en cada cambio:
| Métrica | Valor | Qué significa |
|---|---|---|
| Concordancia en la recuperación de nombres | 90.2% global (81.3% en la peor banda, umbral mínimo ≥ 0.81) en 44 compilaciones de referencia | Nombres de funciones recuperados comparados contra el propio .symtab ELF de cada compilación, la verdad de referencia externa — TestSymtabDifferential. Marcador completo por compilación: BENCHMARK.md (make bench). |
| Validez sintáctica del descompilador | 100% Dart válido | Cada función de pseudocódigo emitida se parsea como Dart — TestDecompileQualityCorpus. |
| Tasa de fabricación | 0% | La regla §2: nunca emitir un nombre, tipo u objetivo de llamada adivinado como hecho. Los valores desconocidos se muestran con honestidad (indirectCall, <unknown>, dynamic). |
Los gemelos de verdad de referencia son compilaciones de producción reales que no podemos redistribuir, por lo que esas
verificaciones diferenciales se ejecutan localmente; la CI pública valida build, vet, gofmt,
staticcheck, pruebas unitarias (mezcladas) y un umbral de cobertura en toda la matriz
de plataformas. samples/ no es redistribuible y está en gitignore, por lo que las pruebas dependientes de muestras
se omiten allí — pero solo cuando el corpus está completamente ausente; un corpus que está
presente y le falta una muestra registrada falla, porque esos casos significan cosas opuestas.
Cómo se mide cada número aquí (verdad de referencia, definiciones de métricas, reproducción): METHODOLOGY.md. Véase
SECURITY.md para la verificación del binario de release y el alcance honesto a continuación.
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 para ver un recorrido de cinco minutos sobre una instantánea real, o WORKFLOW.md para conocer la metodología paso a paso cuando tengas un APK sin procesar y no sepas por dónde empezar.
AOTopsy trata la instantánea AOT de Dart como una gramática binaria determinista. Cada byte tiene exactamente una interpretación correcta dadas las restricciones adecuadas (estructura ELF, magic de la instantánea, hash de versión, tabla CID, codificación de clústeres). El analizador aplica restricciones hasta que solo sobrevive una interpretación — sin heurísticas, sin adivinanzas.
El pipeline se ejecuta por etapas, cada una una función pura de bytes a datos estructurados:
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]
Dos backends independientes comparten la misma mitad frontal (ELF hasta el llenado de clústeres), luego se dividen por arquitectura: internal/disasm para ARM64, internal/disasm/x86.go para x86_64. El descompilador (internal/decompiler) maneja ambas arquitecturas a través de un IR unificado.
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 VM de Dart para deserializar el snapshot a través de sus propias rutas de código. Fidelidad perfecta, pero requiere compilar un SDK de Dart compatible para cada versión objetivo — y es solo para ARM64, sin soporte estático para x86_64. AOTopsy es el único analizador estático e independiente de la versión con un descompilador de pseudocódigo nativo y una precisión de referencia publicada.