png是一种采用无损压缩算法的位图格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。
PNG文件经常出现在CTF杂项题目当中,常见的考察方法有更改图片长宽高、修改区块信息等方式。难度不等。因此想要做好这一类题目必须要对PNG文件对格式有深入理解。
PNG文件结构:
根据PNG文件的定义来说,其文件头位置总是由位固定的字节来描述的:
十进制数 | 137 80 78 71 13 10 26 10 |
---|---|
十六进制数 | 89 50 4E 47 0D 0A 1A 0A |
其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。文件中剩余的部分由3个以上的PNG的数据块(Chunk)按照特定的顺序组成,因此,一个标准的PNG文件结构应该如下:
PNG文件标志 | PNG数据块 | …… | PNG数据块 |
---|---|---|---|
PNG数据块(Chunk)
PNG 定义了两种数据块,一种是关键数据块(critical chunk),这是标准数据块,是PNG文件中必须所具备的。另一种是辅助数据块(ancillary chunk),这是可选的数据块。PNG文件一共定义了四种关键数据块,每一个PNG文件当中都具备有这些关键数据块。
PNG文件格式中的数据块 | ||||||
---|---|---|---|---|---|---|
数据块符号 | Hex | 数据块名称 | 多数据块 | 可选否 | 位置限制 | 关键数据块 |
IHDR | 49 48 44 52 | 文件头数据块 | 否 | 否 | 第一块 | 是 |
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 | 49 44 41 54 | 图像数据块 | 是 | 否 | 与其他IDAT连续 | 是 |
tIME | 图像最后修改时间数据块 | 否 | 是 | 无限制 | ||
tEXt | 文本信息数据块 | 是 | 是 | 无限制 | ||
zTXt | 压缩文本数据块 | 是 | 是 | 无限制 | ||
fRAc | (专用公共数据块) | 是 | 是 | 无限制 | ||
gIFg | (专用公共数据块) | 是 | 是 | 无限制 | ||
gIFt | (专用公共数据块) | 是 | 是 | 无限制 | ||
gIFx | (专用公共数据块) | 是 | 是 | 无限制 | ||
IEND | 49 45 4E 44 | 图像结束数据 | 否 | 否 | 最后一个数据块 | 是 |
数据块结构
PNG文件中,每个数据块由4个部分组成并按照如下顺序排列:
名称 | 字节数 | 说明 |
---|---|---|
Length (长度) | 4字节 | 指定数据块中数据域的长度,其长度不超过(231-1)字节 |
Chunk Type Code (数据块类型码) | 4字节 | 数据块类型码由ASCII字母(A-Z和a-z)组成。例如:IDAT |
Chunk Data (数据块数据) | 可变长度 | 存储按照Chunk Type Code指定的数据 |
CRC 32(循环冗余检测) | 4字节 | 存储用来检测是否有错误的循环冗余码 |
在这里需要详细讲解一下这里的Length的计算范围,Length的计算长度不包含Chunk Type Code和CRC校验数据,只包括ChunkData。CRC校验的范围包括了Chunk Type Code和Chunk Data
下面,我们依次来了解一下各个关键数据块的结构。
IHDR
文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。
文件头数据块由13字节组成,它的格式如下表所示。
域的名称 | 字节数 | 说明 |
---|---|---|
Width | 4 bytes | 图像宽度,以像素为单位 |
Height | 4 bytes | 图像高度,以像素为单位 |
Bit depth | 1 byte | 图像深度: 索引彩色图像:1,2,4或8 灰度图像:1,2,4,8或16 真彩色图像:8或16 |
ColorType | 1 byte | 颜色类型: 0:灰度图像, 1,2,4,8或16 2:真彩色图像,8或16 3:索引彩色图像,1,2,4或8 4:带α通道数据的灰度图像,8或16 6:带α通道数据的真彩色图像,8或16 |
Compression method | 1 byte | 压缩方法(LZ77派生算法) |
Filter method | 1 byte | 滤波器方法 |
Interlace method | 1 byte | 隔行扫描方法: 0:非隔行扫描 1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) |
PLTE
调色板数据块PLTE(palette chunk)包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。
PLTE数据块是定义图像的调色板信息,PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节组成:
颜色 | 字节 | 意义 |
---|---|---|
Red | 1 byte | 0 = 黑色, 255 = 红 |
Green | 1 byte | 0 = 黑色, 255 = 绿色 |
Blue | 1 byte | 0 = 黑色, 255 = 蓝色 |
因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。
对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。
真彩色图像和带alpha通道数据的真彩色图像也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
IDAT
图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
IDAT存放着图像真正的数据信息,因此,如果能够了解IDAT的结构,我们就可以很方便的生成PNG图像。
IEND
图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。
如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:
00 00 00 00 49 45 4E 44 AE 42 60 82
不难明白,由于数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。
举个栗子
修改长宽高
可以看到IHDR(49 48 44 52)数据块关键字之后紧接的就是定义宽与高的两个四个字节块,很多入门类型的ctf题目乐意修改长宽高来隐藏某些信息。
计算IDAT 长度 CRC类型
这是一段IDAT,在数据块关键字IDAT 49 44 41 54 前的00 01 00 00定义了该数据块的长度,也就是65536个字节。
我们从数据块关键字选择区块,长度为00 01 00 00。
被选中的区块后的四个字节为该数据块的CRC校验码。
CRC校验码的计算所选择的区块还要包含数据块关键字,以这个IDAT区块为例。该数据块的数据块数据为00 01 00 00 ,加上数据块关键字后是 00 01 00 04,我们从标定Length之后即开始选择00 01 00 04。
对所选择块进行CRC32计算,得出结果与数据块数据后的CRC校验码相同。当数据块中的CRC校验码与实际不符合时,该区块不会被使用或显示。