从二进制文件中提取数据

逆向工程 二元分析
2021-06-29 08:40:27

我有一个二进制文件和一个相应数据的文本文件,我知道二进制文件中包含数据的位置。但是,我无法确定数据的编码方式。根据对 hexdump 的分析,数据似乎没有存储为 float16、float32、float64、各种长度的有符号/无符号整数或字符。也许包含数据的部分已被某种算法压缩,或者数字可能以我不熟悉的表示形式存储。

人类可读的数据如下:

20.0,0.001
21.0,0.001
22.0,0.001
23.0,0.001
24.0,0.002
25.0,0.002
26.1,0.002
27.0,0.004
28.0,0.002
29.0,0.002
30.0,0.003
31.0,0.004
(etc)

我有 70 行这样的行,每行都包含表中显示的精度的两个数字。相应的数据部分包括 2312 个十六进制数字,或 1156 个字节。在 ASCII 表中存在重复的字符模式,后跟 @ 符号(例如,4@ = '0x3440'、5@ = '0x3540'、6@ ='3640' 等)。这个“基序”每 16 个字节出现一次,出现 70 次。一个挑战是,尽管 @ ('0x40') 之前的字节的 ASCII 值存在单调递增,但它并不总是增加(即,存在静止点,有时会出现跳过其中一个的跳转) ASCII 字符;例如4@ ... 5@ ... 6@ ...6@ ... 8@ ... 9@ ...)。基于该主题的 16 字节周期性,我假设人类可读表中的每一行在二进制文件中由 16 个连续字节表示。因此,二进制数据表的总大小为 16*70 = 1120 字节。完整的数据表是 1156 字节,所以我进一步假设无法解释的 36 字节包含头信息。实际上,我可以解释大部分标题:包含纯文本数据描述符的 15 个字节,似乎用作偏移量的 16 个字节的零,以及似乎对表中的行数。

我的问题目前如下:

  • 我不知道 16 字节数据的“阶段”(即,一个 16 字节段在哪里结束,下一个开始)
  • 我不知道人类可读的数字是如何编码的。它们似乎不会以各种浮点数或整数表示形式进行编码,也不会以 ASCII 字符的形式进行编码。
  • 我不知道数据是否被压缩,如果是,如何确定压缩方法。如果它们被压缩,那么压缩将仅限于数据表本身,因为我可以通过运行strings二进制文件的其他地方找到纯文本file在二进制文件上运行只是报告它包含“数据”。

下面显示了一个十六进制内容 ( xxddump)的示例,它与上面的内容非常接近并对齐以显示我之前提到的 '0x40' = @ 的进展。该表以“0x46”(十进制 70)开头,我之前提到它是标题的一部分并表示数据表中的行数。冒号前的十六进制数字只是给出了该行距文件开头的偏移量;中间部分显示了 8 个字节(16 个十六进制数字)的数据;表的右侧部分显示了十六进制数据的.ASCII 可打印值(主要表示 ASCII 不可打印值0x00)。

00000180: 4600 0000 0000 0000 0000 3440 0000 0000  F.........4@....
00000190: 0000 583f 0000 0000 0000 3540 0000 0000  ..X?......5@....
000001a0: 0000 503f c3f5 285c 8f02 3640 0000 0000  ..P?..(\..6@....
000001b0: 0000 553f 3e0a d7a3 70fd 3640 0000 0000  ..U?>...p.6@....
000001c0: 0000 453f 0000 0000 0000 3840 0000 0000  ..E?......8@....
000001d0: 0040 5d3f 85eb 51b8 1e05 3940 0000 0000  .@]?..Q...9@....
000001e0: 0000 5e3f cdcc cccc cc0c 3a40 0000 0000  ..^?......:@....
000001f0: 0000 633f f628 5c8f c2f5 3a40 0000 0000  ..c?.(\...:@....
00000200: 00c0 703f 3e0a d7a3 70fd 3b40 0000 0000  ..p?>...p.;@....
00000210: 0000 5a3f 0000 0000 0000 3d40 0000 0000  ..Z?......=@....
00000220: 0020 613f 48e1 7a14 ae07 3e40 0000 0000  . a?H.z...>@....
00000230: 00c0 643f c3f5 285c 8f02 3f40 0000 0000  ..d?..(\..?@....

我想就如何处理这个特定问题提出建议,主要是确定压缩是否是一个因素以及数字如何在二进制文件中表示,因为到目前为止我无法检测到人类可读数据行与重复我描述的 16 字节图案。

1个回答

这个数据实际上是标准的 x86 双(8 字节)表示,前 4 字节是别的东西(如你所说的 46 00 00 00 可能是 70),数据从二进制文件中的偏移量 0x184 开始。

可能让您感到困惑的是,人类可读的数据是四舍五入的,而二进制文件的精度更高。因此,例如,您23.0实际上是22.99,这就是为什么3640会出现两次,而下一个值24.00具有38 40

当我将你的十六进制转换回二进制(编辑出偏移量和字符转储,然后xxd -r -p < x.hex > x.bin),然后在它上面运行以下 perl 程序:

open(F, "<$ARGV[0]");
binmode F;

seek(F, 4, 0);

while (read(F, $dbl, 8)) {
    read(F, $dbl2, 8);
    printf("%15.10f  %15.10f   ", unpack("d", $dbl), unpack("d", $dbl2));
    printf("%4.1f  %5.3f\n", unpack("d", $dbl), unpack("d", $dbl2));
}

我明白了:

20.0000000000     0.0014648438   20.0  0.001
21.0000000000     0.0009765625   21.0  0.001
22.0100000000     0.0012817383   22.0  0.001
22.9900000000     0.0006408691   23.0  0.001
24.0000000000     0.0017852783   24.0  0.002
25.0200000000     0.0018310547   25.0  0.002
26.0500000000     0.0023193359   26.1  0.002
26.9600000000     0.0040893555   27.0  0.004
27.9900000000     0.0015869141   28.0  0.002
29.0000000000     0.0020904541   29.0  0.002
30.0300000000     0.0025329590   30.0  0.003
31.0100000000     0.0000000000   31.0  0.000

如您所见,右侧的 2 列是精度和匹配项中的数字(最后一个 0.004 不在您的文件中),而前 2 列是高精度的,有时其值略小比下一个整数。

我通过将 20.0 转换为 double 并检查十六进制来发现这一点:

perl -e "print pack('d', 20.0);" | xxd
0000000: 0000 0000 0000 3440                      ......4@

然后从文件中的拟合偏移量开始并将内容转换回来。