如何解压缩 MSZIP 块?

How do I decompress a MSZIP block?

我有一个 CAB 压缩文件,我希望从 Linux 中提取一个文件。由于 Linux 上没有任何本地 CAB 提取器,我想我会尝试完成一个。

虽然我看过 MSZIP 文档[0] 以及 [1] 和 [2],但我在解压缩它时遇到困难,即使每个块都是使用修改后的 DEFLATE 压缩器压缩的。虽然我确实使用 [3] 来解压缩块,但除了第一个块之外,其余块都有很多丢失的数据 [完全无效的数据 [即0x00 填充].

所以我相信我需要手动解决。

很遗憾,我无法理解 [2]。所以, 具有以下数据:

ED 9D 79 70 1C C7 75 87 07 E0 4D 88 24 EE 8B 94
04 1E A2 44 51 04 81 05 08 2D 11 4A C2 5E 00 96
...

以及以下用于剖析数据的代码:

#!/bin/env python

import os


def to_bin(in_item):
    b = ord(chr(in_item))
    retval = bin(b).replace("0b", "")
    if len(retval) < 8:
        lx = 8 - len(retval)
        q = "0" * lx
        retval = q + retval
    return retval


def swapbytes(in_bit_str):
    p = in_bit_str[:4]
    q = in_bit_str[4:]
    return q + p


pth = os.path.join("comp", "test_0.dat")

fp = open(pth, 'rb')
fpd = fp.read(10)
fp.close()

dx = ""
for item in fpd:
    cx = to_bin(item)
    scx = swapbytes(cx)
    dx += scx
    print(cx)
    print("---> %s" % scx)

print("Final: %s" % dx)

bfinal = dx[0]
btype = dx[1:3]

print("Bfinal: %s" % bfinal)
print("Btype: %s" % btype)

rest = dx[3:]

hlit = rest[:5]
hdist = rest[5:11]
hclen = rest[11:15]

hlitd = int(hlit, 2)
hdistd = int(hdist, 2)
hclend = int(hclen, 2)
print("HLIT: %s [%d]" % (hlit, hlitd))
print("HDIST: %s [%d]" % (hdist, hdistd))
print("HCLEN: %s [%d]" % (hclen, hclend))

我得到以下输出:

$ python tstdcmp.py
11101101
---> 11011110
10011101
---> 11011001
01111001
---> 10010111
01110000
---> 00000111
00011100
---> 11000001
11000111
---> 01111100
01110101
---> 01010111
10000111
---> 01111000
00000111
---> 01110000
11100000
---> 00001110
Final: 1101111011011001100101110000011111000001011111000101011101111000011100000
0001110
Bfinal: 1
Btype: 10
HLIT: 11110 [30]
HDIST: 11011 [27]
HCLEN: 0011 [3]

所以我从 RFC1951 [2] 中得到的基本概念是,在一个字节中,它是从最高位到最低位。即 7 6 5 4 3 2 1 0 但对于多个字节,即 AB CD,字节交换为 CD AB。

所以根据代码,我确定前两个字节 ED 9D 实际上应该是 9D ED.

并且以位为单位: 11011110 11011001

这意味着 BFinal == 1 和 BTYPE == 10(因此是动态霍夫曼编码)。

删除前三位后,我得到: 11110 11011001

所以从 [2] 的 3.2.7 开始,我应该得到接下来几组位的以下信息:


 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
 5 Bits: HDIST, # of Distance codes - 1        (1 - 32)
 4 Bits: HCLEN, # of Code Length codes - 4     (4 - 19)

所以,HLIT = 11110 [30] HDIST = 11011 [27] HCLEN = 0011 [3]

但是从上面来看,HLIT应该在257到286之间 HCLEN 在 4 和 9 之间;但我得到 HLIT = 30 和 HCLEN = 3.

也就是说,3.2.7 中的文档:

For even greater compactness, the code length sequences
themselves are compressed using a Huffman code.

所以这是用霍夫曼码进一步压缩的例子? 我曾希望理解这个 RFC1951[2];但这非常令人困惑。

我看过一些关于 DEFLATE 压缩的 Youtube 视频;但它们主要显示在算法上,而不是字节在文件中的打包方式。

非常感谢任何帮助。

谢谢

[0] - https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-mci/27f0a9bf-9567-4e40-ad66-6ae9ab9d2786

[1] - How to decompress MSZIP files with c#?

[2] - https://datatracker.ietf.org/doc/html/rfc1951

[3] - https://www.nayuki.io/page/simple-deflate-decompressor

  1. 不,您认为从 RFC 1951 中获得的“基本概念”是完全错误的。首先,每个字节中的位从最低有效位到最高有效位读取。其次,您不反转流中的字节。首先从第一个字节读取前八位,从第二个字节读取第二个八位,依此类推。 (存储块中的长度存储为小端,但既不反转也不不反转。这只是字节流中 16 位长度的序列化方式。)
  2. 一旦您正确读取了这些位,HLIT 等值就会完全按照 RFC 中的说明存储。前五位是literal/length个码数减去257。因此,您取 5 位的值,给出 0..31 中的数字,然后将 257 添加到该值。这给出了 257..288 范围内的数字。正如同一行中所述,允许的范围实际上是 257..286,因此五位的最后两个可能值 30 和 31 不应出现在有效的压缩流中。
  3. RFC 1951 一点也不混乱。它是对格式的清晰而完整的描述。但是,您需要有足够的压缩背景,尤其是霍夫曼代码,才能理解它。 RFC 并非旨在成为一本关于压缩的教科书,也不是一本关于如何用位编码整数的教科书。
  4. 很明显,您需要一些时间才能弄清楚这一切。幸运的是,您不需要编写自己的充气器。您可以改为使用 zlib。阅读 zlib.h 中所有 inflate 函数的文档。
  5. 在 CAB 文件中,MSZIP CFDATA 块使用之前 CFDATA 块的历史记录,直到到达文件夹边界。即使每个块都是正确终止的压缩流,下一个块也可以引用前一个块的未压缩数据。要在第一个之后处理 CFDATA 块,您需要使用 zlib 的 inflateResetKeep() 函数重新启动膨胀过程,同时保留先前膨胀操作的字典。

作为参考,这里是对您提供的 deflate 流的初始字节的解码,使用 infgen:

! infgen 2.5 output
!
last            ! 1
dynamic         ! 10
count 286 30 16     ! 1100 11101 11101
code 16 4       ! 100
code 17 7       ! 111
code 0 4        ! 100 000
code 8 3        ! 011
code 7 4        ! 100
code 9 3        ! 011
code 6 4        ! 100
code 10 3       ! 011
code 5 4        ! 100
code 11 3       ! 011
code 4 5        ! 101
code 12 3       ! 011
code 3 7        ! 111
code 2 6        ! 110 000
lens 6          ! 1100
lens 8          ! 000
lens 8          ! 000
lens 9          ! 001
repeat 6        ! 11 1110
lens 9          ! 001
lens 8          ! 000
lens 10         ! 010
lens 9          ! 001
lens 9          ! 001
lens 9          ! 001
lens 8          ! 000
repeat 6        ! 11 1110
repeat 4        ! 01 1110
lens 9          ! 001
lens 9          ! 001
lens 10         ! 010
lens 10         ! 010
lens 10         ! 010
lens 8          ! 000
lens 9          ! 001
repeat 3        ! 00 1110
lens 10         ! 010
lens 9          ! 001
lens 10         ! 010
lens 10         ! 010
lens 9          ! 001
lens 10         ! 010
lens 9          ! 001
lens 10         ! 010
lens 9          ! 001
lens 8          ! 000
lens 9          ! 001
lens 8          ! 000
lens 8          ! 000
lens 7          ! 1101
lens 8          ! 000
lens 8          ! 000
lens 9          ! 001
lens 8          ! 000
lens 10         ! 010
lens 7          ! 1101
lens 9          ! 001
lens 8          ! 000
lens 12         ! 100
lens 9          ! 001
lens 10         ! 010
lens 10         ! 010
lens 10         ! 010
lens 8          ! 000
lens 7          ! 1101
repeat 4        ! 01 1110
lens 8          ! 000
lens 8          ! 000
lens 8          ! 000
lens 7          ! 1101
lens 9          ! 001