非标准 LZ77 压缩头

逆向工程 固件 开箱 解压 幻数
2021-07-07 18:14:17

我正在对 SJ4000 相机固件进行反转,但在打开它时我发现了一个问题。

这是我在图片上找到的标题:

00000000  42 43 4c 31 81 66 00 09  00 54 68 e0 00 2f 2b bf  |BCL1.f...Th../+.|

如您所见,BCL1 是使用 LZ77 算法的“基本压缩库”(http://bcl.comli.eu/home-en.html的标头,但我无法用它解压缩图像。

与固件头文件相比,我使用 BCL 构建了一些文件,并发现了这一点:

00000000  42 43 4c 31 81 66 00 09  00 54 68 e0 00 2f 2b bf  |BCL1.f...Th../+.| < FIRMWARE
00000000  42 43 4c 31 00 00 00 09  00 00 4f 88 99 7f 45 4c  |BCL1......O...EL| < LZ
00000000  42 43 4c 31 00 00 00 02  00 00 4f 88 20 03 06 90  |BCL1......O. ...| < HUFFMAN
00000000  42 43 4c 31 00 00 00 01  00 00 4f 88 99 7f 45 4c  |BCL1......O...EL| < RLE
00000000  42 43 4c 31 00 00 00 0a  00 00 4f 88 56 01 64 9f  |BCL1......O.V.d.| < SF
00000000  42 43 4c 31 00 00 00 03  00 00 4f 88 00 7f 45 4c  |BCL1......O...EL| < RICE8
00000000  42 43 4c 31 00 00 00 04  00 00 4f 88 00 45 7f 46  |BCL1......O..E.F| < RICE16
00000000  42 43 4c 31 00 00 00 05  00 00 4f 88 00 46 4c 45  |BCL1......O..FLE| < RICE32

据此压缩算法是LZ77,除了2个字节外,它遵循相同的结构。

42 43 4c 31 < Magic Number
81 66 00 09 < unknown 2 bytes + 2 standard bytes 
00 54 68 e0 < Original Size
00 2f 2b bf < Compressed Size

知道这 2 个字节是什么意思吗?

编辑:我尝试编辑那 2 个字节并用 00 00 覆盖它们,以便标头符合标准。之后尝试用BCL LZ77解压,提示分段错误:

LZ77 decompress FW96655A_ZERO.bin to test...
Input file: 3091395 bytes
Output file: 5531872 bytes
Segmentation fault

检查长度字节我得到以下结果:

0x005468E0 > Big Endian Long: 5531872
0x002F2BBF > Big Endian Long: 3091391

正如您所看到的,压缩数据长度有 4 个字节的差异,这可能会导致 Seg。过错。

1个回答

我不确定,但可以想到两种可能性。

  1. 一个是它只是一个编码错误,并且在创建文件时只初始化了 32 位寄存器的低半部分。如果是这种情况,那么只需将这两个字节归零即可使用标准工具成功解压。

  2. 另一个是它是对正常 LZ77 减压的(专有)修改,在这种情况下,它可能是一个小改进,而不是一个全新的方案。

我在互联网上很容易找到的所有相机固件样本都使用标准00 00 00 09标签,这可能表明第一种可能性的可能性更大。

在检查您提到的二进制文件后,很明显文件中存在错误bfc.c具体来说,bfc.c它的第 192 行说:

if( command == 'd' )
{
    /* Read header */
    algo = ReadWord32( f );  /* Dummy */
    algo = ReadWord32( f );
    outsize = ReadWord32( f );
    insize -= 12;
}

但是,这是不正确的,因为它无法读取infile标题中写入大小。要快速解决此问题,只需将这些行更改为这些并重新编译:

if( command == 'd' )
{
    /* Read header */
    algo = ReadWord32( f );  /* Dummy */
    algo = ReadWord32( f );
    outsize = ReadWord32( f );
    ReadWord32( f );
    insize -= 16;
}

当我这样做时,解压缩代码没有问题。要对输出文件进行相应的更改(即压缩),请更改第 364 行周围的代码bfc.c

/* Write output file */
WriteWord32( outsize, f );
fwrite( out, outsize, 1, f );
fclose( f );

对此:

/* Write output file */
fwrite( out, outsize, 1, f );
fclose( f );

我可能应该提及完成此更改的可能原因。在原始程序中,文件大小可用于确定大小,但在无差别的字节数组(即无文件系统)中(如 ROM 中经常出现的情况),将输入和输出文件大小都编码在标题。