联发科BootROM漏洞深度剖析

本文详细记录了作者通过联发科BootROM漏洞修复变砖小米手机的完整过程,包括漏洞原理分析、逆向工程方法和具体实现细节,为硬件安全研究提供了宝贵的技术参考。

联发科BootROM漏洞深度剖析

一部变砖的小米手机让我在Github上发现了一个使用未公开联发科BootROM漏洞的项目。该漏洞由Xyz发现,由Chaosmaster实现。这个初始漏洞已经存在相当长一段时间了。既然我成功修复了手机,我将记录修复过程并解释该漏洞的工作原理。该漏洞允许未签名代码执行,从而使我们能够从手机中读取/写入任何数据。

对于专业人士:你可以直接跳到BootROM漏洞工作原理部分(剧透:非常简单)。本指南将尝试指导初学者,以便他们可以为自己的手机添加支持。我想展示所有内容,但这会违反联发科的版权,因此我只提供引导ROM反编译的片段。

手机变砖与理解SP Flash Tool

我喜欢使用小米手机,因为它相对便宜,解锁引导程序的方式简单,而且在泰国这里很容易买到。有了解锁的引导程序,我从未陷入无法恢复的启动循环,因为我通常可以进入fastboot模式并使用原始ROM重新刷机。我通常购买搭载高通SOC的手机,但这次我购买了搭载联发科SOC(MT6873,也称为Dimensity 800)的Redmi 10X Pro 5G。但事实证明:你可能会变砖而无法进入fastboot模式。

几年前,重新刷写联发科手机很容易:进入BROM模式(通常在手机关机时按住音量增大按钮并插入USB),然后使用SP Flash Tool覆盖所有内容(包括boot/recovery分区)。它的工作方式是:我们进入BROM模式,SP Flash Tool将上传DA(下载代理)到手机,SP Flash Tool将与DA通信以执行操作(擦除闪存、格式化数据、写入数据等)。

但现在他们增加了更多安全性:当我尝试刷写手机时,它显示了一个身份验证对话框。事实证明,这不是普通的Mi Account对话框,而是需要成为授权Mi Account持有者(通常来自服务中心)。事实证明,仅仅刷写联发科手机可能会进入启动循环,而无法进入fastboot模式。引用XDA文章中的话:

为Redmi Note 8 Pro开发的开发人员发现,该设备很容易因多种原因变砖。有些人在从恢复分区刷写恢复分区时手机变砖,而其他人发现在解锁的引导程序上通过fastboot安装官方ROM也会使设备变砖。小米需要一种更好的方法来解砖其设备,而不是依赖授权Mi Account。

我找到了一位ROM改装者,他不得不与互联网上一个 shady 的人打交道,使用远程Team Viewer来修复他的手机。他对MTK BootROM安全性进行了一些解释。总结:BROM可以有SLA(串行链路授权)、DAA(下载代理授权)或两者都有。如果我们未经授权,SLA会阻止加载DA。而DA可以呈现另一种类型的身份验证。使用自定义DA,我们可以绕过DA安全性,假设我们可以绕过SLA以允许加载DA。

当我读到这些文章时,我决定放弃。我已经准备好放弃我的数据。

MTK绕过

幸运的是,在我手机变砖两天后,我发现了一个针对各种MTK设备的绕过方法。不幸的是:MT6873尚未得到支持。要支持设备,你只需要编辑一个文件(device.c),其中包含一些地址。其中一些地址可以从外部来源找到(例如从该SOC发布的Linux内核),但大多数地址没有访问BootROM本身就无法找到。我尽可能多地阅读了关于BROM协议的文档。我找到的一些文档:

  • 联发科详情:SoC启动:有指向BROM文档的链接
  • Oxygen Forensic® Detective中对联发科设备的支持(解释BROM保护)

几天后又来了一个运气:Chaosmaster发布了一个通用payload来转储BootROM。我很幸运:通用payload第一次尝试就在我的手机上立即生效,我得到了我的引导ROM转储。现在我们需要弄清楚要填写哪些地址。此时,我没有其他ROM可以比较,所以我需要巧妙地找出这些地址。我们需要找到以下内容:

  • send_usb_response
  • usbdl_put_dword
  • usbdl_put_data
  • usbdl_get_data
  • uart_reg0
  • uart_reg1
  • sla_passed
  • skip_auth_1
  • skip_auth_2

从使用这些地址的主文件中我们可以看到:

  • uart_reg0和uart_reg1是正常握手所必需的。这些地址可以在公共Linux内核源代码中找到。
  • usbdl_put_dword和usbdl_put_data用于向我们的计算机发送数据
  • usbdl_get_data用于从计算机读取数据
  • sla_passed、skip_auth_1和skip_auth_2是我们需要覆盖的主要变量,以便我们可以绕过身份验证

我们可以开始反汇编从通用转储器获得的固件。我们需要将其加载到地址0x0。没有多少字符串可供交叉引用,所以我们需要发挥创意。

不知何故,generic_dump_payload可以找到usb_put_data的地址,以将转储的字节发送到Python脚本。它怎么知道?generic_dump_payload的源代码可在ChaosMaster的存储库中找到。但我没有更早发现这些信息,所以我只是反汇编了文件。这是一个小二进制文件,因此我们可以使用Binary Ninja轻松进行逆向工程。事实证明,它进行了一些模式匹配来查找函数的序言:2d e9 f8 4f 80 46 8a 46。实际上,它搜索具有该序言的第二个函数。

generic_dump_payload中的模式查找器

现在我们找到了send_word函数,我们可以看到发送是如何工作的。事实证明,它通过一次发送一个字节来发送一个32位的值。注意:我尝试继续使用Binary Ninja,但在原始二进制文件上查找内存地址的交叉引用并不容易,因此我切换到Ghidra。在稍微清理代码之后,它看起来像这样:

generic_dump_payload找到的内容

现在我们只需要找到function_pointers的引用,我们就可以找到sendbyte的真实地址。通过查看相关函数,我能够找到以下地址:usbdl_put_dword、usbdl_put_data、usbdl_get_data。请注意,可以通过将usbdl_put_dword替换为对usbdl_put_data的调用来稍微简化漏洞利用,这样我们就少了一个需要担心的地址。

对我来说最难的部分是找到send_usb_response以防止超时。从主文件中,我知道它需要3个数字参数(不是指针),并且必须在通过USB发送数据之前的某个地方调用它。这大大缩小了范围,我可以找到正确的函数。

现在是全局变量:sla_passed、skip_auth_1和skip_auth_2。当我们查看Python中的主要漏洞利用时,它首先做的事情之一是读取当前配置的状态。这是通过进行握手然后检索目标配置来完成的。

目标配置

在引导ROM中必须有一个大的"switch"语句来处理所有这些命令。我们可以找到握手字节(A0 0A 50 05)来查找握手例程的引用(实际上找到了两个,一个用于USB,一个用于UART)。从那里我们可以找到大switch语句的引用。

握手

你应该能够找到类似这样的内容:握手后开始处理命令

并且大switch应该清晰可见。

处理各种命令的switch

现在我们找到了switch,我们可以找到命令0xd8(获取目标配置)的处理程序。注意在python中,代码是这样的:

注意位掩码

通过查看位掩码,我们可以得出设置配置值功能的函数名称。例如:我们可以将设置安全启动位的函数命名为bit_is_secure_boot。知道了这一点,我们可以检查每个bit_is_sla和bit_is_daa

我们可以从它设置的位来命名函数

对于SLA:我们需要找到调用bit_is_sla的交叉引用,我们可以看到总是咨询另一个变量。如果SLA未设置,或者SLA已经通过,我们被允许执行下一个操作。

查找sla_passed

现在我们需要再找两个变量来通过DAA。查看bit_is_daa,我们发现在这个函数的末尾,它调用了一个检查我们是否已经通过它的例程。这些是我们要找的最后两个变量。

BootROM漏洞利用工作原理

漏洞利用结果非常简单。

  • 我们被允许上传数据到某个内存空间
  • USB控制传输的处理程序盲目索引函数指针表

基本上是这样的:handler_arrayvalue*13;

但实际上存在一些问题:

  • 这个数组的值是未知的,但我们知道大多数设备将0x100a00作为其中一个元素
  • 我们可以暴力破解USB控制传输的值来调用payload
  • 我们可能还需要尝试不同的地址(因为并非所有设备都有0x100a00作为可用的元素)

还提供了另一个payload来仅重启设备。这将使找到正确的地址和控制值变得容易。

结束语

虽然当我的手机变砖时我非常沮丧,但解决这个问题的经历非常令人兴奋。感谢Xyz发现这个漏洞,感谢ChaosMaster实现它、简化它,并回答我的问题并审阅这篇文章。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计