CTF Misc 隐写实战从 JPG 尾部数据到 Gnuplot 绘制二维码的完整技术解析在 CTF 竞赛的 Misc 类别中文件隐写术一直是考察选手基础技能的重要题型。本文将深入解析一种典型的隐写解题思路从 JPG 文件尾部提取十六进制坐标数据并使用 Gnuplot 工具将其还原为二维码的全过程。不同于单一的题目 Writeup我们将提炼出一套通用、可复用的技术方法论。1. JPG 文件结构与尾部数据识别JPGJPEG作为最常见的图像格式之一其文件结构有着严格的标准规范。理解这些规范是发现异常数据的关键。JPG 文件核心标记FF D8文件起始标记SOIFF D9文件结束标记EOIFF E0~FF EF应用标记段FF C0~FF CF帧开始标记注意任何出现在FF D9之后的数据都不属于标准 JPG 文件内容这正是我们需要重点检查的区域。使用 010 Editor 或 Hex Fiend 等工具分析文件时可按照以下步骤操作with open(target.jpg, rb) as f: data f.read() eoi_pos data.find(b\xFF\xD9) if eoi_pos ! -1 and len(data) eoi_pos 2: print(f发现 {len(data)-eoi_pos-2} 字节的尾部附加数据)异常数据特征判断表数据类型特征出现频率坐标数据连续的数字对如(x,y)格式高Base64包含 A-Za-z0-9/ 字符集中加密数据随机分布的十六进制值低2. 十六进制坐标数据的提取与转换当发现尾部存在疑似坐标数据时需要将其转换为可处理的格式。以下是完整的 Python 处理脚本def extract_coordinates(input_file, output_file): with open(input_file, rb) as f: data f.read() eoi data.find(b\xFF\xD9) if eoi -1: raise ValueError(无效的JPG文件) raw_data data[eoi2:] # 跳过EOI标记 # 尝试ASCII和十六进制两种解码方式 try: decoded raw_data.decode(ascii) except UnicodeDecodeError: decoded .join(f{x:02x} for x in raw_data) # 写入处理结果 with open(output_file, w) as out: out.write(decoded) return decoded # 示例用法 coordinates extract_coordinates(hidden.jpg, coordinates.txt)数据清洗技巧使用正则表达式匹配坐标模式r\((\d),(\d)\)处理异常分隔符如将|替换为空格过滤非打印字符ASCII 值 32 或 1263. Gnuplot 的安装与配置Gnuplot 是一款强大的命令行绘图工具特别适合处理坐标数据可视化。以下是各平台的安装指南Windows 系统访问 Gnuplot 官网 下载最新稳定版安装时勾选 Add application directory to your PATH验证安装gnuplot --versionmacOS 系统brew install gnuplotLinux 系统sudo apt-get install gnuplot # Debian/Ubuntu sudo yum install gnuplot # CentOS/RHEL提示如果遇到命令找不到的问题可能需要手动添加环境变量。在 Windows 中可以通过setx PATH %PATH%;C:\Program Files\gnuplot\bin命令添加。4. 数据格式转换与绘图Gnuplot 要求输入数据为空格分隔的 x y 坐标对。以下是将原始数据转换为适用格式的 Python 脚本def convert_to_gnuplot(input_file, output_file): with open(input_file, r) as f: content f.read() # 多种格式兼容处理 points [] if ( in content: # (x,y) 格式 import re matches re.findall(r\((\d),\s*(\d)\), content) points [f{x} {y} for x, y in matches] elif , in content: # x,y 格式 points [line.replace(,, ) for line in content.splitlines()] else: # 纯十六进制数据 hex_pairs [content[i:i2] for i in range(0, len(content), 2)] points [f{i} {int(h, 16)} for i, h in enumerate(hex_pairs)] with open(output_file, w) as f: f.write(\n.join(points)) # 转换数据 convert_to_gnuplot(coordinates.txt, plot_data.txt)Gnuplot 绘图命令详解# 基本绘图命令 set terminal pngcairo size 800,800 enhanced font Arial,12 set output qr_code.png unset border unset xtics unset ytics set size square plot plot_data.txt with points pointtype 5 pointsize 1高级参数调整表参数作用典型值pointtype点形状样式5实心方块pointsize点大小0.5-2set xrangeX轴范围[0:100]set yrangeY轴范围[0:100]set margin边距调整05. 自动化脚本与实战技巧将上述流程整合为自动化脚本可大幅提高解题效率。以下是完整的 Python 自动化处理方案import os import re from subprocess import run def jpg_to_qr(input_jpg, output_pngqr_code.png): # 1. 提取尾部数据 with open(input_jpg, rb) as f: data f.read() eoi data.find(b\xFF\xD9) if eoi -1: raise ValueError(无效的JPG文件) # 2. 转换坐标格式 hex_data data[eoi2:].hex() coords [(i, int(hex_data[j:j2], 16)) for i, j in enumerate(range(0, len(hex_data), 2))] # 3. 生成Gnuplot数据文件 with open(temp.dat, w) as f: f.write(\n.join(f{x} {y} for x, y in coords)) # 4. 生成Gnuplot脚本 gnuplot_script f set terminal pngcairo size {len(coords)//2},{len(coords)//2} set output {output_png} unset key; unset border; unset xtics; unset ytics set margins 0,0,0,0 plot temp.dat with points pt 5 ps 0.5 with open(plot.gp, w) as f: f.write(gnuplot_script) # 5. 执行Gnuplot run([gnuplot, plot.gp], checkTrue) print(f二维码已生成: {output_png}) # 使用示例 jpg_to_qr(challenge.jpg)实战经验分享当二维码不清晰时尝试调整pointsize参数0.2-2.0 范围遇到坐标范围异常时先用awk {print $1} temp.dat | sort -n | tail -1检查最大值对于密集点阵可添加set palette defined (0 white, 1 black)增强对比度在无GUI环境下使用时添加set terminal dumb可查看ASCII艺术预览6. 二维码解码与验证生成图像后使用专业工具验证结果推荐工具组合在线解码zxing.org/w/decode.jspx命令行工具zbarimg(安装sudo apt install zbar-tools)zbarimg qr_code.pngPython 库from pyzbar.pyzbar import decode from PIL import Image result decode(Image.open(qr_code.png)) print(result[0].data.decode() if result else 解码失败)常见问题排查问题现象可能原因解决方案无任何输出数据提取位置错误检查FF D9后是否有数据图像杂乱坐标范围不正确调整set xrange/set yrange点阵稀疏数据步长错误尝试for i in range(0, len(data), 4)解码失败二维码版本不匹配调整图像尺寸为 21x21 的整数倍7. 扩展应用与变种题型掌握基础方法后可以应对各种变形题目变种类型加密坐标坐标经过简单加密如 XOR 操作decoded bytes(x ^ 0xAA for x in raw_data)分块存储数据分散在多个文件标记段中非标准编码使用 Base85 或自定义编码方案三维坐标需要splot命令进行三维渲染性能优化技巧对于超大数据集10万点使用set sampling random 0.1降采样二进制数据直接处理比文本解析快 10 倍以上启用 GPU 加速set terminal pngcairo backend cairo通过本技术路线我们不仅能够解决特定题目更重要的是建立了一套通用的文件隐写分析框架。这种从二进制数据到可视化结果的转化能力在数字取证、安全分析等领域都有广泛应用价值。