
Полиморфный бинарный кодировщик для полезных нагрузок в сфере наступательной безопасности. Кодирует шеллкод с помощью цепи обратной связи на основе LFSR, внедрения мусорных инструкций и многоитерационной обфускации для обхода статического обнаружения.
<div align="center">
<img src="https://assets.kitploit.com/production/public/readmes/3551/71476533a52676b901aed0ae0ab78a9dd1cf687fd6360bdaad6e5998b944842f.png">
<br>
[![GitHub All Releases][release-img]][release]
[![Build][workflow-img]][workflow]
[![Issues][issues-img]][issues]
[![Crates][crates-img]][crates]
[![License: MIT][license-img]][license]
</div>
[crates]: https://crates.io/crates/sgn
[crates-img]: https://img.shields.io/crates/v/sgn
[release]: https://github.com/EgeBalci/sgn/releases
[release-img]: https://img.shields.io/github/v/release/EgeBalci/sgn
[downloads]: https://github.com/EgeBalci/sgn/releases
[downloads-img]: https://img.shields.io/github/downloads/EgeBalci/sgn/total?logo=github
[issues]: https://github.com/EgeBalci/sgn/issues
[issues-img]: https://img.shields.io/github/issues/EgeBalci/sgn?color=red
[license]: https://raw.githubusercontent.com/EgeBalci/sgn/master/LICENSE
[license-img]: https://img.shields.io/github/license/EgeBalci/sgn.svg
[google-cloud-shell]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/EgeBalci/sgn&tutorial=README.md
[workflow-img]: https://github.com/EgeBalci/sgn/actions/workflows/main.yml/badge.svg
[workflow]: https://github.com/EgeBalci/sgn/actions/workflows/main.yml
[fe-article]: https://www.fireeye.com/blog/threat-research/2019/10/shikata-ga-nai-encoder-still-going-strong.html
[lfsr]: https://en.wikipedia.org/wiki/Linear-feedback_shift_register
SGN — это полиморфный бинарный энкодер для целей наступательной безопасности, например для создания статически необнаруживаемых бинарных полезных нагрузок. В нём используется аддитивный контур обратной связи для кодирования заданных бинарных инструкций, аналогичный [LFSR][lfsr]. Этот проект является переработкой [оригинального Shikata ga nai](https://github.com/rapid7/metasploit-framework/blob/master/modules/encoders/x86/shikata_ga_nai.rb) на Go со множеством улучшений.
> [!WARNING]
> Недавно проект был портирован на Rust. Эта версия сохраняет исходный дизайн и поведение, но заменяет текстовый ассемблер [keystone](https://www.keystone-engine.org/) на чисто Rust-ассемблер [`iced-x86`](https://docs.rs/iced-x86), поэтому **нет зависимостей от нативных библиотек** — сборка выполняется обычной командой `cargo build`. Для устаревшей версии на Go смотрите ветку [sgn-go](https://github.com/EgeBalci/amber/tree/sgn-go).
## Зачем?
Для сообщества наступательной безопасности оригинальная реализация энкодера Shikata ga nai считалась лучшим шеллкод-энкодером (до сих пор). Но с годами исследователи безопасности нашли несколько недостатков, позволяющих статически обнаруживать заглушку декодера (связанная работа — [статья FireEye][fe-article]). Основной мотив этого проекта — создать более совершенный энкодер, который кодирует заданный бинарный файл до состояния полной неотличимости от случайных данных, делая невозможным обнаружение наличия декодера.
- [x] Поддержка 64 бит. `Finally properly encoded x64 shellcodes !`
- [x] Новая уменьшенная заглушка декодера. `LFSR key reduced to 1 byte`
- [x] Кодирование заглушки по псевдослучайной схеме. `Decoder stub is also encoded with a psudo random schema`
- [x] Без видимого условия цикла. `Stub decodes itself WITHOUT using any loop conditions !!`
- [x] Обфускация заглушки декодера. `Random garbage instruction generator added with keystone`
- [x] Безопасный режим работы с регистрами. `Non of the registers are clobbered (optional preable, may reduce polimorphism)`
## Как это работает
Каждый проход кодирования:
1. (необязательно) добавляет суффикс восстановления регистров (безопасный режим);
2. добавляет в начало случайные мусорные инструкции, сохраняющие значения;
3. шифрует полезную нагрузку шифром **ADFL** (аддитивная цепь обратной связи) и добавляет в начало декодер без циклов, который восстанавливает её во время выполнения;
4. если не указано `--plain`, дополнительно шифрует саму заглушку случайным **схемным шифром** (`XOR/ADD/SUB/ROL/ROR/NOT` над DWORD) и добавляет в начало самолокализующийся декодер схемы, так что даже декодер выглядит как случайные данные;
5. опционально повторяется `--enc` раз с новыми seed-значениями;
6. (необязательно) добавляет в начало префикс сохранения регистров (безопасный режим).
## Установка
```sh
cargo install sgn
```
Также вы можете получить готовые скомпилированные бинарные файлы [ЗДЕСЬ][release].
**Использование**
`-h` довольно понятен без объяснений, используйте `-v`, если хотите видеть, что происходит за кулисами `( ͡° ͜ʖ ͡°)_/¯`
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/3551/d7536cd852e99bfd9dfcfb21e7d050a763a678d5bb65a6db80e89d4c4f5a3a8b.gif">
</p>
```
__ _ __ __ _
___ / / (_) /_____ _/ /____ _ ___ ____ _ ___ ___ _(_)
(_-</ _ \/ / '_/ _ `/ __/ _ `/ / _ `/ _ `/ / _ \/ _ `/ /
/___/_//_/_/_/\_\\_,_/\__/\_,_/ \_, /\_,_/ /_//_/\_,_/_/
========[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
```
Пример:
```sh
sgn -i shellcode.bin -o encoded.bin -a 64 --badchars '\x00\x0a\x0d'
```
## Порядок выполнения
На изображении ниже показана базовая схема работы энкодера. Имейте в виду, что размеры, расположение и порядок мусорных инструкций, декодеров и декодеров схем будут меняться на каждой итерации.
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/3551/1db7423a884b71b9464011392860cb7f32e6f9e3652f5cd26ed443e5297f52fc.png">
</p>
Сам по себе LFSR довольно мощный с точки зрения пространства вероятностей. Для ещё большего полиморфизма в начало незакодированной полезной нагрузки добавляются мусорные инструкции. На изображении ниже показана сопровождающая матрица характеристического многочлена LFSR; если обозначить seed как вектор-столбец, она также показывает состояние регистра в конфигурации Фибоначчи через k шагов.
<p align="center">
<img src="https://raw.githubusercontent.com/egebalci/sgn/HEAD/.github/matrices.svg">
</p>
## Использование в качестве библиотеки
```rust
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`.
## Тестирование
```sh
cargo test
```
Набор тестов включает модульные тесты шифрования round-trip, а также **реальные тесты выполнения**: закодированный x64 shellcode отображается в исполняемую память и выполняется в процессе, а x86 shellcode выполняется через вспомогательный стенд `cc -m32` (автоматически пропускается, если 32-битный тулчейн отсутствует). Обе архитектуры проверяются в режимах plain, schema, multi-layer и safe-register, включая цикл случайных стресс-тестов.
## Примечания о портировании
* Генерация инструкций использует типизированный API ассемблера `iced-x86` вместо текстового ассемблера keystone; заглушки декодеров были переработаны вокруг RIP-относительной адресации (x64) и двухпроходной схемы `call/pop` (x86).
* Схемный шифр выражается непосредственно в нативном little-endian представлении DWORD, что эквивалентно, но понятнее, чем смешанная арифметика big/little-endian в оригинале.
* Мёртвый код из оригинала (неиспользуемая таблица набора инструкций объёмом ~3k строк и генератор «unsafe garbage», который никто не вызывал) был удалён.
* Незначительные исправления: `random_byte` теперь покрывает весь диапазон `0..=255`, а бюджет размера мусорных данных (`--max`) теперь последовательно применяется как ограничение на блок.