图片隐写这个类很大,所以就慢慢讲,不定期更新=。=
PNG——藏在二进制里的秘密
PNG格式
PNG格式文件由文件署名和数据块(chunk)组成。
文件署名
PNG前8个字节,分别为89 50 4E 47 0D 0A 1A 0A
,ascii显示为,这一个一般就是一个png的开头,有的可能会破坏这个序列导致png无法打开,所以只要记住应该就没有什么问题
数据块
这里有两种类型的数据块,一种是称为关键数据块,就是必须要有的块;另一种叫做辅助数据块。
每个数据块都由下表所示的的4个域组成:
名称 | 字节数 | 说明 |
---|---|---|
长度 | 4字节 | 指定数据块中数据域的长度,其长度不超过(231−1)(231−1)字节 |
数据块类型码 | 4字节 | 数据块类型码由ASCII字母(A-Z和a-z)组成 |
数据块实际内容 | 可变长度 | 存储按照Chunk Type Code指定的数据 |
循环冗余检测 | 4字节 | 存储用来检测是否有错误的循环冗余码 |
而在关键数据块种的文件头数据块IHDR中又有一些图像的基本信息,如:
域的名称 | 字节数 | 说明 |
---|---|---|
Width | 4 bytes | 图像宽度,以像素为单位 |
Height | 4 bytes | 图像高度,以像素为单位 |
Bit depth | 1 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