浅析png-filter

admin 2022年7月12日21:36:58评论251 views字数 4416阅读14分43秒阅读模式

一篇浅析png-filter的水文

前言

PNG(Portable Network Graphics,便携式网络图形)作为常见的图像文件存放格式之一,具有「无损压缩」「支持透明通道」「可移植」等特点,广泛运用于互联网及其他方面。总之对其filter做一篇浅析,在后文的介绍中,都是围绕CTF隐写点做介绍。

PNG和其他主流格式比较

PNG是一种光栅图形文件格式,支持无损数据压缩,PNG是作为GIF改进的。而JPEG是一种常用的有损压缩数字图像的方法,通常实现 10:1 压缩,图像质量几乎没有可察觉的损失。因此在常见的隐写中,一般不会在使用stegsolve对JPEG图片进行分析,因为其有损的原因,除了一些软件去对JPEG做LSB隐写,几乎不会直接在stegsolve中提取出可用的信息。

PNG结构

PNG分为signature和chunk

signature

其中signature即常说的文件头,PNG的文件头为x89x50x4ex47x0dx0ax1ax0a,通过文件头来判断文件类型是最常见的一种判断方式 。一般情况下,出现这8个字节,就代表后面的数据包含一个PNG图像,且直到IEND结束(PNG的"文件尾",字节为x00x00x00x00x49x45x4ex44xaex42x60x82)

chunk

接下来是数据块,每个数据块都是由Length,Chunk type,data,CRC组成的。

其中Length是4字节的无符号数,范围是0~2^31-1。

chunk type为块的类型,例如PNG的数据块其chunk type就为IDAT。

data为该块的大小,该值可以为空

CRC是根据Chunk type+data计算出来的,不包含Length,采用循环冗余码算法。详细见参考链接3

总结如下

数据块字段 长度 特点
uint32 Length 4 bytes 最大0xFFFFFFFF
Type 4 bytes 每个字节限制在[a-zA-Z]
data 不定字节,最小为0 可为0
uint32 CRC 4 bytes 计算时不包括Length

chunk Type命名规则

第一个字母: 大写表示渲染图片所必需的块,小写表示用于渲染图像的辅助块,即非必须。

第二个字母:大写表示公共标准块,而小写表示专有或非标准。

第三个字母:始终是大写字母。

第四个字母:小写字母表示如果生成新的 PNG 则可以安全复制。否则制作衍生PNG文件时,要么重新生成它,要么将其删除。

例如:

浅析png-filter

带有PLTE的图像

IHDR

对于chunk来说,IHDRIDATIEND是必须的。这里只讲一手IHDR,因为后面开始解析IDAT

IHDR头即块中Type字段为IDAT,IHDR应该是PNG数据流中的第一个块且唯一。其data包含如下信息

字段 长度 特点
Width(宽) 4 bytes uint32
Height(高) 4 bytes uint32
Bit depth(位深) 1 bytes 不同类型允许的位深度不同
Colour type(颜色类型) 1 bytes 0,2,3,4,6五个值
Compression method(压缩方式) 1 bytes 值只有0,deflate算法
Filter method(过滤器类型) 1 bytes 值只有0,包含5种类型
Interlace method(扫描方式) 1 bytes 逐行扫描(0),Adam7隔行扫描(1)


浅析png-filter

其中 颜色类型如下:

类型值 类型 描述
0 灰度 灰度图像
2 真彩色 常见的RGB图,包含R,G,B三种通道
3 索引色 索引图,每个像素都是一个调色板索引,会出现PLTE块
4 带alpha灰度色 灰度+alpha
6 带alpha真彩色 RGB+A

解析IDAT内容

解压缩

IDAT中的data是采用zlib库的DEFLATE算法进行压缩的,在python中直接使用zlib库解压缩

import zlib
f = open('file.txt','rb').read() #file.txt为一段压缩后的数据
print(zlib.decompress(f))

过滤

在之前提到Filter method只有一种类型,但是包含5种算法,分别是表中内容

表1:定义变量

c b
a x

x为当前字节,a为x左边那一像素值,b为上一条扫描线对应的像素值,c为左上

或者这个

浅析png-filter

表2:

类型 名称 过滤函数 重构函数
0 None F(x) = O(x) Rec(x) = F(x)
1 Sub F(x) = O(x) - O(a) Rec(x) = F(x) + Rec(a)
2 Up F(x) = O(x) - O(b) Rec(x) = F(x) + Rec(b)
3 Average F(x) = O(x) - (O(a)+O(b)) / 2 Rec(x) = F(x) + (Rec(a) + Rec(b)) / 2
4 Paeth F(x) = O(x) - PaethPredictor(O(a), O(b), O(c)) Rec(x) = O(x) + PaethPredictor(O(a), O(b), O(c))

其中 PaethPredictor函数定义如下:

    p = a + b - c
pa = abs(p - a)
pb = abs(p - b)
pc = abs(p - c)
if pa <= pb and pa <= pc then Pr = a
else if pb <= pc then Pr = b
else Pr = c
return Pr


浅析png-filter


注意:

1.如果在计算完之后超出了255或者小于了0,如果没有uint,那么就需要手动&0xFF才行

2.对于average中的除以2,使用>>1即可,例如X[j] = (X[j] - ((A[j] + B[j])>>1)) & 0XFF

  1. 对于Sub,第一个值为标准值,不做任何过滤 对于Up,如果是第一行那么也不做任何过滤 对于average,如果是第一行,则第一个值不做任何过滤,其他值则计算X[j] = (X[j] - (A[j]>>1)) & 0XFF 如果是第一列,则计算X[j] = (X[j] - (B[j]>>1)) & 0XFF 对于Paeth,如果是第一行,则同Sub;如果是第一列,则与上一行作差

针对Filter进行隐写

根据filter一共有5种类型,出一道指定filter的题目,其中包含5种类型的filter,构造4进制,用None类型进行分割

可以手写出5种filter类型的Filter Function,自己调用并修改字节,最后进行压缩,构造好chunk并添加signature IHDR IEND

例如构造一张10*5的图片,其filter类型分别为0 1 2 3 4 5,原图的RGB值全为128 128 128

from PIL import Image
pic = Image.new('RGB',(10,5),(128,128,128))
pic.save('tmps.png')

通过zlib库解压缩可以看一下得到的字节

import zlib
import struct
f = open('tmps.png','rb').read()
ind = f.index(b'IDAT')
length, = struct.unpack('>L',f[ind-4:ind])
data = f[ind+4:ind+4+length] #只有一个IDAT块
print(zlib.decompress(data))
#b'x01x80x80x80x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x02x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x02x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x02x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x02x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00'


浅析png-filter

接下来,有多种方法实现,例如提取RGB值,重构整个data。

先用数组存储一下pixel

from PIL import Image
img = Image.open('tmps.png')
w,h = img.size
pixel = []
for i in range(h):
    tmp = []
    for j in range(w):
        tmp.append(img.getpixel((j,i)))
    pixel.append(tmp)
print(pixel)


浅析png-filter

接下来,为了演示的方便,所以写的比较直接,没有任何优化,甚至不能直接拿去用

浅析png-filter

哦,是多么好看的字节

要注意的是,在使用的时候是拿像素的值去做加减,而不是拿字节

然后再用zlib库进行压缩,最后重新写进去,得到这样

浅析png-filter

78 9C 63 68 C0 0B 18 81 98 01 37 60 C2 23 07 04 CC 0E 0E 0E 78 A4 59 F0 EB 06 00 14 0E 11 4B 可以自己去inflate看看

浅析png-filter

简直一模一样(因为本来就一模一样) 这样就可以实现自己指定filter类型 实现隐写咯

不放脚本,是真的因为写的太垃圾了,只用了PIL去处理

题目试炼

既然文章写的那么短,要不再给大伙出个简单题,见下图图一与图二,flag格式mumuzi{}

题目名称:GURA

题目下载:https://pan.baidu.com/s/1eC7-XRK9RXnnF6-u9n2Gdw 提取码(GAME)

题目试练地址:https://www.ichunqiu.com/battalion?t=1&r=70900

浅析png-filter

图一

浅析png-filter

图二

参考链接

1.https://en.wikipedia.org/wiki/Portable_Network_Graphics: Portable Network Graphics.

2.https://en.wikipedia.org/wiki/JPEG: JPEG

3.https://en.wikipedia.org/wiki/Cyclic_redundancy_check : CRC

4.https://www.w3.org/TR/PNG/Information technology — Computer graphics and image processing — Portable Network Graphics (PNG): Functional specification. ISO/IEC 15948:2003 (E)

致谢

特别感谢**Rightp4th(TRPH)**的倾囊相授与对文章错误的修改


原文始发于微信公众号(i春秋):浅析png-filter

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月12日21:36:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅析png-filterhttp://cn-sec.com/archives/1173257.html

发表评论

匿名网友 填写信息