
用于攻击性安全载荷的多态二进制编码器。利用基于LFSR的反馈回路、垃圾指令注入和多轮迭代混淆对shellcode进行编码,以规避静态检测。
SGN 是一个用于进攻性安全目的的多态二进制编码器,例如生成静态无法检测的二进制载荷。它使用类似于 LFSR 的加法反馈循环来对给定的二进制指令进行编码。本项目是用 golang 对原始 Shikata ga nai 的重新实现,并带来了许多改进。
[!WARNING]
该项目最近已移植到 Rust。此移植保留了原始设计和行为,但使用纯 Rust 的iced-x86汇编器取代了 keystone 文本汇编器,因此没有任何原生库依赖——只需普通的cargo build即可构建。如需旧版 Go 版本,请查看 sgn-go 分支。
对于进攻性安全社区而言,shikata ga nai 编码器的原始实现一直被认为是最好的 shellcode 编码器(直到现在)。但多年来,安全研究人员已经发现了静态检测解码器存根的几个缺陷(相关工作参见 FireEye 文章)。本项目的主要动机是创建一个更好的编码器,将给定的二进制编码到与完全随机数据无法区分的程度,并且无法检测到解码器的存在。
终于可以正确编码 x64 shellcode 了!LFSR 密钥缩减为 1 字节解码器存根也使用伪随机 schema 进行编码存根无需使用任何循环条件即可自我解码!!新增了基于 keystone 的随机垃圾指令生成器不会破坏任何寄存器(可选前导,可能会降低多态性)每次编码过程:
--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)重新构建。random_byte 现在覆盖完整的 0..=255 范围,垃圾大小预算(--max)也一致地作为每个块的上限应用。