2. 嵌入式系统硬件基础:微控制器架构、存储器层次与外设总线的安全考量

大家好,我是老周。今天咱们聊聊嵌入式系统的硬件基础。很多人觉得硬件安全就是加个加密芯片,其实远没那么简单。我见过太多产品,软件写得滴水不漏,结果硬件上被人从SPI线上把密钥给抓走了。嗯,咱们得从根上把安全做扎实。

2.1 微控制器架构:你的MCU到底在信任谁?

先说说MCU的架构。市面上主流的有ARM Cortex-M系列、RISC-V,还有老牌的8051。我个人习惯把MCU架构分成两类:冯·诺依曼结构哈佛结构

哈佛结构把指令总线和数据总线分开。这有什么好处?你想想看,攻击者想通过数据溢出修改程序代码,在哈佛结构里就难得多。我在项目中遇到过一款Cortex-M3的芯片,就是因为用了哈佛结构,才没让缓冲区溢出攻击得逞。

安全要点:选择MCU时,优先考虑支持MPU(内存保护单元)TrustZone的架构。这些硬件特性可以隔离安全和非安全代码。

说白了,MPU就像给程序划了地盘。普通代码只能在用户区玩,核心密钥操作必须进到特权区。我曾经见过一个产品,所有代码都跑在特权模式下,结果一个串口解析漏洞直接让攻击者拿到了系统控制权。嗯,这就是没用好MPU的教训。

2.2 存储器层次:Flash、SRAM和寄存器的攻防博弈

存储器的安全,是嵌入式安全的重头戏。咱们一层层说。

2.4.1 Flash存储器

Flash存的是固件和关键配置。攻击者最想干的事,就是把Flash里的固件读出来逆向分析。我建议你做到以下几点:

  • 读保护(RDP)必须开启:大多数MCU都有读保护等级。Level 1能防住大部分调试接口攻击,Level 2基本就焊死了。我习惯在产品量产时直接设成Level 2。
  • 加密存储敏感数据:密钥不要明文放在Flash里。可以用芯片的唯一ID做密钥派生。我在一个IoT项目中,就是把设备密钥用芯片UID加密后存到Flash的OTP区域。
  • 注意Flash的磨损均衡:频繁擦写的区域容易被侧信道攻击定位。嗯,这个坑我踩过。

避坑指南:我曾经遇到一个案例,工程师把RDP设成了Level 1,但忘了禁用调试接口的复位功能。攻击者通过复位后快速读取,还是把固件dump出来了。记住:Level 1不是万能的

2.4.2 SRAM

SRAM存的是运行时数据,包括密钥、密码、会话令牌。这里有个大坑——数据残留

你想想看,一个函数处理完密钥后,密钥数据还在SRAM里躺着。如果系统崩溃了,攻击者通过调试接口或者冷启动攻击,就能把残留数据读出来。我建议:

  • 使用完敏感数据后,立即用memset_sSecureZeroMemory清零。
  • 开启SRAM的ECC功能(如果有的话),防止故障注入攻击篡改数据。
  • 把关键数据放在电池备份域的SRAM里,并加上篡改检测。

小技巧:在Cortex-M系列上,可以用__attribute__((section(".noinit")))把敏感数据放在不初始化的区域,防止复位后数据被自动清零。但记得手动管理生命周期。

2.4.3 寄存器

寄存器是MCU的神经末梢。攻击者通过修改控制寄存器,可以绕过安全机制。我见过最典型的攻击是:

  • 修改时钟寄存器,让CPU跑飞,触发故障注入。
  • 修改GPIO寄存器,把调试接口重新使能。
  • 修改DMA寄存器,让外设直接访问受保护的内存区域。

怎么防?写保护寄存器。很多MCU提供了寄存器写保护功能,需要先写入一个特定的"解锁密钥"才能修改关键寄存器。嗯,这个功能一定要用上。

2.3 外设总线:SPI、I2C、UART的安全考量

外设总线是芯片和外部世界的桥梁,也是最容易被攻击的环节。咱们一个一个说。

2.3.1 SPI总线

SPI是高速全双工总线,常用于连接Flash、传感器、显示屏。它的安全问题很突出:

  • 明文传输:SPI线上传输的数据都是明文的。我在一个项目中,用逻辑分析仪直接抓到了SPI Flash的读写命令和数据。嗯,密钥就这么暴露了。
  • 无认证机制:谁都可以发起SPI通信。攻击者可以伪造SPI主设备,直接读取外部Flash。

我建议的做法:

  • 如果SPI连接的是安全敏感器件(如安全芯片),使用加密SPI协议。比如在数据帧里加入加密和MAC校验。
  • 在PCB布局上,把SPI走线放在内层,减少被探针接触的可能。
  • 使用SPI写保护引脚,防止固件被篡改。

实战经验:我曾经设计过一个安全网关,SPI Flash里存着根证书。我们在SPI通信中加入了简单的XOR混淆和CRC校验。虽然不能防住专业攻击者,但能挡住90%的脚本小子。

2.3.2 I2C总线

I2C是低速双线总线,常用于传感器、EEPROM、RTC。它的安全弱点在于:

  • 总线冲突:攻击者可以拉低SCL或SDA线,造成总线死锁。
  • 地址欺骗:I2C设备地址是7位或10位,攻击者可以伪造从设备地址,窃取数据。
  • 无流控:主设备可以无限次读取从设备,导致信息泄露。

我建议:

  • 对I2C总线加上总线隔离器(如PCA9545),在安全域和非安全域之间做物理隔离。
  • 使用带认证的I2C协议。比如在每次读写前,先交换一个动态挑战码。
  • 在软件层面,限制I2C的读写速率和次数,防止暴力读取。

注意:I2C的漏极开路特性,让攻击者很容易注入毛刺信号。我曾经在测试中,通过向I2C时钟线注入一个短脉冲,就让从设备跳过了安全校验。嗯,硬件安全真的不能只看软件。

2.3.3 UART

UART是最简单的串行通信接口,也是攻击者的最爱。为什么?因为几乎所有MCU都留了UART调试口。

  • 调试接口暴露:很多产品量产时没禁用UART调试口。攻击者接上串口工具,就能拿到shell。
  • 无加密:UART数据是裸的ASCII码。我在一个路由器上,直接用串口抓到了管理员密码。
  • 缓冲区溢出:UART接收中断处理不当,很容易被溢出攻击。

我建议的做法:

  • 量产时禁用UART调试口。如果必须保留,用跳线或eFuse控制。
  • 如果UART用于安全通信(如与安全芯片交互),使用加密串口协议。比如在数据帧里加入时间戳和HMAC。
  • 在UART中断处理函数中,严格检查接收缓冲区长度,防止溢出。

个人习惯:我设计产品时,会在UART调试口上串联一个100欧姆电阻。这样即使攻击者接上了,信号也会被衰减,很难稳定通信。嗯,小成本大作用。

2.4 总结:硬件安全是地基

说了这么多,其实就一句话:硬件安全是嵌入式安全的地基。软件做得再好,硬件上被人从SPI线、I2C线或者UART口攻破了,一切归零。

我建议你在设计初期,就画一张硬件安全边界图。标出哪些总线是可信的,哪些是不可信的。然后针对每个边界,设计对应的安全措施。别等到产品出来了,才发现调试口没关、Flash没加密、SPI线裸奔。嗯,那时候就晚了。

下一章,咱们聊聊嵌入式软件安全架构,包括RTOS的安全设计、任务隔离、安全启动链。到时候见。