有人认识这种英特尔 HEX 固件格式的变体吗?

逆向工程 固件 文件格式
2021-06-25 17:03:42

我有一个 ARM Cortex-M0+ 的固件文件,它的格式与 Intel HEX 非常相似。所有数据都封装在一个 0x3a ... 0xa 包装器中。这些字段似乎主要匹配 16 位 Intel HEX 结构,其中第二个字段绝对是记录长度,接下来的两个字段似乎是加载有效负载的地址。Intel HEX 下的下一个应该是记录类型,稍后我将介绍我在这里看到的内容。

我看到与标准英特尔 HEX 的两个显着变化(我没有使用它的经验,只是我读过的)

  1. 这些字段都以原始字节指定,而不是字节的 ASCII 表示。例如,大多数记录的长度为 16 字节,十六进制转储显示:0x3a 0x10 ... 0xa
  2. 记录定界符之前的最后一个字段应该是所有字段总和的 LSB 的二进制补码。这不是我所看到的,我不知道它是如何计算的。这有点重要,因为我想修改这个固件。另外,我用于计算两个恭维的 python 代码可能是错误的,所以请随时告诉我我只是一个白痴。

任何想法这种格式可能是什么?这是一个小示例 hexdump 输出:

00000000  3a 10 04 00 00 ff ff ff  ff ff ff ff ff ff ff ff  |:...............|
00000010  ff 7e fb ff ff 81 0a 3a  10 08 00 00 00 60 00 20  |.~.....:.....`. |
00000020  c1 08 00 00 49 93 01 00  4d 93 01 00 e1 0a 3a 10  |....I...M.....:.|
00000030  08 10 00 51 93 01 00 55  93 01 00 59 93 01 00 5d  |...Q...U...Y...]|
00000040  93 01 00 2c 0a 3a 10 08  20 00 61 93 01 00 65 93  |...,.:.. .a...e.|
00000050  01 00 69 93 01 00 6d 93  01 00 dc 0a 3a 10 08 30  |..i...m.....:..0|
00000060  00 71 93 01 00 75 93 01  00 79 93 01 00 1f 2f 02  |.q...u...y..../.|
00000070  00 4d 0a 3a 10 08 40 00  1f 2f 02 00 1f 2f 02 00  |.M.:..@../.../..|

回到那个记录类型字段。在 200kb 的文件中,我只有 6 条不是 0 类型的记录:

  • 类型 1:一个实例。应该是EOF,最后一条记录,但不是。它是一个有效负载长度为零的记录,应该是这样,但在它之后我还有一个记录,类型 6。
  • 类型 2:我有三个实例。应该是扩展段地址,根据维基百科,地址字段通常为零,数据长度应为二。这就是我在所有三个实例中看到的。
  • 类型 3:就在类型 1 (EOF?) 记录之前。符合维基百科告诉我对类型 3 的期望。看起来像:3a 04 00 00 03 00 00 08 c1 30 0a。也许这就是维基百科所说的 CS:IP?
  • 类型 6:最后一条记录,看起来像:3a 04 00 00 06 f9 f7 b1 9f b6 0a。

更新:我文件中的校验和字段实际上是两者的互补,我写的原始代码在几个方面被破坏了。Dang 无类型 python 代码。最终用于计算校验和的是:

def twos_comp(val, bits):
    return val - (1 << bits)        

def calc_checksum(record_byte_string):
    return abs(twos_comp(sum(record_byte_string)%256, 8))%256

所以我剩下的唯一真正的问题是:最后的类型 6 记录是什么?我猜它是一个 4 字节的 CRC,但我不知道是什么。有人见过这个吗?

2个回答

由于它使用原始字节而不是文本表示,因此它似乎与 Intel HEX 或 Motorola S-Record 完全无关。它可能只是设备制造商开发的一些基于块的自定义格式(尽管可能受到 Intel Hex 的启发)。我建议你重新固件更新程序或固件中的接收部分,也许它会有处理未知记录的代码。

事实证明,记录类型 6 是一个 32 位 CRC,按双端顺序排列。CRC 是典型的(据我所知)CRC32,它产生与Linux 系统上 Perl 模块中crc32包含实用程序相同的结果Archive::Zip诀窍是引导加载程序实际上是在存储应用程序代码的闪存区域上计算它。我还没有完全跟踪引导加载程序,但代码似乎正在安装固件更新,然后在引导后根据此值检查其完整性。我不确定它是什么引导加载程序,但它似乎不是Kinetis 引导加载程序,因为 Kinetis 引导加载程序引导加载程序配置区 (BCA) 中缺少所需的魔法字符串。

因此,诀窍是弄清楚应用更新文件中指定的更新后闪存的外观,并仅对引导加载程序正在计算的闪存区域执行 CRC 在我的情况,这是0x8000x3afff同样在我的情况下,未指定的字节被设置为0xFF,因此我可以通过查看文件来计算离线 CRC。但是对于我拥有的另一个更新文件,未指定字节的假设0xFF不成立,所以我想我只是很幸运。

一个有趣的注意事项:crcmod当我发布我的原始问题时,我尝试使用Python 模块来尝试计算我的 CRC。即使我知道正在使用的 CRC 算法,以及在什么字节范围内,我仍然无法获得任何 crcmod CRC 算法来给我正确的结果。我摆弄了一会儿,但最终放弃了,只使用了crc32shell 实用程序。我从来没有找到那个问题的根源,它很可能是我 Python 代码中的一个错误,但我想提醒一下遇到类似问题的任何人。