【Misc】PNG宽高修改 - PNG图片宽高CRC爆破

引言

在CTF竞赛的MISC(杂项)题型中,PNG图片的宽高修改是高频考点之一。这类题目往往通过隐藏图片的真实尺寸,结合CRC校验或文件结构特性来干扰选手获取关键信息。本文将从文件结构解析宽高修改原理工具三个方面,深入探讨PNG图片的宽高修改技术,并附带具体操作示例。

一、PNG文件结构与宽高存储原理

PNG文件的固定头为 89 50 4E 47 0D 0A 1A 0A,用于标识文件类型。关键数据块IHDR位于文件头之后,存储图像的元数据。

图像元数据包括:

  • 宽度:IHDR块的第16-19字节(4字节,大端序存储)
  • 高度:IHDR块的第20-23字节(4字节,大端序存储)
  • 其他参数:色深、颜色等值。

在第二行(0010H)打头4字节就是宽度,紧随其后的就是高度。图示宽度为00 00 04 8A,高度为00 00 01 22。将16进制转换为10进制得到宽度为:1162,高度为290。
每个数据块末尾包含了4字节的CRC32校验码,用于验证数据块(包括IHDR类型码和宽高数据)的完整性。若直接修改宽高值而未更新CRC,图片将无法正常显示。但是某些图片浏览软件会忽视CRC32校验,直接显示修改后的值。

图中选中的蓝色底色字体(00 18 BF 68)则为此处IHDR的CRC32校验码。
校验码的计算方式参考:

import zllib
zllib.crc32(IHDR+widht+height+后部字符)

其实IHDR位一直从图中的49 48 44 52一直到00 18 BF的前面。

二、修改宽高

你可以直接使用010Editor修改宽度位置或高度位置。

上图是一个已经包含FLAG的图片,我们需要修改高度造成FLAG的不显示。

原图值是00 00 01 82,也就是386px,我们设置为200px,也就是00 00 00 C8

保存后,再次观察图片:

图片的下半部分已经被隐藏。

三、恢复宽高

当我们拿到这样一个Misc题目时,我们需要首先了解原图大概在多少。但是其实盲目修改,只要范围不是很大,也可以成功。
例如我直接改为00 00 01 C8

图片中同样也是会包含FLAG的,下部分黑色区域则是没有图像内容导致的。

我们使用Brute_Crack_PNG的GUI工具,可以轻松还原图片。

很明显当前PNG图片是高度位置不符,所以应该爆破高度,我们按照上图所示在工具中勾选爆破高度模式,点击开始爆破。

不出1秒,就找到了有效高度。
点击保存文件,即可保存修改后的图片。

修复的分毫不差。
工具地址:https://github.com/Moxin1044/Brute_Crack_PNG

为了效率,目前使用Go编写该项目,基本代码和原理可以参考master分支中的python版本,具体代码如下:

import zlib


def png_crc32_crack_wh(file_name):
    with open(file_name, 'rb') as f:
        hexdata = f.read().hex()
    PNG_data = hexdata[:16]
    if PNG_data == "89504e470d0a1a0a":
        IHDR = bytes.fromhex(hexdata[24:32])
        width = int(hexdata[36:40], 16)
        height = int(hexdata[44:48], 16)
        str2 = bytes.fromhex(hexdata[48:58])
        crc32 = int(hexdata[58:66], 16)
        add_num = 20000  # 最大宽高,合理修改快速出flag
        for w in range(width, width + add_num):
            for h in range(height, height + add_num):
                width_bytes = w.to_bytes(4, 'big')
                height_bytes = h.to_bytes(4, 'big')
                if zlib.crc32(IHDR + width_bytes + height_bytes + str2) == crc32:
                    return f"PNG图片宽度:{ hex(w)} | {w}\nPNG图片高度:{hex(h)} | {h}"
            if zlib.crc32(IHDR + width_bytes + height_bytes + str2) == crc32:
                break
    else:
        return "可能不是PNG文件,或文件头有修改。\nPNG文件头:89504e470d0a1a0a"


if __name__ == "__main__":
    filename = "test.png"
    print(png_crc32_crack_wh(filename))
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值