
Recupera ciò che l'offuscamento nasconde nei binari Rust (stringhe, crate, funzioni di libreria, flusso di controllo appiattito) tramite analisi statica + emulazione Unicorn, senza eseguirli. Per il triage di malware e l'audit di applicazioni protette. ELF/PE/Mach-O, x86-64/ARM64/32-bit.
strings non vede nulla. FLOSS non vede nulla. rust-deobf vede il C2.
rust-deobf recupera ciò che i binari Rust offuscati nascondono — indirizzi C2, percorsi di file, mutex, chiavi di registro, configurazione — senza mai eseguire il campione.
Demo · Usalo da un agente AI · Avvio rapido · Risultati · Malware reale · Limiti
Un programma Rust compilato senza simboli, costruito come viene distribuito il malware (opt-level="z", fat LTO, panic=abort),
con i suoi otto indicatori cifrati tramite obfstr — le stringhe
risiedono in una struct, in rami match, in confronti e in un ramo che non viene mai eseguito. Nessuna viene mai stampata.
Generato dall'output catturato di un'esecuzione reale (Linux x86-64, rustc 1.97): il testo è verbatim, i colori e l'animazione di digitazione sono aggiunti, l'attesa di emulazione di ~8 s è accelerata e l'output è ridotto alle stringhe decodificate. L'output in testo semplice qui sotto è la stessa esecuzione.
Cosa ti mostra il file:
$ B=build/scatter_obfstr__elf__size-lto__strip
$ strings $B | grep -ciE 'northwind|10\.66|NW-5c1e9a|skip-update'
0
Cosa recupera rust-deobf (statico + emulazione Unicorn; il binario non viene mai eseguito):
$ python3 scripts/recover.py $B
binary: scatter_obfstr__elf__size-lto__strip format=ELF arch=x86_64
static x86-64 (recover_callsite): 0 strings decoders=9 seconds=0.2
emulation (recover_emulate): 8 strings functions=416/416 timed_out=False workers=1 seconds=6.8
== decoded strings ==
10.66.12.7:8443 [ip]
function 0xf20c (emulation)
C:\ProgramData\NorthwindSvc\state.db [path]
function 0xf20c (emulation)
Mozilla/5.0 (Windows NT 10.0; Win64; x64) NorthwindAgent/2.1
function 0xf20c (emulation)
--skip-update-check
function 0xf443 (emulation)
Software\Northwind\Agent\Settings [registry]
function 0xf51b (emulation)
Global\NW-5c1e9a [mutex]
function 0xf5a8 (emulation)
https://telemetry.northwind.example/api/v3/beacon [url]
function 0xfd58 (emulation)
nw-agent::stage=collect
function 0xfd58 (emulation)
8/8 in meno di 10 secondi, ogni stringa etichettata e ricondotta alla funzione che la costruisce.
Riproducilo con ONLY=scatter_obfstr PROFILES=size-lto FORMATS=elf bash scripts/build_all.sh
(output ridotto alle stringhe decodificate). (L'input è un programma di benchmark
benigno; ogni stringa inserita è elencata in bench_src/manifest.json.)
Il malware Rust è in aumento, e i suoi strumenti di deoffuscamento sono di fatto assenti. FLOSS — il deoffuscatore di stringhe open-source standard — non recupera nulla dai comuni crate di offuscamento Rust:
| offuscatore | strings | FLOSS | rust-deobf |
|---|---|---|---|
| litcrypt / litcrypt2 | 0/5 | 0/5 | 5/5 |
| obfstr (XOR+ADD inline) | 0/5 | 0/5 | 5/5 |
| cryptify (chiave predefinita) | 0/5 | 0/5 | 5/5 |
| muddy (ChaCha20-Poly1305 AEAD) | 0/5 | 0/5 | 5/5 |
| stringhe strette sullo stack | 0/5 | 0/5 | 5/5 |
| XOR / ADD / SUB artigianali | 0/5 | 0/5 | 5/5 |
Benchmark canary, build release, ELF e PE x86-64 e AArch64, con e senza strip. Tabella completa, incluso l'unico caso che richiede recupero dinamico (cryptify con chiave personalizzata), in Risultati.
Su campioni reali: EDDIESTEALER (PE) — 32 stringhe offuscate inclusi il suo dominio C2 nascosto e il percorso URL, dove gli strumenti statici e FLOSS recuperano 0. AmnesiaStealer (macOS Mach-O) — URL di esfiltrazione C2, configurazione del crypto-clipper, percorsi di staging. Maggiori dettagli sotto.
Deoffuscamento di stringhe — recupero statico dei call-site (secondi) + un motore di emulazione Unicorn con
un vero loader, un runtime C, esplorazione dei percorsi e replay dei costruttori globali. Recupera stringhe costruite
solo in un ramo di match o su un branch che non viene mai eseguito.
Triage SOC su una schermata — soc_triage.py: formato/arch, toolchain Rust, fingerprint delle capacità dei crate
(networking / crypto / recon / persistence / staging), indicatori raggruppati per tipo, e
un segnale calibrato LOW / MEDIUM / HIGH con le sue evidenze. --json per un SIEM, --fail-on HIGH
per il gating automatizzato. --fast (metadati + indicatori letterali, senza decodifica) scala con la dimensione del binario:
meno di un secondo su un piccolo campione, ~20 s su un binario da 20 MB.
Multi-arch, multi-formato — x86-64, ARM64 e x86 a 32 bit su ELF, PE e Mach-O.
Si adatta al tuo flusso di lavoro — esportazione compatibile con FLOSS (--floss-json), annotazione headless di Ghidra
(commenti plate, bookmark, rinomine), identificazione delle funzioni di libreria tramite firma byte
(lib_<name> nei binari con strip), modalità batch su una directory (--jsonl).
Oltre le stringhe — estrazione di config/C2, risoluzione degli import API offuscati tramite hash, carving dello
stage-2 non impacchettato, rilevamento del control-flow-flattening e ricostruzione (x86-64 e AArch64,
validato su goldberg, OLLVM -fla e un vero pass di flattening LLVM-18), semplificazione MBA / predicati opachi
con Z3, recupero delle vtable dei trait-object Rust (recover_types.py), e
inversione delle ottimizzazioni (un-switching delle jump-table, rilevamento dei loop srotolati — premium/opt_reverse.py).
Virtualizzazione del codice — detect_vm.py segnala VM bytecode in stile VMProtect/Themida/Tigress; il
devirtualizzatore premium le riporta indietro. Da una VM con opcode randomizzati e operandi cifrati,
ricostruisce:
return (((a0 > 1000) & (((((a0 * 31) + a1) ^ (a0 >> 3)) % 97) == 0)) ? (a1 + a0) : 0xffffffff);
— il codice sorgente esatto della funzione.
Sicuro per progettazione — statico + emulazione per impostazione predefinita: l'emulazione esegue funzioni del binario sotto Unicorn, mai il binario stesso. Il recupero dinamico (Frida) è opt-in e solo per VM isolate.
Metadati Rust che sopravvivono a strip — versione di rustc, crate di terze parti con versioni esatte,
e panic site, ricostruiti dai percorsi sorgente che il compilatore lascia in ogni binario Rust (nessun
cargo-auditable necessario).