PNG
文件格式
对于一个 PNG 文件来说,其文件头总是由位固定的字节来描述的,剩余的部分由 3 个以上的 PNG 的数据块(Chunk)按照特定的顺序组成。
文件头 89 50 4E 47 0D 0A 1A 0A
+ 数据块 + 数据块 + 数据块……
数据块CHUNk
PNG 定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了 4 个标准数据块,每个 PNG 文件都必须包含它们,PNG 读写软件也都必须要支持这些数据块。
IHDR
文件头数据块
否
否
第一块
cHRM
基色和白色点数据块
否
是
在PLTE和IDAT之前
gAMA
图像γ数据块
否
是
在PLTE和IDAT之前
sBIT
样本有效位数据块
否
是
在PLTE和IDAT之前
PLTE
调色板数据块
否
是
在IDAT之前
bKGD
背景颜色数据块
否
是
在PLTE之后IDAT之前
hIST
图像直方图数据块
否
是
在PLTE之后IDAT之前
tRNS
图像透明数据块
否
是
在PLTE之后IDAT之前
oFFs
(专用公共数据块)
否
是
在IDAT之前
pHYs
物理像素尺寸数据块
否
是
在IDAT之前
sCAL
(专用公共数据块)
否
是
在IDAT之前
IDAT
图像数据块
是
否
与其他IDAT连续
tIME
图像最后修改时间数据块
否
是
无限制
tEXt
文本信息数据块
是
是
无限制
zTXt
压缩文本数据块
是
是
无限制
fRAc
(专用公共数据块)
是
是
无限制
gIFg
(专用公共数据块)
是
是
无限制
gIFt
(专用公共数据块)
是
是
无限制
gIFx
(专用公共数据块)
是
是
无限制
IEND
图像结束数据
否
否
最后一个数据块
对于每个数据块都有着统一的数据结构,每个数据块由 4 个部分组成
Length(长度)
4字节
指定数据块中数据域的长度,其长度不超过(231-1)字节
Chunk Type Code(数据块类型码)
4字节
数据块类型码由 ASCII 字母(A - Z 和 a - z)组成
Chunk Data(数据块数据)
可变长度
存储按照 Chunk Type Code 指定的数据
CRC(循环冗余检测)
4字节
存储用来检测是否有错误的循环冗余码
CRC(Cyclic Redundancy Check)域中的值是对 Chunk Type Code 域和 Chunk Data 域中的数据进行计算得到的。
IHDR
文件头数据块 IHDR(Header Chunk):它包含有 PNG 文件中存储的图像数据的基本信息,由 13 字节组成,并要作为第一个数据块出现在 PNG 数据流中,而且一个 PNG 数据流中只能有一个文件头数据块
其中我们关注的是前8字节的内容
Width
4 bytes
图像宽度,以像素为单位
Height
4 bytes
图像高度,以像素为单位
我们经常会去更改一张图片的高度或者宽度使得一张图片显示不完整从而达到隐藏信息的目的。
这里可以发现在 Kali 中是打不开这张图片的,提示 IHDR CRC error
,而 Windows 10 自带的图片查看器能够打开,就提醒了我们 IHDR 块被人为的篡改过了,从而尝试修改图片的高度或者宽度发现隐藏的字符串。
例题
WDCTF-finals-2017
观察文件可以发现,文件头及宽度异常
这里需要注意的是,文件宽度不能任意修改,需要根据 IHDR 块的 CRC 值爆破得到宽度,否则图片显示错误不能得到 flag。
得到宽度值为 709 后,恢复图片得到 flag。
PLTE
调色板数据块 PLTE(palette chunk):它包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的 PNG 数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
IDAT
图像数据块 IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
储存图像像数数据
在数据流中可包含多个连续顺序的图像数据块
采用 LZ77 算法的派生算法进行压缩
可以用 zlib 解压缩
值得注意的是,IDAT 块只有当上一个块充满时,才会继续一个新的块。
用 pngcheck
去查看此 PNG 文件
可以看到,正常的块的 length 是在 65524 的时候就满了,而倒数第二个 IDAT 块长度是 45027,最后一个长度是 138,很明显最后一个 IDAT 块是有问题的,因为他本来应该并入到倒数第二个未满的块里.
利用 python zlib
解压多余 IDAT 块的内容,此时注意剔除 长度、数据块类型及末尾的CRC校验值。
IEND
图像结束数据 IEND(image trailer chunk):它用来标记 PNG 文件或者数据流已经结束,并且必须要放在文件的尾部。
IEND 数据块的长度总是 00 00 00 00
,数据标识总是 IEND 49 45 4E 44
,因此,CRC 码也总是 AE 42 60 82
。
其余辅助数据块
背景颜色数据块 bKGD(background color)
基色和白色度数据块 cHRM(primary chromaticities and white point),所谓白色度是指当
R=G=B=最大值
时在显示器上产生的白色度图像 γ 数据块 gAMA(image gamma)
图像直方图数据块 hIST(image histogram)
物理像素尺寸数据块 pHYs(physical pixel dimensions)
样本有效位数据块 sBIT(significant bits)
文本信息数据块 tEXt(textual data)
图像最后修改时间数据块 tIME (image last-modification time)
图像透明数据块 tRNS (transparency)
压缩文本数据块 zTXt (compressed textual data)
LSB
LSB 全称 Least Significant Bit,最低有效位。PNG 文件中的图像像数一般是由 RGB 三原色(红绿蓝)组成,每一种颜色占用 8 位,取值范围为 0x00
至 0xFF
,即有 256 种颜色,一共包含了 256 的 3 次方的颜色,即 16777216 种颜色。
而人类的眼睛可以区分约1000万种不同的颜色,意味着人类的眼睛无法区分余下的颜色大约有6777216种。
LSB 隐写就是修改 RGB 颜色分量的最低二进制位(LSB),每个颜色会有 8 bit,LSB 隐写就是修改了像数中的最低的 1 bit,而人类的眼睛不会注意到这前后的变化,每个像素可以携带 3 比特的信息。
通过下方的按钮可以观察每个通道的信息,例如查看 R 通道的最低位第 8 位平面的信息。
LSB 的信息借助于 Stegsolve 查看各个通道时一定要细心捕捉异常点,抓住 LSB 隐写的蛛丝马迹。
例题
HCTF - 2016 - Misc
这题的信息隐藏在 RGB 三个通道的最低位中,借助 Stegsolve-->Analyse-->Data Extract
可以指定通道进行提取。
可以发现 zip
头,用 save bin
保存为压缩包后,打开运行其中的 ELF 文件就可以得到最后的 flag。
隐写软件
Last updated