
做CTF Misc的朋友对.zip压缩包肯定是又爱又恨。每次拿到一个压缩包双击一看出“输入密码”思路就开始分裂是爆破是明文攻击还是干脆这是个伪加密zip伪加密这道坎我当年卡了很久尤其是用010Editor把标志位改了之后还是提示密码差点以为是工具不好使。后来把ZIP文件结构搞清楚才发现问题往往出在只改了一半。这篇文章以一个CTF平台上常见的MISC题为例把zip伪加密从原理到实战完整拆一遍。全程用010Editor做十六进制手工修复每一步都讲清楚为什么要这么改以及在哪个位置改。如果你刚接触CTF或者被“压缩包要密码但找不到密码”卡住这篇文章很适合照做。学会之后以后遇到八成以上的伪加密题三十秒内就能绕过。1. 先摸清底细zip伪加密到底“伪”在哪儿1.1 ZIP文件的三段式结构要改伪加密就不能只知道“把某字节改成00”。你至少得知道这个字节长什么样、在哪一段里。ZIP文件表面上是一个整体内部其实分了三块本地文件头Local File Header实际的压缩数据Data中央目录Central Directory和中央目录结尾End of Central Directory每一条压缩文件记录都会有一个本地文件头描述这个文件叫什么、用了什么压缩方式、数据放在哪里、原始大小多少。而中央目录则相当于整份压缩包的索引把每条文件记录的元信息再汇总一遍方便解压软件快速定位。你在Windows资源管理器里双击zip系统第一件事往往就是读中央目录看里面有哪些文件、是否标记了加密。本地文件头和中央目录里各有一个“通用标志位”General Purpose Bit Flag。这个标志位占两个字节其中最低的bit0专门用来标记“这条记录是否加密”。bit0为1解压软件就认为文件加了密码bit0为0就认为是普通文件。1.2 伪加密就是把“门锁”贴了假标签“伪加密”的精髓在于数据本身从头到尾都没被加密压缩算法该是什么样还是什么样但通用标志位里的加密位被改成了1。就好比一个抽屉根本没上锁但外面贴了一张“此处有锁”的纸条。解压软件看到纸条就停下来向你要密码。很多CTF平台喜欢出这类题就是因为代码实现简单又能考查选手对二进制格式的敏感度。题目制作者用普通方式压一个文本进去然后把本地文件头、中央目录里的标志位改成带bit0的值让你误以为压缩包加了密。常见的伪加密标志位值有两个01 00表示bit0置1别的标志都是0。09 00表示bit0置1bit3也置1。bit3是“数据描述符”标志有时代表示CRC和大小信息不在本地文件头里而是放在数据段后面。所以你在010Editor里看到的标志位不一定是简单的“加密1”它可能同时携带了其他含义。这也是为什么网上有些教程让你把09改成00但我在后面会建议你更稳妥地只清除bit0比如把09改成08。这个差异看起来小遇到某些严格解压工具时就是能不能解开的区别。1.3 判断一个zip是不是伪加密拿到压缩包先别急着爆破。最快的方式就是打开010Editor看本地文件头和中央目录的标志位。如果两个位置都出现了01 00或09 00而文件里没有任何加密数据特有的字段那大概率是伪加密。更专业的做法是看解压时的表现正常加密的zip输入错密码会直接报错伪加密则无论你怎么输都报错因为核心数据根本没加密解压软件只是在“检查密码”这一步就被拦住了。还有一种判断技巧用压缩软件打开如果能正常看到文件列表、文件大小、甚至能预览但解压时要求密码这种也高度可疑。因为真加密状态下很多软件连列表信息都要解密才能读全。2. 工具选型与准备工作为什么用010Editor2.1 十六进制编辑器对比010Editor的优势处理二进制文件可选工具很多HxD、Hex Fiend、WinHex、010Editor。我只用010Editor不是因为别的不能用而是它有几个针对这类操作特别顺手的功能。支持一次查找所有匹配项能把文件里所有50 4B 03 04列出来不用一个个翻。有ZIP相关模板能帮你看懂每段的含义。搜索和跳转偏移都很直观适合做字节级修改。支持脚本熟悉之后能把重复操作变成批处理。不过要说明一点010Editor是收费软件但试用期足够做CTF题。HxD免费版也完全够用。工具不重要重要的是你清楚自己在改什么。2.2 实操前的两个好习惯打开zip之前我强烈建议先复制一份出来命名成flag_fixed.zip直接在副本上改。伪加密修改通常只有一两个字节但一旦改错原文件就废了。CTF赛题下载一次没问题可如果是比赛环境下只能下载一次你就知道备份有多重要。第二步在010Editor里把偏移显示打开默认是十六进制偏移。这样你搜索到ZIP头后能快速数到对应偏移位置。很多人改完发现没生效就是数错了偏移。另外拿到文件先做基本检查file flag.zip如果输出是Zip archive data说明至少是个标准zip不是藏在图片里的隐藏数据。CTF Misc题经常把一个文件塞进另一个文件尾部所以也可以顺手用binwalk扫一下binwalk flag.zip确认没有其他附加数据再进入伪加密处理阶段。3. 核心实操010Editor手工修改伪加密标志位3.1 第一步定位本地文件头在010Editor中打开zip副本按CtrlF搜索方式选“Hex Values”输入50 4B 03 0450 4B 03 04就是ZIP本地文件头的签名。找到之后看签名后面的字节。本地方文件头的结构里签名占4字节第五、六字节是“解压所需版本”第七、八字节才是通用标志位。举个例子你搜到的文件头开头长这样50 4B 03 04 14 00 09 00 00 00逐段拆开50 4B 03 04本地文件头签名14 00解压所需版本09 00通用标志位00 00压缩方式也就是说标志位从文件头偏移6的位置开始占两个字节。当前值09 00里bit0是1表示加密。我们接下来把bit0清零。这里就是关键分歧点。如果按最粗暴的做法把09 00整个改成00 00大多数题目也能解压因为原始zip本来就没用数据描述符。但为了不破坏bit3更严谨的做法是把值0x0009与0xFFFE按位与得到0x0008也就是改成08 00。如果原始值是01 00改成00 00即可。在010Editor里操作很简单点击09这个字节输入08保存。3.2 第二步定位中央目录文件头光改本地文件头可能还是不够。接下来搜索中央目录签名50 4B 01 02中央目录文件头比本地文件头多了几个字段。签名占4字节第五、六字节是“版本由谁创建”第七、八字节是“解压所需版本”第九、十字节才是通用标志位。所以中央目录里的标志位在偏移8的位置。如果看到类似50 4B 01 02 1E 03 14 00 09 00 00 00那么14 00后面紧跟着的09 00就是中央目录的标志位。同样把09改成08或者按原始值对应的正确结果修改。为什么中央目录必须改因为很多解压软件比如Windows自带的资源管理器读取的是中央目录而不是本地文件头。你只改本地文件头的话资源管理器一看中央目录里还写着“加密”照样找你要密码。反过来也一样某些命令行工具可能优先看本地文件头。所以稳妥的做法就是两个位置都查到、都改到。3.3 第三步保存并验证修改完成后CtrlS保存。然后不要用010Editor去看解压效果而是退出去用命令行或资源管理器解压unzip flag_fixed.zip如果之前是伪加密这时会直接解压出里面的文件不会问密码。如果还提示密码也别急往下看常见问题。我这里多说一句有的压缩包里有多个文件条目意味着会有多个本地文件头和多个中央目录记录。你要分别查找所有50 4B 03 04和所有50 4B 01 02把每条记录里偏移6和8的标志位都检查一遍。常见错误就是只改了第一个文件头后面那个没管结果解压到一半又被卡住。4. 进阶一条命令恢复和脚本自动化4.1 用ZipCenOp.jar快速验证如果你只是想快速验证某个zip是不是伪加密还有一个老牌工具ZipCenOp.jar。当年在CTF圈子里流传很广命令是java -jar ZipCenOp.jar r flag.zip这里的r表示restore作用就是扫描zip里的本地文件头和中央目录把加密标志位清掉。如果运行后提示成功再用解压软件试一下通常就能解开。但这个工具有个缺点它会把所有条目的加密位都清掉不分真伪。如果遇到真加密的zip执行之后压缩包会变坏数据还是加密的但标志位已经不认了。所以更适合做“验证伪加密”的操作而不是最终修复手段。另外它对中文文件名、非常规压缩选项支持一般输出信息也比较含糊。我自己的习惯是能手动改就手动改手动改完如果失败再用这个工具试试反向排除问题。4.2 用Python脚本批量处理如果题目给你几十个zip或者你想把整套操作沉淀下来可以写个小脚本。核心逻辑还是那句话扫描本地文件头和中央目录把通用标志位的最低bit清零其他位保留。import struct import shutil def clear_fake_encryption(src, dst): shutil.copy2(src, dst) with open(dst, rb) as f: data bytearray(f.read()) # 本地文件头签名 PK\x03\x04标志位在偏移 6 idx 0 while True: idx data.find(bPK\x03\x04, idx) if idx -1: break flags struct.unpack_from(H, data, idx 6)[0] if flags 0x0001: struct.pack_into(H, data, idx 6, flags 0xFFFE) print(flocal header at 0x{idx:04X}: flags 0x{flags:04X} - 0x{flags 0xFFFE:04X}) idx 4 # 中央目录签名 PK\x01\x02标志位在偏移 8 idx 0 while True: idx data.find(bPK\x01\x02, idx) if idx -1: break flags struct.unpack_from(H, data, idx 8)[0] if flags 0x0001: struct.pack_into(H, data, idx 8, flags 0xFFFE) print(fcentral dir at 0x{idx:04X}: flags 0x{flags:04X} - 0x{flags 0xFFFE:04X}) idx 4 with open(dst, wb) as f: f.write(data) if __name__ __main__: clear_fake_encryption(flag.zip, flag_fixed.zip)这个脚本用了最简单的全文件扫描方式对CTF题目里的小zip足够用。唯一要注意的是如果压缩包数据段里恰好也出现了50 4B 03 04这样的字节序列可能会被误判。不过题目文件一般都很小实际遇到误判的概率很低。如果要写得更严谨可以解析中央目录偏移然后通过中央目录记录里的“本地文件头偏移”字段定位但那就超出这次的篇幅了。5. 实战案例复盘一题看完整个解题链5.1 拿到压缩包后的第一动作用一次完整过程来说明。我在某个CTF练习平台上遇到一道MISC题下载下来的文件名是misc1.zip大小只有几百字节。先执行file misc1.zip unzip misc1.zipunzip直接提示Archive: misc1.zip [misc1.zip] flag.txt password:这时候千万别急着上爆破工具。几十万条密码跑下去大概率白费力气。先问自己题目有没有给任何密码线索如果没有任何提示伪加密的可能性立刻排在前面。用binwalk扫了一下没有附加数据文件就是标准的zip。于是进入010Editor。5.2 十六进制定位记录打开后搜索50 4B 03 04找到了一个本地文件头。开头几个字节是50 4B 03 04 14 00 09 00 00 00 08 00标志位是09 00bit0是1很可疑。再搜索50 4B 01 02找到中央目录开头是50 4B 01 02 1E 03 14 00 09 00 00 00中央目录的标志位同样是09 00。也就是说这个压缩包被做了双重伪加密标记本地文件头和中央目录都写着“加密”。按照第3节的操作我把本地文件头偏移6处的09改成08把中央目录偏移8处的09改成08保存。再执行unzip misc1_fixed.zip这次没有任何密码提示直接解出了flag.txt。打开一看MISC{zip_fake_encryption_is_just_a_flag}整个过程两分钟没有跑爆破没有猜密码。5.3 这类题的变体情况伪加密题在改标志位时还存在几种变体只改了本地文件头中央目录没改。这类题用某些第三方解压软件可能能开用命令行就卡住。只改了中央目录本地文件头没改。这时候Windows资源管理器会要密码但部分命令行工具可能直接解出来。压缩包里有多个文件只对其中一个文件做伪加密其他正常。如果解压时提示密码要检查每条记录的标志位。某个条目的本地文件头是01 00中央目录是09 00两个地方不一致说明制作者改得比较粗暴。不管怎么变检查“所有”文件头的标志位永远是通用解法。6. 常见问题与排查实录6.1 为什么我改了标志位还是提示密码这个问题我遇到太多次了原因基本逃不出下面几类。第一只改了本地文件头没改中央目录。前面说过Windows资源管理器优先读中央目录中央目录还标记加密就会继续要求密码。第二压缩包里有多个文件你只改了第一个文件头。第二条记录的文件头可能还是加密标志位解压到那个文件时照样卡住。第三改错偏移。本地文件头的标志位在偏移6中央目录在偏移8有人把一个文件里的所有09都改成00反而把压缩方式、CRC字段都碰了。第四这个包其实不是伪加密而是真加密。真加密的数据段已经加入了基于密码的加密流就算你把标志位清零解压时也会发现数据不对或者直接报CRC错误。排查方法很简单搜索50 4B 03 04和50 4B 01 02逐个数偏移再结合原始备份对比。如果清掉标志位后能解压但解出来的内容CRC校验失败就说明不是伪加密赶紧恢复备份走爆破或者明文攻击路线。6.2 修改后提示CRC错误或文件损坏有几种可能。如果是真加密清除标志位后解压程序会尝试按普通数据解压但压缩后的字节流实际是解密后的数据两个对不上CRC自然错。这时候必须还原文件用其他方式。还有一种可能是你把bit3清掉了。原始标志位是09代表同时启用了bit0和bit3。如果你把整个09改成00数据描述符的信息就丢了。解压时如果本地文件头里的CRC和大小本来就是零解压器找不到正确的数据位置就会提示损坏。这正是我前面强调“只清bit0、保留其他位”的原因。遇到这种情况把00改回08再试大概率能恢复。如果文件确实损坏还可以用zip -F来自动修复但那是另一套流程了。比赛环境里我建议每次改之前都保留原始副本才能回退。6.3 伪加密和真加密到底怎么彻底区分最直接的方法是看数据段结构。真加密的zip每个压缩条目在数据段开头会有一段长度基于密码的加密头字段排列和普通zip不同。伪加密则完全没有这些数据段就是普通压缩流。更省力的判断方式是先用010Editor清除标志位如果能正常解压且CRC通过就是伪加密如果解压出来是乱码、CRC错误或文件大小对不上就是真加密。这个“测试性修改”不会破坏原始文件只要改了之后不行马上恢复即可。还有一点CTF题目里很少会只给你一个真加密的zip然后让你爆破因为大字典爆破非常耗时间。如果看到zip要密码又没有任何密码线索先按伪加密处理成本最低。确认伪加密被排除后再考虑弱密码爆破已知明文攻击CRC32碰撞压缩包注释里藏线索文件尾部附加数据6.4 结尾附加内容容易被忽略改完标志位解压出文件之后不要太快结束。有些题目会把真正的flag藏在压缩包尾部或者把第二个zip塞进第一个zip的注释字段。010Editor看文件末尾EOCD签名是50 4B 05 06结尾处还有“注释长度”字段可以留意有没有多余字节。另外压缩包注释用zipnote也可以查看zipnote misc1_fixed.zip如果注释里有话往往就是提示或者隐藏密码。同理解压出的文件如果不是flag格式也要再检查一遍文件头和尾部防止套娃题。7. 结尾一点个人体会我现在處理伪加密题固定流程已经变成先file看一眼再unzip试一下提示要密码就开010Editor搜本地文件头看偏移6搜中央目录看偏移8清掉bit0保存解压。全过程不到一分钟。如果一定要选一个最值得养成的习惯我觉得是“不要看到一个加密标志就急着上爆破工具”。二进制世界里很多东西只是“看起来如此”zip伪加密尤其典型。手动改过几个字节之后你对文件格式的敏感度会明显提高以后遇到更深层的隐藏数据、隐写分析也会更有底气。希望这篇实战拆解能帮你把zip伪加密从“听说过”变成“闭着眼也能解”。下次再看到压缩包要密码先打开010Editor看一眼大概率就是那一个字节的事。