1. 题目背景与文件分析
这道来自ISCTF2025的"小蓝鲨的二维码"题目属于Misc(杂项)类别,主要考察参赛者对文件分析、编码转换和图像处理技术的综合运用能力。题目提供了两个关键文件:enc.png和flag.png,我们需要通过一系列操作从这两个图像中提取出隐藏的flag。
提示:在CTF比赛中,Misc类题目往往需要选手具备"侦探式"的思维,善于从各种看似无关的线索中发现关联。
1.1 初始文件检查
首先对两个文件进行基础分析:
- enc.png:经过特殊处理的二维码图像,表面看起来是损坏或加密的状态
- flag.png:看似正常的二维码图像,但单独扫描无法获得有效信息
使用file命令查看文件基本信息:
bash复制$ file enc.png flag.png
enc.png: PNG image data, 256 x 256, 8-bit/color RGB, non-interlaced
flag.png: PNG image data, 256 x 256, 8-bit/color RGB, non-interlaced
两幅图像尺寸相同(256×256),都是标准的RGB彩色PNG格式,这提示我们可能需要将两幅图像进行某种形式的组合或比对。
2. 加密图像初步分析
2.1 文件尾部隐藏信息
使用十六进制编辑器(如010 Editor或随波逐流工具)查看enc.png文件末尾,发现异常字符串:

在文件末尾发现可疑字符串:23xXvfzai
经验之谈:PNG文件的标准结尾应该是IEND块,如果在IEND之后还有数据,很可能是出题人故意隐藏的线索。
2.2 Base58解码
将发现的字符串23xXvfzai进行Base58解码(可以使用在线工具如CyberChef):

解码结果为:zigzag
这明显是一个提示,说明enc.png图像可能使用了ZigZag变换进行加密。
3. ZigZag变换还原
3.1 ZigZag变换原理
ZigZag变换是一种常用于图像加密的扫描方式,其核心思想是按照"之"字形路径遍历图像像素。在加密时,像素被按照这种特殊顺序重新排列;解密时则需要逆向操作。
典型的ZigZag扫描顺序如下图所示:
code复制(0,0) → (0,1) → (1,0) → (2,0) → (1,1) → (0,2) → (0,3) → ...
3.2 Python实现ZigZag还原
我们需要编写Python脚本对enc.png进行ZigZag逆变换还原。以下是完整的实现代码:
python复制import numpy as np
import cv2
import os
def zigzag_indices(rows, cols):
"""生成ZigZag扫描顺序的索引列表"""
indices = []
for diag in range(rows + cols - 1):
if diag % 2 == 0: # 偶数对角线,方向为右上
r = min(diag, rows - 1)
c = diag - r
while r >= 0 and c < cols:
indices.append((r, c))
r -= 1
c += 1
else: # 奇数对角线,方向为左下
c = min(diag, cols - 1)
r = diag - c
while c >= 0 and r < rows:
indices.append((r, c))
r += 1
c -= 1
return indices
def zigzag_unscramble_2d(matrix_2d):
"""对二维矩阵进行ZigZag逆变换"""
rows, cols = matrix_2d.shape
indices = zigzag_indices(rows, cols)
flattened = matrix_2d.flatten()
unscrambled = np.zeros_like(matrix_2d)
for idx, (r, c) in enumerate(indices):
unscrambled[r, c] = flattened[idx]
return unscrambled
def zigzag_unscramble_image(img):
"""对图像进行ZigZag逆变换,支持灰度图和RGB图"""
if img.ndim == 2: # 灰度图
return zigzag_unscramble_2d(img)
elif img.ndim == 3 and img.shape[2] == 3: # RGB图
unscrambled = np.zeros_like(img)
for ch in range(3): # 分别处理三个通道
unscrambled[:, :, ch] = zigzag_unscramble_2d(img[:, :, ch])
return unscrambled
else:
raise ValueError("仅支持灰度图或标准RGB彩色图")
if __name__ == "__main__":
encrypted_image_path = "enc.png"
# 检查文件是否存在
if not os.path.exists(encrypted_image_path):
print(f"错误:找不到加密图像文件'{encrypted_image_path}'")
print("请确保加密图像文件存在,或修改文件路径。")
exit(1)
# 读取图像
img_bgr = cv2.imread(encrypted_image_path)
if img_bgr is None:
print("无法读取加密图像,请检查路径或格式是否支持")
exit(1)
# 转换颜色空间(BGR→RGB)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
print(f"成功读取加密图像,尺寸: {img_rgb.shape}")
# 执行ZigZag逆变换
decrypted_img = zigzag_unscramble_image(img_rgb)
# 转换回BGR并保存
decrypted_bgr = cv2.cvtColor(decrypted_img, cv2.COLOR_RGB2BGR)
output_path = "decrypted_zigzag.png"
cv2.imwrite(output_path, decrypted_bgr)
print(f"图像已解密并保存为: {output_path}")
3.3 脚本使用说明
-
确保已安装必要的Python库:
bash复制
pip install numpy opencv-python -
将脚本保存为
zigzag_decrypt.py,与enc.png放在同一目录下 -
运行脚本:
bash复制
python zigzag_decrypt.py -
脚本会生成解密后的图像
decrypted_zigzag.png
避坑指南:如果遇到OpenCV读取图像失败的问题,请检查:
- 文件路径是否正确
- 文件是否损坏
- OpenCV是否安装正确
4. 图像组合与异或操作
4.1 双图异或原理
现在我们有:
decrypted_zigzag.png:经过ZigZag还原的图像flag.png:原始提供的另一幅图像
CTF中常见的图像处理技术包括:
- 简单叠加(Alpha混合)
- 按位运算(AND/OR/XOR)
- 通道分离与重组
这里我们需要使用**按位异或(XOR)**操作,因为:
- XOR是可逆操作
- 常用于信息隐藏和加密
- 两个看似无关的图像经过XOR可能显现隐藏信息
4.2 使用随波逐流工具进行异或
使用"随波逐流"工具进行双图异或操作:
- 打开工具,选择"图片-双图组合"功能
- 加载
decrypted_zigzag.png和flag.png - 选择"按位异或"操作模式
- 生成
XOR.png

得到的XOR图像如下:

4.3 异或结果分析
观察XOR.png,可以看到:
- 图像中出现了类似二维码的结构
- 但缺少标准的二维码定位标记(三个角落的方形标记)
- 这意味着我们需要手动添加定位标记才能扫描
5. 二维码重构与扫描
5.1 二维码结构基础
标准QR码包含以下关键部分:
- 定位图案:三个角落的大正方形,用于确定二维码方向和位置
- 对齐图案:较小的正方形,辅助校正变形
- 时序图案:黑白相间的线条,帮助确定模块大小
- 格式信息:存储纠错级别和掩码模式
- 数据区域:实际存储信息的模块
5.2 添加定位标记
使用"随波逐流"工具的"图片-二维码添加定位符"功能:
- 选择
XOR.png作为基础图像 - 工具会自动识别并添加标准QR码定位标记
- 生成可扫描的二维码图像

5.3 扫描获取flag
使用手机微信或其他二维码扫描工具扫描处理后的二维码,即可获得比赛flag。
专业提示:如果扫描不成功,可以尝试:
- 调整图像对比度
- 确保定位标记大小比例正确
- 尝试不同的二维码扫描工具
6. 技术总结与扩展
6.1 解题流程回顾
- 文件分析:检查文件尾部隐藏信息
- 编码转换:Base58解码获得提示
- 图像处理:ZigZag逆变换还原图像
- 密码学操作:双图按位异或
- 信息重构:添加二维码定位标记
- 信息提取:扫描二维码获取flag
6.2 相关技术扩展
-
Base系列编码:CTF中常见的编码方式包括Base16/32/58/64/85等,各有特点:
- Base58:去除了容易混淆的字符(0,O,I,l等),常用于比特币地址
- Base64:最常见的编码,使用A-Z,a-z,0-9,+,/
-
图像加密技术:
- 像素重排(如ZigZag、Arnold变换)
- 像素值变换(异或、加减、置换)
- 频域变换(DCT、DFT、小波变换)
-
二维码隐写术:
- 利用纠错码特性隐藏信息
- 修改模块颜色或排列
- 多层二维码叠加
6.3 实战建议
-
工具准备:
- 十六进制编辑器:010 Editor、WinHex、随波逐流
- 编码工具:CyberChef、Python base64模块
- 图像处理:OpenCV、Pillow、GIMP
-
解题思路:
- 先检查文件头和尾
- 尝试常见编码和加密方式
- 注意题目给出的任何提示(如文件名、大小等)
- 当有两幅图像时,考虑组合操作(异或、叠加等)
-
技能提升:
- 学习基本的图像处理算法
- 熟悉常见编码的特点和识别方法
- 积累CTF常见套路和解题模式
在实际比赛中,这类题目通常需要综合运用多种技术,关键在于细心观察和系统性思考。通过这道题目,我们不仅学习了几种实用的技术,更重要的是培养了分析问题和分步解决的思维能力。