Retour aux mises à jour
New releaseJul 29, 2026

sgn v1.0.0-rs

Encodeur binaire polymorphe pour payloads de sécurité offensive. Encode du shellcode avec une boucle de rétroaction basée sur LFSR, une injection d'instructions parasites et une obfuscation multi-itérations pour échapper à la détection statique.

Partager

GitHub All Releases Build Issues Crates License: MIT

SGN est un encodeur binaire polymorphe destiné à des fins offensives en sécurité, telles que la génération de charges utiles binaires statiquement indétectables. Il utilise une boucle de rétroaction additive pour encoder les instructions binaires fournies, de manière similaire au LFSR. Ce projet est la réimplémentation de l'original Shikata ga nai en golang, avec de nombreuses améliorations.

[!WARNING]
Le projet a récemment été porté vers Rust. Ce port conserve la conception et le comportement d'origine, mais remplace l'assembleur textuel keystone par l'assembleur purement Rust iced-x86, donc il n'y a aucune dépendance native à des bibliothèques — il se compile avec un simple cargo build. Consultez la branche sgn-go pour l'ancienne version Go.

Pourquoi ?

Pour la communauté de la sécurité offensive, l'implémentation originale de l'encodeur shikata ga nai est considérée comme le meilleur encodeur de shellcode (jusqu'à présent). Mais au fil des années, les chercheurs en sécurité ont découvert plusieurs failles permettant de détecter statiquement le stub de décodage (travail connexe article FireEye). La principale motivation de ce projet était de créer un meilleur encodeur, qui encode le binaire fourni au point de le rendre identique à des données totalement aléatoires et de rendre impossible la détection de la présence d'un décodeur.

  • Support 64 bits. Enfin des shellcodes x64 correctement encodés !
  • Nouveau stub de décodeur plus petit. Clé LFSR réduite à 1 octet
  • Stub encodé avec un schéma pseudo-aléatoire. Le stub du décodeur est également encodé avec un schéma pseudo-aléatoire
  • Aucune condition de boucle visible Le stub se décode lui-même SANS utiliser de condition de boucle !!
  • Obfuscation du stub de décodeur. Générateur aléatoire d'instructions factices ajouté avec keystone
  • Option de registres sûrs. Aucun des registres n'est écrasé (préambule optionnel, peut réduire le polymorphisme)

Comment ça fonctionne

Chaque passe d'encodage :

  1. (optionnel) ajoute un suffixe de restauration des registres (mode sûr) ;
  2. préfixe des instructions factices aléatoires qui préservent les valeurs ;
  3. chiffre la charge utile avec le chiffrement ADFL (boucle de rétroaction additive) et préfixe un stub de décodeur sans boucle qui l'inverse à l'exécution ;
  4. sauf avec --plain, chiffre le stub lui-même avec un chiffrement de schéma aléatoire par exécution (XOR/ADD/SUB/ROL/ROR/NOT sur des DWORD) et préfixe un décodeur de schéma auto-localisant, de sorte que même le décodeur ressemble à des données aléatoires ;
  5. répète éventuellement --enc fois avec de nouvelles graines ;
  6. (optionnel) préfixe une sauvegarde des registres (mode sûr).

Installation

cargo install sgn

Vous pouvez également obtenir les binaires pré-compilés ICI.

Utilisation

-h est assez explicite ; utilisez -v si vous voulez voir ce qui se passe en coulisses ( ͡° ͜ʖ ͡°)_/¯

       __   _ __        __                               _ 
  ___ / /  (_) /_____ _/ /____ _  ___ ____ _  ___  ___ _(_)
 (_-</ _ \/ /  '_/ _ `/ __/ _ `/ / _ `/ _ `/ / _ \/ _ `/ / 
/___/_//_/_/_/\_\\_,_/\__/\_,_/  \_, /\_,_/ /_//_/\_,_/_/  
========[Author:-Ege-Balcı-]====/___/=======v2.0.2=========  
    ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻           (ノ ゜Д゜)ノ ︵ 仕方がない

sgn [OPTIONS]

Options:
  -i, --input <INPUT>        Input binary path
  -o, --out <OUT>            Encoded output binary name (default: <input>.sgn)
  -a, --arch <ARCH>          Binary architecture (32/64) [default: 64]
  -c, --enc <ENC>            Number of times to encode the binary [default: 1]
  -M, --max <MAX>            Maximum bytes per garbage block [default: 50]
      --plain                Do not encode the decoder stub
      --ascii                Generate a fully ASCII-printable payload (slow)
  -S, --safe                 Preserve all register values (no clobber)
      --badchars <BADCHARS>  Avoid these bytes, hex format (e.g. \x00\x0a)
  -v, --verbose              Verbose mode
  -h, --help                 Print help
  -V, --version              Print version

Exemple :

sgn -i shellcode.bin -o encoded.bin -a 64 --badchars '\x00\x0a\x0d'

Flux d'exécution

L'image suivante est un schéma de flux de travail de base pour l'encodeur. Mais gardez à l'esprit que les tailles, les emplacements et les ordres changeront pour les instructions factices, les décodeurs et les décodeurs de schéma à chaque itération.

Le LFSR lui-même est assez puissant en termes d'espace de probabilité. Pour encore plus de polymorphisme, des instructions factices sont ajoutées au début de la charge utile brute non encodée. L'image ci-dessous montre la matrice compagnon du polynôme caractéristique du LFSR et, en désignant la graine comme un vecteur colonne, l'état du registre en configuration de Fibonacci après k étapes.

Utilisation en tant que bibliothèque

use sgn::Encoder;

let shellcode = std::fs::read("payload.bin")?;
let mut encoder = sgn::Encoder::new(64)?;
let encoded = encoder.encode(&shellcode)?;
println!("encoded {} bytes", encoded.len());

Voir examples/encode_binary.rs.

Tests

cargo test

La suite comprend des tests unitaires de chiffrement aller-retour ainsi que des tests d'exécution réels : les shellcodes x64 encodés sont mappés en mémoire exécutable et exécutés dans le processus, et les shellcodes x86 sont exécutés via un harnais d'assistance cc -m32 (ignoré automatiquement si aucune chaîne d'outils 32 bits n'est présente). Les deux architectures sont testées dans les modes plain, schema, multi-couche et safe-register, y compris une boucle de stress aléatoire.

Notes sur le portage

  • La génération d'instructions utilise l'API d'assembleur typé de iced-x86 plutôt que le texte d'assemblage de keystone ; les stubs de décodeur ont été reconstruits autour de l'adressage relatif à RIP (x64) et d'un schéma en deux passes call/pop (x86).
  • Le chiffrement de schéma est exprimé directement dans la vue DWORD native little-endian du processeur, ce qui est équivalent mais plus clair que l'arithmétique mixte big/little-endian de l'original.
  • Le code mort de l'original (la table d'instructions de ~3 000 lignes inutilisée et le générateur de « déchets non sécurisés » qui n'était appelé par rien) a été supprimé.
  • Corrections mineures : random_byte couvre désormais toute la plage 0..=255, et le budget de taille des déchets (--max) est appliqué de manière cohérente comme une limite par bloc.

Catégories