识别在专有文件格式中使用的DEFLATE算法变体

时间:2016-07-08 22:05:45

标签: algorithm compression reverse-engineering huffman-code deflate

免责声明:此问题需要非常了解DEFLATE算法。

我希望我能够征求一些识别特定文件格式中使用的压缩算法的想法。这是我的应用程序需要支持的传统专有格式,因此我们正在尝试对其进行反向工程。 (由于我不能进入的原因,不能选择原始创作者)。

我非常接近破解它,但我觉得我生活在Xeno的悖论中,因为每天我似乎都离中线越来越近了但从来没有出现过!

这是我目前所知道的:

它绝对使用与DEFLATE算法非常相似的东西。相似之处 -

  • 压缩数据由规范的霍夫曼码表示 (通常以000开头,但我不确定是否总是如此 案件)。
  • 数据在标题表之前(我相信立即) 它标识每个实际代码的位长度。喜欢 DEFLATE,该表还包括经典的霍夫曼码 (从0或00开始)。这些代码提供了比特长度 0-255 +字母表中的每个字符加上任何距离代码 可能会被使用。
  • 最后,再次像DEFLATE一样,带有表头 主要代码的位长度也在之前(我想立即) 通过一系列用于导出标题表代码的3位代码 (我将这称为" pre-header")。

此时相似之处似乎已经结束。

预标题中的3位代码没有按照DEFLATE指定的16,17,18,0,8 ...最佳顺序出现,而是按顺序排列,如6 7 8 9 ....

另一个不同之处在于每个3位代码不一定是文字位长。例如,这里有一个我主要破译的标题(我99.99%的信心是正确的):

00000001011 100 010 110 010 010 011 010 110 101 100 011 010 010 011 100 010 111
            *0*     skA                 *3* *4* *5* *6* *7* *8* *9* skB

忽略未标记的位,这会产生以下代码表:

00       7-bits
01       8-bits
100      6-bits
101      9-bits
1100     0-bits (skip code)
1101     skA = skip 3 + value of next 4 bits
1110     5-bits
11110    4-bits
111110   skB = skip 11? + value of next 9 bits
111111   3-bits

最明显的问题是标头表中还有其他未使用的位长度。事实上,它们根本不可用,因为不能有任何额外的2位或3位代码,例如,代码是规范的(对吗?)。

作者还使用16+的非标准代码。他们似乎根本不使用复制代码(DEFLATE中的16个);主标题都有相同长度代码的巨大字符串(非常低效......),跳过代码分别使用接下来的4和9位来确定跳过次数,而不是像DEFLATE中那样确定3和7。

另一个关键区别在于标题的第一位。在DEFLATE中,第一位是HLIT(5),HDIST(5)和HCLEN(4)。如果我使用LSB打包解释上述标题,我得到HLIT = 257(正确),HDIST = 21(不确定是否正确)和HCLEN = 7(绝对不正确)。如果我使用MSB打包,我会得到HLIT = 257,HDIST = 6(更可能是正确的)和HCLEN = 16(看起来是正确的)。但是,我不认为前缀实际上是14位,因为我似乎需要" 100" (见上文)0位(跳过)代码的位数。并且在其他示例中,比特10-13看起来根本不与预报头的长度相关联。

说到其他示例,并非每个文件都遵循相同的标题格式。这是另一个标题:

00000001000 100 100 110 011 010 111 010 111 011 101 010 110 100 011 101 000 100 011

在第二个例子中,我再次知道标题的代码表是:

0         8-bits
10        7-bits
110       6-bits
11100     skA
11101     5-bits
111100    0-bits (skip)
111101    skB
111110    9-bits
1111110   3-bits
1111111   4-bits

但是,正如您所看到的,许多所需的代码长度根本不在标题中。例如,没有" 001"代表8位代码,它们甚至不接近顺序(既不是连续的也不是最佳的16,17,18 ......)。

然而,如果我们将剩下的位移1:

                           skA                 *0* *5* *6* *7* *8* *9*
0000000100 010 010 011 001 101 011 101 011 101 110 101 011 010 001 110 100 010 001 1

这样做要好得多,但我们仍然无法正确导出skB(110),或3或4(111)的代码。换了一点并不能改善这种情况。

顺便提一下,如果您想知道我对自己是否知道这两个示例中的代码表有多信心,那么答案就是大量艰苦的逆向工程,即查看文档中略有不同或有差异的位可辨别的模式,并导出正在使用的规范代码表。这些代码表肯定是99 +%。

总而言之,我们似乎有一个非常接近的DEFLATE变体,但出于莫名其妙的原因,使用某种非标准的预标题。当然,我被绊倒的地方是识别哪个预标头位对应于主标头的代码位长度。如果我有这个,一切都会落到实处。

我还有其他一些我可以发布的例子,但不是要求人们为我做模式匹配,我真正在祈祷的是有人会认识到正在使用的算法并且能够指出我它。我发现作者不太可能不会使用现有的标准,而是从头开始编写自己的算法的麻烦,99%像DEFLATE,但后来只是稍微改变了预标题结构。这没有道理;如果他们只是想混淆数据以防止我想要做的事情,那么有更简单,更有效的方法。

顺便提一下,该软件的历史可以追溯到90年代末,即21世纪初,所以请考虑当时正在做什么。这不是"中间"或任何新的和疯狂的。它有些古老而且可能模糊不清。我猜测那个时候在一些半流行的图书馆中使用的DEFLATE的一些变种,但是我没有太多运气找到任何实际上没有爆炸的信息。

很多,非常感谢任何意见。

彼得

PS - 根据要求,这是帖子中第一个示例的完整数据块。我不知道它是否会有多大用处,但是这里有。顺便说一句,前四个字节是未压缩的输出大小。第五个字节开始预标题。

B0 01 00 00 01 71 64 9A D6 34 9C 5F C0 A8 B6 D4 D0 76 6E 7A 57 92 80 00 54 51 16 A1 68 AA AA EC B9 8E 22 B6 42 48 48 10 9C 11 FE 10 84 A1 7E 36 74 73 7E D4 90 06 94 73 CA 61 7C C8 E6 4D D8 D9 DA 9D B7 B8 65 35 50 3E 85 B0 46 46 B7 DB 7D 1C 14 3E F4 69 53 A9 56 B5 7B 1F 8E 1B 3C 5C 76 B9 2D F2 F3 7E 79 EE 5D FD 7E CB 64 B7 8A F7 47 4F 57 5F 67 6F 77 7F 87 8F 97 9D FF 4F 5F 62 DA 51 AF E2 EC 60 65 A6 F0 B8 EE 2C 6F 64 7D 39 73 41 EE 21 CF 16 88 F4 C9 FD D5 AF FC 53 89 62 0E 34 79 A1 77 06 3A A6 C4 06 98 9F 36 D3 A0 F1 43 93 2B 4C 9A 73 B5 01 6D 97 07 C0 57 97 D3 19 C9 23 29 C3 A8 E8 1C 4D 3E 0C 24 E5 93 7C D8 5C 39 58 B7 14 9F 02 53 93 9C D8 84 1E B7 5B 3B 47 72 E9 D1 B6 75 0E CD 23 5D F6 4D 65 8B E4 5F 59 53 DF 38 D3 09 C4 EB CF 57 52 61 C4 BA 93 DE 48 F7 34 B7 2D 0B 20 B8 60 60 0C 86 83 63 08 70 3A 31 0C 61 E1 90 3E 12 32 AA 8F A8 26 61 00 57 D4 19 C4 43 40 8C 69 1C 22 C8 E2 1C 62 D0 E4 16 CB 76 50 8B 04 0D F1 44 52 14 C5 41 54 56 15 C5 81 CA 39 91 EC 8B C8 F5 29 EA 70 45 84 48 8D 48 A2 85 8A 5C 9A AE CC FF E8

编辑2015年7月11日

我设法破译了相当多的额外信息。该算法肯定使用LZ77和Huffman编码。长度代码和额外位似乎都与Deflate中使用的匹配。

我还能够了解关于预标题的更多细节。它具有以下结构:

                     HLEN  0  SkS SkL ??  3   4   5   6   7   8   9  HLIT
00000 00101110 001 0 1100 100 100 110 10 110 101 100 011 010 010 011 100010111

HLEN = the last bit-length in the pre-header - 3 (e.g. 1100 (12) means 9 is the last bit-length code)
HLIT = the number of literal codes in the main dictionary
SkS = "skip short" - skips a # of codes determined by the next 4-bits
SkL = "skip long" - skips a # of codes determined by the next 9-bits
0 - 9 = the number of bits in the dictionary codes for the respective bit lengths

未标记的位我仍然无法破译。此外,我现在看到的是前标头代码本身似乎有一些额外的位(注意SkL和3之间的??)。它们不是所有直接的3位代码。

因此,现在唯一缺少的基本信息是:

  • 如何解析前标题以获取额外的位和诸如此类的东西;和
  • 文字代码后面有多少个距离代码

如果我有这些信息,我实际上可以通过手动提供代码长度字典以及正确的文字与距离代码数来将剩余数据提供给zlib。这个标题之后的所有内容都跟着DEFLATE到了这封信。

以下是一些示例标题,其中指示了位长代码以及文字和长度代码的数量。请注意,每一个我能够对答案进行反向工程,但我仍然无法将未解密的位与这些统计数据相匹配。

Sample 1
(273 literals, 35 length, 308 total)
????? ???????? ??? ? HLEN  0  SkS SkL   ??  3  ?  4  ?  5   6   7   8   9       HLIT
00000 00100010 010 0 1100 110 101 110   10 111 0 111 0 101 011 010 001 110      100010001

Sample 2
(325 literal, 23 length, 348 total)
????? ???????? ??? ? HLEN  0  SkS SkL   ??  3   4   5   6   7   8   9           HLIT
00000 00110110 001 0 1100 101 101 110   10 110 000 101 000 011 010 001          101000101

Sample 3
(317 literal, 23 length, 340 total)
????? ???????? ??? ? HLEN  0  SkS SkL   ???  4   5  ?  6   7   8   9            HLIT
00000 01000100 111 0 1100 000 101 111   011 110 111 0 100 011 010 001           100111101

Sample 4
(279 literals, 18 length, 297 total)
????? ???????? ??? ? HLEN  0  SkS SkL   ??  3   4   5   6   7   8   9           HLIT
00000 00101110 001 0 1100 100 100 110   10 110 101 100 011 010 010 011          100010111

Sample 5
(258 literals, 12 length, 270 total)
????? ???????? ??? ? HLEN  0  SkS SkL   ??  2   3   4                           HLIT
00000 00000010 000 0 0111 011 000 011   01 010 000 001                          100000010

我仍然希望有人之前见过像这样的非标准DEFLATE风格标题。或者也许你会看到一种我未能看到的模式......非常感谢任何进一步的投入。

1 个答案:

答案 0 :(得分:1)

好吧,我终于完全破解了它。它确实使用了LZ77和Huffman编码的实现,但非常类似于非标准的类似DEFLATE的方法来存储和导出代码。

事实证明,前标头代码本身是固定字典霍夫曼代码,而不是文字位长。弄清楚距离代码同样棘手,因为与DEFLATE不同,他们没有使用与文字相同的比特长度代码,而是使用另一个固定的哈夫曼字典。

对于任何感兴趣的人来说,显而易见的是,使用DEFLATE衍生产品存在旧的文件格式。它们可以通过测定进行逆向工程。在这种情况下,我可能总共花费了大约100个小时,其中大部分是从已知的解压缩样本中手动重建压缩数据以便找到代码模式。一旦我充分了解他们正在做什么来自动化该过程,我就可以制作几十个示例标题,从而找到模式。

我仍然无法理解为什么他们这样做而不是使用标准格式。与仅仅使用ZLib相比,它必须进行大量的工作才能获得新的压缩格式。如果他们试图对数据进行模糊处理,他们可以通过加密数据,与其他值等等进行更有效的操作。不,不是这样。我想,他们决定向老板展示他们的天才,想出一些东西" new"即使与标准的差异微不足道,除了让我的生活变得困难之外,没有其他任何价值。 :)

感谢那些提供意见的人。