
Récupérez ce que l'obfuscation dissimule dans les binaires Rust (chaînes, crates, fonctions de bibliothèque, flux de contrôle aplati) via analyse statique + émulation Unicorn, sans les exécuter. Pour le triage de malwares, les audits d'applications protégées. ELF/PE/Mach-O, x86-64/ARM64/32 bits.
strings ne voit rien. FLOSS ne voit rien. rust-deobf voit le C2.
rust-deobf récupère ce que les binaires Rust obfusqués cachent — adresses C2, chemins de fichiers, mutex, clés de registre, configuration — sans jamais exécuter l'échantillon.
Démo · L'utiliser depuis un agent IA · Démarrage rapide · Résultats · Vrai malware · Limites
Un programme Rust strippé compilé comme le font les malwares (opt-level="z", LTO fat, panic=abort),
avec ses huit indicateurs chiffrés par obfstr — les chaînes
résident dans une struct, des branches match, des comparaisons, et une branche qui ne s'exécute jamais. Aucune n'est jamais affichée.
Généré à partir de la sortie capturée d'une exécution réelle (Linux x86-64, rustc 1.97) : le texte est verbatim, les couleurs et l'animation de frappe sont ajoutées, l'attente d'émulation d'environ 8 s est accélérée, et la sortie est tronquée aux chaînes décodées. La sortie en texte brut ci-dessous correspond à la même exécution.
Ce que le fichier vous montre :
$ B=build/scatter_obfstr__elf__size-lto__strip
$ strings $B | grep -ciE 'northwind|10\.66|NW-5c1e9a|skip-update'
0
Ce que rust-deobf récupère (analyse statique + émulation Unicorn ; le binaire n'est jamais exécuté) :
$ 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 en moins de 10 secondes, chaque chaîne étiquetée et tracée jusqu'à la fonction qui la construit.
Reproduisez-le avec ONLY=scatter_obfstr PROFILES=size-lto FORMATS=elf bash scripts/build_all.sh
(sortie réduite aux chaînes décodées). (L'entrée est un programme de référence bénin ;
chaque chaîne implantée est listée dans bench_src/manifest.json.)
Les malwares Rust sont en augmentation, et leurs outils de désobfuscation sont pratiquement absents. FLOSS — le désobfuscateur de chaînes open-source standard — ne récupère rien des crates d'obfuscation Rust courantes :
| obfuscateur | strings | FLOSS | rust-deobf |
|---|---|---|---|
| litcrypt / litcrypt2 | 0/5 | 0/5 | 5/5 |
| obfstr (XOR+ADD inliné) | 0/5 | 0/5 | 5/5 |
| cryptify (clé par défaut) | 0/5 | 0/5 | 5/5 |
| muddy (ChaCha20-Poly1305 AEAD) | 0/5 | 0/5 | 5/5 |
| chaînes sur pile compactes | 0/5 | 0/5 | 5/5 |
| XOR / ADD / SUB faits main | 0/5 | 0/5 | 5/5 |
Benchmark canary, builds release, ELF et PE x86-64 et AArch64, strippés et non strippés. Tableau complet, y compris le seul cas nécessitant une récupération dynamique (cryptify avec une clé personnalisée), dans Résultats.
Sur des échantillons réels : EDDIESTEALER (PE) — 32 chaînes obfusquées dont son domaine C2 caché et son chemin d'URL, là où les outils statiques et FLOSS n'en récupèrent 0. AmnesiaStealer (macOS Mach-O) — URL d'exfiltration C2, configuration du crypto-clipper, chemins de staging. Plus bas.
Désobfuscation de chaînes — récupération statique au site d'appel (quelques secondes) + un moteur d'émulation Unicorn avec
un vrai loader, un runtime C, l'exploration de chemins et le rejeu des constructeurs globaux. Récupère les chaînes construites
uniquement dans une branche match ou sur une branche qui ne s'exécute jamais.
Triage SOC sur un écran — soc_triage.py : format/arch, chaîne d'outils Rust, empreinte de capacités des crates
(réseau / crypto / reconnaissance / persistance / staging), indicateurs groupés par type, et
un signal calibré LOW / MEDIUM / HIGH avec ses preuves. --json pour un SIEM, --fail-on HIGH
pour un filtrage scripté. --fast (métadonnées + indicateurs littéraux, sans décodage) s'adapte à la taille du binaire :
moins d'une seconde sur un petit échantillon, ~20 s sur un binaire de 20 Mo.
Multi-arch, multi-format — x86-64, ARM64 et x86 32 bits sur ELF, PE et Mach-O.
S'intègre à votre flux de travail — export compatible FLOSS (--floss-json), annotation Ghidra headless
(commentaires de plaque, signets, renommages), identification des fonctions de bibliothèque par signature d'octets
(lib_<name> dans les binaires strippés), mode batch sur un répertoire (--jsonl).
Au-delà des chaînes — extraction de config/C2, résolution des imports d'API obfusqués par hachage, carving de
stage-2 décompressé, détection et reconstruction de l'aplatissement du flux de contrôle (x86-64 et AArch64,
validé sur goldberg, OLLVM -fla et une vraie passe d'aplatissement LLVM-18), simplification MBA / prédicats opaques
avec Z3, récupération des vtables d'objets trait Rust (recover_types.py), et
inversion des optimisations (dé-switch des tables de saut, détection des boucles déroulées — premium/opt_reverse.py).
Virtualisation de code — detect_vm.py signale les VM bytecode de style VMProtect/Themida/Tigress ; le
devirtualiseur premium les remonte. À partir d'une VM avec des opcodes randomisés et des opérandes chiffrés,
il reconstruit :
return (((a0 > 1000) & (((((a0 * 31) + a1) ^ (a0 >> 3)) % 97) == 0)) ? (a1 + a0) : 0xffffffff);