ZIP伪加密看似简单,实则也是大有学问在里面

这里细节讲解一下伪加密和相关工具的使用

首先我们需要了解一下zip文件结构

zip文件结构

zip文件结构大致分为三个部分

一个record区,一个dirEntry区,一个endLocator区

三个部分相互依存,这里是010下的zip文件对照表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
+ ZIPFILERECORD record # 压缩源文件数据区
- char frSignature[4] # 压缩源文件标志
- ushort frVersion # 压缩源文件版本
- ushort frFlags # 压缩源文件标志 (有无加密,偶数位修改了最低位所以会显示有密码)
- enum COMPTYPE frCompression
- DOSTIME frFileTime # 压缩源文件时间
- DOSDATE frFileDate # 压缩源文件日期
- uint frCrc # 压缩源文件CRC32校验值
- uint frCompressedSize # 压缩源文件压缩后大小
- uint frUncompressedSize # 压缩源文件压缩前大小
- ushort frFileNameLength # 压缩源文件名长度
- ushort frExtraFieldLength # 压缩源文件扩展域长度
- char frFileName[frFileNameLength] # 压缩源文件名
- uchar frData[frCompressedSize] # 压缩源文件数据
+ ZIPDIRENTRY dirEntry # 压缩源文件目录区
- char deSignature[4] # 目录标志
- ushort deVersionMadeBy # 创建该条目的版本
- ushort deVersionNeeded # 解压该条目所需的版本
- ushort deFlags # 标志位 (有无加密,偶数位修改了最低位所以会显示有密码)
- enum COMPTYPE deCompression # 压缩方法
- DOSTIME deFileTime # 最后修改时间
- DOSDATE deFileDate # 最后修改日期
- uint deCrc # CRC32校验值
- uint deCompressedSize # 压缩后的大小
- uint deUncompressedSize # 压缩前的大小
- ushort deFileNameLength # 文件名长度
- ushort deExtraFieldLength # 扩展域长度
- ushort deFileCommentLength # 文件评论长度
- ushort deDiskNumberStart # 起始磁盘编号
- ushort deInternalAttributes # 内部属性
- uint deExternalAttributes # 外部属性
- uint deRelativeOffset # 该条目在 ZIP 文件中的偏移位置
- char deFileName[deFileNameLength] # 文件名
- char deExtraField[deExtraFieldLength] # 扩展域
- char deFileComment[deFileCommentLength] # 文件评论
+ ZIPENDLOCATOR endLocator # 压缩源文件目录结束标志
- char elSignature[4] # 结束标志
- ushort elDiskNumber # 当前磁盘编号
- ushort elStartDiskNumber # 目录开始的磁盘编号
- ushort elEntriesOnDisk # 当前磁盘上的条目数量
- ushort elEntriesTotal # 总条目数量
- uint elSizeOfDirectory # 目录的总大小
- uint elOffsetOfDirectory # 目录开始的偏移位置
- ushort elCommentLength # 注释长度
- char elComment[elCommentLength] # 注释

可以发现最重要的就是两个标志位了,他们是判断伪加密的标准

其实其余部分也很重要,因为如果不对应的话直接解压会发现报错——CRC校验错误

首先我们要知道

全局方式位标记的四个数字中只有第二个数字对其有影响,其它的不管为何值,都不影响它的加密属性,即:
第二个数字为奇数时 –>加密
第二个数字为偶数时 –>未加密

也就是说,0900/0100代表着加密,0000/0800代表着无加密

伪加密就是把本来无加密的文件修改了标志位从而显示出加密的样子

那我们怎么修改伪加密呢?为什么有些工具可以直接解压有些却不行呢?

工具选择

010editor

010editor肯定是最普遍和最准确的修改伪加密的东西了

如果是伪加密的话直接010修改0900为0000

如果两区(record和dirEntry)都是0900直接全改即可

bandizip

经过测试,bandizip识别一个文件是否加密是通过看dirEntry记录中的deflag位

如果deFlag位是加密的话无论另一个frflag是否是加密bandizip都会识别为加密

winRAR

winRAR和bandizip一样,都是识别dirEntry记录中的deflag位

7zip

7z就很神奇,它与上面两个工具不同。

7zip判断是否为加密的方式是看record记录中的frflag位

因此有些明明是伪加密的zip 7z却能直接解压

所以那句“不会出的出题人只修改一个struct区”是因为换个工具就能解压

随波逐流

经过测试,随波逐流提供的伪加密修复只会修改dirEntry记录中的deflag位,而不管frFlag位

ZipCenOp

不好用!!

运行

1
java -jar ZipCenOp.jar -r 111.zip

会显示

success 1 flag(s) found

但是它不帮我改,有啥用?。。

其他工具

传闻2345好压改两个区也能直接开。而且还有360解压缩

然而我不敢尝试,怕删不干净。。

细节注意

zip伪加密还有一个细节需要注意的是我们都知道两个flag位置改成奇数时加密,偶数时无加密

那改成奇数时0100和0900有什么区别,能不能乱改??

答案是否定的。

8神提供的解释:

这两字节就叫Flags,010模板中写fr和de是为区分filerecord块和direntry块。Flags两字节一共16bit,只有最低位用于记录加密与否,倒数第2 3两个bit记录压缩选项,大部分情况下修改不会出现问题,但如果将这两字节修改为09的话就会修改倒数第4个bit:记录数据描述符,如果这个bit是1,则表示标头中的CRC32和文件大小未知,但它们的实际数据会以额外的12或16字节结构出现在压缩数据后面。把一个实际上没有数据描述符的压缩包的flags倒数第4个bit改成1会导致软件被指示去读取一个不存在的描述符片段,从而导致后续的数据读取全部错乱,进而解压失败。

感觉实际情况的时候无法很明确的判断是改成0900还是0100,建议两种都试试,那种对了就用哪种

flag通用位置位详解

pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

这是zip结构的格式规范。

根据zip文件规范,flag位置有16个字节,每个字节都有不同的含义:

位(从右往左) 详细描述
0 如果设置,表示文件已加密。
(flag是奇数的话就会使这一位为1,显示为加密)
1 对于压缩方法6(Imploding),表示使用8K滑动字典(否则为4K)。
对于方法8和9(Deflate),与位1结合表示压缩选项。
2 对于方法6(Imploding),表示使用3个Shannon-Fano树(否则为2个)。
对于方法8和9,结合位1表示压缩选项。
3 如果设置,CRC-32、压缩大小和未压缩大小在本地文件头中为零,实际值在数据描述符中。
(即表示标头的CRC32和文件大小未知,读取数据时)
4 保留用于方法8(Deflate)的增强功能。
5 如果设置,表示文件包含压缩后的补丁数据(需要PKZIP 2.70或更高版本)。
6 强加密。如果设置,提取所需版本号至少为50,且位0必须设置。对于AES加密,版本号至少为51。
7 目前未使用。
8 目前未使用。
9 目前未使用。
10 目前未使用。
11 语言编码标志(EFS)。如果设置,文件名和注释字段必须使用UTF-8编码(见附录D)。
12 保留用于PKWARE的增强压缩。
13 如果设置,表示中央目录已加密,本地文件头中的某些字段被掩码。
14 保留用于PKWARE的替代流。
15 保留用于PKWARE。

那么0900和0100有什么区别呢?

已知在 010editor 使用了小端字节序,所以0900实际上也就是0x0009

什么是小端字节序?

拆分成二进制也就是

1
0000 0000 0000 1001

所以它修改了第0位和第3位,相对的,0100也就只修改了第0位

第0位代表着加密与否,所以0900和0100都代表着zip加密了

而0900比0100多修改了第三位,表示CRC-32、压缩大小和未压缩大小在本地文件头中为零,实际值在数据描述符中。

也就是8神所说的“表示标头中的CRC32和文件大小未知”,所以乱修改的话读取会错乱。