当前位置: 首页 > news >正文

CTF实战:巧用文件结构修复图片宽高

1. CTF图片宽高修改实战入门

第一次参加CTF比赛时,遇到一张打不开的图片题目,我完全摸不着头脑。后来才知道,这类"图片隐写"题目往往通过修改图片文件结构中的关键参数来隐藏信息。最常见的套路就是篡改图片的宽高值,让图片无法正常显示,需要选手手动修复才能获取flag。

JPG和PNG作为最常用的图片格式,它们的文件结构就像乐高积木一样由多个标准化的"模块"组成。比如JPG文件以0xFFD8开头,PNG文件以固定的8字节签名开头。理解这些基础结构,就是解决图片宽高修改题目的第一步。

2. JPG文件结构与宽高修改

2.1 JPG文件格式深度解析

JPG文件就像一本精心编排的相册,每个章节都有特定的标记码引导。最重要的SOF0(Start of Frame)标记就是存放宽高信息的"黄金位置"。它的结构是这样的:

标记代码:0xFFC0(固定) 数据长度:2字节 精度:1字节(通常为08) 图像高度:2字节 图像宽度:2字节 颜色分量数:1字节 颜色分量信息:可变长度

实战中遇到过这样一个案例:用hex编辑器打开题目图片,搜索FFC0标记后,发现宽度被设为0000。这就是典型的宽高篡改手法——将实际尺寸清零导致图片无法渲染。

2.2 三步搞定JPG宽高修复

  1. 定位关键标记:使用010 Editor或WinHex搜索FFC0
  2. 读取原始数据:FFC0后第5-8字节就是高度和宽度(注意大端序)
  3. 修改数值:比如将异常的0000改为实际的0280(640像素)
with open('corrupted.jpg', 'rb') as f: data = f.read() sof0_pos = data.find(b'\xff\xc0') height = data[sof0_pos+5:sof0_pos+7] width = data[sof0_pos+7:sof0_pos+9] print(f"原始宽高:{int.from_bytes(width,'big')}x{int.from_bytes(height,'big')}")

记得修改后要检查SOF0段的CRC校验。有次比赛我就因为没校验,改完图片还是打不开,白白浪费半小时。

3. PNG文件结构与爆破技巧

3.1 PNG文件头与IHDR块

PNG的文件结构更像集装箱货轮,每个数据块(chunk)都有明确的标签。关键尺寸信息存放在IHDR块中,这个块的结构非常规范:

长度:4字节(固定13) 类型:4字节("IHDR") 宽度:4字节 高度:4字节 位深:1字节 颜色类型:1字节 压缩方法:1字节 滤波器:1字节 交错方法:1字节 CRC:4字节

遇到过最狡猾的题目是把IHDR块的CRC校验值也改了,导致常规编辑器直接报错。这时候就需要用到CRC爆破技术。

3.2 自动化爆破宽度实战

当遇到CRC校验正确的篡改时,可以写脚本爆破实际宽度:

import binascii import struct with open('flag.png', 'rb') as f: data = f.read() ihdr = data[12:29] # 获取IHDR块内容 for w in range(1000): # 假设宽度在1000像素内 # 重建IHDR块(替换宽度字段) new_ihdr = ihdr[:4] + struct.pack('>i', w) + ihdr[8:] # 计算CRC32(注意包含块类型和内容) crc = binascii.crc32(b'IHDR' + new_ihdr[4:13]) & 0xffffffff if crc == int.from_bytes(ihdr[13:17], 'big'): print(f"爆破成功!实际宽度:{w}") break

这个脚本我在三场不同比赛中都用到过,最快的一次只用了2秒就爆破出正确宽度。记得保存原始文件,有次我直接在内存中修改导致源文件损坏,不得不重新下载题目附件。

4. 高级技巧与避坑指南

4.1 双重校验陷阱

去年某CTF出了道阴险题目:不仅改了宽高,还同时在APP1段(Exif信息)里存了假尺寸。用常规方法修改后,某些图片查看器仍会读取Exif里的错误尺寸。解决方法是用exiftool彻底清除Exif数据:

exiftool -all= corrupted.jpg

4.2 尺寸与压缩的关联

修改JPG宽度时要注意,SOF0里的采样因子(在颜色分量信息中)会影响实际显示比例。有次我把800x600改为1600x300,结果图片显示异常扭曲,就是因为没同步调整采样因子。

4.3 工具链推荐

  • 010 Editor:二进制编辑神器,有现成的JPG/PNG模板
  • pngcheck:快速验证PNG文件结构
  • HxD:轻量级十六进制编辑器
  • Pillow库:Python处理图片的最后防线
from PIL import Image try: img = Image.open('modified.jpg') img.verify() # 验证图片完整性 except Exception as e: print(f"图片校验失败:{str(e)}")

5. 实战案例复盘

去年一道真题给出显示为100x100的图片,但flag明显被截断。用脚本读取真实尺寸发现实际是100x800,修改高度后果然显示完整二维码。关键点在于:

  1. 先用file命令确认是PNG24格式
  2. 发现IHDR显示的100x100与CRC校验匹配
  3. 怀疑存在IDAT块数据异常
  4. 最终发现题目在IDAT块后追加了隐藏数据

这种多层嵌套的题目,就需要结合hex编辑和Python脚本分析:

import zlib with open('challenge.png','rb') as f: data = f.read() idat_pos = data.find(b'IDAT') idat_data = data[idat_pos+4:idat_pos+8] # 解压缩IDAT数据 decompressed = zlib.decompress(data[idat_pos+8:-12]) print(len(decompressed)) # 通过数据量反推真实高度
http://www.gsyq.cn/news/1605473.html

相关文章:

  • Android中App电量优化
  • 防止 iOS 应用被二次打包 代码混淆 和 签名校验的防篡改方案
  • Ryujinx:在PC上免费体验Nintendo Switch游戏的全能模拟器
  • 元器件为什么会失效?
  • 一颗芯片撬动48款爆款产品:杰理2026最新矩阵与尚凌科技供应链布局揭秘
  • 企业微信API开发会话数据进入业务系统时,需要注意哪些边界
  • 《电工学》核心解题思路精讲:从电路定理到暂态分析
  • LoadRunner 11.0 在 Windows 11 上的完整部署与本地化实战
  • 从单线程到多线程 IO,Redis 7.2 到底快了多少?
  • 从0开始学梯形图:10个经典案例,一次讲透!
  • C/C++ 堆与栈的区别——面试完整知识体系
  • 怎么知道供应商在不在行业黑名单里
  • 密码学 | 数字签名进阶:Schnorr签名的线性之美与密钥聚合
  • 为什么 CPU/内存指标不足以支撑真实业务伸缩
  • 软硬一体销售会话分析软硬件一体方案选型与落地参考
  • vitest + vue3 踩坑记录
  • 【课程设计/毕业设计】基于 SpringBoot 的餐厅前台点餐后台管理系统 轻量化餐饮订单服务管理系统设计与实现【附源码、数据库、万字文档】
  • vide coding软件开发流程
  • 2026 私域全面严打,无层级矩阵拼团为什么能安稳做
  • 6个真实用户反馈 森优时铁锌维 白发转黑发 改善周期测评
  • 二层三层交换机选型
  • 如何从三星帐户恢复联系人?分步指南
  • 2026 APP竞品分析怎么做?一套完整流程分享
  • 我做了一个 macOS 菜单栏日历应用:白纸日历
  • 为什么多数AI培训学完用不上?因为课程从来不是在真实业务里
  • 告别ROI计算滞后!实测AI Agent实现预算实时动态转移,重塑企业利润链
  • 快手小店商家端采集
  • 城乡结合部村口通行,乡村出行更规范
  • 计算机毕业设计之基于深度学习的垃圾分类与管理系统
  • 地陪APP平台系统开发公司,陪玩平台酒店渠道价值深度解读