STM32 固件加密与防破解实战:读保护与唯一 ID 校验的双重防线
👁 7 阅读 · 2026-08-18 · STM32 安全
在嵌入式产品开发中,固件被非法读取或克隆是常见的安全威胁。本文以 STM32 为例,深入讲解如何通过读保护(RDP)防止调试接口读取 Flash,以及如何利用芯片唯一 ID 实现软件防克隆。文章涵盖原理分析、配置步骤、完整代码示例及注意事项,帮助开发者构建低成本、高可靠性的固件安全方案。
## 引言
嵌入式产品一旦量产,固件安全便成为核心问题。攻击者可能通过调试接口(如 SWD/JTAG)直接读取 Flash,或者通过复制芯片和固件来仿制产品。STM32 提供了硬件级别的安全机制,包括读保护(RDP)和唯一 ID(Unique ID),合理组合使用可以有效提升破解难度。本文将从原理到实践,带你构建固件加密与防破解的双重防线。
## 原理篇:STM32 的安全机制
### 1. 读保护(RDP)
读保护是 STM32 内嵌 Flash 模块提供的一种访问控制机制,通过设置选项字节(Option Bytes)中的 RDP 级别来限制调试接口和内置程序对 Flash 的访问。
- **Level 0**:无保护,所有访问均允许。
- **Level 1**:禁止调试接口(SWD/JTAG)读取 Flash,但程序仍可正常执行。这是最常用的保护级别。
- **Level 2**:最高保护,禁止所有外部访问,且无法降级,芯片相当于永久锁定。适用于对安全要求极高的场景,但需谨慎使用,因为一旦设置无法恢复。
注意:从 Level 1 降级到 Level 0 会触发 Flash 全片擦除,防止固件被读取。
### 2. 唯一 ID(Unique ID)
每颗 STM32 芯片在出厂时都有一个 96 位的唯一标识符(UID),存储在特定地址(如 0x1FFFF7E8)。该 ID 无法修改,可用于软件层面的身份验证。通过将固件与特定芯片绑定,即使固件被完整复制,也无法在其他芯片上运行。
## 配置步骤:启用读保护
### 方式一:使用 STM32CubeProgrammer
1. 连接开发板,打开 STM32CubeProgrammer。
2. 在 "Option Bytes" 选项卡中,找到 "Read Protection" 设置。
3. 选择 "Level 1",点击 "Apply"。
4. 若需解除保护,选择 "Level 0",软件会提示将擦除 Flash,确认后执行。
### 方式二:通过代码设置(以 STM32F1 为例)
在程序启动阶段,可以检查当前 RDP 级别,若未保护则主动设置。以下代码使用标准外设库实现:
```c
#include "stm32f1xx.h"
void EnableReadProtection(void) {
FLASH_Unlock(); // 解锁 Flash 寄存器
// 检查当前 RDP 级别
if (FLASH_GetReadOutProtectionStatus() == DISABLE) {
// 设置 RDP 为 Level 1
FLASH_ReadOutProtection(ENABLE);
}
FLASH_Lock(); // 重新锁定 Flash
}
```
注意:设置 RDP 后,调试器将无法读取 Flash,但程序运行不受影响。若需调试,请先解除保护(会擦除 Flash)。
## 唯一 ID 校验实现
### 读取唯一 ID
STM32 的唯一 ID 由三个 32 位字组成,起始地址因系列而异。例如:
- STM32F1/F4:0x1FFFF7E8
- STM32L0/L4:0x1FF80050
以下函数读取 UID 并存入数组:
```c
void GetUniqueID(uint32_t *uid) {
uid[0] = *(volatile uint32_t *)0x1FFFF7E8;
uid[1] = *(volatile uint32_t *)0x1FFFF7EC;
uid[2] = *(volatile uint32_t *)0x1FFFF7F0;
}
```
### 校验策略
常见做法是将 UID 与预存的密钥进行哈希或加密运算,生成校验码。这里以简单的 CRC32 为例:
```c
#include "stm32f1xx.h"
#include
// 预存校验码(在开发时计算并写入 Flash)
uint32_t stored_crc = 0x12345678; // 示例值
uint32_t CalculateCRC(const uint32_t *data, uint32_t len) {
// 使用硬件 CRC 或软件实现,此处简化
uint32_t crc = 0xFFFFFFFF;
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (int j = 0; j < 32; j++) {
if (crc & 0x80000000) {
crc = (crc << 1) ^ 0x04C11DB7;
} else {
crc <<= 1;
}
}
}
return ~crc;
}
int CheckUID(void) {
uint32_t uid[3];
GetUniqueID(uid);
uint32_t crc = CalculateCRC(uid, 3);
return (crc == stored_crc);
}
```
在 main 函数中调用校验,若失败则停止运行或进入死循环:
```c
int main(void) {
// 系统初始化...
if (!CheckUID()) {
while(1); // 非法芯片,停机
}
// 正常业务逻辑
}
```
## 完整示例:结合读保护与 UID 校验
以下是一个综合示例,演示如何初始化系统、启用读保护并执行 UID 校验:
```c
#include "stm32f1xx.h"
#include
// 唯一 ID 地址(根据芯片调整)
#define UID_BASE 0x1FFFF7E8
// 预存校验码(开发时生成)
#define STORED_CRC 0xA5A5A5A5
void SystemClock_Config(void);
void EnableReadProtection(void);
uint32_t CalculateCRC(const uint32_t *data, uint32_t len);
int CheckUID(void);
int main(void) {
HAL_Init();
SystemClock_Config();
// 启用读保护(仅首次运行时需要,之后每次启动检查)
EnableReadProtection();
// UID 校验
if (!CheckUID()) {
// 非法芯片,进入死循环
while(1);
}
// 用户代码...
while(1) {
// 主循环
}
}
void EnableReadProtection(void) {
// 使用 HAL 库示例
if (HAL_FLASH_GetReadOutProtection() == OB_RDP_LEVEL_0) {
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
// 设置 RDP Level 1
HAL_FLASH_OB_RDP_LevelConfig(OB_RDP_LEVEL_1);
HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
// 注意:设置后系统可能复位,需重新启动
NVIC_SystemReset();
}
}
uint32_t CalculateCRC(const uint32_t *data, uint32_t len) {
// 软件 CRC32 实现(略,可参考上文)
// 此处省略具体代码,建议使用硬件 CRC 外设
return 0;
}
int CheckUID(void) {
uint32_t uid[3];
uid[0] = *(volatile uint32_t *)UID_BASE;
uid[1] = *(volatile uint32_t *)(UID_BASE + 4);
uid[2] = *(volatile uint32_t *)(UID_BASE + 8);
uint32_t crc = CalculateCRC(uid, 3);
return (crc == STORED_CRC);
}
```
## 注意事项
- **读保护级别选择**:Level 1 适合大多数产品,Level 2 不可逆,务必确认需求。
- **UID 校验的局限性**:攻击者可以 patch 掉校验代码,因此建议将校验逻辑分散在多个位置,并加入反调试技巧。
- **加密算法**:CRC 仅用于示例,实际应使用强哈希(如 SHA-256)或加密算法(如 AES)结合密钥。
- **烧录流程**:量产时,先烧录固件,再设置读保护。若先设置保护,则无法再烧录。
- **调试影响**:设置 Level 1 后,调试器无法连接,需通过解除保护(擦除 Flash)才能重新调试。
- **芯片差异**:不同系列 STM32 的 UID 地址和 RDP 寄存器可能不同,请参考对应参考手册。
## 总结
通过读保护与唯一 ID 校验,可以构建基础但有效的固件安全防线。读保护阻止了静态读取,而 UID 校验则防止了固件移植。对于高安全需求,建议结合加密存储、安全启动(Secure Boot)等技术。希望本文能帮助你在产品中实现可靠的固件保护。