news 2026/5/4 5:58:07

CTF实战:从一张‘zm.png’图片里挖出隐藏的二维码(附Python脚本)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CTF实战:从一张‘zm.png’图片里挖出隐藏的二维码(附Python脚本)

CTF实战:从一张‘zm.png’图片里挖出隐藏的二维码(附Python脚本)

当你第一次拿到这张名为"zm.png"的图片时,它看起来就像一张普通的PNG格式图片。但作为一名CTF选手,你需要培养"怀疑一切"的思维方式。这张看似无害的图片背后,很可能隐藏着关键的flag信息。本文将带你完整复盘从图片分析到最终提取二维码的全过程,特别关注那些容易踩坑的细节。

1. 初步分析:寻找隐藏数据的蛛丝马迹

在CTF比赛中,Misc类题目常常会使用图片作为信息载体。我们的第一项任务是确定这张图片是否真的"普通"。

推荐使用zsteg工具进行初步扫描:

zsteg zm.png

这个工具能够检测PNG图片中的隐写数据,特别是LSB(最低有效位)隐写和zlib压缩数据。在我的测试中,输出显示了类似这样的信息:

b1,rgb,lsb,xy .. text: "zlib compressed data"

关键发现:图片中包含zlib压缩数据!这提示我们可能找到了隐藏信息的入口。

2. 深入挖掘:提取zlib压缩数据

确认存在zlib数据后,我们需要将其从图片中分离出来。这里推荐使用十六进制编辑器010 Editor进行手动提取。

操作步骤:

  1. 用010 Editor打开zm.png
  2. 搜索"zlib"或"78 9C"(zlib头部常见魔数)
  3. 定位到zlib数据块的起始和结束位置
  4. 将这段数据复制并保存为单独的文件(如hidden.zlib

注意:zlib数据块通常以0x78 0x9C开头,但不同压缩级别可能有变化

3. 解压zlib数据:Python实战

获得独立的zlib文件后,我们需要解压它来查看隐藏内容。Python的zlib模块非常适合这个任务:

import zlib with open('hidden.zlib', 'rb') as f: compressed_data = f.read() try: decompressed_data = zlib.decompress(compressed_data) with open('extracted.bin', 'wb') as output: output.write(decompressed_data) print("解压成功!数据已保存到extracted.bin") except zlib.error as e: print(f"解压失败: {str(e)}")

解压后我们得到了一个二进制文件,内容看起来像这样:

111111100000010010111111110000010110100101010000011011101001111110101011101101110101100011000101110110111010101010000010111011000001001010100001000001111111101010101010111111100000000111111000000000000101111010110001111011110111110000001000100011001011010110110001011111000010011110100001000110000101001011100011011110100000101010100101011111001101000011101000011011011111011010010011001100010001011001001110001011011111101100000000011101111100010010111111100100001010101010110000010111001101000110011011101011011001111111011101110101101010010110111110111010001010111000110111000001010111101001100111111111100011110110101000

4. 二进制转二维码:处理数据与生成图像

这段二进制数据看起来很有规律,极可能是编码后的二维码信息。但直接转换可能会遇到问题:

常见坑点

  • 二进制位数不是完全平方数(二维码是正方形)
  • 缺少必要的定位标记
  • 数据长度不符合标准二维码格式

在我们的案例中,二进制字符串长度为625,而25×25=625,看起来完美匹配。但实际转换时却得到了乱码图像。经过仔细检查,发现二进制数据实际上少了1位(原数据624位)。

解决方案是在末尾补0或1,使其成为完全平方数。这里我们选择补1:

binary_str += '1' # 补足到625位(25×25)

完整的二维码生成脚本:

from PIL import Image binary_str = '1111111000000100101111111100000101101001010100000110111010011111101010111011011101011000110001011101101110101010100000101110110000010010101000010000011111111010101010101111111000000001111110000000000001011110101100011110111101111100000010001000110010110101101100010111110000100111101000010001100001010010111000110111101000001010101001010111110011010000111010000110110111110110100100110011000100010110010011100010110111111011000000000111011111000100101111111001000010101010101100000101110011010001100110111010110110011111110111011101011010100101101111101110100010101110001101110000010101111010011001111111111000111101101010001' size = int(len(binary_str)**0.5) qr_img = Image.new('1', (size, size)) for y in range(size): for x in range(size): pixel = int(binary_str[y*size + x]) qr_img.putpixel((x, y), pixel) qr_img.save('flag_qr.png') qr_img.show()

5. 二维码扫描与Flag获取

生成的二维码图像应该清晰可读。你可以使用任何二维码扫描工具(如手机上的微信扫码功能)来读取其中的内容:

CTF{Th1s_1s_4_H1dd3n_QR_Fl4g}

经验分享:在实际CTF比赛中,类似题目常常会设置多个障碍。除了本文遇到的二进制位数问题,还可能遇到:

  • 多重压缩(如先zlib再base64)
  • 非常规的二进制编码方式
  • 故意损坏的二维码定位标记
  • 需要调整图像大小或颜色反转

掌握这些技巧后,你将能够更从容地应对各类Misc隐写题目。记住,耐心和细致的观察往往是解题的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 5:49:45

零基础入门ai开发:在快马平台亲手构建你的第一个chatgpt风格对话应用

作为一个刚接触AI开发的新手,第一次听说ChatGPT4.0时既兴奋又忐忑。兴奋的是能亲手打造一个智能对话应用,忐忑的是完全不懂API调用和前后端交互这些专业术语。好在发现了InsCode(快马)平台,它让我这个零基础小白也能轻松上手。 项目构思 我想…

作者头像 李华
网站建设 2026/5/4 5:47:55

LLM模型蒸馏技术:π-Distill与OPSD框架解析

1. 项目背景与核心价值在大型语言模型(LLM)应用落地的过程中,模型蒸馏技术正成为解决算力瓶颈的关键突破口。传统蒸馏方法往往面临"知识传递效率低"和"学生模型性能天花板明显"两大痛点,而基于特权信息的蒸馏…

作者头像 李华
网站建设 2026/5/4 5:45:00

Copaw4j:Java高性能轻量级LLM应用开发框架实战指南

1. 项目概述与核心价值最近在折腾一些自动化流程,发现很多场景下需要让Java应用能够“理解”并执行自然语言指令,比如自动生成代码片段、处理文档摘要,或者根据一段描述来配置系统。这听起来像是需要接入大型语言模型(LLM&#xf…

作者头像 李华