
sgn v1.0.0-rs
用于攻击性安全载荷的多态二进制编码器。利用基于LFSR的反馈回路、垃圾指令注入和多轮迭代混淆对shellcode进行编码,以规避静态检测。
SGN 是一个用于进攻性安全目的的多态二进制编码器,例如生成静态无法检测的二进制载荷。它使用类似于 LFSR 的加法反馈循环来对给定的二进制指令进行编码。本项目是用 golang 对原始 Shikata ga nai 的重新实现,并带来了许多改进。
[!WARNING]
该项目最近已移植到 Rust。此移植保留了原始设计和行为,但使用纯 Rust 的iced-x86汇编器取代了 keystone 文本汇编器,因此没有任何原生库依赖——只需普通的cargo build即可构建。如需旧版 Go 版本,请查看 sgn-go 分支。
为什么?
对于进攻性安全社区而言,shikata ga nai 编码器的原始实现一直被认为是最好的 shellcode 编码器(直到现在)。但多年来,安全研究人员已经发现了静态检测解码器存根的几个缺陷(相关工作参见 FireEye 文章)。本项目的主要动机是创建一个更好的编码器,将给定的二进制编码到与完全随机数据无法区分的程度,并且无法检测到解码器的存在。
- 64 位支持。
终于可以正确编码 x64 shellcode 了! - 新的更小解码器存根。
LFSR 密钥缩减为 1 字节 - 存根采用伪随机 schema 编码。
解码器存根也使用伪随机 schema 进行编码 - 没有可见的循环条件
存根无需使用任何循环条件即可自我解码!! - 解码器存根混淆。
新增了基于 keystone 的随机垃圾指令生成器 - 安全寄存器选项。
不会破坏任何寄存器(可选前导,可能会降低多态性)
工作原理
每次编码过程:
- (可选)附加一个寄存器恢复后缀(安全模式);
- 前置随机的、保留值的垃圾指令;
- 使用 ADFL(加法反馈循环)密码对载荷进行加密,并前置一个可在运行时将其还原的无循环解码器存根;
- 除非使用
--plain,否则使用每次运行随机生成的 schema 密码(基于 DWORD 的XOR/ADD/SUB/ROL/ROR/NOT)对存根本身进行加密,并前置一个自定位 schema 解码器,从而使解码器本身看起来也像随机数据; - 可选择使用新的随机种子重复
--enc次; - (可选)前置一个寄存器保存前缀(安全模式)。
安装
cargo install sgn
你也可以从此处获取预编译的二进制文件。
用法
-h 已经相当一目了然了,如果想看看幕后发生了什么,请使用 -v ( ͡° ͜ʖ ͡°)_/¯
__ _ __ __ _
___ / / (_) /_____ _/ /____ _ ___ ____ _ ___ ___ _(_)
(_-</ _ \/ / '_/ _ `/ __/ _ `/ / _ `/ _ `/ / _ \/ _ `/ /
/___/_//_/_/_/\_\\_,_/\__/\_,_/ \_, /\_,_/ /_//_/\_,_/_/
========[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
示例:
sgn -i shellcode.bin -o encoded.bin -a 64 --badchars '\x00\x0a\x0d'
执行流程
下图是编码器的基本工作流程图。但请记住,在每次迭代中,垃圾指令、解码器和 schema 解码器的大小、位置和顺序都会发生变化。
就概率空间而言,LFSR 本身非常强大。为了进一步增强多态性,垃圾指令会被附加在未编码原始载荷的开头。下图展示了 LFSR 特征多项式的友矩阵,并将种子表示为列向量,即 Fibonacci 配置下寄存器经过 k 步后的状态。
作为库使用
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());
参见 examples/encode_binary.rs。
测试
cargo test
该套件包含密码往返单元测试以及真实执行测试:编码后的 x64 shellcode 会被映射为可执行内存并在进程内运行,x86 shellcode 则通过 cc -m32 辅助测试程序执行(如果不存在 32 位工具链,则自动跳过)。两种架构都会在 plain、schema、多层和安全寄存器等模式下接受测试,并包含一个随机化压力循环。
移植说明
- 指令生成使用
iced-x86的类型化汇编器 API,而非 keystone 汇编文本;解码器存根围绕 RIP 相对寻址(x64)和call/pop两遍方案(x86)重新构建。 - schema 密码直接以 CPU 原生的 little-endian DWORD 视图表示,这与原始版本中混合大小端运算的方式等效,但更加清晰。
- 原始版本中的死代码(未使用的约 3000 行指令集表,以及从未被调用的“不安全垃圾”生成器)已被移除。
- 次要修复:
random_byte现在覆盖完整的0..=255范围,垃圾大小预算(--max)也一致地作为每个块的上限应用。