
Analisi binaria parallela con IDA Pro, naming di funzioni basato su AI, grafo di conoscenza Neo4j e motore di emulazione/hooking/fuzzing phantomrt per reverse engineering automatizzato su formati PE, ELF e NSO.
Fantasma attraverso i binari.
Un assistente di reverse engineering locale e basato su IA: analisi parallela di IDA Pro, denominazione delle funzioni tramite IA, un terminale che non fa schifo, un grafo di conoscenza Neo4j di tutto ciò che ha mai scoperto, e un server MCP così Claude può cercare e concatenare attraverso quel grafo direttamente.
E ora — con phantomrt — non solo legge i muri. Li attraversa: emula, hooka e fuzza le funzioni che ha denominato, e scrive ciò che effettivamente accade di nuovo sul grafo.
✓ 00 ✓ 01 ✓ 02 ✓ 03 ▸ 04 · 05 · 06 · 07 ✓ 08 ✓ 09 ✓ 10 ✓ 11 ✓ 12 ✓ 13 ▸ 14 · 15
14/16 shards │ 141,203 functions found ████████████████████████████░░░░ 89% ~4s remaining
---
## Cos'è
L'analisi automatica di IDA Pro è a thread singolo. Su una DLL il2cpp da 34 MB ci vogliono *minuti*. spectrIDA divide il binario in N frammenti, li esegue in parallelo tramite idalib, li unisce in un unico `.i64`, e poi lascia che un modello 8B messo a punto **dia un nome a ogni funzione** — tutto da un'unica interfaccia terminale con tema cyberpunk e la giusta dose di sarcasmo.
Questo è il Capitolo 1, e regge da solo: pura velocità, nessuna IA richiesta se non la si vuole.
Il Capitolo 2 trasforma l'output in qualcosa che sopravvive alla sessione — un grafo Neo4j in cui un client MCP (Claude, [pi](https://pi.dev), qualunque cosa parli MCP) può realmente vivere, invece di dover copiare e incollare l'output del decompilatore in una finestra di chat una funzione alla volta:```
Binary ─▶ Parallel IDA Analysis ─▶ Demangle ─▶ AI Naming ─▶ Neo4j Graph ─▶ MCP Server ─▶ Claude
(N idalib shards) (free, real) (stripped (persists, (search/chain/
leftovers forever, rename, live)
only) across sessions)
Capitolo 3 (phantomrt) aggiunge la metà che gli strumenti statici non hanno mai: esegue il codice.
Emula una funzione senza OS (funziona su binari che non puoi nemmeno lanciare, come un .nso di Switch), oppure aggancia il processo live, o lo fuzza — e appone il verdetto (crashes / needs live state / clean) sullo stesso nodo del grafo del nome.
Il resoconto completo, compreso ciò che è ancora nella lista delle cose da fare, si trova nel Capitolo 2 e nel Capitolo 3.
Non è Ghidra. Fa una cosa fastidiosa (analisi lenta + naming) velocemente, ed è davvero divertente da usare. 199 download parlano da soli.
Niente cloud. Niente telemetria. Funziona interamente sulla tua macchina.
| compito | tempo |
|---|---|
| DLL di Among Us — IDA single-thread | ~4 ore |
| DLL di Among Us — spectrIDA (16 worker) | 67 secondi |
| Binario da 153.649 funzioni — passaggio di naming completo | durante la notte |
| Panoramica del binario (cosa fa questa cosa?) | ~30 secondi |
Hardware su cui sono state misurate: AMD Ryzen 7 5800X3D (8C/16T), 32 GB RAM, RTX 4070 12 GB.
Hardware diverso sposta i numeri dell'analisi parallela (più core, più shard, più veloce); i numeri del naming sono per lo più vincolati alla GPU.
I dati di Among Us di 4 ore/67 secondi precedono il Capitolo 2 e non vengono verificati indipendentemente in ogni release — esegui di nuovo spectrida analyze tu stesso se vuoi un numero per la tua macchina e il tuo binario, i risultati variano con la densità degli shard e la dimensione del binario.
Numeri effettivamente riverificati durante lo sviluppo del Capitolo 2, stesso hardware:
Quella riga NSO è il vero equivalente della vecchia affermazione di Among Us "4 ore → 67 secondi", misurata fresca in questa release su un binario Switch da 74.790 funzioni, senza coinvolgere AI nel naming (solo demangling — populate=False).
La fase parallela (16 core, ~55s) esegue la scoperta iniziale con shard; la fase di merge (~143s) è single-threaded per progettazione — un database IDA, uno scrittore — quindi se guardi il Task Manager durante quella parte e vedi 15 core che dormono, non è un bug report, è fisica.
Ottenere un numero onesto qui è stata una piccola storia dell'orrore a sé stante. La prima versione del supporto NSO girava pulita, usciva con codice 0, e restituiva orgogliosamente 727 funzioni per un binario che ne ha circa 75.000 — non un crash, solo spettacolarmente, fiduciosamente sbagliato, che in qualche modo è peggio. Si scopre che IDA non ha un loader NSO nativo, quindi il file veniva caricato silenziosamente come x86 normale ("metapc") anche se Switch è ARM64 fin dal lancio della console. Ogni shard eseguiva uno scanner di prologo x86 su istruzioni puramente AArch64 e chiamava "funzione" qualsiasi cosa corrispondesse accidentalmente. Sistemare l'architettura non ha risolto nulla da solo, perché il binario era anche ancora compresso in LZ4 in memoria — quindi metà di ciò che veniva scansionato era, generosamente, rumore. E anche una volta decompresso correttamente e contrassegnato come AArch64, ogni shard cercava solo i target delle chiamate all'interno della propria piccola fetta del binario, perdendo ogni chiamata che attraversava il confine dello shard — che in un binario di queste dimensioni sono la maggior parte. Tre bug, un numero, e nessuno di loro ha avuto la decenza di lanciare un'eccezione. (Abbiamo anche provato a ridurre la fase di merge saltando l'analisi dello stack frame di IDA — abbiamo ottenuto un bellissimo speedup e un database in cui Hex-Rays si è gentilmente rifiutato di decompilare metà di esso. Questo è stato rapidamente annullato. Abbiamo invece tenuto il salto della firma FLIRT, molto più piccolo e sicuro, che vale un trascurabile ~3% e non ha rotto nulla, il che a questo punto sembrava un tratto della personalità da mantenere.)
Sulla precisione del naming: non è la verità di base di livello Ghidra, è un modello 8B che indovina dallo pseudocodice.
Helper/getter generici tendono a funzionare bene; la logica profondamente specifica del gioco è più un lancio di moneta. Rinomina qualsiasi cosa sbagli — ecco perché rename_function persiste direttamente nel grafo.
.i64. Worker configurabili tramite flag, config o variabile d'ambiente..so/Linux) sono inclusi; aggiungerne uno nuovo è un singolo file, nessuna modifica al core. spectrida formats elenca ciò che è registrato. Vedi Aggiungere un nuovo formato binario.N. Guardalo pensare. Il nome appare.B per nominare ogni funzione sub_* nella lista. Allontanati. Torna indietro.O o esegui spectrida overview file.i64. Il modello legge 120 nomi di funzione campionati e ti dice cosa fa il binario, quali sono i suoi sottosistemi e qualsiasi cosa rilevante per la sicurezza. Ha identificato correttamente un runtime IL2CPP da 153.000 funzioni in 30 secondi.C mostra chiamanti e chiamati. Il modello li usa come contesto durante il naming — una funzione chiamata da ottiene un nome migliore di una isolata.pip install spectrida
Requisiti: **IDA Pro 9.x** with idalib · **Python 3.10+** · **Ollama**```bash
# install Ollama (Windows)
winget install Ollama.Ollama
# pull the model (8.7 GB — go get coffee)
ollama pull hf.co/gdfhhjk/spectrida-re-gguf:latest
# first run — detects your IDA install and sets everything up
spectrida onboard
# or just try the demo right now
spectrida --demo
spectrida analyze GameAssembly.dll spectrida analyze GameAssembly.dll --workers 8 # custom worker count
spectrida open file.i64
spectrida overview file.i64 spectrida overview file.i64 --addr 0x10001000 --addr 0x10353fd0 # include specific functions
spectrida export file.i64 -f idc # IDA script — apply names to any install spectrida export file.i64 -f json # full dump with addresses + sizes spectrida export file.i64 -f csv # spreadsheet spectrida export file.i64 -f symbols # addr name pairs spectrida export file.i64 --named-only # skip sub_* functions
spectrida serve
spectrida onboard
---
## Tasti TUI
| Tasto | Azione |
|-------|--------|
| `N` | Assegna nome alla funzione selezionata — AI trasmette il risultato in tempo reale |
| `R` | Rinomina — precompilato con il suggerimento dell'AI |
| `D` | Attiva/disattiva pseudocodice decompilato (Hex-Rays) |
| `C` | Catena di chiamate — chiamanti e chiamati |
| `B` | Assegna nome in batch a tutte le funzioni `sub_*` nell'elenco corrente |
| `O` | Panoramica — riepilogo AI dell'intero binario |
| `/` | Ricerca fuzzy |
| `?` | Aiuto |
| `Q` | Esci |
---
## API programmatica
Niente TUI necessario — guida spectrIDA da script, Claude Code, notebook, qualsiasi cosa:```python
import asyncio
from spectrida.api import open_i64
async def main():
async with open_i64("GameAssembly.i64") as db:
# list all 153k functions
funcs = await db.list_functions()
# name one function — returns name + reasoning + confidence
result = await db.name_function(0x10001000)
print(result["new_name"]) # init_atexit_handler
print(result["reasoning"]) # allocates array of 3 fn ptrs, calls _atexit...
# batch name everything (with live progress)
async def on_progress(done, total, r):
print(f" {done}/{total} {r['old_name']} -> {r['new_name']}")
await db.batch_name(limit=500, rename=True, progress_cb=on_progress)
# ask what the binary does
overview = await db.overview()
print(overview)
# export to IDA script
await db.export("names.idc", fmt="idc", named_only=True)
asyncio.run(main())
hf.co/gdfhhjk/spectrida-re-gguf — Qwen3-8B
fine-tunato per il reverse engineering.
Addestrato su:
jtsylve/ida-mcp — IDA senza testa con idalibApproccio di addestramento: SFT mirato ai neuroni + GRPO. Vengono ottimizzati solo i neuroni rilevanti per RE — la conoscenza di base di Qwen3 rimane intatta, hai semplicemente aggiunto una competenza molto specifica sopra.
Funziona localmente tramite Ollama. GGUF — funziona su CPU, GPU, o entrambi.
Stai facendo reverse engineering di qualcosa. Hai un binario con 150.000 funzioni. Forse 2.000 hanno nomi dai
metadati. Le altre 148.000 sono sub_XXXXXXXX. Vuoi trovare il codice di rete.
Non puoi cercarlo con grep perché niente ha ancora un nome.
Un RE umano può nominare ~50-100 funzioni all'ora se è veloce. A quel ritmo, 150k funzioni = 3 anni.
spectrIDA le nomina in una notte. Non perfettamente — forse 70% di precisione su funzioni generiche,
molto più alto su pattern che il modello riconosce. Ma ora invece di 148k funzioni sub_ hai
network_send_packet, serialize_player_state, validate_checksum — e sai dove guardare.
Non sostituisce un reverse engineer esperto. Fa l'80% noioso così puoi concentrarti sul 20% interessante. È il livello di orientamento.
Casi d'uso reali:
sub_140001234 per 20 minuti pensando deve esserci un modo migliore~/.spectrida/config.toml:```toml
[ida]
idalib = "C:/Program Files/IDA Professional 9.1"
output_dir = "~/.spectrida/output"
[ollama] base_url = "http://localhost:11434" model = "spectrida-re" # any ollama model name works
[pipeline] workers = 16
Override delle variabili d'ambiente: `SPECTRIDA_IDALIB` · `SPECTRIDA_MODEL` · `SPECTRIDA_WORKERS` · `SPECTRIDA_OLLAMA_URL`
---
## Aggiungere un nuovo formato binario
Il supporto dei formati è un sistema a plugin, non un ammasso di rami if/elif — PE, NSO e ELF sono tutti semplicemente file sotto `spectrida/analysis/formats/`, scoperti automaticamente. Esegui `spectrida formats` per vedere cosa è attualmente registrato.```bash
$ spectrida formats
ELF spectrida.analysis.formats.elf.ELFHandler
NSO spectrida.analysis.formats.nso.NSOHandler
PE spectrida.analysis.formats.pe.PEHandler
generic spectrida.analysis.formats.generic.GenericHandler
Il compito di un gestore di formato è limitato — esaminare un file e stabilire se lo si possiede, quindi descrivere il suo
layout del codice. Tutto il resto (strategia di sharding, scansione del prologo GPU, unione degli shard in un unico
.i64) viene gestito una volta, in modo generico, al di fuori del pacchetto format. NSO è l'esempio completo: la sua
logica di decompressione LZ4 + idaapi mem2base/add_segm esisteva già in nso_loader.py (la correzione per
i bug di wrong-arch/still-compressed/locally-blind-entry-point della storia del Capitolo 2) —
formats/nso.py è un adattatore sottile che espone quel modulo esistente e validato attraverso il
contratto FormatHandler, non una riscrittura.
Per aggiungere un formato, inserisci un nuovo file in spectrida/analysis/formats/ e nient'altro. Nessuna
modifica a parallel_analyze.py, shard_worker.py o al registro — viene rilevato dalla scansione
della directory per qualsiasi modulo che espone un'istanza HANDLER.```python
from spectrida.analysis.formats.base import FormatHandler, PreparedImage, Section
class MyFormatHandler(FormatHandler): name = "MYFMT"
@staticmethod
def sniff(header: bytes, path: str) -> bool:
return header[:4] == b"MYF0" # however you recognize the format
def prepare(self, path: str, workdir: str) -> PreparedImage:
# Format idalib already loads natively (ELF, PE, Mach-O)? Just parse
# the section/segment table — return the original path unchanged.
return PreparedImage(
binary_path=path,
image_base=0,
sections=[Section(name=".text", va=0x1000, raw_off=0x400,
raw_size=0x2000, vsize=0x2000, is_code=True)],
arch=None, # set "x86_64"/"arm64" only if IDA can't detect it itself
)
# Only needed if idalib has NO native loader for this format (NSO is the
# example): do any manual idaapi/ida_segment setup here, called right
# after idapro.open_database() succeeds, before analysis starts.
# def post_open(self) -> None: ...
HANDLER = MyFormatHandler()
Questo è l'intero contratto:
| Metodo | Obbligatorio? | Cosa fa |
|---|---|---|
| `sniff(header, path)` | sì | Controllo magic-byte/estensione — questo handler possiede il file? |
| `prepare(path, workdir)` | sì | Restituisce un `PreparedImage`: il file che idalib dovrebbe aprire + la sua tabella delle sezioni |
| `post_open()` | no (default no-op) | Configurazione manuale dei segmenti per formati senza loader IDA nativo (vedi `nso.py`) |
| `make_shard_binary(image, dst, va_start, va_end)` | no (default funziona) | Esegui l'override solo se azzerare i byte delle sezioni fuori shard è sbagliato per il tuo formato (vedi `nso.py` — non azzerare mai un file compresso) |
| `code_range(image)` | no (default funziona) | Esegui l'override solo se 'min/max delle sezioni `is_code`' non è la risposta giusta |
| `read_bytes(image, va_start, va_end)` | no (default funziona) | Esegui l'override se `prepare()` contiene già i byte rilevanti in memoria (NSO) invece che su disco |
| `global_entry_points(image, text_start, text_end)` | no (default: None) | Esegui l'override solo se una scansione locale per shard perderebbe i punti di ingresso reali — NSO ne ha bisogno perché le funzioni foglia AArch64 sono rilevabili solo tramite destinazioni BL viste altrove nel binario, non tramite una scansione locale del prologo |
Guarda `formats/pe.py` per l'handler più semplice possibile (puro parsing dell'intestazione, nessun override) e
`formats/nso.py` per il caso completo (include decompressione + configurazione manuale dei segmenti + tutti gli override).
Anche i pacchetti di terze parti possono registrare un handler, senza toccare affatto il codice sorgente di spectrIDA, tramite il gruppo di entry-point `spectrida.formats`:```toml
# in a separate package's pyproject.toml
[project.entry-points."spectrida.formats"]
myformat = "spectrida_myformat_plugin:HANDLER"
Test coverage for the format system lives in tests/test_formats.py — pure Python, no IDA/idalib required, so it runs in CI.
Chapter 1 was a faster, funnier IDA. Chapter 2 is spectrIDA as a teammate: a persistent, queryable knowledge graph of every function it's ever named, and an MCP server so Claude (or any MCP client — pi works too) can search and reason through it directly, instead of you copy-pasting decompiler output into a chat window.```bash spectrida install mcp
Questo è tutto. Registra automaticamente il server con Claude Code e pi (includendo `mcp` + `neo4j` se una semplice `pip install spectrida` li ha saltati), scrive la loro configurazione e ti dice quale riavvio gli devi.
**Cosa ottiene effettivamente Claude, una volta che Neo4j è in esecuzione (config `[graph]` di `spectrida`, o basta puntarlo a un'istanza locale):**
- `search_functions` / `get_function` / `get_callees` / `get_callers` / `trace_chain` — letture rapide e memorizzate nella cache del grafo. `get_function` restituisce pseudocodice **e** disassemblato (limiti esatti delle istruzioni e operandi — il livello che lo pseudocodice non può darti, che conta nel momento in cui passi da "cosa fa questo" a "dove esattamente dovrei patchare questo") più chiamanti/chiamati inline, così Claude decide se approfondire la catena guardando se un chiamato è ancora `sub_*` proprio lì nella risposta — nessun viaggio aggiuntivo solo per scoprire che non c'è nient'altro da vedere.
- `get_full_pseudocode` / `rename_function` — letture/scritture live e autorevoli direttamente nel `.i64` quando lo snippet memorizzato non è sufficiente o un nome è finalmente stato scoperto.
- `analyze_binary` — dagli un binario che non ha mai visto (PE o NSO, parallelizzato in ogni caso) ed esegue l'intera pipeline — analizza → demangla (Itanium *e* MSVC) → assegna nomi AI ai resti realmente spogliati → inserisce tutto nel grafo — come un job in background che interroghi, così un'esecuzione di diversi minuti non blocca mai la conversazione.
- `doctor` / `start_all` — verifica o avvia llama-server + Neo4j senza uscire dalla chat. Se llama-server stesso non è installato da nessuna parte, `start_all` lo recupera tramite winget (Windows) o brew (macOS) prima — nessun download/configurazione separata di llama.cpp necessaria.
Non è magia — una funzione che è ancora `sub_140001234` perché nessuno ci ha ancora guardato è ancora `sub_140001234`. Ma il grafo ricorda tutto ciò che il modello *ha* scoperto, per sempre, tra sessioni, e Claude può percorrerlo come un collega che ha già letto il codebase invece di fissare una funzione alla volta.
**In arrivo:**
- **Assegnazione nomi con contesto profondo** — segui alberi di chiamate a N livelli di profondità, fornisci la catena completa al modello. Una funzione a 3 salti da `encrypt_block` dovrebbe sapere che è nel percorso crittografico.
- **Deoffuscamento** — rilevamento di pattern TigressVM e tracciamento degli handler
- **Patching reale** — il disassemblato è ora nel grafo, quindi un agente *può* pianificare una patch a livello di byte; trasformare "ecco l'istruzione esatta da cambiare" in "ed ecco la scrittura" è il prossimo passo.
---
## Capitolo 3 — il fantasma cammina attraverso i muri
I capitoli 1 e 2 leggono il binario e lo ricordano. Ma leggere una funzione ti dice cosa *è*, mai cosa *fa* quando premi il grilletto. Il capitolo 3 — [**phantomrt**](https://pypi.org/project/phantomrt/) — preme il grilletto.```bash
pip install "spectrida[atlas]"
Prende una funzione spectrIDA già denominata e fa una di tre cose inquietanti:
.nso di Switch o un .so di Android.Poi appone il verdetto sullo stesso nodo Neo4j come proprietà dyn_*, così l'agente che cammina
sul grafo vede il nome e il comportamento in un unico posto. Sei nuovi strumenti MCP —
emulate_function, hunt_crashes, live_trace, dynamic_overview, risk_functions,
learn_vm — tutti a supporto di un unico grafo. L'angolo del ragionamento è qualunque LLM stia guidando; phantomrt
si assicura solo che abbia fatti di runtime reali con cui ragionare invece di sensazioni.```
spectrIDA (names it) ─▶ phantomrt (runs it) ─▶ graph (dyn_status / crash / live args) ─▶ the agent reasons
**E, nello spirito dell'incubo NSO di cui sopra:** la prima vera caccia ai bug onestamente non ha trovato *niente* — e il motivo è stato più interessante di quanto sarebbe stato un crash. Puntato su un FreeType genuinamente vecchio e vulnerabile, il fuzzing cieco ha ottenuto zero. Il fuzzing guidato dalla copertura ha poi riportato un trionfale **64.000 edge** coperti — il che era una bugia, perché il binario era PIE e ASLR stava silenziosamente ri-randomizzando gli indirizzi a ogni esecuzione, quindi la "nuova copertura" era per lo più il loader che mescolava le carte. Disabilita ASLR, il numero onesto scende a *reali* 727 edge, e si stabilizza lì — perché ogni seed era un font TrueType, quindi il fuzzer era intrappolato in un parser e non poteva mutare strutturalmente per entrare nel codice CFF/Type1/BDF dove i bug vivono realmente. La soluzione non è stata un mutatore più intelligente, ma semi migliori: l'agente ha recuperato veri campioni OpenType/Type1/BDF, la copertura è saltata da 727 a 4.013 prima di una singola iterazione di fuzz, e ha esplorato correttamente. Ancora nessun crash nel budget — che è l'onesto stato di un'esecuzione di 3 minuti su un core rispetto a un lavoro che richiede normalmente ore. Il meccanismo è reale. Ha catturato il suo *stesso* numero falso invece di segnalarlo. Questo è il punto.
**Dichiarazione onesta del fantasma:** phantomrt è `0.1.0`. Alpha. È un solido strato dinamico, non un oracolo magico di bug — ha trovato bug piantati in un target giocattolo all'istante, e su target reali ti dice onestamente quando è bloccato (`needs_state`), quando un crash è solo un *candidato* (vai a verificare che il puntatore sia effettivamente controllato dall'input), e quando ha solo bisogno di più tempo e semi migliori. I binari Switch non girano live — Frida ha bisogno di qualcosa che possa lanciare — quindi lì, l'emulazione è l'unica porta e dirà `needs_state` spesso. Questo è onesto, non rotto.
È il pesante extra intenzionale (`torch`, `unicorn`, `frida`) — il base `pip install spectrida` non ne tira nessuno. Il codice sorgente vive in [`phantomrt/`](https://github.com/ggfuchsi-oss/spectrida-reverse_engineering_stack/blob/HEAD/phantomrt/); il suo README in stile fantasma è [proprio lì](https://github.com/ggfuchsi-oss/spectrida-reverse_engineering_stack/blob/HEAD/phantomrt/README.md).
*Il fantasma statico nomina le tue funzioni. Questo le fa confessare.* 👻
---
## License
MIT. Fai quello che vuoi. Se funziona, figo.
Se non funziona, dai la colpa alla quantizzazione GGUF.
Costruito con rancore, caffè e una RTX 4070.
Il modello ha 199 download con zero marketing. Ognuno aggiunge lo 0,01% alla velocità di sviluppo.
(Non è vero. Ma ci si avvicina.) 👻
| binario | funzioni | compito | tempo/risultato |
|---|
| test_small.dll (PE) | 189 | analisi parallela, 4 worker, CLI | 6.4s |
| test_small.dll (PE) | 164 | pipeline MCP completa (analyze + demangle + graph write) | 9.8s |
| main.nso — Mario Odyssey (NSO), 16 worker | 28.038 funzioni seed | fase di scansione parallela con shard | 54.5s |
| main.nso — Mario Odyssey (NSO), 16 worker | 74.790 funzioni totali | + fase di merge/analisi completa | 143.1s |
| main.nso — Mario Odyssey (NSO), 16 worker | 74.790 funzioni totali | tempo totale end-to-end | 197.6s |
| main.nso — Mario Odyssey (NSO) | 74.790 | risolto tramite solo demangling (ABI Itanium, gratuito, nessuna AI) | 67.300 (90.0%) |
Player$$TakeDamageD attiva/disattiva lo pseudocodice di Hex-Rays..idc o file di simboli. Il .idc applica tutti i nomi generati dall'AI in qualsiasi installazione IDA con un clic.from spectrida.api import open_i64. Guida tutto da script, notebook o Claude Code senza toccare la TUI.spectrida install mcp lo collega direttamente a Claude Code e/o pi, nessuna modifica manuale del JSON. Claude può quindi cercare/leggere/attraversare un grafo di funzioni basato su Neo4j (nome, pseudocodice, disassemblato, chiamanti/chiamati) e avviare una nuova analisi su un nuovo binario stesso — analyze_binary esegue l'intera pipeline (analisi parallela → demangle → naming AI → grafo) da una singola chiamata di strumento, come job in background che polla. Funziona su PE e NSO. Vedi Capitolo 2 sotto.spectrida --demo) — prova tutto con zero configurazione. Niente IDA, niente Ollama.