1. 压缩包隐写到底是什么从文件结构说起1.1 一个压缩包能藏多少东西很多人第一次接触“压缩包隐写”这个概念是在CTF比赛或者电子取证场景里。题目给一个看似普通的ZIP或RAR文件解压出来只有一张图片或一个文本但题目提示“flag不止于此”。这时候你要做的就是意识到这个压缩包的二进制结构里可能还嵌着别的东西。压缩包隐写的本质是利用压缩包文件格式的冗余空间或结构特性把额外数据藏进去。它和图片隐写LSB、调色板、EXIF注释的思路一脉相承但操作对象变成了ZIP、RAR、7z这类归档格式。常见的藏法有几种在ZIP的注释字段里塞Base64字符串、在文件末尾追加数据append after EOF、利用ZIP的“伪加密”位标记、把多个压缩包拼接在一起、或者在压缩包内部的文件名里做文章。我最早接触这个方向是处理一个取证样本一个从邮件附件提取出来的ZIP大小只有2MB但解压后里面的文档加起来不到200KB。多出来的1.8MB去哪了用十六进制编辑器打开一看文件末尾在PK结束标记之后还有一大段数据提取出来是一个加密的7z包。这就是最典型的“尾部追加”隐写。1.2 为什么压缩包适合做隐写载体压缩包有几个天然优势。第一它本身就是二进制文件里面多一段少一段普通用户根本看不出来双击照样能正常解压。第二ZIP格式允许在文件末尾追加任意数据而不影响解压这是ZIP规范里明确允许的——解压器读到中央目录结束标记EOCD就停了后面的内容它不管。第三压缩包经常在网络上传输一个ZIP文件里多藏点东西不会像图片那样引起怀疑。从取证角度来说压缩包隐写比图片隐写更难排查因为图片你还能看像素、看通道压缩包你得理解它的二进制结构才能找到蛛丝马迹。这也是为什么“取证隐写”和“misc隐写”这两个词经常和压缩包一起出现。1.3 适合谁来学这部分内容如果你在做CTF的Misc方向压缩包隐写是必过的一关。如果你在做电子数据取证理解压缩包的隐藏手法能帮你发现被刻意藏匿的证据。如果你只是普通用户学这个能让你在遇到“压缩包解压密码怎么解除”这类问题时多一条排查思路——有时候密码就藏在文件本身的某个字段里。下面我会从ZIP格式的结构讲起然后逐个拆解常见的隐写手法配上具体的操作命令和排查步骤。所有操作都是我在实际项目和比赛中反复用过的你可以直接抄作业。2. ZIP文件结构拆解隐写点到底在哪2.1 ZIP的三大核心区块要理解压缩包隐写必须先搞清楚ZIP文件的物理结构。一个标准ZIP文件由三部分组成本地文件头Local File Header每个被压缩的文件前面都有一段记录了文件名、压缩方法、CRC32校验值、压缩前后大小等信息。签名是PK\x03\x04。文件数据File Data紧跟在本地文件头后面就是压缩后的实际内容。中央目录Central Directory所有文件的本地文件头信息汇总方便快速索引。签名是PK\x01\x02。中央目录结束记录EOCD整个ZIP的结尾标记签名是PK\x05\x06里面记录了中央目录的偏移和大小。隐写就藏在这些结构的“缝隙”里。比如本地文件头和文件数据之间可以插入额外字段Extra FieldEOCD后面可以追加任意数据中央目录里的注释字段可以塞东西。2.2 用十六进制工具看一个真实ZIP我拿一个自己构造的测试ZIP来演示。用xxd命令查看前256字节xxd test.zip | head -20输出大概是这样00000000: 504b 0304 1400 0000 0800 21a5 8b56 8c1e PK........!..V.. 00000010: 5d8a 0c00 0000 0a00 0000 0800 0000 7465 ].............te 00000020: 7374 2e74 7874 0102 1400 1400 0000 0800 st.txt.......... ...看到504b 0304就是本地文件头7465 7374 2e74 7874是文件名“test.txt”。如果你在文件名后面看到一段不正常的十六进制数据那可能就是隐写内容。2.3 三个最容易被利用的字段根据我的经验下面三个位置是压缩包隐写的重灾区字段位置签名/标识常见隐写方式排查难度EOCD之后PK\x05\x06之后追加任意文件、拼接第二个ZIP低本地文件头Extra FieldPK\x03\x04之后塞入自定义数据、Base64中中央目录注释EOCD中的注释长度字段藏文本、藏密码提示低文件名本身本地文件头中的文件名超长文件名、特殊字符编码中注意ZIP规范允许EOCD后面有数据但很多解压软件会忽略它。如果你用unzip解压正常但文件大小对不上优先怀疑尾部追加。3. 常见压缩包隐写手法与实操排查3.1 尾部追加最简单也最常用这是最基础的压缩包隐写。操作方式很简单把一个ZIP文件和一个秘密文件比如另一张图片、一个文本用二进制方式拼接。cat original.zip secret.txt stego.zip这样生成的stego.zip用unzip解压只会得到original.zip里的内容secret.txt的内容被追加在EOCD之后解压器不会去读。但如果你用binwalk或者foremost这类工具扫描就能发现尾部有额外数据。排查方法# 查看文件末尾 tail -c 200 stego.zip | xxd # 用binwalk扫描 binwalk stego.zip # 用foremost提取 foremost -i stego.zip -o output/我实测下来binwalk对这类隐写的识别率很高它会直接告诉你“Zip archive data, at offset 0”和“ASCII text, at offset 12345”这样的信息。3.2 伪加密改一个字节就能骗过解压器ZIP的“伪加密”不是真的加密而是通过修改本地文件头和中央目录里的“通用位标记”General Purpose Bit Flag来让解压器误以为文件被加密了。具体来说这个字段的第0位如果被置1解压器就会要求输入密码。但有意思的是本地文件头和中央目录里各有一个这样的标记。如果只改了其中一个有些解压器会认为加密有些则认为没加密。这就给了我们绕过“压缩包解压密码怎么解除”这个问题的机会。排查和修复步骤用十六进制编辑器打开ZIP文件。找到本地文件头的PK\x03\x04偏移6-7字节是通用位标记。找到中央目录的PK\x01\x02偏移8-9字节是通用位标记。把这两个位置的值改成00 00保存。再次尝试解压。我遇到过很多次这种情况一个ZIP提示要密码但用上面的方法改完两个字节后直接就能解压。这不是破解密码而是文件本身就没有真正加密。3.3 文件名隐写藏在名字里的秘密ZIP允许文件名使用UTF-8编码而且文件名长度可以很长。有人会把Base64编码后的字符串直接作为文件名或者把多个文件名拼起来形成一段有意义的信息。排查方法# 列出ZIP内所有文件名包括隐藏的 unzip -l stego.zip # 用zipinfo查看更详细的信息 zipinfo -v stego.zip如果看到文件名是一串看似乱码的字符尝试Base64解码或者十六进制转ASCII。我见过一个CTF题文件名是ZmxhZ3t0aGlzX2lzX2FfZmxhZ30Base64解码后就是flag。3.4 多压缩包拼接一个文件多个身份把两个ZIP文件直接拼接第一个ZIP的EOCD之后跟着第二个ZIP的本地文件头。有些解压器会只读第一个有些会读最后一个还有些会报错。这种手法在CTF里常用来藏“第二层”内容。排查时用binwalk扫描如果看到多个“Zip archive data”偏移就说明有拼接。提取方法# 假设binwalk显示第二个ZIP从偏移12345开始 dd ifstego.zip ofsecond.zip bs1 skip123453.5 压缩包注释字段最容易被忽略的地方ZIP的EOCD结构里有一个“注释长度”字段和“注释”字段。注释可以长达65535字节足够藏一段文本或一个小文件。很多解压软件不会显示注释所以这里成了隐写的温床。查看注释的方法unzip -z stego.zip或者用Python的zipfile模块import zipfile z zipfile.ZipFile(stego.zip) print(z.comment)我处理过一个取证案例嫌疑人把密码提示放在了ZIP注释里内容是“生日后四位”。这种信息如果不用专门命令去看根本发现不了。4. 实战从零构造一个压缩包隐写样本4.1 环境准备与工具选型我平时做压缩包隐写分析常用这几样工具binwalk自动扫描文件中的嵌入数据支持几十种格式。foremost基于文件头尾特征提取文件适合批量恢复。xxd / hexdump十六进制查看定位具体偏移。Python zipfile模块编程方式读写ZIP结构灵活处理注释和额外字段。7z / unzip / zipinfo常规解压和信息查看。安装binwalk和foremost在Debian系系统上很简单sudo apt install binwalk foremostPython环境建议用3.8以上zipfile模块是标准库不需要额外安装。4.2 构造一个带注释隐写的ZIP我用Python写一段代码创建一个ZIP并在注释里藏一段Base64import zipfile import base64 secret flag{zip_comment_stego} encoded base64.b64encode(secret.encode()).decode() with zipfile.ZipFile(comment_stego.zip, w) as z: z.writestr(readme.txt, This is a normal file.) z.comment encoded.encode() print(ZIP created with comment:, encoded)运行后生成的comment_stego.zip用普通解压看不到任何异常。但用unzip -z就能看到注释内容Base64解码后就是flag。4.3 构造尾部追加隐写这个更简单直接用命令行echo This is a normal file. normal.txt zip normal.zip normal.txt echo flag{append_after_eocd} secret.txt cat normal.zip secret.txt append_stego.zip用binwalk append_stego.zip扫描输出会显示两个条目一个是ZIP一个是ASCII文本。用dd提取尾部# 先找到ASCII文本的偏移假设是200 dd ifappend_stego.zip ofextracted.txt bs1 skip200 cat extracted.txt4.4 构造伪加密ZIP伪加密需要手动改字节。先用zip创建一个普通ZIP然后用Python修改通用位标记with open(normal.zip, rb) as f: data bytearray(f.read()) # 找到第一个本地文件头 lfh_offset data.find(bPK\x03\x04) # 通用位标记在偏移6-7 data[lfh_offset6:lfh_offset8] b\x01\x00 # 找到中央目录 cd_offset data.find(bPK\x01\x02) # 通用位标记在偏移8-9 data[cd_offset8:cd_offset10] b\x01\x00 with open(fake_encrypt.zip, wb) as f: f.write(data)这样生成的ZIP用unzip解压会提示输入密码。但如果你把两个标记都改回00 00就能正常解压。这就是“压缩包密码怎么解除”的一种典型场景——密码根本不存在。5. 常见问题与排查技巧实录5.1 解压提示密码但实际没有密码这是最常见的问题。排查顺序先用zipinfo -v查看通用位标记。如果本地文件头和中央目录的标记不一致尝试统一改成00 00。如果标记都是01 00说明可能真的加密了需要走密码恢复流程。检查是否有注释字段包含密码提示。我踩过的坑有一次改完本地文件头忘了改中央目录结果还是提示密码。后来发现两个位置必须同时改而且有些解压器只认中央目录的标记。5.2 binwalk扫描不到尾部数据binwalk不是万能的。如果尾部追加的数据没有明显的文件头特征比如是一段纯文本binwalk可能不会标记。这时候需要手动看文件末尾tail -c 500 stego.zip | strings如果看到可读文本基本就是隐写内容。另外如果追加的数据被加密或压缩过binwalk也识别不了需要结合文件大小和实际内容判断。5.3 文件名乱码导致无法解压有些ZIP在Windows上创建文件名用GBK编码在Linux上解压会乱码。这时候可以用unzip -O GBK指定编码unzip -O GBK stego.zip如果文件名本身就是隐写内容乱码反而是线索。用unzip -l列出原始字节然后手动解码。5.4 多个压缩包拼接后解压报错拼接的ZIP不同解压器行为不一样。unzip通常只读第一个7z可能会报错。我的做法是先用binwalk找到所有ZIP的偏移然后逐个提取binwalk stego.zip # 假设输出显示偏移0和偏移5000各有一个ZIP dd ifstego.zip ofpart1.zip bs1 count5000 dd ifstego.zip ofpart2.zip bs1 skip5000然后分别解压part1和part2。5.5 常见问题速查表现象可能原因排查命令解决方式解压提示密码伪加密zipinfo -v改通用位标记为00 00文件大小异常尾部追加binwalk、tail -cdd提取尾部数据文件名乱码编码不一致unzip -l指定编码或手动解码解压报错多包拼接binwalk按偏移分割后分别解压看不到注释注释隐写unzip -z读取注释并解码解压后内容缺失中央目录被改zipinfo -v对比本地头和中央目录提示做压缩包隐写分析时永远先用binwalk扫一遍再用xxd看头部和尾部最后用zipinfo -v看结构细节。这三步能覆盖90%以上的情况。6. 进阶压缩包隐写在取证与CTF中的实际应用6.1 取证场景从邮件附件里挖出隐藏证据电子取证中压缩包隐写常出现在邮件附件、聊天记录文件、下载目录里。嫌疑人会把敏感文件藏在正常ZIP的尾部或者用伪加密阻止取证人员快速查看。我的标准流程是计算文件哈希记录原始状态。用binwalk和foremost做初步扫描。用zipinfo -v检查结构异常。用strings提取所有可读字符串搜索关键词。如果发现尾部数据用dd提取并单独分析。检查注释字段和文件名编码。有一次我在一个看似正常的ZIP尾部提取出一个加密的7z包里面是完整的账本记录。如果只做常规解压这部分证据就漏掉了。6.2 CTF场景Misc方向的典型套路CTF里的压缩包隐写题通常不会只考一种手法而是组合拳。比如先伪加密让你以为要密码改完标记后发现注释里有Base64解码后得到一个提示“看尾部”然后binwalk发现尾部有PNG提取出来图片里还有LSB隐写。这种题目的解题节奏是第一步file命令看文件类型确认是ZIP。第二步binwalk扫一遍看有没有嵌入文件。第三步zipinfo -v看结构检查通用位标记和注释。第四步unzip -l看文件名有没有可疑的长文件名或乱码。第五步xxd看头部和尾部找异常字节。第六步根据线索逐步深入不要跳步。我打CTF的经验是压缩包隐写题往往不难但容易漏步骤。把上面六步走完基本不会丢分。6.3 工具组合的取舍逻辑为什么我推荐binwalk而不是只用foremost因为binwalk是扫描式速度快能给出偏移信息foremost是提取式适合批量恢复但需要指定输出目录。两者配合用先binwalk定位再foremost或dd提取。为什么用Python的zipfile而不是只用命令行因为命令行工具对注释和额外字段的支持有限Python可以直接读写这些字段适合构造和解析复杂样本。为什么强调zipinfo -v因为它能显示每个文件的本地文件头和中央目录的详细字段包括通用位标记、压缩方法、CRC值。这些信息在排查伪加密和结构篡改时至关重要。7. 我个人在实际操作中的几点体会压缩包隐写这个方向入门容易精通难。工具用熟了大部分题目和取证场景都能应付但有几个细节值得反复提醒自己。第一不要假设文件是“正常”的。我见过太多人拿到ZIP直接双击解压看到内容就结束了。实际上文件大小、修改时间、注释字段、文件名编码每一个都可能是线索。养成先看结构再解压的习惯。第二伪加密的修改要成对操作。本地文件头和中央目录的通用位标记必须一致否则不同解压器行为不同容易把自己绕进去。改之前先备份原始文件改完用多个解压器验证。第三尾部追加的数据不一定在最后。有些样本会在EOCD之后先放一段垃圾数据再放真正的隐写内容。用binwalk看所有偏移不要只看最后一个。第四注释字段的编码可能是Base64、十六进制、URL编码甚至是反转字符串。遇到看不懂的注释先试几种常见编码再考虑自定义编码。第五做取证时一定要记录每一步操作和文件哈希。压缩包隐写分析涉及多次修改和提取没有记录的话证据链容易断。最后分享一个小技巧如果你怀疑一个ZIP有尾部追加但binwalk没报可以用python快速比较文件实际大小和ZIP结构声明的结束位置。EOCD里的中央目录偏移加上中央目录大小就是ZIP逻辑上的结束位置超出部分就是追加数据。这个方法我用了很多次比任何工具都直接。
