图片隐写这个类很大,所以就慢慢讲,不定期更新=。=

PNG——藏在二进制里的秘密

PNG格式

​ PNG格式文件由文件署名和数据块(chunk)组成。

文件署名

​ PNG前8个字节,分别为89 50 4E 47 0D 0A 1A 0A,ascii显示为TIM截图20200315144237.jpg,这一个一般就是一个png的开头,有的可能会破坏这个序列导致png无法打开,所以只要记住应该就没有什么问题

数据块

​ 这里有两种类型的数据块,一种是称为关键数据块,就是必须要有的块;另一种叫做辅助数据块。

​ 每个数据块都由下表所示的的4个域组成:

名称字节数说明
长度4字节指定数据块中数据域的长度,其长度不超过(231−1)(231−1)字节
数据块类型码4字节数据块类型码由ASCII字母(A-Z和a-z)组成
数据块实际内容可变长度存储按照Chunk Type Code指定的数据
循环冗余检测4字节存储用来检测是否有错误的循环冗余码

​ 而在关键数据块种的文件头数据块IHDR中又有一些图像的基本信息,如:

域的名称字节数说明
Width4 bytes图像宽度,以像素为单位
Height4 bytes图像高度,以像素为单位
Bit depth1 byte图像深度:索引彩色图像:1,2,4或8 ;灰度图像:1,2,4,8或16 ;真彩色图像:8或16

​ 一般一些简单题中会调整图片的高度,导致之前高度以下的一部分信息显示不出,如果调整长度的话,照片便会变化,因为png的填充方式是一格一格填充,改变长度会破坏原本图像样式。

IDAT隐写

​ 这一个算是最老的一个隐写手段了=。=(犹记刚刚入门的时候怎么看都看不懂)。其原理是在已有的png图片基础后面加上自己的png的IDAT并压缩。这种情况下我们可以先使用pngcheck看看png图片是否有IDAT异常,一般的异常是指在IDAT本应该结束后方多出来了一块新的IDAT,使用脚本解密即可:

import zlib

import binascii

IDAT = "IDAT_data".decode('hex')

result = binascii.hexlify(zlib.decompress(IDAT))

print (result.decode('hex'))

print (len(result.decode('hex')))

放IEND后面隐写=。=

​ png在IEND后将不读取,使用在IEND后无论写什么都不会导致图像有变化,也是最简单,最纯粹的一种隐写。一般在后面的文件可以是一段字符,或是base64编码后的字符,或是一个压缩包,更有甚者在后面放过一个内存取证包(我不是,我没有,别瞎说)

​ 这差不多就是png的二进制里可以隐写的内容,如有遗漏,请在下面留言告知,谢谢~

PS:二进制隐写可能不算多,但是修复png还是算多的233,详情可以看UNCTF——Happy_puzzle