卡在固件的异或解密上

逆向工程 固件 解密 异或
2021-06-20 22:25:19

TL; 博士

我有一个加密图像和明文版本,我几乎可以肯定它是一个 XOR 密码,但我不知道如何解密它。我在下面列出了一些线索。

细节

我正在尝试对专业音频设备的固件进行逆向工程,我已经完成了大部分工作,但我被困在中间的一步。

我很确定这是一个 XOR 密码(因为已经有一个 XOR 解密来达到这一点,之后还有另一个)。它似乎应用于 0x1000 字节的块,因为密钥似乎在这么多字节后重置。初始密钥似乎是2并且每 0x2000 个字节增加 1。在每个块的开始,当前的“初始密钥”被异或0x4002以产生真正的密钥。因此,键在这些偏移处重置为这些值:

Offset | Key
-------+--------------------
0x0000 | 2 ^ 0x4002 = 0x4000
0x1000 | 2 ^ 0x4002 = 0x4000
0x2000 | 3 ^ 0x4002 = 0x4001
0x3000 | 3 ^ 0x4002 = 0x4001
0x4000 | 4 ^ 0x4002 = 0x4006

密码似乎以 16 位小端的单位/字工作。在每个字与密钥进行异或运算后,密钥右移1

然而,这是我解脱的地方。有时,显然是随机的,密钥需要被 异或0x4002我看不到这种情况何时发生的模式。最初看起来它每四个字节(两个字)发生一次,但这在偏移量 0x22 处分崩离析。

我尝试对密文和明文进行 XOR 运算以生成一个长的 XOR 密钥文件,然后尝试找出如何重现该密钥文件。通过将我的猜测与大密钥文件进行异或,0x00如果我是正确的,它应该以字节结束然而,它通常以0x02 0x40字节结尾,通过编写检测这些错误的代码并相应地调整密钥,我能够看到密钥需要在何时何地进行异或以0x4002正确解密数据。我已经盯着它看了几个小时,但在我看来它只是随机的,我找不到任何模式!

例如,在从偏移量 0 开始的块中,键需要与0x4002偏移量0、4、8、12 (每四个字节)进行异或运算,但随后在偏移量 20 处它回落到每两个字节(20、22 , 24, -not 26-, 28, 2A, 2A, -not 2E-, etc.) 我看不出什么时候会发生这个额外的 XOR 键的模式。它似乎与之前的值、偏移量、键值等无关(当然可能是我只是看不到它。)

背景

源数据来自制造商的固件文件,我从 MIDI SysEx 事件中解码并使用 XOR 密码解密。我知道这个密码是正确的,因为它产生一个正确的页眉和页脚,以及每 256 个字节的块号。在处理完这些之后,我最终得到了一个与固件转储完全相同的源图像,该固件转储直接从带有 Minipro EEPROM 读取器的设备的 ROM 芯片中获取。

我正在努力使用的 XOR 密码是从制造商的固件文件到闪存到 ROM 芯片的最后一步(反之亦然,因为对自定义 ROM 进行编程是我的最终目标)。

请注意,闪存到 ROM 芯片中的内容(我在这里指的是明文)本身是进一步 XOR 加密的,但是我已经成功解密了它,所以这里没有问题(并且需要这种闪存加密,因为引导加载程序期待它。)

如果有人能够帮助我找出这个算法,我将实施它并将其作为开源发布,作为我正在进行的项目的一部分

拆卸

对于@Igor Skochinsky 提出的问题,我还没有尝试反汇编引导加载程序,因为我不确定如何去做。这是引导加载程序代码,根据Blackfin ADSP-BF531 数据表,它可能包含一个 10 字节的标头

为了避免混淆,如果有人能够反汇编代码,我所询问的 XOR 加密仅在闪存 ROM 编程阶段应用。一个数据块通过 MIDI 端口作为 7 位数据传入(功能编号 0x34“写入闪存块”),被解码为 8 位数据,TZ'04应用与密钥无关的 XOR 解密(密钥从偏移量加载0x2C84),CRC被检查,三字节的头被剥离,剩下的256字节的块(大概)存储在RAM中。

手册说设备只在第 16 个 256 字节块(4 kB 数据)之后确认写入。有问题的 XOR 算法每 4 kB 重置一次密钥,因此看起来设备会收集 256 字节的块,直到达到 4 kB 并在那时应用未知的 XOR 密码,然后将结果明文写入闪存芯片(这是一个 SST39SF040,标识为芯片 ID 0xBFB7)。

与此无关(但为了帮助指导任何反汇编),引导加载程序还在正常引导期间从闪存芯片加载数据,然后使用“应用程序密钥”对其进行异或解码。这个运行时异或解决如下:

  1. 引导加载程序 XOR 密钥从偏移量 0x3002 加载,长度为 0x38。
  2. 从偏移量 0x303A 读取的引导加载程序密文,长度为 0x38。
  3. 用于生成“应用程序密钥”的引导加载程序 XOR 和密文。
  4. 从偏移量 0x4000 读取闪存,用于 XOR 的应用程序密钥,结果存储在准备执行的内存中。
1个回答

感谢@IgorSkochinsky 的建议,我在pf0camino/cross-bfin-elf Docker 镜像中找到了 Blackfin 架构的反汇编程序使用 Docker 意味着它很容易运行,而且我不必自己安装交叉编译器。然后我可以使用以下命令反汇编图像:

bfin-elf-objdump -D -b binary -mbfin bootloader.bin > bootloader.disasm

这产生了一个大文本文件,由于输出没有应用任何类型的自动识别,因此需要一些时间来完成。尽管如此,在阅读 Blackfin 汇编程序并搜索各种已知常量后,我能够弄清楚代码在做什么。

最终我找到了解密函数,并将其转换为 Javascript 以在我的项目中使用。它是这样出来的:

function decodeBlock(baseBlockNum, data)
{
    // If the block is zero, the function won't change the data, so this magic
    // number is used.  Block 0 is part of the bootloader, which never appears
    // to be reflashed in official firmware images.
    let key = baseBlockNum || 0x545A;

    for (let pos = 0; pos < data.length;) {
        // Let's be fancy and execute the `if` statement without using an `if`.
        //if (key & 1) key ^= 0x8005;
        key ^= (
            ((key & 1) << 15)
            | ((key & 1) << 2)
            | (key & 1)
        );

        // This rotate operation is a bit redundant, because the above XOR
        // always clears the lower bit.  So let's skip that part.
        //key = ((key & 1) << 15) | (key >> 1);
        key >>= 1;

        data[pos++] ^= key & 0xFF;
        data[pos++] ^= key >> 8;
    }
}

baseBlockNum参数是块的 ROM 地址,以 0x1000 字节为单位。因此,对于要在地址 0x4000 处闪存的块,参数为 4。

我已经在完整的固件文件上对此进行了测试,并且成功解密了它!