未知的解压算法

逆向工程 解压
2021-06-30 10:38:05

我从事 ChessBase 档案 (.cbv) 的逆向工程。

找到了文件的大致结构,已经可以解压一些文件了。

你可以在这里看到我目前的工作

但是,一些较大的 .cbv 文件似乎使用了第二种压缩算法。

我能够通过调试ChessBase Reader 2013软件找到第一个压缩算法,但我无法理解第二个压缩算法。

我尝试了一些工具,比如signsrch在没有运气的情况下找出使用了什么算法:它似乎是一个自定义算法。

这是一个我可以使用我的工具部分解压缩文件(我的工具将在检测到使用了未知压缩算法时进行打印)。What to do?

您知道使用什么压缩算法吗?

如果没有,有没有办法通过查看压缩文件找到它?

我能够创建档案,因此我可以拥有既压缩又不压缩的文件:我想知道在这种情况下是否有任何方法可以找到压缩模式。

1个回答

下载ChessBase Reader并稍微玩弄ProcMon以找到读取存档和写入数据文件的功能后,我将整个内容加载到IDA中进行分析。数据是霍夫曼编码的。

每个数据块具有以下结构。请注意,霍夫曼压缩使用位而不是字节,因此下表中的每个大小也以位为单位。例如,块长度为 16 位或 2 个字节。

+----------------------------------------------+
|                                              |
|16 bits - uncompressed block length (len)     |
|                                              |
+-----------+----------------------------------+
|           |                                  |
|Repeat     |  4 bits - length of entry (n)    |
|256        |                                  |
|times      +----------------------------------+
|           |                                  |
|one entry  |  n bits - tree left/right        |
|per byte   |  information for this byte       |
|(0-255)    |                                  |
|           |                                  |
+-----------+----------------------------------+
|                                              |
| Huffman encoded bit sequences. The number of |
| bits isn't stored anywhere, but the number   |
| of sequences, which is equal to the number   |
| of output bytes, is the block length (len)   |
|                                              |
+----------------------------------------------+

假设在这个方案中编码了“foobar”这个词,这可能会导致(我组成了字符的位值):

+----------------+
|Huffman code for|
|character   is  |
+--------+-------+
|        |       |
| o      |   0   |
| f      |   100 |
| b      |   101 |
| a      |   110 |
| r      |   111 |
|        |       |
+--------+-------+

这将导致单词 foobar 被编码为 100 0 0 101 110 111. 长度为 6 个字节,或0000 0000 0000 011016 位。

foobar格式化为上表的位数组 for将读取

0000 0000 0000 0110                      (16 bit output length)
.....    array index   0 for byte '\0'
.....    array index   1 for byte '\1'
.....
0011 110 array index  97 for byte 'a'    (3 bits)
0011 101 array index  98 for byte 'b'    (3 bits)
.....
0011 100 array index 102 for byte 'f'    (3 bits)
.....
0001 0   array index 111 for byte 'o'    (1 bit)
.....
0011 101 array index 114 for byte 'r'    (3 bits)
.....                                    remaining bit combos - 255

100 0 0 101 110 111                      foobar text

该实现从代码表构建一个二叉树。当它读取数据时,它从树的根开始;每个位沿树向下移动,向左或向右移动,具体取决于下一个位值。当到达叶子节点时,输出相应的字节。这将重复直到达到输出流的长度。

二进制文件中的相关函数是:

BECAA0:解码存档数据。读取16位长度;然后在解码器类中的偏移量080A(位)和0E10(位长)处将编码表读入两个数组中在此之后,调用BEC930解码数据字节。

BEBF30:一个参数(位数),从输入数组中获取这么多位。在函数的末尾,offset 处的字1014具有这些位。

BEBAD0: 从位于080A的数组构建树0E10

BEC930: 调用BEBAD0构建树,然后从输入流中读取剩余的位。为每一位遍历树;当找到叶子时发出一个字节。最后,调用BEBA90销毁树。

BEBA90:通过删除左右子节点递归删除节点,即节点本身。

如果您想阅读文件,我认为调试编写器不会更容易;压缩有很多逻辑和数据结构,知道“一种方式”是如何工作的并不一定能帮助您解决问题。在这种情况下,幸运的是,它是一个众所周知的算法,但是如果算法未知,如果您只知道如何解压缩,则很难有效地压缩。