PNG-的IDAT解析

     前段时间在做PNG的解析问题,对于PNG格式基于字节的读写有了一定了解,此文记录如何解析PNG图片的数据字段,也就是真实像素的二进制解析,PNG的data数据基本数据块的组成为 :Filter +  压缩数据。(这里除去校验头尾),二进制读取自己写即可,文件头的解析等,本文也不记录,可以参看https://www.w3.org/TR/PNG/#9Filter或者参考这里中文解释https://www.jianshu.com/p/ecacf2f60cb2,这里面有一些介绍,原理已经讲的很明白了,但是没有代码的干货,这里补充一下,下面的算法是假设我们已经将压缩的数据使用zlip压缩解压了,但是需要根据Filter 类型去还原像素值,算法如下:
		/*
		   -------------
		   |C | B | D |
		   ------------
		   |A | x |   |
		   ------------

		Type	Name	Filter Function	Reconstruction Function
		0	None	Filt(x) = Orig(x)	Recon(x) = Filt(x)
		1	Sub	Filt(x) = Orig(x) - Orig(a)	Recon(x) = Filt(x) + Recon(a)
		2	Up	Filt(x) = Orig(x) - Orig(b)	Recon(x) = Filt(x) + Recon(b)
		3	Average	Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2)	Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
		4	Paeth	Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c))	Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))

		x	the byte being filtered;
		a	the byte corresponding to x in the pixel immediately before the pixel containing x(or the byte immediately before x, when the bit depth is less than 8);
		b	the byte corresponding to x in the previous scanline;
		c	the byte corresponding to b in the pixel immediately before the pixel containing b(or the byte immediately before b, when the bit depth is less than 8).
		*/
		enum FilterTypes
		{
				eFTNone =0,
				eFTSub = 1,
				eFTUp =2,
				eFTAverage = 3,
				eFTPaeth =4,
		};

void GsPNG::Filtering(FilterTypes etype, unsigned char * row, int length)
{
	if (m_Type != eRGBA8)//先实现常用的rgba,这里其实不用管通道顺序,只需要知道几个通道即可,
		return; 
	m_CacheBefore.Allocate(length);
	
	switch (etype)
	{
		//原值不处理
		case eFTNone:break;
		case eFTSub:
		{
			for (int i = 4; i < length; i += 4)
			{
				row[i]	+= row[i - 4];
				row[i + 1] += row[i - 3];
				row[i + 2] += row[i - 2];
				row[i + 3] += row[i - 1];
			}
		}
		break;
		case eFTUp: 
		{
			for (int i = 0; i < length; i += 4)
			{
				row[i] += m_CacheBefore.BufferHead()[i];
				row[i + 1] += m_CacheBefore.BufferHead()[i + 1];
				row[i + 2] += m_CacheBefore.BufferHead()[i + 2];
				row[i + 3] += m_CacheBefore.BufferHead()[i + 3];
			}
		}
		break;
		case eFTAverage:
		{
			row[0] += floor((m_CacheBefore.BufferHead()[0] + 0) / 2);
			row[1] += floor((m_CacheBefore.BufferHead()[1] + 0) / 2);
			row[2] += floor((m_CacheBefore.BufferHead()[2] + 0) / 2);
			row[3] += floor((m_CacheBefore.BufferHead()[3] + 0) / 2);
			for (int i = 4; i < length; i += 4)
			{
				row[i] += floor((m_CacheBefore.BufferHead()[i]+ row[i - 4])/2);
				row[i + 1] += floor((m_CacheBefore.BufferHead()[i+1] + row[i - 3]) / 2);
				row[i + 2] += floor((m_CacheBefore.BufferHead()[i+2] + row[i - 2]) / 2);
				row[i + 3] += floor((m_CacheBefore.BufferHead()[i+3] + row[i - 1]) / 2);
			}
		}
		break;
		case eFTPaeth:
		{
			//从0开始,超出的像素用0 代替,这里在官网有一排小字介绍
			row[0] += PaethPredictor(0, m_CacheBefore.BufferHead()[0], 0);
			row[1] += PaethPredictor(0, m_CacheBefore.BufferHead()[1], 0);
			row[2] += PaethPredictor(0, m_CacheBefore.BufferHead()[2], 0);
			row[3] += PaethPredictor(0, m_CacheBefore.BufferHead()[3], 0);

			for (int i = 4; i < length; i += 4)
			{
				row[i] += PaethPredictor(row[i - 4], m_CacheBefore.BufferHead()[i], m_CacheBefore.BufferHead()[i - 4]);
				row[i + 1] += PaethPredictor(row[i - 3], m_CacheBefore.BufferHead()[i+1], m_CacheBefore.BufferHead()[i - 3]);
				row[i + 2] += PaethPredictor(row[i - 2], m_CacheBefore.BufferHead()[i+2], m_CacheBefore.BufferHead()[i - 2]);
				row[i + 3] += PaethPredictor(row[i - 1], m_CacheBefore.BufferHead()[i+3], m_CacheBefore.BufferHead()[i - 1]);
			}
		}
		break;
	default:
		break;
	}
	//记录一下,下次取BC的值
	m_CacheBefore.Copy(row, length);
}
再次重申上面的代码是将已经zlip解压后的数据使用Filtering 过滤反算后得到真实的像素值的过程,正算一样的,这里就不做过多描述


<think>嗯,用户问的是如何在PNGIDAT块中嵌入恶意代码或者隐藏数据的技术。首先,我需要回忆一下PNG文件的结构,特别是IDAT块的作用。IDAT块存储的是图像的实际像素数据,通常经过压缩,所以隐藏信息可能会利用压缩过程中的某些特性。 根据引用[1],之前有人通过修改图片高度来隐藏信息,导致图片无法正常显示,这样常规工具可能检测不出来。所以,修改文件头中的高度值可能是一种方法。不过用户现在关心的是IDAT块本身,所以可能需要更深入的分析。 然后,引用[2]提到预测器状态用于差分编码,这可能涉及到在压缩前的预处理步骤中插入数据。PNG使用的zlib压缩,可能在压缩后的数据中直接插入额外字节,但需要注意不影响图像的正常显示,否则容易引起怀疑。比如,在IDAT块末尾添加数据,或者利用多个IDAT块的结构,将隐藏信息分散存储。 另外,引用[3]提到IDAT块异常的情况,可能涉及到对压缩数据的修改。比如,在压缩后的数据流中插入特定的字节,或者在编码过程中使用LSB隐写术,但这种方法可能对图像质量影响较大,容易被发现。 还需要考虑的是,如何确保嵌入的数据不会破坏PNG文件的校验和,每个块都有CRC校验,所以修改后必须重新计算CRC值,否则文件会损坏。因此,嵌入数据时需要同时更新CRC。 另外,恶意代码的嵌入可能不只是简单的数据附加,可能需要利用漏洞。例如,某些图像查看器或处理库在解析IDAT块时可能存在缓冲区溢出漏洞,通过构造特定的压缩数据来触发漏洞执行代码。不过这可能属于更高级的攻击技术,需要具体漏洞的支持。 总结可能的步骤:1. 修改文件头参数(如高度)来隐藏数据;2. 在IDAT压缩数据中插入额外字节,利用预测器或过滤方法;3. 使用LSB在像素数据中隐写;4. 利用多个IDAT块分散隐藏信息;5. 构造恶意数据触发漏洞。 但需要注意的是,这些技术可能被用于非法用途,回答时应强调合法性和道德规范,同时提供的信息应足够帮助用户理解原理,而不鼓励滥用。</think>在PNGIDAT块中嵌入隐藏数据或恶意代码的技术主要涉及对图像压缩数据的特殊处理,以下是核心原理和实现方法: ### 一、IDAT块隐藏数据的基本原理 1. **压缩数据插入** PNGIDAT块存储经zlib压缩的像素数据,可通过在压缩流中插入冗余数据实现隐藏。由于zlib支持多IDAT块,可在最后一个有效IDAT块后追加包含隐藏数据的IDAT块[^1]。 2. **预测器编码利用** 在过滤阶段(Filtering)使用差分编码时,可通过修改预测器类型字节(Filter byte)的低位嵌入信息。例如: ``` 原始字节:0x01 像素数据... 修改字节:0x01|嵌入比特 像素数据... ``` 3. **LSB隐写扩展** 对解压后的像素数据实施最低有效位(LSB)修改,再重新压缩。此方法需注意压缩后数据体积变化规律[^2]。 ### 二、关键技术实现步骤 ```python import zlib def embed_data(png_path, payload): # 提取原始IDAT数据 with open(png_path, 'rb') as f: data = f.read() idat_start = data.find(b'IDAT') + 4 idat_end = data.find(b'IEND') zlib_data = data[idat_start:idat_end] # 解压并嵌入数据 decompressed = zlib.decompress(zlib_data) modified = decompressed + b'###' + payload # 添加标记和载荷 new_zlib = zlib.compress(modified) # 重建PNG文件 new_data = data[:idat_start] + new_zlib + data[idat_end:] with open('modified.png', 'wb') as f: f.write(new_data) ``` ### 三、对抗检测的关键 1. **CRC校验修正** 每个IDAT块包含CRC32校验值,嵌入后需重新计算: $$ \text{CRC} = \text{CRC32}(\text{chunk_type} + \text{modified_data}) $$ 2. **多IDAT块分割** 将隐藏数据分散到多个IDAT块中,避免单个块大小异常。标准建议每个IDAT块不超过32768字节[^3]。 3. **压缩率伪装** 保持原始压缩级别(通常zlib level=6),避免压缩率异常引发检测。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值