CTF实战Python自动化拼接36张碎片图片还原二维码全攻略在CTF竞赛中图片碎片重组是典型的体力活挑战——36张无序碎片散落在文件夹里文件名混乱且扩展名错误。手动重命名和拼接不仅耗时还容易出错。本文将用Python脚本实现全自动图片排序→重命名→预拼接最后在Photoshop中一键生成可扫描的二维码。我曾用这个方法将原本需要2小时的手工操作压缩到30秒内完成。1. 环境准备与碎片分析首先确保工作目录中有以下结构假设所有碎片位于./fragments/. ├── assemble.py # 主脚本 └── fragments/ # 存放原始碎片 ├── abc.jpg # 实际是png文件 ├── 123.bmp # 实际是png文件 └── ... # 共36个文件安装必要的Python库pip install pillow numpy关键工具说明Pillow (PIL)处理图片读取和格式转换os/sys文件系统操作numpy矩阵运算辅助排序注意实际CTF比赛中碎片文件常被故意修改扩展名如.jpg实际是.png需用二进制读取判断真实格式。2. 自动化重命名与排序策略2.1 文件特征提取通过分析每个碎片的二进制头信息确定真实格式并生成哈希指纹from PIL import Image import hashlib def get_file_signature(filepath): with open(filepath, rb) as f: data f.read(32) # 读取前32字节判断文件类型 if data.startswith(b\x89PNG): img Image.open(filepath) return hashlib.md5(img.tobytes()).hexdigest() else: raise ValueError(Unsupported file format)2.2 基于规则的排序算法假设已知二维码被分割为6x6网格按从左到右、从上到下编号import re def natural_sort_key(s): 自然排序fragment_9.png应在fragment_10.png之前 return [int(t) if t.isdigit() else t.lower() for t in re.split((\d), s)]执行批量重命名import os def rename_fragments(dir_path): files sorted(os.listdir(dir_path), keynatural_sort_key) for idx, filename in enumerate(files): os.rename( os.path.join(dir_path, filename), os.path.join(dir_path, ffragment_{idx1}.png) )3. 图片预拼接技术实现3.1 计算画布尺寸def calculate_canvas_size(fragments_dir): sample Image.open(os.path.join(fragments_dir, fragment_1.png)) width, height sample.size return (width * 6, height * 6) # 6x6网格3.2 自动化拼接核心代码def assemble_qrcode(fragments_dir, output_path): canvas Image.new(RGB, calculate_canvas_size(fragments_dir)) for row in range(6): for col in range(6): pos row * 6 col 1 fragment Image.open(f{fragments_dir}/fragment_{pos}.png) canvas.paste(fragment, (col * fragment.width, row * fragment.height)) canvas.save(output_path) return canvas参数说明row * fragment.height纵向偏移量计算col * fragment.width横向偏移量计算4. Photoshop最终优化技巧即使自动化拼接后可能仍需微调图层对齐全选所有碎片图层使用自动对齐图层功能修正1-2像素偏差颜色校正图像 → 调整 → 色阶 输入色阶20 / 1.00 / 240扫描测试使用多款扫码工具测试微信、支付宝、专业QR扫描器若部分区域无法识别可尝试高斯模糊半径0.5px后再锐化常见问题解决方案问题现象可能原因解决方法扫码器无法识别碎片间存在缝隙在PS中扩展画布1像素二维码扭曲碎片尺寸不一致统一缩放为相同尺寸黑白反色颜色模式错误转换为灰度模式5. 高阶技巧与性能优化5.1 多线程加速处理当碎片数量超过100张时from concurrent.futures import ThreadPoolExecutor def parallel_assemble(fragments_dir): with ThreadPoolExecutor(max_workers4) as executor: futures [] for fragment in os.listdir(fragments_dir): futures.append(executor.submit(process_fragment, fragment)) results [f.result() for f in futures]5.2 模糊碎片匹配算法当文件名完全无规律时可使用SIFT特征匹配import cv2 def find_adjacent_fragments(img1, img2): sift cv2.SIFT_create() kp1, des1 sift.detectAndCompute(img1, None) kp2, des2 sift.detectAndCompute(img2, None) # 使用FLANN匹配器寻找对应点 matches flann.knnMatch(des1, des2, k2) return [m for m,n in matches if m.distance 0.7*n.distance]5.3 内存优化方案处理超大型图片时def memory_efficient_assembly(): 分块加载技术 for chunk in range(0, total_fragments, chunk_size): fragment_chunk load_fragment_chunk(chunk) yield process_chunk(fragment_chunk)在最近的一次HackTheBox挑战赛中我遇到一个变种问题碎片中包含10%的干扰项。通过给每个碎片添加置信度评分基于二维码定位图案匹配度成功过滤了无效碎片def qr_confidence_score(img): 检测二维码三个定位方块的匹配度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 计算定位图案区域的对比度得分 return contrast_score(binary[0:20, 0:20]) # 左上角区域
CTF实战:手把手教你用Python脚本拼接36张碎片图片,还原隐藏的二维码
发布时间:2026/5/15 15:39:02
CTF实战Python自动化拼接36张碎片图片还原二维码全攻略在CTF竞赛中图片碎片重组是典型的体力活挑战——36张无序碎片散落在文件夹里文件名混乱且扩展名错误。手动重命名和拼接不仅耗时还容易出错。本文将用Python脚本实现全自动图片排序→重命名→预拼接最后在Photoshop中一键生成可扫描的二维码。我曾用这个方法将原本需要2小时的手工操作压缩到30秒内完成。1. 环境准备与碎片分析首先确保工作目录中有以下结构假设所有碎片位于./fragments/. ├── assemble.py # 主脚本 └── fragments/ # 存放原始碎片 ├── abc.jpg # 实际是png文件 ├── 123.bmp # 实际是png文件 └── ... # 共36个文件安装必要的Python库pip install pillow numpy关键工具说明Pillow (PIL)处理图片读取和格式转换os/sys文件系统操作numpy矩阵运算辅助排序注意实际CTF比赛中碎片文件常被故意修改扩展名如.jpg实际是.png需用二进制读取判断真实格式。2. 自动化重命名与排序策略2.1 文件特征提取通过分析每个碎片的二进制头信息确定真实格式并生成哈希指纹from PIL import Image import hashlib def get_file_signature(filepath): with open(filepath, rb) as f: data f.read(32) # 读取前32字节判断文件类型 if data.startswith(b\x89PNG): img Image.open(filepath) return hashlib.md5(img.tobytes()).hexdigest() else: raise ValueError(Unsupported file format)2.2 基于规则的排序算法假设已知二维码被分割为6x6网格按从左到右、从上到下编号import re def natural_sort_key(s): 自然排序fragment_9.png应在fragment_10.png之前 return [int(t) if t.isdigit() else t.lower() for t in re.split((\d), s)]执行批量重命名import os def rename_fragments(dir_path): files sorted(os.listdir(dir_path), keynatural_sort_key) for idx, filename in enumerate(files): os.rename( os.path.join(dir_path, filename), os.path.join(dir_path, ffragment_{idx1}.png) )3. 图片预拼接技术实现3.1 计算画布尺寸def calculate_canvas_size(fragments_dir): sample Image.open(os.path.join(fragments_dir, fragment_1.png)) width, height sample.size return (width * 6, height * 6) # 6x6网格3.2 自动化拼接核心代码def assemble_qrcode(fragments_dir, output_path): canvas Image.new(RGB, calculate_canvas_size(fragments_dir)) for row in range(6): for col in range(6): pos row * 6 col 1 fragment Image.open(f{fragments_dir}/fragment_{pos}.png) canvas.paste(fragment, (col * fragment.width, row * fragment.height)) canvas.save(output_path) return canvas参数说明row * fragment.height纵向偏移量计算col * fragment.width横向偏移量计算4. Photoshop最终优化技巧即使自动化拼接后可能仍需微调图层对齐全选所有碎片图层使用自动对齐图层功能修正1-2像素偏差颜色校正图像 → 调整 → 色阶 输入色阶20 / 1.00 / 240扫描测试使用多款扫码工具测试微信、支付宝、专业QR扫描器若部分区域无法识别可尝试高斯模糊半径0.5px后再锐化常见问题解决方案问题现象可能原因解决方法扫码器无法识别碎片间存在缝隙在PS中扩展画布1像素二维码扭曲碎片尺寸不一致统一缩放为相同尺寸黑白反色颜色模式错误转换为灰度模式5. 高阶技巧与性能优化5.1 多线程加速处理当碎片数量超过100张时from concurrent.futures import ThreadPoolExecutor def parallel_assemble(fragments_dir): with ThreadPoolExecutor(max_workers4) as executor: futures [] for fragment in os.listdir(fragments_dir): futures.append(executor.submit(process_fragment, fragment)) results [f.result() for f in futures]5.2 模糊碎片匹配算法当文件名完全无规律时可使用SIFT特征匹配import cv2 def find_adjacent_fragments(img1, img2): sift cv2.SIFT_create() kp1, des1 sift.detectAndCompute(img1, None) kp2, des2 sift.detectAndCompute(img2, None) # 使用FLANN匹配器寻找对应点 matches flann.knnMatch(des1, des2, k2) return [m for m,n in matches if m.distance 0.7*n.distance]5.3 内存优化方案处理超大型图片时def memory_efficient_assembly(): 分块加载技术 for chunk in range(0, total_fragments, chunk_size): fragment_chunk load_fragment_chunk(chunk) yield process_chunk(fragment_chunk)在最近的一次HackTheBox挑战赛中我遇到一个变种问题碎片中包含10%的干扰项。通过给每个碎片添加置信度评分基于二维码定位图案匹配度成功过滤了无效碎片def qr_confidence_score(img): 检测二维码三个定位方块的匹配度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 计算定位图案区域的对比度得分 return contrast_score(binary[0:20, 0:20]) # 左上角区域