一、PNG 文件结构
一个 PNG 文件由文件头、若干数据块和文件尾组成:
文件头(89 50 4E 47 0D 0A 1A 0A)+ 数据块 + 数据块 + ... + 文件尾(00 00 00 00 49 45 4E 44 AE 42 60 82)- 文件头固定为
89 50 4E 47 0D 0A 1A 0A,即 ASCII 字符.PNG加上换行(0D 0A)、EOF 等标记; - 文件尾固定为
00 00 00 00 49 45 4E 44 AE 42 60 82,IEND表示文件结束。
PNG 定义了两类数据块:
- 关键数据块(critical chunk):标准数据块,PNG 文件必须包含,共 4 个(IHDR、PLTE、IDAT、IEND);
- 辅助数据块(ancillary chunk):可选数据块。
每个数据块都有统一的结构,由 4 个部分组成:
长度(4 字节) + 数据块类型(4 字节) + 数据(长度字段指定的字节数) + CRC 校验值(4 字节)IHDR(文件头数据块)
IHDR 是文件的第一个数据块,由第 11~32 字节组成(从 0 开始计数),包含 PNG 文件中存储的图像数据的基本信息:
- 数据从第 16 字节开始,共 13 字节;
- 前 8 字节分别用 4 个字节(大端序)规定了图片的宽和高,单位为像素。
IDAT(图像数据块)
IDAT 存储实际图像数据,在数据流中可包含多个连续顺序的图像数据块。它采用 LZ77 算法的派生算法进行压缩,可以用 zlib 解压缩。
二、IHDR 篡改图片宽高
IHDR 的前 8 字节规定了图片的宽和高,我们可以用十六进制编辑器修改它们,使图片显示不完整,从而达到隐藏信息的目的。此时图片的像素数据并没有变,在 Windows 图片查看器中,超过规定宽高的部分只是不再显示了。
然而在 Kali Linux 中,这样修改过的图片通常是打不开的,会提示 IHDR CRC error——因为每个数据块的最后 4 字节都有 CRC(循环冗余校验)值,用于检测数据是否被篡改。
篡改方法
用十六进制编辑器打开图片,定位到 IHDR 块中表示宽高的字段:
- 数据块数据起始处的前 4 字节是宽度(十六进制);
- 紧接着的后 4 字节是高度(十六进制)。
例如把宽度改为 0384(对应十进制 900)、高度改为 0096(对应十进制 150),图片就会按修改后的尺寸显示,其余像素被截断隐藏,从而起到隐藏信息的效果。
用 CRC 反推原始宽高
CRC 校验值是根据 IHDR 块的完整数据(含原始宽高)计算得到的。一旦宽高字段被篡改,CRC 就会对不上,解码器随即报 IHDR CRC error。反过来,我们可以枚举宽高,计算每种组合下的 CRC,与文件中已有的 CRC 值比对,一致时即为被隐藏的原始尺寸。
以下脚本根据图片中已有的 CRC 值,计算 PNG(其他图片格式似乎不适用)正确的宽度和高度:
import os
import binascii
import struct
crcbp = open("misc26.png", "rb").read() # 打开图片
crc32frombp = int(crcbp[29:33].hex(), 16) # 读取图片中的 CRC 校验值
print(crc32frombp)
for i in range(4000): # 宽度 1-4000 枚举
for j in range(4000): # 高度 1-4000 枚举
data = crcbp[12:16] + \
struct.pack('>i', i) + struct.pack('>i', j) + crcbp[24:29]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == crc32frombp: # 计算当图片大小为 i:j 时的 CRC,与图片中的 CRC 相同则尺寸确定
print(i, j)
print('hex:', hex(i), hex(j))脚本说明:
crcbp[29:33]取出 IHDR 块末尾 4 字节的 CRC 校验值(IHDR 数据从第 16 字节开始共 13 字节,其后 4 字节即 CRC,位于 29~32 字节);crcbp[12:16]是IHDR四个字节的类型标识;struct.pack('>i', i)与struct.pack('>i', j)分别把当前枚举的宽度、高度写入大端序 4 字节;crcbp[24:29]是 IHDR 数据中除宽高外的其余 5 字节(位深、颜色类型、压缩方法、过滤方法、隔行扫描方式);- 将上述字段组合后计算 CRC32,与文件中的 CRC 值比对,一致即得到被隐藏的原始宽高。
得到原始尺寸后,用十六进制编辑器把宽高改回去,图片即可正常显示,被隐藏的内容也随之露出。
- 本文链接: https://diyuyizhu.github.io/posts/png-ihdr-steganography/
- 版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。
