嵌入式系统架构:ARM/MIPS/RISC-V 基础

做固件安全这么多年,我接触过各种各样的芯片架构。说实话,ARM、MIPS、RISC-V 这三兄弟,基本覆盖了市面上 90% 以上的嵌入式设备。你想想看,从你手里的路由器,到车上的 ECU,再到智能电表,跑的都是这些架构。

我个人习惯把架构理解成「芯片的方言」。指令集就是语法,寄存器就是词汇。搞懂这些,你才能读懂固件在说什么。

ARM 架构:移动端的霸主

ARM 是精简指令集(RISC)的代表。它最大的特点是低功耗。我在分析 IoT 设备固件时,十有八九都是 ARM 核。

ARM 有几种工作模式,我重点说两个:

  • Thumb 模式:指令长度 16 位,代码密度高。很多固件为了省空间,会混用 ARM 和 Thumb 指令。反汇编时如果模式搞错了,那代码就是一团乱码。
  • 异常向量表:复位、中断、数据中止等异常都有固定入口。我曾经遇到一个固件,攻击者就是通过篡改异常向量表来实现持久化控制的。
避坑指南:ARM 的 PC 寄存器(R15)在调试时有个坑——它指向的是当前指令 +8 的地址。很多新手在这里栽跟头,调试半天发现跳转地址不对。

MIPS 架构:网络设备的常客

MIPS 在路由器、交换机里特别常见。它的设计哲学和 ARM 不太一样。

MIPS 有个特点让我印象很深——延迟槽。什么意思呢?就是分支指令后面的那条指令,无论分支是否跳转,都会被执行。我刚开始逆向 MIPS 固件时,经常被这个特性搞晕。

举个例子:

beq $a0, $a1, target  # 如果相等则跳转
addi $v0, $zero, 1     # 这条指令一定会执行!

嗯,这里要注意。如果你在漏洞利用时构造 ROP 链,延迟槽里的指令必须放有效操作,否则程序会崩溃。

RISC-V:开源的新势力

RISC-V 是后起之秀。我最近两年在不少国产芯片上看到了它的身影。它的指令集非常简洁,基础指令只有 40 多条。

RISC-V 的寄存器命名很直观:

寄存器别名用途
x0zero恒为 0,写入无效
x1ra返回地址
x2sp栈指针
x10-x17a0-a7函数参数

我个人觉得 RISC-V 的生态还在成长中。但它的模块化设计(RV32I、RV64I、M 扩展等)让芯片厂商可以按需裁剪,这对安全分析来说,意味着每个芯片的指令集子集都可能不同。

内存映射与寄存器

嵌入式系统的内存布局,说白了就是一张「地址分配表」。CPU 通过地址总线访问外设,本质上和访问内存没有区别——这就是内存映射 I/O

我见过太多固件漏洞,根源就是开发者没搞清楚内存映射。比如:

  • 把只读区域当成了可写
  • 访问了未映射的地址空间,触发总线错误
  • 外设寄存器的地址算错了
我的经验:拿到一个固件后,第一件事就是看它的链接脚本(Linker Script)。那里清清楚楚地标明了 Flash、RAM、外设寄存器的地址范围。这是逆向分析的「地图」。

寄存器是 CPU 和外设沟通的「信箱」。每个外设都有一组控制/状态/数据寄存器。比如 UART 的收发寄存器,你往里面写数据就是发送,读数据就是接收。

我曾经在分析一个蓝牙芯片时,发现它的某个控制寄存器有隐藏位。数据手册没写,但固件里确实在操作。后来我通过动态调试才确认,那是厂商预留的测试模式入口。

外设通信协议:I2C / SPI / UART

这三个协议是嵌入式系统的「三驾马车」。搞固件安全,不懂它们怎么行?

I2C:两线制,多设备

I2C 只有两根线:SCL(时钟)和 SDA(数据)。所有设备挂在同一条总线上,通过地址区分。

我遇到过一种攻击方式:通过注入额外的时钟脉冲,让 I2C 从设备的状态机跑飞,从而读取到不该访问的内存区域。

SPI:四线制,高速

SPI 有四个信号:SCK、MOSI、MISO、CS。它是全双工的,速度比 I2C 快很多。

SPI 的 CS(片选)信号很关键。如果固件在切换设备时没有正确拉高 CS,就可能造成总线冲突。我在一个存储设备上就见过这种问题,导致数据被写乱。

UART:异步串口

UART 是最简单的通信方式。没有时钟线,靠波特率同步。三根线:TX、RX、GND。

UART 是固件调试的「生命线」。很多设备会通过 UART 输出调试信息,甚至暴露一个 shell。我建议你拿到设备后,第一件事就是找 UART 接口——往往就在 PCB 上的排针或测试点上。

注意:UART 的波特率如果不匹配,收到的就是乱码。常见的波特率有 9600、115200、921600。我一般从 115200 开始试,不行再换。

中断与 DMA 机制

中断是 CPU 响应外部事件的「门铃」。没有中断,CPU 就得不停地轮询——那效率太低了。

中断处理流程大致是:

  1. 外设产生中断信号
  2. CPU 保存当前上下文(PC、寄存器等)
  3. 跳转到中断向量表对应的处理函数
  4. 执行中断服务程序(ISR)
  5. 恢复上下文,返回

我在分析固件时,特别关注中断向量表。如果攻击者能篡改它,就能劫持所有中断。这是一种很隐蔽的持久化手段。

DMA 是「数据搬运工」。它不需要 CPU 参与,就能在外设和内存之间搬数据。这对高速外设(比如网卡、显示控制器)特别有用。

DMA 有个安全隐患:如果 DMA 的源地址或目的地址可以被用户控制,攻击者就能实现任意内存读写。我见过一个案例,攻击者通过操控 DMA 描述符,绕过了系统的权限检查。

核心要点:中断和 DMA 都是「异步」的。这意味着它们可能在 CPU 执行任何指令时发生。如果 ISR 或 DMA 回调函数里有竞态条件,那就是漏洞的温床。

本章知识体系

下面这张图,是我梳理的本章核心逻辑。你可以把它当作一张「思维导图」来用。

嵌入式系统架构 CPU 架构 ARM MIPS RISC-V 内存与外设 内存映射 寄存器 通信协议 I2C SPI UART 中断与 DMA 中断向量表 DMA 控制器 竞态条件 图:嵌入式系统架构知识体系

这张图把本章内容串起来了。你看,CPU 架构是核心,内存映射和寄存器是基础,通信协议是「手脚」,中断和 DMA 是「神经系统」。搞懂这些,你就有了分析固件的基本功。

学习建议:别急着背指令集。先找一块开发板,用调试器看看它的内存布局,再写个简单的 UART 收发程序。动手做一遍,比看十遍书都管用。

好了,这一章就到这里。记住,嵌入式系统的本质就是「CPU + 内存 + 外设」。后面的章节,我们会在这个基础上,深入固件的逆向分析和漏洞挖掘。

专注资料整理