物联网安全 - 21通过 UBoot 发现 UART 和固件提取

admin 2024年2月15日15:38:40评论13 views字数 30080阅读100分16秒阅读模式

嵌入式 RE 简介:通过 UBoot 发现 UART 和固件提取

往期作品

物联网安全-物联网介绍及其架构

物联网安全-2.物联网攻击面

物联网安全3.物联网10大安全漏洞

物联网安全-4.低功耗蓝牙BLE

物联网安全-5.ZigBee协议物联网安全-6.ZigBee安全物联网安全-7.物联网固件逆向

物联网安全-8.软件无线电(Software Defined Radio,SDR)简介

物联网安全-9.软件无线电简介(Software Defined Radio,SDR):软件部分

物联网安全 – 10.MQTT 协议和安全简介

物联网安全 – 11 CoAP 协议和安全简介

物联网安全 – 12.MQTT 代理安全

物联网安全-13 硬件分析简

物联网安全 - 14 硬件调试端口指南:概述和识别

物联网安全 – 15 硬件攻击面:SPI

物联网安全 – 16 硬件攻击面 - I2C

物联网安全 – 17硬件攻击面 - UART

物联网安全 – 18 硬件攻击面 - JTAG、SWD

物联网安全 – 19侧信道攻击 (SCA) 简介

物联网安全 – 20扩展 Ghidra:设置开发环境

概述

欢迎阅读嵌入式 RE 简介系列。这篇文章将重点介绍以 Arcade 1UP Marvel 台面橱柜为目标的 UART、UBoot 和 USB。Arcade 1Up 系列橱柜是在家中设置街机的一种经济实惠的方式。自从这些机柜推出以来,已经有很多模组演示了如何更换机柜的内部组件以运行通用的MAME软件。这篇文章将介绍库存硬件并确定如何提取固件。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

目标

在这篇文章中,我们将回顾以下内容:

  • 执行嵌入式系统的拆解

  • 通过IC标记识别组件

  • 用万用表测量接头电压

  • 逻辑分析仪的使用和设置

  • UBoot 分析与审查

  • 编写 UBoot 与 depthcharge 的交互脚本

这篇博文旨在让读者熟悉在目标系统上定位活动 UART、如何使用 UBoot 控制台,以及最终如何利用这两个组件从目标中提取闪存。阅读本文后,读者将熟悉 python3 库的实用程序。screendepthcharge

硬件概述

查看新目标时,首要任务之一是查看可用接口。就这款街机机柜而言,乍一看可用的接口相对纤薄。用户通过机柜侧面的操纵杆/按钮和 USB 端口与此设备进行交互。关于机柜侧面的USB端口的信息似乎很少。请注意,即使在网站上的图片中,也没有USB端口。但是,机柜侧面有一个 USB 设备端口,旨在提供外部控制器支持。在另一侧,我们有一个标准的耳机插孔。这两个外围设备的行为符合预期,USB 端口可用于连接外部控制器,耳机插孔的工作方式与宣传的一样。

回复提示:在一些较旧的手机上,可以将音频插孔配置为在启动时显示串行终端。更多信息可以在这里找到。不幸的是,在这个平台上没有这样的修改。

以目前的知识,我们似乎没有更多可以在外部探索的东西。所以,接下来,我们将打开橱柜,看看里面有什么!

机柜拆解

机柜非常空旷,除了连接到屏幕的金属外壳。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

在金属外壳下方,我们找到了机柜的主PCB;轻轻取下额外的金属防护罩后,我们看到以下内容。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

第一次查看这样的PCB时,我们首先要记下任何零件号,看看是否能找到任何数据表。第一个让我印象深刻的组件以蓝色突出显示。物联网安全 - 21通过 UBoot 发现 UART 和固件提取

如果我们在网上搜索这个零件号,我们会发现大量的信息——请参阅下面从 Rockchip wiki 中提取的信息Rockchip RK3128

  • 中央处理器

    • 四核 ARM Cortex-A7MP Core 处理器,高性能、低功耗和缓存应用处理器

    • 完全实现 ARM 架构 v7-A 指令集,ARM Neon Advanced SIMD(单指令、多数据)支持加速媒体和信号处理计算

  • 图形处理器

    • ARM Mali400 MP2

    • 高性能 OpenGL ES1.1 和 2.0、OpenVG1.1 等

  • 记忆

    • 8KB 内部 SRAM

    • 动态内存接口(DDR3/DDR3L/LPDDR2):兼容JEDEC标准DDR3-1066/DDR3L-1066/LPDDR2-800 SDRAM。支持 32 位数据宽度,2 列(片选),总共 2GB(最大)地址空间。

    • Nand 闪存接口:支持 8 位异步/切换/同步nandflash,最多 4 个库。16 位、24 位、40 位、60 位硬件 ECC

    • eMMC接口:兼容标准eMMC接口,支持MMC4.5协议

  • 视频

    • MPEG-1、MPEG-2、MPEG-4、H.263、H.264、H.265、VC-1、VP8、MVC实时视频解码器

  • 音频

    • 具有 2 个通道的 I8S/PCM:最多 8 个通道(8xTX、2xRX)。音频分辨率从16位到32位。采样率高达192KHz

    • I2S/PCM,带 2 通道:最多 2 个通道(2xTX、2xRX)。音频分辨率从16位到32位。采样率高达192KHz

  • 连接

    • SPI控制器:一个片上SPI控制器

    • UART控制器:3个片上UART控制器

    • I2C 控制器:4 个片上 I2C 控制器

    • USB Host2.0:内嵌 1 个 USB Host 2.0 接口

    • USB OTG2.0:兼容USB OTG2.0规范。支持高速(480Mbps)、全速(12Mbps)和低速(1.5Mbps)模式

仅从这个描述中,我们就对目标处理器了解了很多。我们现在知道了架构以及可用的外设和接口;这些对我们很有用,因为它们可能会勾勒出未来的攻击媒介。重要的是要记住,在逆向工程过程的这个阶段没有太多的信息。在尝试与目标交互之前,我们希望尽可能多地了解它。

在 CPU 的旁边,我们还有另一个组件,在下面以橙色突出显示。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

这个组件被标记了,我们很幸运,在三星的这个网页中搜索这个部件号。此页面上的信息告诉我们,这是一个 2GB DDR3 SDRAM 芯片。也可以从此页面获取数据表;在进行此类工作时,当数据表可用时,总是值得收集的。该芯片负责将可用内存扩展到 CPU 并提供易失性内存源 (RAM)。SEC931 K4B2G1646F-BYMA

到目前为止,我们已经确定了可能是主CPU和外部RAM的内容。但是,我们仍然缺少一种非易失性存储。因此,接下来,让我们检查下面以粉红色突出显示的组件。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

该组件已标记,搜索此部件号可将我们带到此数据表。该器件为1G位串行SLC NAND闪存芯片。根据数据表,该芯片采用串行外设接口,兼容 2.6V 至 3.3V 之间的电压范围。该芯片可能包含机柜使用的大部分数据,并将成为我们固件提取的主要目标。Winbond 25N01GVZEIG

最后一个组件位于 GPIO 线附近,并标记为 。这个组件与我们所看到的其他组件不同,因为我找不到那么多的信息。但是,根据一些网站,该IC似乎是一个音频放大器。我在网上找到的关于这部分的少数提及都提到了它,所以我们暂时假设这就是它的功能。MIX2018A

回顾一下我们迄今为止确定的组件,我们有:

  1. 瑞芯微RK3128 ARM CPU

  2. 三星SRAM芯片

  3. 华邦1GBit NAND闪存

  4. MIX2018A音频放大器

现在我们已经回顾了这块板上的集成电路,让我们看看板上的连接器,看看我们能学到什么。

连接器分析

现在我们已经记录了主板上的分立元件,我们将尝试识别主板上的外部连接器。首先,我们有桶形连接器;此连接器是 在下图中以蓝色勾勒出轮廓:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

此连接器用于为机柜供电。

在桶形连接器的右侧,我们有一个微型USB端口。这应该立即引起人们的注意,原因有两个:

  1. 这不是面向用户的端口

  2. 这不是 USB 主机端口;这是一个微型端口,表示 USB 设备或可能的 OTG(移动)控制器

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

继续向右,我们有两排标头引脚。它们通过前面图像中所示的灰色带状电缆连接。该连接器连接到单独的控制板,用于处理操纵杆/按钮。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

在我们的控制面板连接器之后,还有另一个四针连接器。有了这个连接器,它通向哪里就不那么明显了。例如,此连接器可能通向 USB 连接器或耳机插孔。我们可以尝试使用连续性测试用万用表确定这一点。连续性测试将检查电流是否可以在两个探头之间流动,通常用万用表上的以下符号之一表示:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们可以使用这个模型来测试两个组件是否连接。我将一根 3/4 耳机线插入耳机插孔,并将探头固定在其中一个金属环上以测试这一点。使用另一个探头,我触摸了四针连接器的每个点,在其中一条线路上,万用表发出一声响亮的哔哔声,让我们知道这两个点之间存在连接。三个引脚中的每一个都与音频连接器上的一个环重合;这是我们的音频插孔!

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

接下来,我们有用于显示器的连接器:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

在显示屏附近,我们有两个两针连接器,一个位于右下角,用于为选框的背光供电,另一个用于位于金属外壳外部的开/关开关。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

以下连接器看起来类似于音频连接器;它是一个四针连接器,其电缆连接到控制面板。我们还没有考虑另一个接口,那就是机柜侧面的 USB 连接器。如果我们将万用表设置为连续性模式,并将该连接器的引脚与机柜侧面的 USB 连接器进行测试,我们发现它们确实已连接。这是我们的外部 USB 连接器。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们已经确定了必须断开的所有连接,以便更好地查看电路板。因此,我们只剩下几件事要检查了。检查 PCB 时,要寻找的一件事是任何未使用的测试垫或过孔;我在下图中突出显示了未使用的标题/垫。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

在上图中,我们可以看到我们有三组不同的未填充的标题或焊盘。在PCB的顶部,我们有三个通孔;过孔用于在PCB的多层之间建立连接。在检查嵌入式系统时,像这样的过孔通常是一个很好的起点,因为它们可能代表开发过程中使用的调试头。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

另一个未填充的由 16 个垫子组成,由一个白色矩形和一个小圆圈表示。这组焊盘可能用于该板上不需要的另一个集成电路。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

最后,最后一组打击垫看起来与用于 USB 和音频的连接器非常相似。在查看未使用的焊盘时,像这样的四引脚连接通常是通过UART进行调试控制台的候选者;我们将在下一节中检查这些标头并讨论 UART。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

检查调试标头

在查看上一节中指出的未知接头时,我通常从测量电压开始。我们可以使用万用表来做到这一点。为了计算这些焊盘上的电压,我们将万用表设置为直流测量模式,并在将黑色探头放在接地点的同时探测感兴趣的位置。引脚测量如下:

电压等级
1 0V
2 0V
3 0V
4 0V

这些线路上没有电压;虽然这令人失望,但并不出乎意料。如果这是有源UART或其他正在传输的数字信号,我们预计会看到一些电压波动形式的活动。让我们继续讨论另一个三针接头。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

电压等级 颜色
1 2.7V 电压 粉红色
2 1.4-3.3V 黄色
3 接地

测量该连接器时,我们的第二个引脚在启动时波动剧烈,然后稳定在 3.3V;请参阅下面的 GIF,了解这些波动的示例:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

回复说明:在搜索串行端口时,您可能并不总是看到这种幅度的电压波动。波动与信号的活跃程度直接相关,这意味着如果流量很少,您将看到很少甚至没有波动。如果您怀疑自己有 UART 接头或某种数字接口,最好使用逻辑分析仪进行检查。

我们看到信号活动的样子(基于电压波动)。接下来,我们将使用逻辑分析器检查此流量。逻辑分析仪帮助我们将这些电压波动转换为人类可读的 1 和 0 序列。为此,我们将使用母-母跳线将逻辑分析仪连接到我们的两个兴趣点,如下所示:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

连接分析仪后,我们将启动 Pulseview 并从下拉菜单中选择我们的分析仪;此设备在 PulseView 中显示为“Saleae Logic”设备。该分析仪的最大捕获速率为24MHz,我们将用于分析。我们还需要指定样本计数,我将其设置为 500G 样本。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们将通过单击“运行”来启动捕获,然后使用这些设置打开机柜的电源。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

成功!我们已经捕获了一些流量;在进一步了解 PulseView 之前,让我们先谈谈 UART 在信号级别上的工作原理。我们已经确认有某种流量通过这些线路传输;接下来,我们需要更多地了解 UART 流量以及如何分析它。

串口

UART 代表 通用异步接收器发送器。UART 是一种双线异步串行协议,允许两个设备进行通信。每一方所需的两条线路是发送 (Tx) 和接收 (Rx) 线路。UART 可用于嵌入式系统上的许多用途,包括与其他处理器通信、传感器通信和调试访问。UART是一种异步协议,这意味着不需要时钟信号。相反,通信双方都预先配置为以特定速度进行通信,称为波特率。波特率以每秒比特数为单位。

UART 数据包/传输由以下字段组成:

位位置 名字 描述
0:1 起始位 用于表示数据包的开始
1:9 数据位(也可以配置为任何值,但通常为 8) 要发送/读取的数据,请注意,数据通常以最低有效位在先发送
9:10 奇偶校验位 如果数据位包含关闭的 0 个,则为 <>,否则为 <>
10:12 停止位 这表示数据包已结束

即使使用上面的数据包定义,我们也很难确定逻辑捕获的内容。幸运的是,Pulseview 有一个我们可以利用的 UART 解码器。

解码 UART 流量

使用 pulseview,我们可以尝试解码此流量,并查看它是否确实是活动的 UART。要设置解码器,请单击下面的绿色和黄色符号。这将打开解码器选择窗口,在搜索栏中键入 uart,然后选择 UART 解码器。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

接下来,我们需要配置 UART 解码器。我们需要选择合适的通道,并设置该解码器所需的任何特定于协议的参数。请参阅下面的可配置参数:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

首先,我们选择我们的 Rx 线作为包含流量的通道;在我们的例子中,这将是.对于所有其他字段,我们将将它们保留为默认值、8 位数据宽度、无奇偶校验等。D1

有一件事我们需要自己调查和填写:波特率。请记住,双方必须提前就波特率达成一致;没有协商/启动顺序。我们需要自己确定这个波特率;否则,解码器将不知道如何正确解析这些信号。要确定波特率,我们可以执行以下操作。

  1. 放大似乎是最小的脉冲之一(据推测,这代表通过线路发送一位)

  2. 使用 Pulseview 中的数据标记选择脉冲宽度,单击下面显示的按钮以启用它们:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

  1. 选择小脉冲范围后,Pulseview 将自动计算频率并以赫兹为单位进行测量,如下图所示。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

赫兹以每秒周期数为单位;回想一下,我们的波特率是每秒比特数的度量。因此,如果我们突出显示了通过导线发送的一位和该脉冲的频率,我们就有了波特率。

我们根据 Pulseview 计算的频率为 115.384 kHz,这意味着波特率为 115385 位/秒。熟悉调试控制台的人可能会注意到,这与常用的波特率 115200 非常接近。因此,让我们将该值插入解码器,看看会发生什么。

如果我们查看下面的屏幕截图,我们可以看到我们有一个看似有效的调试日志。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们有一个活跃的UART并知道它的波特率,但现在我们需要找到一种方法来与它进行交互。为此,我们将使用 Raspberry Pi。更新后的机柜引脚排列如下:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

配置 Raspberry Pi

Raspberry Pi 有多个可用的 UART;我们将使用的UART如下图所示:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们需要确保启用相应的设备树 blob 以启用此 UART。设备树 blob 的用途是为内核提供一种了解可用硬件外围设备的方法。内核将在启动时读取此二进制信息,并枚举指定的外围设备。在对嵌入式 Linux 系统进行逆向工程时,提取此信息可能是有益的,因为它可以被反编译并勾勒出各种外围设备在内存中的位置。

Raspberry Pi 上所有相关的设备树 blob 都可以位于 中。在此文件夹中,您将找到用于多种硬件配置的设备树二进制对象,其中一些用于可以连接到 Pi 的特定帽子(为 Pi 设计的定制 PCB),而另一些则用于启用各种 IO 外围设备。我们可以使用该工具为 UART 外设启用适当的 DTB。/boot/overlays/raspi-config

使用 Raspi 配置

raspi-config是一个用户空间工具,它允许我们配置 Raspberry Pi 的各个方面,其中之一是启用各种外部接口。我们将用于启用 UART 接口;首先启动该工具,如下所示:raspi-config

sudo raspi-config这将导致出现以下屏幕:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

接下来,我们将选择 ,然后如下图所示:Interface OptionsSerial Port

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

选择此选项后,我们将看到两个问题:

  1. 您希望通过串行访问登录 shell 吗?

  2. 是否要启用串行端口硬件?

    • 是的

我们现在已经在 Raspberry Pi 上启用了 UART。接下来,我们需要将其连接到我们的机柜。我们将机柜的 Tx 连接到 Pi 的 Rx,将机柜的 Rx 连接到 Pi 的 Tx:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

UART工具

使用 Raspberry Pi 上的 UART 接口,我们可以尝试连接到目标上的此串行端口。为了与此串行端口进行交互,我们将使用该实用程序。Screen 要求我们在与 UART 接口时向它传递设备和波特率;由于我们知道上一段的波特率,因此我们将按如下方式运行屏幕:screen

sudo screen -L -Logfile cabinet-bootup.log /dev/ttyS0 115200

  • -L -Logfile cabinet-bootup.log- 将会话记录到 ' 文件cabinet-bootup.log

  • /dev/ttyS0- 要使用的串行设备

  • 115200-波特率

现在,我们可以在配置 UART 并启动屏幕后打开机柜电源。当我们打开机柜电源时,我们看到以下内容:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

最终,我们发现自己处于控制台:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

我们现在有一个 root 控制台;我们可以探索文件系统,看看目标上运行了什么,并了解更多关于它是如何构造的。如果可能的话,我们的下一步将是对分区进行映像;让我们先看看挂载了哪些分区:

[root@rk3128:/]# mount
/dev/root on / type squashfs (ro,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=103544k,nr_inodes=25886,mode=755)
proc on /proc type proc (rw,relatime)
devpts on /dev/pts type devpts (rw,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /dev/shm type tmpfs (rw,relatime,size=112248k,nr_inodes=28062,mode=777)
tmpfs on /tmp type tmpfs (rw,relatime,size=112248k,nr_inodes=28062)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,relatime,size=112248k,nr_inodes=28062,mode=755)
sysfs on /sys type sysfs (rw,relatime)
debug on /sys/kernel/debug type debugfs (rw,relatime)
pstore on /sys/fs/pstore type pstore (rw,relatime)
/dev/root on /var type squashfs (ro,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,relatime,size=112248k,nr_inodes=28062,mode=755)
/dev/rkflash0p5 on /userdata type ext2 (rw,relatime)
none on /sys/kernel/config type configfs (rw,relatime)
adb on /dev/usb-ffs/adb type functionfs (rw,relatime)

我们的根文件系统是以只读方式挂载的,并且使用 squashfs 格式。此外,还挂载了另一个标记为 的分区。如果我们检查可用的块设备,我们会看到以下内容:userdata

[root@rk3128:/]# ls -lathr /dev/block/by-name/
lrwxrwxrwx 1 root root 16 Jan 1 00:00 userdata -> ../../rkflash0p5
lrwxrwxrwx 1 root root 16 Jan 1 00:00 uboot -> ../../rkflash0p1
lrwxrwxrwx 1 root root 16 Jan 1 00:00 trust -> ../../rkflash0p2
lrwxrwxrwx 1 root root 16 Jan 1 00:00 rootfs -> ../../rkflash0p4
lrwxrwxrwx 1 root root 16 Jan 1 00:00 boot -> ../../rkflash0p3
drwxr-xr-x 3 root root 380 Jan 1 00:00 ..
drwxr-xr-x 2 root root 140 Jan 1 00:00 .

我们可以看到,我们的 SPI 闪存器件可能位于 。要获取此块设备的图像,我们可以将 U 盘插入机柜的 USB 端口并使用该实用程序。当我们插入 USB 闪存驱动器时,可以使用以下命令使用 SPI 闪存的内容对 USB 驱动器进行映像:/dev/rkflash0dd/dev/sda

sudo dd if=/dev/rkflash0 of=/dev/sda status=progress

如果我们将 USB 驱动器插入 Pi 并检查分区表,我们会看到相应的分区已映像到驱动器!

pi@voidstar:~ $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 57.8G 0 disk
├─sda1 8:1 1 4M 0 part
├─sda2 8:2 1 2M 0 part
├─sda3 8:3 1 9M 0 part
├─sda4 8:4 1 80.8M 0 part
└─sda5 8:5 1 8M 0 part

现在我们有了闪存的备份,这是我们在尝试修改嵌入式系统时应该采取的第一步;在我们尝试修改任何内容或重新刷新任何分区之前,我们应该确保我们有办法恢复它们;为此,我们将调查引导加载程序。首先,让我们注意启动日志开头的以下行:

CLK: (uboot. arm: enter 600000 KHz, init 600000 KHz, kernel 0N/A)
apll 600000 KHz
dpll 600000 KHz
cpll 650000 KHz
gpll 594000 KHz
armclk 600000 KHz
aclk_cpu 148500 KHz
hclk_cpu 74250 KHz
pclk_cpu 74250 KHz
aclk_peri 148500 KHz
hclk_peri 74250 KHz
pclk_peri 74250 KHz
Net: Net Initialization Skipped
No ethernet found.
Hit key to stop autoboot('CTRL+C'): 0

如果我们在为机柜通电时按住屏幕提示,我们会看到以下内容:Ctrl-c

Hit key to stop autoboot('CTRL+C'):  0
=> <INTERRUPT>

我们现在有一个 UBoot 提示符;在我们进一步深入研究之前,让我们先谈谈 UBoot 及其工作原理。

UBoot的

UBoot 是嵌入式系统中常用的开源引导加载程序。它支持多种架构和 CPU 类型。但是,UBoot 的职责通常是加载操作系统内核或嵌入式系统的主应用程序。

UBoot 还包括在逆向工程工作中有用的调试实用程序;最值得注意的是 UBoot 命令提示符。

UBoot 命令

UBoot 控制台可以包含大量内置实用程序。这些实用程序可以在标准启动过程中(通常通过环境变量)或在 UBoot 命令行中使用。可用的命令将根据 UBoot 映像的构建方式而有所不同。

现在我们已经发现了一个 UBoot 控制台,让我们首先通过运行help command

  android_print_hdr base bdinfo bidram_dump BMP boot boot_android bootavb
bootd bootm bootp bootrkp bootz charge cmp coninfo cp crc32 ...
=>
? - alias for 'help'
android_print_hdr- print android image header
base - print or set address offset
bdinfo - print Board Infostructure
bidram_dump- Dump bidram layout
BMP - manipulate BMP image data
boot - boot default, i.e., run 'bootcmd'
boot_android- Execute the Android Bootloader flow.
bootavb - Execute the Android avb a/b boot flow.
bootd - boot default, i.e., run 'bootcmd'
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootrkp - Boot Linux Image from rockchip image type
Bootz - boot Linux zImage image from memory
charge - Charge display
CMP - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
DHCP - boot image via network using DHCP/TFTP protocol
dm - Driver model low level access
download- enter rockusb/bootrom download mode
dtimg - manipulate dtb/dtbo Android image
dump_atags- Dump the content of the atags
dump_irqs- Dump IRQs
echo - echo args to console
editenv - edit environment variable
env - environment handling commands
exit - exit script
ext2load- load binary file from a Ext2 filesystem
ext2ls - list files in a directory (default /)
ext4load- load binary file from a Ext4 filesystem
ext4ls - list files in a directory (default /)
ext4size- determine a file's size
false - do nothing, unsuccessfully
fastboot- use USB or UDP Fastboot protocol
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fantasize - determine a file's size
fatwrite- write file into a dos filesystem
fdt - flattened device tree utility commands
fstype - Lookup a filesystem type
go - start application at address 'addr'
gpt - GUID Partition Table
help - print command description/usage
iomem - Show iomem data by device compatible(high priority) or node name
lcdputs - print string on video framebuffer
load - load binary file from a filesystem
loop - infinite loop on address range
ls - list files in a directory (default /)
MD - memory display
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC subsystem
mmcinfo - display MMC info
MW - memory write (fill)
NFS - boot image via network using NFS protocol
nm - memory modify (constant address)
part - disk partition related commands
ping - Send ICMP ECHO_REQUEST to network host
printenv- print environment variables
pxe - commands to get and boot from pxe files
rbrom - Perform RESET of the CPU
reboot - Perform RESET of the CPU, alias of 'reset'
reset - Perform RESET of the CPU
rkimgtest- Test if storage media have rockchip image
rknand - rockchip nand flash sub-system
rksfc - rockchip SFC sub-system
rktest - Rockchip board modules test
rockchip_show_bmp- load and display BMP from resource partition
rockchip_show_logo- load and display log from resource partition
rocks - Use the rockusb Protocol
run - run commands in an environment variable
save - save file to a filesystem
setcurs - set cursor position within screen
setenv - set environment variables
showvar - print local hushshell variables
size - determine a file's size
source - run script from memory
sysboot - command to get and boot from syslinux files
sysmem_dump- Dump system layout
sysmem_search- Search a available system region
test - minimal test like /bin/sh
TFTP - download image via network using TFTP protocol
true - do nothing, successfully
ums - Use the UMS [USB Mass Storage]
USB - USB sub-system
usbboot - boot from USB device
version - print monitor, compiler, and linker version
=>

在我们花太多时间查看每个命令之前,让我们重新审视一下我们的主要目标,即能够从引导加载程序中读取和写入根文件系统分区,以防我们以后需要恢复这个机柜。以下命令立即脱颖而出,因为它们涉及内存读取和写入:

download- enter rockusb/bootrom download mode
dtimg - manipulate dtb/dtbo Android image
dump_atags- Dump the content of the atags
ext2load- load binary file from a Ext2 filesystem
ext2ls - list files in a directory (default /)
ext4load- load binary file from a Ext4 filesystem
ext4ls - list files in a directory (default /)
ext4size- determine a file's size
fastboot- use USB or UDP Fastboot protocol
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fantasize - determine a file's size
fatwrite- write file into a dos filesystem
mm - memory modify (auto-incrementing address)
rknand - rockchip nand flash sub-system
rksfc - rockchip SFC sub-system
ums - Use the UMS [USB Mass Storage]
USB - USB sub-system

接下来,我们可以使用命令查看此引导加载程序配置的环境变量。这将为我们提供更多关于该平台如何启动、正在使用的内存地址以及可能可用的其他接口的更多上下文。printenv

UBoot 环境变量

在为设备构建或配置 UBoot 映像时,可以配置各种环境变量。这些环境变量控制启动时执行的操作。有多种方法可以存储这些变量。有时它们被硬编码到二进制文件本身中;它们还可以驻留在闪存分区上,允许用户从 UBoot 提示符修改它们。

我们可以使用以下命令检查环境变量:printenv

=> printenv
arch=arm
baudrate=115200
board=evb_rk3128
board_name=evb_rk3128
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_net_usb_start=usb start
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc1 mmc0 rknand0 usb0 pxe dhcp
bootargs=storagemedia=nand androidboot.storagemedia=nand androidboot.mode=normal
bootcmd=boot_android ${devtype} ${devnum};bootrkp;run distro_bootcmd;
bootcmd_dhcp=run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_rknand0=setenv devnum 0; run rknand_boot
bootcmd_usb0=setenv devnum 0; run usb_boot
bootdelay=0
cpu=armv7
devnum=0
devtype=spinand
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
ethaddr=d2:79:07:fc:f7:89
fdt_addr1_r=0x61700000
fdt_addr_r=0x68300000
kernel_addr1_r=0x62008000
kernel_addr_r=0x62008000
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
pxefile_addr1_r=0x60600000
pxefile_addr_r=0x60600000
ramdisk_addr1_r=0x63000000
ramdisk_addr_r=0x6a200000
rkimg_bootdev=if mmc dev 1 && rkimgtest mmc 1; then setenv devtype mmc; setenv devnum 1; echo Boot from SDcard;elif mmc dev 0; then setenv devtype mmc; setenv devnum 0;elif mtd_blk dev 0; then setenv devtype mtd; setenv devnum 0;elif mtd_blk dev 1; then setenv devtype mtd; setenv devnum 1;elif mtd_blk dev 2; then setenv devtype mtd; setenv devnum 2;elif rknand dev 0; then setenv devtype rknand; setenv devnum 0;elif rksfc dev 0; then setenv devtype spinand; setenv devnum 0;elif rksfc dev 1; then setenv devtype spinor; setenv devnum 1;fi;
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x60500000
scriptaddr1=0x60500000
serial#=c3d9b8674f4b94f6
soc=rockchip
stderr=serial,vidconsole
stdout=serial,vidconsole
usb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fi
vendor=rockchip

Environment size: 3477/32764 bytes
=>

我想在下表中指出一些有趣的变量:

变量 意义
bootcmd 此命令用于定义默认引导行为
board=evb_rk3128 This identifies the CPU / development board in use
devtype=spinand 这定义了正在使用的闪存设备类型

在这一点上,如果我们交叉引用我们在硬件审查期间收集的信息,我们会看到一致的结果。在硬件审查之后,我们假设 SPI 闪存是主要的存储方法,这一假设正在 UBoot 环境变量和可用命令中得到验证。

让我们从检查命令开始;快速的谷歌搜索告诉我们,这是瑞芯微的SPI SFC(串行闪存控制器)接口工具。此命令具有以下可用的子命令:rksfc

=> rksfc
rksfc - rockchip sfc sub-system

Usage:
rksfc scan - scan Sfc devices
rksfc info - show all available Sfc devices
rksfc device [dev] - show or set current Sfc device
dev 0 - spinand
dev 1 - spinor
rksfc part [dev] - print partition table of one or all Sfc devices
rksfc read addr blk# cnt - read `cnt' blocks starting at block
`blk#' to memory address `addr'
rksfc write addr blk# cnt - write `cnt' blocks starting at block
`blk#' from memory address `addr'

我们可以通过以下命令获取有关 SPI 闪存的信息:

=> rksfc scan
=> rksfc info
Device 0: Vendor: 0x0308 Rev: V1.00 Prod: rkflash-SpiNand
Type: Hard Disk
Capacity: 107.7 MB = 0.1 GB (220672 x 512)
=> rksfc device 0

Device 0: Vendor: 0x0308 Rev: V1.00 Prod: rkflash-SpiNand
Type: Hard Disk
Capacity: 107.7 MB = 0.1 GB (220672 x 512)
... is now current device
=> rksfc part 0

Partition Map for SPINAND device 0 -- Partition Type: EFI

Part Start LBA End LBA Name
Attributes
Type GUID
Partition GUID
1 0x00002000 0x00003fff "uboot"
attrs: 0x0000000000000000
type: ea450000-0000-424f-8000-0cd500004c0a
guid: 325b0000-0000-4d21-8000-6e10000051c5
2 0x00004000 0x00004fff "trust"
attrs: 0x0000000000000000
type: b44a0000-0000-4121-8000-4e1600002902
guid: 62500000-0000-4f7f-8000-4a7800006ca1
3 0x00005000 0x000097ff "boot"
attrs: 0x0000000000000000
type: 6c1e0000-0000-4833-8000-5d07000065c4
guid: 442c0000-0000-4c4c-8000-2ed400005ecb
4 0x00009800 0x00031dff "rootfs"
attrs: 0x0000000000000000
type: 9b050000-0000-4e44-8000-5f3000007157
guid: 614e0000-0000-4b53-8000-1d28000054a9
5 0x00031e00 0x00035dde "userdata"
attrs: 0x0000000000000000
type: c8050000-0000-4b18-8000-3b1a000041c3
guid: 40780000-0000-493e-8000-688900001525
=>

使用这些命令,我们可以了解有关 SPI 闪存的更多信息。我们可以看到块大小为 512,芯片总共包含 220672 (0x35E00) 块,分为五个分区:

  • uboot - 可能包含我们的 UBoot 映像/第一阶段引导加载程序

  • trust - 可信执行环境镜像

  • boot - 内核镜像 / ramdisk

  • rootfs - 我们最大的分区,内核的根文件系统

  • 用户数据 - 特定于用户的数据,可能用于高分、用户设置等

请注意,此数据与我们之前在根控制台提示符中看到的数据相匹配。我们现在了解了闪存是如何分区的,以及可能有哪些数据可用,但是我们如何在不将额外线路焊接到电路板上的情况下读取/写入这些数据呢?如果我们检查该命令,我们会看到以下内容:usb

=> usb
usb - USB sub-system

Usage:
usb start - start (scan) USB controller
usb reset - reset (rescan) USB controller
usb stop [f] - stop USB [f]=force stop
usb tree - show USB device tree
usb info [dev] - show available USB devices
usb test [dev] [port] [mode] - set USB 2.0 test mode
(specify port 0 to indicate the device's upstream port)
Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]
usb storage - show details of USB storage devices
usb dev [dev] - show or set current USB storage device
usb part [dev] - print partition table of one or all USB storage devices
usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'
to memory address `addr'
usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'
from memory address `addr'

使用机柜侧面的 USB 端口,如果我们插入设备并运行,则生成以下输出:USB startUSB info

=> usb start
starting USB...
Bus usb@10180000: Bus usb@101c0000: USB EHCI 1.00
Bus usb@101e0000: USB OHCI 1.0
scanning bus usb@10180000 for devices... 1 USB Device(s) found
scanning bus usb@101c0000 for devices... RKPARM: Invalid parameter part table
2 USB Device(s) found
scanning bus usb@101e0000 for devices... 1 USB Device(s) found
scanning usb for storage devices... 1 Storage Device(s) found
=> usb info
1: Hub, USB Revision 1.10
- U-Boot Root Hub
- Class: Hub
- PacketSize: 8 Configurations: 1
- Vendor: 0x0000 Product 0x0000 Version 0.0
Configuration: 1
- Interfaces: 1 Self Powered 0mA
Interface: 0
- Alternate Setting 0, Endpoints: 1
- Class Hub
- Endpoint 1 In Interrupt MaxPacket 2 Interval 255ms

1: Hub, USB Revision 2.0
- u-boot EHCI Host Controller
- Class: Hub
- PacketSize: 64 Configurations: 1
- Vendor: 0x0000 Product 0x0000 Version 1.0
Configuration: 1
- Interfaces: 1 Self Powered 0mA
Interface: 0
- Alternate Setting 0, Endpoints: 1
- Class Hub
- Endpoint 1 In Interrupt MaxPacket 8 Interval 255ms

2: Mass Storage, USB Revision 2.10
- USB DISK 3.0 0719146D1CBF9257
- Class: (from Interface) Mass Storage
- PacketSize: 64 Configurations: 1
- Vendor: 0x13fe Product 0x6300 Version 1.0
Configuration: 1
- Interfaces: 1 Bus Powered 498mA
Interface: 0
- Alternate Setting 0, Endpoints: 2
- Class Mass Storage, Transp. SCSI, Bulk only
- Endpoint 1 In Bulk MaxPacket 512
- Endpoint 2 Out Bulk MaxPacket 512

1: Hub, USB Revision 1.10
- U-Boot Root Hub
- Class: Hub
- PacketSize: 8 Configurations: 1
- Vendor: 0x0000 Product 0x0000 Version 0.0
Configuration: 1
- Interfaces: 1 Self Powered 0mA
Interface: 0
- Alternate Setting 0, Endpoints: 1
- Class Hub
- Endpoint 1 In Interrupt MaxPacket 2 Interval 255ms

太好了,我们可以看到 USB 堆栈成功枚举并检测到我们的大容量存储设备。

在继续之前,让我们回顾一下我们对 UBoot 环境的了解:

  1. 检查环境变量为我们提供了RAM中的可用地址

  2. 使用该实用程序,我们可以将 SPI 闪存扇区读取到 RAM 中rksfc read

  3. 使用 USB 命令,我们可以枚举 USB 设备并写入它

我们可以将 SPI 闪存读入 RAM,连接 USB 设备,然后使用命令将 SPI 闪存数据写入 USB 设备。如果此方法有效,我们还应该能够通过反转步骤,从USB驱动器读取数据,然后使用将其写回闪存来恢复闪存图像。让我们从测试读取开始。USB writerksfc write

首先,我们将尝试使用以下命令将整个 SPI 闪存读取到 RAM 中,对于我们的目标地址,我们将尝试存储其中的地址:$ramdisk_addr_r0x6a200000

=> rksfc read $ramdisk_addr_r 0 0x35E00

spinand read: device 0 block # 0, count 220672 ... undefined instruction
pc : ce695528 lr : 1fadca4d
sp : 6be17bd8 ip : 00000020 fp : 60093204
r10: 00004254 r9 : 6be1bdf8 r8 : ad758c77
r7 : ebaa79cb r6 : b052b720 r5 : 36395b84 r4 : f3a911be
r3 : 780fb750 r2 : 00000000 r1 : 600a62fc r0 : 200a226c
Flags: nZcv IRQs on FIQs off Mode SVC_32


Call trace:
PC: [< ce695528 >]
LR: [< 1fadca4d >]

Stack:
[< ce695528 >]

Copy info from "Call trace..." to a file(eg. dump.txt), and run
command in your U-Boot project: ./scripts/stacktrace.sh dump.txt

Resetting CPU ...

### ERROR ### Please RESET the board ###

这没有用;我们以某种方式触发了未定义的指令异常。我们可能在UBoot正在利用的过程中搞砸了一些东西;让我们看看当我们尝试另一个内存较低的地址时会发生什么:

=> rksfc read $scriptaddr 0 0x35E00

spinand read: device 0 block # 0, count 220672 ... 220672 blocks read: OK

移动到RAM中较低的地址可以完成读取,而不会造成任何干扰;让我们看看现在是否可以将此数据写回 USB 驱动器:

usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'
from memory address `addr'
=> usb write $scriptaddr 0 0x35E00

usb write: device 0 block # 0, count 220672 ... 220672 blocks written: OK
=>

现在让我们通过将其插入 Raspberry Pi 来查看此驱动器的内容,看看我们有什么:

pi@voidstar:~ $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 57.8G 0 disk
├─sda1 8:1 1 4M 0 part
├─sda2 8:2 1 2M 0 part
├─sda3 8:3 1 9M 0 part
├─sda4 8:4 1 80.8M 0 part
└─sda5 8:5 1 8M 0 part

在这里,我们可以看到 USB 驱动器上的分区表与命令输出中显示的内容相匹配。接下来,我们将使用该实用程序提取各种分区进行分析。rksfc part 0dd

pi@voidstar:~/marvel-cab/parts $ sudo dd if=/dev/sda1 of=part1.bin
4194304 bytes (4.2 MB, 4.0 MiB) copied, 0.18225 s, 23.0 MB/s
pi@voidstar:~/marvel-cab/parts $ sudo dd if=/dev/sda2 of=part2.bin
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.109297 s, 19.2 MB/s
pi@voidstar:~/marvel-cab/parts $ sudo dd if=/dev/sda3 of=part3.bin
9437184 bytes (9.4 MB, 9.0 MiB) copied, 0.386968 s, 24.4 MB/s
pi@voidstar:~/marvel-cab/parts $ sudo dd if=/dev/sda4 of=part4.bin
84672512 bytes (85 MB, 81 MiB) copied, 2.96481 s, 28.6 MB/s
pi@voidstar:~/marvel-cab/parts $ sudo dd if=/dev/sda5 of=part5.bin
8371712 bytes (8.4 MB, 8.0 MiB) copied, 0.314125 s, 26.7 MB/s
pi@voidstar:~/marvel-cab/parts $ file *
part1.bin: data
part2.bin: data
part3.bin: Android bootimg, kernel (0x10008000), second stage (0x10f00000), page size: 2048
part4.bin: Squashfs filesystem, little endian, version 4.0, xz compressed, 71663599 bytes, 1185 inodes, blocksize: 131072 bytes, created: Mon May 31 09:06:53 2021
part5.bin: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=42185cbc-b698-4af6-8a47-e444e5635787, volume name "userdata" (large files)

到目前为止,此数据与我们在查看正在运行的系统上的挂载输出和 UBoot 菜单中的分区表时看到的数据相匹配。因此,我们可以提取 squashfs 分区 via 并尝试挂载 ext2 分区以确认它们有效:unsquashfs

pi@voidstar:~/marvel-cab/parts $ unsquashfs part4.bin
Parallel unsquashfs: Using four processors
1029 inodes (1792 blocks) to write
create_inode: could not create character device squashfs-root/dev/console, because you're not superuser!
created 596 files
created 157 directories
created 431 symlinks
created 0 devices
created 0 fifos
pi@voidstar:~/marvel-cab/parts $ ls squashfs-root/
bin busybox.config data dev etc lib lib32 linuxrc media misc mnt moo OEM opt proc root run sbin sdcard sys timestamp tmp udisk userdata usr var
pi@voidstar:~/marvel-cab/parts $ ls squashfs-root/moo/
docs logo.mp4 MOO MOO-Ship-MIME_CCADE_MSH_2P-BRK01 SKUShell.MIME_CCADE_SF2_2P.19.exe start.sh _ui assets

看起来我们有一个有效的根文件系统,我们现在可以开始对软件进行逆向工程,并了解更多关于如何修改这个系统以玩其他游戏或运行自定义固件的信息。

现在我们已经确认可以读取闪存,让我们测试一下,看看是否可以使用上述方法将此图像写回闪存:

 => usb read  $scriptaddr 0 0x35E00
usb read: device 0 block # 0, count 220672 ... 220672 blocks read: OK
=> rksfc write $scriptaddr 0x35E00 0
spinand write: device 0 block # 0, count 220672 ... 220672 blocks written: OK

现在我们重新启动,希望我们重新刷新的映像仍然有效。

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

成功!我们现在可以使用 USB 驱动器从 UBoot 读取/写入 SPI 闪存;这对于测试补丁和固件修改很有用!

现在我们可以用UBoot读/写这个机柜的闪存,如果我们能自动擦除单个分区和闪存段,而不需要每次都手动输入范围,那就太好了。为此,我们将使用 Depthcharge 实用程序来自动化我们的 UBoot 交互!

使用 Depthcharge 编写 UBoot 脚本

在使用 UBoot 环境时,我们经常需要自动化我们的交互。例如,在我们的例子中,我们可能希望自动覆盖特定的闪存分区,而不必每次都手动输入地址偏移量。幸运的是,NCC集团的人们已经整合了一个工具来帮助我们解决这个问题,称为深水炸弹。我们可以使用它来自动读取闪存芯片和外部 USB 驱动器的数据。我们的脚本需要执行以下操作:

  1. 连接到 UART 并识别 UBoot 提示符

  2. 使用以下命令读取和写入 SPI 闪存rksfc read/write

  3. 使用命令读取和写入 USB 驱动器USB read/write

首先,我们需要安装模块;我们可以通过执行以下命令在 Pi 上安装:.odepthchargesudo pip install depthcharge

连接到 UART 并识别 UBoot 提示符

我们可以使用以下 python 代码连接到我们的 UBoot 提示符:

def console_setup():
console=Console("/dev/ttyS0",baudrate=115200)
ctx = Depthcharge(console,arch="arm")
return ctx

在上面的函数中,我们正在创建一个对象,它要求我们提供串行端口和波特率的路径。然后,此控制台对象用于创建 Depthcharge 上下文,我们将使用它来访问 Depthcharge 必须提供的功能。深水装药文档有一个有据可查的示例,并详细描述了设置过程。Console

通过深度充电进行闪存读取和写入

现在我们已经连接到我们的接口,我们需要实现读取和写入命令。我们可以使用 depthcharge 的 API 来做到这一点。此 API 调用允许我们生成 UBoot 命令并将其发送到命令提示符并返回响应。在下面的示例中,我们在变量中构造读取命令并确保参数格式正确,然后使用 API 发出命令。rksfcsend_command()cmd_strsend_command()



def rksfc_read(ctx,dest_addr,src_addr,size):
cmd_str = f"rksfc read 0x{dest_addr:02x} 0x{src_addr:02x} 0x{size:02x}"
resp = ctx.send_command(cmd_str)
return resp

def rksfc_write(ctx,dest_addr,src_addr,size):
cmd_str = f"rksfc write 0x{dest_addr:02x} 0x{src_addr:02x} 0x{size:02x}"
resp = ctx.send_command(cmd_str)
time.sleep(10)
return resp

我们现在已经实现了闪存读取和写入,接下来我们需要枚举 USB 堆栈,然后从闪存驱动器读取/写入。

通过深度充电进行 USB 读写

与实现命令的方式类似,我们接下来将实现这些命令。该过程将类似于用于命令的过程:rksfcusbrksfc


'''
usb_setup
This script is used to enumerate and set up the USB port
'''

def usb_setup(ctx,reset=False):
resps = []
if not reset:
resp = ctx.send_command("usb start")
else:
resp = ctx.send_command("usb reset")
resps.append(resp)
resp = ctx.send_command("usb storage")
resps.append(resp)
resp = ctx.send_command("usb dev 0")
resps.append(resp)
return resps

'''
USB write addr blk# cnt - write `cnt' blocks starting at block `blk#'
from memory address `addr'
'''

def usb_raw_write(ctx,source_addr,block,size):
cmd = f"usb write 0x{source_addr:x} 0x{block:x} 0x{size:x}"
resp = ctx.send_command(cmd)
return resp

'''
USB read addr blk# cnt - read `cnt' blocks starting at block `blk#'
to memory address `addr'
'''

def usb_raw_read(ctx,source_addr,block,size):
cmd = f"usb read 0x{source_addr:x} 0x{block:x} 0x{size:x}"
resp = ctx.send_command(cmd)
return resp

用 Depthcharge 倾倒闪光灯

现在我们已经定义了适当的函数,我们可以尝试以下操作:

if __name__ == "__main__":
log.info("Marvel Super Heroes Depthcharge Test...")
ctx = console_setup()
usb_setup(ctx,reset=False)
# Read the SPI flash into RAM
rksfc_read(ctx,TARGET_RAM_ADDR,0,0x35E00)
log.info("Flash read into RAM")
# Write the data from RAM to a USB drive
usb_raw_write(ctx,TARGET_RAM_ADDR,0,0x35E00)
log.info("Flash written to USB")

如果我们运行此脚本,我们会看到以下输出:

pi@voidstar:~/marvel-cab/scripts $ python3 mvc.py
[+] Marvel Super Heroes Deptcharge Test...
[*] Using default payload base address: ${loadaddr} + 32MiB
[*] No user-specified prompt provided. Attempting to determine this.
[*] Identified prompt: =>
[*] Retrieving command list via "help"
[*] Reading environment via "printenv"
[!] Disabling payload deployment and execution due to error(s).
[*] Version: U-Boot 2017.09-g4857df5-dirty #lzy (Mar 24 2021 - 16:18:22 +0800)
[*] Enumerating available MemoryWriter implementations...
[*] Available: CpMemoryWriter
[*] Available: CRC32MemoryWriter
[*] Excluded: I2CMemoryWriter - Command "i2c" required but not detected.
[*] Excluded: LoadbMemoryWriter - Command "loadb" required but not detected.
[*] Excluded: LoadxMemoryWriter - Command "loadx" required but not detected.
[*] Excluded: LoadyMemoryWriter - Command "loady" required but not detected.
[*] Available: MmMemoryWriter
[*] Available: MwMemoryWriter
[*] Available: NmMemoryWriter
[*] Enumerating available MemoryReader implementations...
[!] Excluded: CpCrashMemoryReader - Operation requires crash or reboot, but opt-in not specified.
[*] Available: CRC32MemoryReader
[!] Excluded: GoMemoryReader - Payload deployment+execution opt-in not specified
[*] Excluded: I2CMemoryReader - Command "i2c" required but not detected.
[*] Excluded: ItestMemoryReader - Command "itest" required but not detected.
[*] Available: MdMemoryReader
[*] Available: MmMemoryReader
[*] Excluded: SetexprMemoryReader - Command "setexpr" required but not detected.
[*] Enumerating available Executor implementations...
[!] Excluded: GoExecutor - Payload deployment+execution opt-in not specified
[*] Enumerating available RegisterReader implementations...
[!] Excluded: CpCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: CRC32CrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: FDTCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: ItestCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: MdCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: MmCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: NmCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] Excluded: SetexprCrashRegisterReader - Operation requires crash or reboot, but opt-in not specified.
[!] No default RegisterReader available.
[+] spinand read: device 0 block # 0, count 220672 ...
[+] Flash read into RAM
[+] => usb write 0x61700000 0x0 0x35e00

usb write: device 0 block # 0, count 220672 ...
[+] Flash written to USB

当我们将 U 盘插入 Pi 时,我们会看到以下分区:

pi@voidstar:~/marvel-cab/scripts $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 57.8G 0 disk
├─sda1 8:1 1 4M 0 part
├─sda2 8:2 1 2M 0 part
├─sda3 8:3 1 9M 0 part
├─sda4 8:4 1 80.8M 0 part
└─sda5 8:5 1 8M 0 part

成功!我们已使用 Depthcharge 将 SPI 闪存提取到 USB 设备!

文件系统内容

现在我们有了一种可靠的方法来读取和写入闪存,让我们简要检查一下内容。有趣的文件位于文件夹中。此文件夹包含模拟器及其相关资产。Moo 是一个使用自定义 ROM 格式的自定义模拟器;2020 年,一些研究人员在不同版本的模拟器上做了出色的工作。但是,如果我们查看目录内容,就会发现一些有趣的事情:/moo

pi@voidstar:~/marvel-cab/parts/squashfs-root/moo $ file *
docs: symbolic link to ../userdata
logo.mp4: ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
MOO: symbolic link to MOO-Ship-MIME_CCADE_MSH_2P-BRK01
MOO-Ship-MIME_CCADE_MSH_2P-BRK01: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 4.4.0, stripped
SKUShell.MIME_CCADE_SF2_2P.19.exe: PE32+ executable (GUI) x86-64, for MS Windows
start.sh: POSIX shell script, ASCII text executable
_ui: directory
zassets: directory

这个系统上肯定不可能有 PE32 Windows 可执行文件,对吧?好吧,如果我们将此文件复制到 Windows 机器上并尝试执行它:

物联网安全 - 21通过 UBoot 发现 UART 和固件提取

它运行!显然,这是一个作者没有意识到在系统上的构建工件;他在Twitter上联系了我,我们就逆向工程/仿真进行了很好的对话。

通过本文中概述的方法,我们现在可以使用 UBoot 在 USB 驱动器中读取和写入 SPI 闪存。我们已经提取了根文件系统并确定了核心仿真组件。我们的下一步将是对此目标上的一些二进制文件进行逆向工程,以确定运行自定义固件的难度。

结论

在这篇博文中,我们回顾了如何使用我们的万用表/逻辑分析仪对嵌入式设备进行初始拆解并识别潜在的调试接头。然后,我们回顾了如何分析未知的 UART 流量并使用 Raspberry Pi 连接到串行端口。连接到串口后,我们发现可以通过按 访问 UBoot 控制台。在查看了 UBoot 控制台后,我们编写了一个 depthcharge 脚本,将每个 SPI 闪存分区提取到外部闪存驱动器。

原文始发于微信公众号(安全狗的自我修养):物联网安全 – 21通过 UBoot 发现 UART 和固件提取

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月15日15:38:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   物联网安全 - 21通过 UBoot 发现 UART 和固件提取http://cn-sec.com/archives/2190042.html

发表评论

匿名网友 填写信息