MCU(微控制器单元)的加密方法可以从硬件、软件和通信协议三个层面来理解。以下是常见的MCU加密手段,按类型分类说明:
针对目前 STM32 系列微控制器在程序加密保护方面手段单一、保护效果有限的问题,本文介绍并分析了四种常用的程序保护方法:
-
闪存读写保护
-
芯片唯一 ID 验证
-
外接加密芯片
-
引导程序加密(Bootloader 加密)
实际在 STM32 开发板上的测试表明,合理组合多种保护机制,能显著增强芯片程序与数据的安全性,有效防止闪存程序被读取、拷贝或篡改,为嵌入式系统提供更可靠的安全保障。
1 闪存读写保护法
STM32 微控制器提供了闪存读写保护的功能,
用来防止对闪存的非法访问。 闪存读写保护功能概述:
读保护(RDP)
-
通过修改 Option Bytes 中的 RDP 位来启用。
-
启用后,Flash 只能被内部正常执行的程序读取,无法通过 JTAG/SWD 调试接口或从 RAM 启动的程序读取。
-
解除读保护会触发整片 Flash 擦除,防止逆向工程与调试工具破解。
-
以 STM32L1 系列为例,RDP 分为 3 个等级,不同等级对应不同的安全强度。
等级 | 描述 |
---|---|
0 | 无保护(默认)。可以通过 JTAG/SWD 或从 RAM 启动的程序任意读取 Flash。 |
1 | Flash 受保护,防止由调试器(JTAG/SWD)直接读取,也不能通过在 RAM 中加载执行程序来读取。 解除 Level 1 保护将触发全片擦除。 |
2 | 最严格保护——禁用所有调试功能。 一旦设为 Level 2,不可复原,需谨慎使用。 |
写保护(WRP)
-
通过在 Option Bytes 中设置 WRP 位来启用。
-
启用后,对被保护页的任何写或擦除操作都会被硬件阻止,并在状态寄存器中产生错误标志。
-
可以防止恶意修改中断向量表或关键代码区域。
优缺点对比:
优点:
由硬件层面直接提供,成本为零,易于配置。
能有效防止通过调试接口的非法读写。
缺点:
单一的读/写保护难以抵抗高级物理攻击或侧信道分析。
需要与软件层面的校验(如 CRC、HASH)或更高级的加密方法(如外部加密芯片、Bootloader 加密)组合,才能构建更完善的防护体系。
实施的方法如下,下面以 STM32G4 系列为例,分别介绍如何通过 ST-Link/CubeProgrammer 工具以及 运行时(HAL)编程 两种方式来启用 Flash 的读写保护(RDP 和 WRP)。
方法一:通过 STM32CubeProgrammer 界面设置
打开 CubeProgrammer
启动 STM32CubeProgrammer,选择 “ST-Link” 连接。
读取当前 Option Bytes
在左侧菜单选择 “Option Bytes”。
点击 “Read”,查看当前 RDP(Read Protection)和 WRP(Write Protection) 配置。
配置读保护(RDP)
在 “Read protection” 下拉框中选择:
Level 0:无保护
Level 1:Flash 只允许内部执行读取
Level 2:全部调试接口禁用(不可逆)
推荐一般项目选 Level 1。
配置写保护(WRP)
在 “Write protection” 区域,勾选你要保护的 Flash 扇区(Page)。
比如要保护第 0~3 扇区,就勾选对应页号。
写入并重启
点击 “Apply”,CubeProgrammer 会提示需要复位芯片以生效。
确认后,设备重启,Option Bytes 即更新完毕。
方法二:在用户代码中动态配置(HAL 库示例)
如果希望在程序首次运行时,或通过 Bootloader 进行保护设置,可以在固件内部调用 HAL API 来修改 Option Bytes。
/*** @brief 启用 RDP Level1 并对指定扇区启用写保护* @note 必须在系统启动早期执行,且芯片复位后才生效*/
void Protect_Flash(void)
{HAL_FLASH_Unlock();HAL_FLASH_OB_Unlock();/* 1) 读保护 → Level1 */FLASH_OBProgramInitTypeDef OBInit = {0};OBInit.OptionType = OPTIONBYTE_RDP;OBInit.RDPLevel = OB_RDP_LEVEL_1;if (HAL_FLASHEx_OBProgram(&OBInit) != HAL_OK) {/* 错误处理 */}/* 2) 写保护 → 保护第 0~15 KB(页 0~15) */memset(&OBInit, 0, sizeof(OBInit));OBInit.OptionType = OPTIONBYTE_WRP;OBInit.WRPState = OB_WRPSTATE_ENABLE;OBInit.WRPPage = OB_WRP_PAGES0TO3 | OB_WRP_PAGES4TO7 | OB_WRP_PAGES8TO11 | OB_WRP_PAGES12TO15;if (HAL_FLASHEx_OBProgram(&OBInit) != HAL_OK) {/* 错误处理 */}HAL_FLASH_OB_Lock();HAL_FLASH_Lock();// /* 复位生效 */
// NVIC_SystemReset();
}
其次你会发现程序也烧写不进去。此时目标也达成。
我们说了加密,那如何解密呢?问题在于每次切换RDPLevel 1到0的过程中的时候,会将flash的内容全部擦除。因此这是解密的难点之一。
我们引入思考点一,针对闪存读写保护法这类内容也就是寄存器置位,如果你要切换RDP1到RDP0 的过程伴随着一个问题,STM32 上的 Read-Out Protection(RDP)并不是真正的“加密”,而是一种硬件级的“只读保护”机制——它保证 任何 试图“解开”这层保护的操作(也就是把 RDP 从 Level 1 降回 Level 0),都会触发一次整片 Flash 的 不可逆擦除,从而根本不可能在芯片上恢复出原来的固件!!!
一旦启用 RDP Level 1,就不可能在器件内“解密”或“读取” Flash 内容;任何试图取消保护的行为都会把所有数据抹掉。
为什么不能“解密”现有固件???
STM32 的 RDP 设计上就是「不可逆保护」:降级就擦除。
Flash 中的代码以明文形式存储,Option Bytes 保护只是阻止「直接」访问,但并不做真实的加密。
一旦需要「访问明文」,只能通过内部执行(CPU 跳转),而不是通过调试器或外部读口。
因此问题点来了!!!我们要做的是,在启用了 RDP Level 1 以后,所有基于 JTAG/SWD 或 外部调试器 的直接读 Flash 操作都会被阻止——硬件层面上不可能再通过外部工具把 Flash 整片“拷”出来。不过,RDP Level 1 并不影响 MCU 自身程序对 Flash 的正常读取;换句话说,你只要在芯片内跑一段用户代码,通过 UART/CAN/USB 等总线把 Flash 内容“转发”出去,就能获得完整的二进制镜像。
首先我们要将程序运行在SRAM中,根据自己的芯片主控观察大小,笔者这边是20KB的如下所示:
因此我们的环境在keil,如何配置呢,跟着笔者一步一步来吧。
第一步就是新建立一个模板了 如上所示,我们进行切换。
其次就是宏定义了,加上下述内容!
这边就是改配置内容了
接下来就是这个内容的更改了,配置文件具体如何写如下所示:
LR_IROM1 0x20000000 0x00005000 { ; Total 20KB SRAM regionER_IROM1 0x20000000 0x00003000 { ; Code section: 12KB*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20003000 0x00002000 { ; Data section: 8KB.ANY (+RW +ZI)}
}
其次就是这个仿真器命令文件了,如下所示:
具体内容如下所示:
/***********************************************************/
/* Debug_RAM.ini: Initialization File for Debugging from Internal RAM */
/******************************************************/
/* This file is part of the uVision/ARM development tools. */
/* Copyright (c) 2005-2014 Keil Software. All rights reserved. */
/* This software may only be used under the terms of a valid, current, */
/* end user licence from KEIL for a compatible version of KEIL software */
/*development tools. Nothing else gives you the right to use this software */
/***************************************************/FUNC void Setup (void) {
SP = _RDWORD(0x20000000); // 设置栈指针SP,把0x20000000地址中的内容赋值到SP。
PC = _RDWORD(0x20000004); // 设置程序指针PC,把0x20000004地址中的内容赋值到PC。
_WDWORD(0xE000ED08, 0x20000000); // Setup Vector Table Offset Register
}LOAD %L INCREMENTAL // 下载axf文件到RAM
Setup(); //调用上面定义的setup函数设置运行环境//g, main //跳转到main函数,本示例调试时不需要从main函数执行,注释掉了,程序从启动代码开始执行
最后就是debug处了。
之后进入调试你会发现程序在SRAM的运行处。观察汇编代码。
因此,程序成功在SRAM处运行。
都要重新点击“Debug”按钮下载SRAM程序,再全速运行才能正常查看输出。
接着就是写拷贝flash的内容到SRAM处通过UART转发出来。
这是原本的程序地址所示如下了。
如下是通过SRAM来拷贝Flash转发到uart的内容
void dump_flash_via_uart(void)
{char hex_str[8];for (uint32_t addr = FLASH_START_ADDR; addr < (FLASH_START_ADDR + FLASH_SIZE_BYTES); addr++) {uint8_t byte = *(volatile uint8_t*)addr;sprintf(hex_str, "%02X ", byte); // 格式化成十六进制字符串HAL_UART_Transmit(&huart1, (uint8_t*)hex_str, strlen(hex_str), HAL_MAX_DELAY);}const char* end_msg = "\r\n[FLASH DUMP DONE]\r\n";HAL_UART_Transmit(&huart1, (uint8_t*)end_msg, strlen(end_msg), HAL_MAX_DELAY);
}
含义 | 十六进制值 | 小端转换(实际值) |
---|---|---|
初始栈地址 MSP | F8 04 00 20 | 0x200004F8 ✅ |
Reset_Handler | 05 01 00 08 | 0x08000105 ✅ |
NMI_Handler? | 5B 13 00 08 | 0x0800135B ✅ |
HardFault_Handler? | C9 11 00 08 | 0x080011C9 ✅ |
这些值完全一致!
但是在WDP与DRP设置了之后。。
📌 根本原因:
✅ 你能正常读取 Flash,是因为:
-
STM32 默认 RDP 是 Level 0(无保护);
-
此时,即便运行在 SRAM,也能直接访问
0x08000000
地址处的 Flash 内容;
❌ 设置 RDP Level 1 后:
-
Cortex-M3 核心(STM32F1)硬件禁止访问 Flash 内容;
-
这是芯片的硬件安全特性,即使代码跑在 SRAM,依然无法读取 Flash 数据,目的就是为了防止“SRAM 下载程序+读取 Flash 代码”这种逆向手段;
因此上述破解方案无效,无法再从芯片内部获取任何 Flash 数据了,数据已经从访问层面彻底封锁。如果想切回回0,flash内容也会全部擦除!
方法 | 原理 | 是否可行 | 备注 |
---|---|---|---|
芯片解封/酸蚀 + 显微探测 | 打开芯片封装,用电子显微镜直接读 Flash 电荷状态 | ✔️理论可行 | 需要实验室级别设备、极高技术 |
功耗分析(侧信道攻击) | 用高精度设备分析芯片运行时功耗波动,推测程序数据 | 理论可行,但难度极高 | 适用于破解算法或加密密钥,不适合整段程序恢复 |
激光/聚焦扫描干扰 | 用激光打干扰点,欺骗 Flash 控制器读取受保护数据 | ✔️高端实验室可能可行 | 用于芯片逆向取证等高价值目标 |
IC 解封 + Flash 直接读出 | 拆解裸片,对 Flash 数字结构逐位扫描 | ✔️有可能,但需要芯片内部结构图 | 这属于“物理攻击”范畴 |
⚠️ 这些都需要 IC 物理逆向实验室、百万级设备成本,不具备民用可行性。
手段 | 原理 | 应用场景 |
---|---|---|
X-Ray(X 射线扫描) | 非破坏式探测芯片内部结构,识别 layout,理论可判断 Flash 存储 | 逆向分析芯片、找 ROM Mask |
开盖 + 紫外线(UV)曝光 | 对芯片裸露的 silicon 层曝光,激发电荷或使 EEPROM 状态改变 | 用于清除 EEPROM 或恢复旧锁定状态 |
补充:
STM32 跟一些 MCU(比如 AVR、ESP32)那种通过 熔丝(fuse)位 永久写死不同。
STM32 的 Option Bytes 是可擦除/可重写的 Flash 区域;
只不过 RDP Level 2 一旦写入,就不可降级、无法还原;
RDP Level 1 还可以通过 全芯片擦除 恢复成 Level 0(但数据没了)。