解开HP固件更新-本系列的第2部分已完成

2020-05-23 01:57:34

这篇文章是记录固件更新的不同结构和阶段的四部分博客系列的第二篇。该系列的下一部分将在我们撰写时逐周上传。

在上一篇文章中,我们详细介绍了如何解压HP固件光栅图形并提取其编码数据。此时,第二层编码数据应该是可见的。这篇文章详细介绍了如何破译下一个编码层。

第二固件编码层开始于一系列大约6,000条结构化的ASCII行,随后是大的二进制数据斑点。在我们的研究中,我们最初忽略了开头的ASCII行,主要关注二进制数据。然而,在这篇文章中,我们稍后将回到这个二进制数据。

第二阶段文件的ASCII行(位于文件开头)使用下面解释的“S-Record”格式,并加入了一些专有扩展名。

摩托罗拉开发了S-Record格式,这是一种用于编码二进制数据的ASCII格式。其常规用途是对EEPROM/闪存芯片进行编程。它通常由固件开发人员使用,并由不同类型的记录组成,主要有两类:*数据记录,由加载地址后跟数据组成*包含用于在编程结束时重定向执行的地址的开始地址记录。

每个S记录都是以“<;LF>;”(\n)结尾的单独行,由5个字段组成:

每个字段的使用取决于记录类型。除了第一个字符(这是S)之外,所有其他字符都是ASCII-十六进制。

在我们的例子中,第二阶段固件文件的ASCII部分以7个类型A的S记录开始,每个记录的长度为0x27(为方便起见增加了空格):

S A 27 0202026F02EA000801008F8B97F7FFC72708D194D0F4181400BF28865C57BDEFADCDDE136EB6 21<;LF&>;S A 27 5E116D98172B43812D1FA171C827EA524AC7A7699AA4AAA66D09C92F0FFE82FC8C611E6D2E90 F7<;LF&>T;S A 27 9527FD68BC05365133E59A9C3F5C3E63819D64D7BBD839AF5B40ACCA7A4964ED97ECD21476B8 55<;LF&>;S A 27 46E0ADF0F9BF0D9210B70EA7E6FB96C007CED8DF8F92FCA2F5A4ADDE730EF4565049F2523097 2D<;LF>;S A 27 1B1A3B9BD4ADB31654C1B7A94D98629BB52974BB362F18FCD7D2898B6D20AA872070EC0BC3FC E5<;LF&>;S A 27 87737AEB4FDAA840BFC6355D3C6F405FFFB8F074141D1B59B302E4808944AC5CD4AE8FE3DEB8 CF<;LF&>T;S A 27 AC2DD17268E76FBAD51F8B979994DD70760F2BE56356659086F46FD5B109762F84ACBBCE0E42 4B<;LF>;

这种类型的S-Record是专有的,没有文档记录。一些调查证实它很可能是身份验证头(可能是更新包的签名)。

在身份验证报头之后,有一个类型为0的S记录。类型为0的记录称为第一标头记录。其地址字段通常为零,并且其数据字段通常包含以人类可读格式描述下一块S记录的文本信息。此记录类型不影响记录解析。在我们的示例中,它包含字符串“flash”(空格是故意的):

S 00F 0000 7265666C6173682020200000 AB<;LF>;R e f l a s h_[空格]。

接下来的5,908行是类型3的S-Records。类型3记录指示闪存编程器将记录数据存储到指定的4字节存储器地址。例如,下面的S记录行表示“在地址401D0000存储20字节数据”:

该记录没有数据字段,地址字段是控制(CPU执行)传递到的4字节地址。

使用开源工具SREC_INFO_INFO(有关链接,请参阅下面的参考部分),我们可以获得有关该文件的一些信息(请务必手动删除前7行S-Records,以便SREC_INFO能够识别该文件)。输出应如下所示:

$SREC_INFO OUTER.srec格式:Motorola S-Record Header:";Reflash";执行开始地址:401D8D38 SREC_INFO:outer.srec:5911:警告:忽略垃圾行数据:401D0000-401E4D95 401E4D98-401EBE67 401EBE98-401EBF C01F02E0-C01F11BF C01F8

此输出显示加载的数据在内存中不是连续的。SREC_INFO命令引用的垃圾行是紧跟在S7记录之后的两行:

似乎这些是特殊的(专有)记录,以“F”或“P”开头,后跟一个四字节地址。它可以用来在分支之前设置一些寄存器或状态,作为版本号的指示符,或者任何其他真正的东西,但是我们没有进一步研究这一点。

在s记录之后是二进制数据。在我们的研究中,这是我们分析的第一部分。

二进制数据中的许多序列作为人类可读的字符串很容易被检测到,但是它们是“断裂的”,即它们是字符串的可读片段,而不是整个字符串。这表明我们正在接近最终的原始数据,并且该格式没有经过压缩、加密或大量编码。

通过盯着二进制数据看一位,就会出现一种模式。看一下从二进制文件中提取的证书:

您可以看到每40字节重复一次的二进制数据的明确模式。这7个字节显然不是真实数据的一部分(即,它们包含非ASCII字节)。初步观察表明,33个2D字节中的第二个和第三个字节是某种报头,而接下来的4个字节每次都保持某种递增0x28的计数器。第一个字节看起来像某种校验和。

在内部,我们将这种模式称为“神秘的7字节模式”。有几个问题引起了我们的兴趣:(此模式的目的是什么?*文件开头的ASCII命令的目的是什么?*校验和是如何计算的(它是校验和吗)?

让我们看一下第一个字节,它看起来像一个校验和。请注意,对于两个相同的序列,该值是不同的。考虑以下问题:

看起来,对于每0x28字节,校验和字节通常递减0x28(模0x100),但偶尔会递减0x29。

要了解这一点,我们来看一下图像中的地址0x13AB3E以及其后的三个校验和字节。我们可以检查校验和值之间的绝对差异:

1A+28=42(地址0x13AB3E的第一个字节)F1+29=1A^+-(地址0x13AB9C的第一个字节)。

快进许多令人精疲力竭的小时,在这段时间里,我们盯着数据,它也在盯着我们。最后,它泄露了自己的秘密。可以推导出校验和计算和整个格式。

原来,第一个字节实际上是之前“7字节模式”的最后一个字节,而这个模式只不过是摩托罗拉S-Record的二进制版本--确实很神秘!

二进制S记录中的记录与其对应的ASCII记录具有相同的结构,不同之处在于ASCII版本中的SX值(其中,4X表示类型)被二进制版本中的0x3X值替换,其余数据是二进制数据,而不是ASCII编码的。此外,还省略了换行符(<;LF>;)。

33 2D 00 18 A08 A3 62 02 B2 38 CC DA 54 6F 9B 4A 02 FC 81 0D 32 0E BB B1 3C 37 D6 8F A8 3C E7 9D F2 38 8C 63 FC F6 EA FC 38 68 03 2B F2 AA。

33-记录类型(3)2D-记录长度(45)00 18 A00 08-记录地址A3 62 02 B2 38 CC DA 54 6F 9B 4A 02 FC 81 0D 32 0E BB B1 3C 37 D6 8F A8 3C E7 9D F2 38 8C 63 FC F6 EA FC 38 68 03 2B F2-数据AA-记录校验和

那么这一切意味着什么呢?为什么我们有ASCII S-Records后跟二进制版本的S-Records?为什么惠普要使用二进制S-Record格式而不是简单的ASCII版本?如果他们决定使用二进制S记录,那么为什么还要使用ASCII S记录呢?

我们查看了由(ASCII)S-Record层编码的数据,编写了一个简单的Python脚本来提取记录,并将它们加载到Ghidra的地址0x401D0000,这是S-Record指明要写入此节的地址。

一个有趣的函数位于地址0x401d84d4。以下是Ghidra反编译的摘录:

您看,最初的ASCII S-Record行包含解析二进制S-Records的代码(是的,您的理解是正确的。

为什么惠普要这么做?我们没有一个明确的答案,但空间可能是二进制S记录的一个原因。当以ASCII S-Record格式表示时,二进制数据的大小将扩大约2.4倍(参见SRecord参考手册第134页)。固件更新文件已经相当大了-接近40MB,没有理由将其大小翻倍。

为什么要进行双重编码?可能是为了使所有更新与具有解码ASCII S记录的代码的打印机版本向后兼容。我也可能与更新固件时执行的阶段性进程有关。

在下一篇博客文章中,我们将看到如何将此图像加载到内存中进行逆向工程(以及常规的打印机操作)。