简介:本资源是一套面向嵌入式开发初学者与进阶工程师的STM32F103程序加密保护综合实验源码,聚焦固件安全防护核心需求,涵盖Bootloader定制、Flash写保护配置、AES软件加解密集成及时间戳密钥生成等关键技术点,适用于工业控制、智能终端等对代码防逆向有明确要求的项目场景。压缩包共122个文件,含58个头文件(.h)定义外设寄存器与加密接口、56个C源文件(.c)实现RTC(DS1302)、HS0038红外解码、SysTick精准延时及加密加载流程,另含Keil工程文件(.uvprojx/.uvoptx)、编译脚本(.bat)、启动汇编(.s)及可执行镜像(.hex),整体体积仅395KB,结构紧凑便于学习与移植。已有177人下载学习,提供完整可运行工程框架、模块化分层代码结构及关键注释,帮助读者深入理解STM32底层安全机制,快速掌握从密钥管理、加密存储到受信启动的全链路防护实践。
1. 项目概述:为什么STM32的程序也需要“上锁”?
最近在整理资料时,翻出了一个老项目——“基于STM32f103单片机程序加密保护实验软件例程源代码.rar”。这个压缩包名字听起来有点技术门槛,但说白了,它就是一套教你如何给STM32单片机里的程序“上锁”的完整工具包和教学案例。你可能觉得,单片机程序烧录进去不就完事了吗?为什么还要大费周章地加密?这恰恰是很多嵌入式开发者,尤其是产品化初期容易忽略的关键一步。
想象一下这个场景:你花了几个月时间,精心打磨了一个基于STM32的智能硬件产品,从算法到驱动都调得完美。产品上市后,很快出现了功能一模一样的“山寨版”,价格还比你低一半。拆开一看,主控芯片同样是STM32F103,里面的程序逻辑甚至变量命名都跟你的如出一辙。问题出在哪?很可能就是你的程序在芯片里“裸奔”,被人用简单的调试器或者编程器直接读取、复制,然后批量生产了。这种“逆向工程”在消费电子、工控设备领域并不少见,直接导致原创者的前期投入血本无归。
STM32F103作为一款经典的ARM Cortex-M3内核单片机,因其性价比高、生态完善,被广泛应用于各种产品中。也正因如此,它成为了被“重点关注”的对象。这个“程序加密保护实验”项目,核心目的就是教你如何利用STM32芯片内部提供的硬件安全特性,构建一道防线,防止未经授权的程序读取、复制和调试。它不仅仅是几个配置选项,更是一套从原理到实践,包含多种加密手段组合拳的完整解决方案。无论你是正在将创意产品化的独立开发者,还是负责公司产品安全的工程师,理解并实施程序保护,都是将代码转化为有价值资产的关键一步。
2. 加密保护的核心原理与STM32的硬件支持
要给程序加密,首先得知道STM32F103这颗芯片提供了哪些“保险箱”功能。它不是通过软件算法在运行时混淆代码(虽然那也是一种辅助手段),而是主要依赖芯片设计时内置的硬件安全机制。理解这些机制,是正确实施保护的前提。
2.1 读保护(RDP)等级:程序存储器的第一道门禁
读保护是STM32最基础、也是最核心的硬件保护功能。你可以把它理解为给芯片内部的Flash存储器(也就是存放你程序的地方)设置访问权限。STM32的读保护通常分为几个等级,以Level 0、Level 1、Level 2常见。
- Level 0:无保护。这是出厂默认状态。任何通过调试接口(如SWD/JTAG)或启动加载程序(Bootloader)的连接,都可以自由读取、写入和擦除整个Flash内存,包括你的核心程序和数据。
- Level 1:使能读保护。这是最常用的保护等级。一旦设置,任何通过调试接口的外部访问(比如用ST-Link通过SWD线连接)都无法读取Flash内存的内容。尝试读取只会得到一堆0xFF或0x00(取决于芯片)。但是,芯片自己运行的程序是可以正常读取Flash来执行的,这保证了程序功能正常。要解除Level 1保护,唯一的办法是执行一次完整的Flash擦除(Mass Erase),这会清空整个Flash,包括你的程序,让芯片恢复到空白状态。这意味着抄袭者无法在不破坏程序的前提下窃取它。
- Level 2:永久性读保护。这是最高级别的保护。启用后,不仅禁止外部读取,连通过系统存储器自举模式(即通过串口等下载方式)进行的擦除和写入操作也被禁止。调试接口被永久禁用(无法再通过SWD/JTAG连接)。这个操作是不可逆的。一旦设置为Level 2,没有任何办法可以恢复调试功能或更新程序。它通常用于产品生命周期结束、程序绝对不允许再更改的场合。
注意:在实验和产品开发阶段,强烈不建议使用Level 2。因为一旦设置,你将无法再通过调试器更新程序,芯片就“锁死”了,只能报废。Level 1是产品量产的黄金选择。
2.2 写保护(WRP):防止程序区被意外或恶意修改
读保护是防止“偷看”,写保护则是防止“乱写”。你可以将Flash存储器划分成多个扇区,并对指定的扇区启用写保护。启用后,无论是通过调试接口还是芯片内部运行的程序,都无法对这些扇区进行擦除或编程操作,除非先解除写保护。
这个功能非常实用:
- 保护核心固件:将存放主程序、关键算法库的扇区写保护,防止程序跑飞后意外修改自身代码区域,提高系统鲁棒性。
- 保护固定参数:将存储产品序列号、校准参数、网络MAC地址等关键数据的扇区保护起来,确保这些信息不会被篡改。
- 配合Bootloader:在采用IAP(在应用编程)方案时,通常将Bootloader区写保护,防止用户程序出错时破坏升级功能。
2.3 调试端口访问控制:关上调试的后门
即使设置了读保护,调试接口(SWD的SWCLK和SWDIO引脚)在物理上仍然是存在的。STM32允许你通过选项字节(Option Bytes)来关闭这些调试端口的功能,使其变成普通的GPIO。这样,即使攻击者将芯片焊下来,也无法通过物理引脚连接调试器。这相当于把门禁系统的电源线也拔了,是硬件层面的深度防护。
2.4 芯片唯一ID(UID)的妙用:实现“一机一码”
每一片STM32芯片在出厂时都被烧录了一个全球唯一的96位或128位唯一标识符(Unique ID)。这个ID是物理刻在芯片里的,无法被修改。我们可以利用这个UID,创造出强大的绑定加密方案。
基本思路是:在程序编译后、烧录前,动态地将芯片UID作为因子,参与到程序的某部分校验计算中。例如,你可以设计一个算法,用UID生成一个密钥,然后用这个密钥去加密程序中的几个关键函数或跳转表。烧录程序时,这个“加密”过程是针对目标芯片的UID实时完成的。这样生成的程序镜像,只有在这片特定的芯片上才能正确运行。因为程序运行时,会用当前芯片的UID再次计算密钥进行解密,如果UID不匹配(即程序被复制到另一片芯片),解密就会失败,导致程序无法正常运行或功能错乱。
这种方法的好处是,即使有人将Flash的二进制内容完整复制出来,烧录到另一片芯片上,程序也无法工作,因为新芯片的UID不同。这实现了软件与硬件的强绑定。
3. 实验例程源代码深度拆解与实操
光讲原理不够,我们直接打开这个“实验软件例程源代码.rar”,看看里面到底提供了哪些干货,以及每一步具体怎么操作。这个例程包通常会包含一个完整的Keil MDK或IAR工程,我们以最常见的Keil环境为例进行解析。
3.1 工程结构解析:模块化设计清晰
解压后,你会看到一个标准的STM32工程目录。我们重点关注以下几个部分:
Project/ ├── Core/ // 核心启动文件、主程序main.c ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ // HAL库文件 │ └── CMSIS/ // Cortex内核支持包 ├── Inc/ // 头文件目录 │ ├── flash_protect.h // Flash读写保护操作头文件 │ ├── unique_id_bind.h // 芯片UID绑定相关头文件 │ └── ... ├── Src/ // 源文件目录 │ ├── flash_protect.c // 保护功能实现 │ ├── unique_id_bind.c // UID绑定算法实现 │ ├── main.c // 主程序,演示流程 │ └── ... └── MDK-ARM/ // Keil工程文件 └── Project.uvprojxflash_protect.c/.h:这是实现读保护、写保护功能的核心模块。它通过直接操作寄存器或调用HAL库函数来配置选项字节(Option Bytes)。unique_id_bind.c/.h:这里包含了利用芯片UID进行程序绑定的算法。例程可能会展示一种简单的校验和算法,或更复杂的AES加密片段。main.c:主程序文件,它按顺序演示了如何读取UID、如何设置保护、以及如何验证保护是否生效的完整流程。
3.2 核心代码段解读:如何设置读保护(Level 1)
我们深入flash_protect.c,看一个关键函数:启用读保护。
/** * @brief 启用Flash读保护 (RDP Level 1) * @retval HAL status: HAL_OK on success. */ HAL_StatusTypeDef FLASH_EnableReadProtection(void) { HAL_StatusTypeDef status; FLASH_OBProgramInitTypeDef OBInit; // 1. 解锁选项字节编程 HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); // 2. 设置读保护选项为Level 1 OBInit.OptionType = OPTIONBYTE_RDP; OBInit.RDPLevel = OB_RDP_LEVEL_1; // 关键参数:设置为等级1 // 3. 开始编程选项字节 status = HAL_FLASHEx_OBProgram(&OBInit); if (status != HAL_OK) { // 错误处理... HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); return status; } // 4. 选项字节更改后,必须执行系统复位才能生效 HAL_FLASH_OB_Launch(); // 此函数会触发系统复位 // 复位后,后续代码不会执行 // 函数理论上不会返回到这里 HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); return HAL_ERROR; }代码实操要点:
- 顺序至关重要:必须先解锁Flash(
HAL_FLASH_Unlock),再单独解锁选项字节(HAL_FLASH_OB_Unlock)。顺序反了或只做一个,都会导致编程失败。 HAL_FLASHEx_OBProgram是核心:这个HAL库函数负责将配置(这里是OB_RDP_LEVEL_1)写入芯片内部的非易失性选项字节区域。HAL_FLASH_OB_Launch()的强制性:这是最容易忽略的一步。修改选项字节(如读保护等级)后,必须调用此函数来触发一个系统复位,新的保护设置才会在复位后生效。如果你忘了调用,代码可能继续运行,但保护并未真正开启,给你的程序留下安全隐患。- 复位后的世界:一旦启动
OB_Launch,芯片会立刻复位。因此,在这个函数调用之后,你写的任何代码(比如点个灯提示操作成功)都是无效的。通常的做法是,在设置保护前,通过串口打印提示信息,或者用LED闪烁特定序列,告知用户“保护设置即将开始,系统将复位”。
3.3 利用芯片UID实现程序绑定:一个简明的示例
例程中的unique_id_bind.c可能会展示一种基础的绑定方法。我们来看一个简化版的思路:
假设我们在程序中有一个关键功能函数void Critical_Function(void),我们想让它只在UID匹配的芯片上工作。
步骤一:在编程阶段,获取目标芯片UID并生成“锁”
- 编写一个辅助的“密钥生成器”小程序(可以在PC上运行,也可以用单片机先读出来再计算)。
- 将目标芯片的UID(通过调试器读取)输入这个生成器。
- 生成器运行一个算法(比如
Key = Hash(UID + “Salt”),其中“Salt”是你自己定义的一个固定字符串),产生一个密钥或校验码。 - 这个密钥被硬编码到你的主程序中,作为一个常量数组。
步骤二:在程序运行时,验证“钥匙”是否匹配在主程序初始化时,加入验证逻辑:
// 在unique_id_bind.c中 uint32_t isChipAuthorized(void) { uint32_t uid[3]; // STM32的UID通常是96位,3个32位字 uint32_t storedKey[4]; // 预先根据目标芯片UID计算并存储的密钥 // 1. 读取本芯片的实际UID uid[0] = *(uint32_t*)(UID_BASE); uid[1] = *(uint32_t*)(UID_BASE + 4); uid[2] = *(uint32_t*)(UID_BASE + 8); // 2. 使用同样的算法,根据读到的UID计算当前密钥 uint32_t calculatedKey[4]; calculateKeyFromUID(uid, calculatedKey); // 自定义的算法函数 // 3. 与程序中预存的密钥对比 for(int i=0; i<4; i++) { if(calculatedKey[i] != storedKey[i]) { return 0; // 不匹配,未授权 } } return 1; // 匹配,授权通过 } // 在main.c中 int main(void) { // ... 硬件初始化 if(!isChipAuthorized()) { // 芯片非法,进入错误处理 // 例如:关闭所有功能,只让LED快速闪烁报警 Error_Handler(); } // 芯片合法,正常执行后续功能 // ... }实操心得:这种方法的强度取决于你的
calculateKeyFromUID算法复杂度。简单的异或、相加很容易被逆向。建议使用标准的加密算法,如AES或SHA-256。虽然STM32F103没有硬件加密引擎,但你可以移植一个轻量级的软件加密库(如TinyAES)。计算密钥的过程可以放在一个不起眼的初始化函数里,甚至将校验分散在程序的多个地方,增加破解难度。
3.4 写保护(WRP)的配置示例
写保护通常用于保护Bootloader或参数区。在HAL库中,配置写保护扇区也很直观:
OBInit.OptionType = OPTIONBYTE_WRP; // 设置操作类型为写保护 OBInit.WRPState = OB_WRPSTATE_ENABLE; // 启用写保护 OBInit.WRPSector = OB_WRP_SECTOR_0 | OB_WRP_SECTOR_1; // 保护扇区0和扇区1 status = HAL_FLASHEx_OBProgram(&OBInit);关键点:STM32F103的Flash扇区大小不一(前几页小,后面大),在规划程序存储布局时,就要提前考虑好哪些内容需要保护,并将其分配到连续的扇区中,以便统一设置写保护。
4. 完整实验流程与操作记录
现在,让我们跟随例程的main.c,走一遍完整的实验流程,记录下每个步骤的现象和背后的原理。
4.1 实验准备阶段:硬件与软件环境
硬件:
- 一块STM32F103C8T6核心板(俗称“蓝桥杯”或最小系统板即可)。
- 一个ST-Link V2调试器/编程器。
- USB转串口模块(用于打印调试信息,可选但强烈推荐)。
软件:
- Keil uVision 5 (MDK-ARM)。
- 已安装STM32F1系列的Device Family Pack。
- 串口调试助手(如XCOM、Putty)。
工程配置:
- 解压例程包,用Keil打开
Project.uvprojx。 - 检查目标芯片型号是否正确(Device选择
STM32F103C8)。 - 在
Options for Target -> Debug中,确认调试器选择为ST-Link Debugger。 - 在
Utilities设置中,勾选Update Target before Debugging,确保每次调试前程序已烧录。
4.2 第一阶段:验证无保护状态下的程序可读性
在第一次运行例程前,我们先做一个基线测试。
- 编译例程,但不做任何修改,直接通过ST-Link下载到芯片。
- 程序运行后,可能会通过串口打印:“当前读保护等级:0 (无保护)”。
- 关键验证:在Keil中进入调试模式,然后打开
Memory窗口,输入Flash的起始地址0x08000000。你应该能清晰地看到你的程序代码(一堆十六进制数据)。尝试用File -> Save Memory功能,可以将整个Flash的内容保存为一个.bin文件。这模拟了攻击者轻易获取完整程序镜像的过程。
4.3 第二阶段:启用读保护(Level 1)并验证
现在,我们让例程执行保护操作。
- 找到
main.c中,可能被条件编译#ifdef ENABLE_PROTECTION包围的代码段,或者找到一个明确的函数调用FLASH_EnableReadProtection()。 - 确保该代码会被执行(比如取消注释或定义宏)。
- 重新编译并下载程序。程序运行后,会执行设置读保护的操作,并通过串口打印类似“正在设置读保护,系统即将复位...”的信息,随后芯片复位。
- 复位后验证:
- 尝试再次通过Keil的调试模式连接芯片。你可能会发现连接失败,或者虽然能连接但无法暂停程序、无法查看寄存器。
- 再次尝试在
Memory窗口查看0x08000000地址。你会发现读取到的全是0xFF或0x00,你的程序代码“消失”了。 - 尝试使用STM32 ST-Link Utility或OpenOCD等独立编程软件,执行“Read Flash”操作。同样会读取失败或得到空数据。
- 功能验证:虽然读不出程序,但芯片本身的功能是正常的。如果例程写了LED闪烁或串口循环发送数据,你会发现这些功能在复位后依然正常运行。这证明了Level 1保护的特点:禁止外部读取,但内部执行正常。
4.4 第三阶段:尝试解除保护与后果体验
为了深刻理解Level 1保护的含义,我们可以尝试解除它。
- 在STM32 ST-Link Utility中,连接芯片(如果还能连接的话)。
- 找到“Target”菜单下的“Option Bytes...”选项。
- 你会看到“Read Out Protection”显示为
Level 1。 - 尝试将其修改为
Level 0并点击“Apply”。软件会弹出一个非常明确的警告框,提示你“此操作将执行全片擦除!所有数据将丢失!”。 - 确认后,工具会先执行全片擦除(Mass Erase),然后才将保护等级降为0。完成后,芯片Flash变为全空(0xFF)。
- 此时,你可以重新下载程序了。但之前那个被保护的程序,已经随着擦除而彻底消失。攻击者即使通过此方法解除了保护,得到的也只是一个空芯片,而非你的程序代码。
这个流程清晰地展示了Level 1保护的威慑力:要得到代码,就必须毁掉代码。
4.5 第四阶段:集成UID绑定功能
如果例程包含了UID绑定演示,流程会更有趣。
- 首先,你需要为当前实验用的这块具体芯片生成绑定的密钥。例程可能提供了一个PC端工具,或者你需要修改源码中的
storedKey数组。 - 将正确的密钥编译进程序,下载运行。程序启动时校验通过,所有功能正常。
- 模拟抄袭:将这个程序的.bin文件(在设置读保护前备份的),烧录到另一块不同的STM32F103芯片中。
- 在另一块芯片上运行程序。程序在启动校验时,读取到的是新芯片的UID,计算出的密钥与程序中预存的不匹配。根据例程设计,它可能会进入死循环、疯狂重启或者仅仅限制部分核心功能。
- 通过串口观察,可能会收到“芯片验证失败”等错误信息。
这个实验生动地展示了“一机一码”如何有效防止程序被简单复制扩散。
5. 产品化实践中的进阶策略与避坑指南
将实验例程中的技术应用到真实产品中,需要考虑更多工程细节。以下是一些进阶策略和必须避开的“坑”。
5.1 保护策略的部署时机:开发、测试与量产
绝不能在产品开发的全周期都开着读保护,那会严重影响调试效率。一个成熟的流程是:
- 开发与调试阶段:完全关闭所有保护(Level 0)。方便使用调试器设置断点、观察变量、修改代码。
- 内部测试阶段:可以开始集成UID绑定算法的验证逻辑,但读保护仍保持关闭。这样可以在不同测试板之间自由烧录同一份程序,同时测试绑定逻辑是否正确。
- 小批量试产/固件冻结阶段:在最终确认固件功能无误后,先在少量样机上启用读保护(Level 1)进行老化测试。测试内容包括:长期运行、异常断电、高低温循环等,确保保护功能的开启不会引入不稳定性(理论上不会,但需验证)。
- 大规模量产阶段:使用量产编程工具(如脱机编程器、自动化烧录夹具),在烧录程序的最后一步,自动执行“启用读保护”操作。这个步骤通常被集成到烧录脚本或工具链中,确保每一片出货的芯片都处于受保护状态。
5.2 结合Bootloader(IAP)的安全升级方案
很多产品需要后期升级固件。在启用读保护后,通过调试口升级的路被堵死了,必须通过IAP方式。一个安全的IAP方案设计如下:
- Bootloader区域(0x08000000起始):这个区域启用写保护,防止被应用程序误擦写。Bootloader本身要尽可能精简、健壮,其核心任务是验证新程序的合法性和执行跳转。它可以通过串口、CAN、USB、以太网等接收新固件。
- 应用程序区域:这是主程序存放的地方,受读保护。
- 升级流程:
- Bootloader通过通信接口接收新固件,暂存到Flash的另一个区域(如备份区)或外部Flash中。
- Bootloader对新固件进行验证,包括:CRC校验、数字签名(如果支持)等。
- 验证通过后,Bootloader先解除应用程序区的写保护,然后擦除旧程序,写入新程序。
- 写入完成后,重新启用应用程序区的读保护。
- 跳转到新的应用程序执行。
避坑指南:在Bootloader中解除和重新启用读保护是可行的,但务必确保流程万无一失。如果在擦写过程中系统断电,可能导致芯片“变砖”(程序区既不是有效的旧程序,也不是完整的新程序)。对策是采用“双备份”或“A/B分区”设计,永远保留一个可启动的备份程序。
5.3 软件混淆与反调试技巧
硬件保护是基础,软件层面可以增加更多障碍,提高逆向工程的成本。
- 代码混淆:使用编译器提供的混淆选项(如果支持),或者手动编写难以静态分析的代码结构。例如,将简单的
if-else逻辑改为通过函数指针表跳转。 - 关键数据分散存储:不要将加密密钥、校验码等敏感数据集中放在一个
const数组里。可以将其打散,分别隐藏在多个不相关的常量数组中,运行时再动态组合。 - 插入“陷阱”代码:在代码中插入一些看似正常,但一旦被调试(如断点触发)就会改变程序流程或清除关键数据的指令。例如,检查核心寄存器的值是否被调试器修改过。
- 利用芯片唯一ID派生更多密钥:不要只用UID做一次校验。可以用UID派生出一个主密钥,然后用这个主密钥去动态解密其他关键函数或数据块。这样,静态分析二进制文件看到的只是一堆密文。
5.4 常见问题排查与解决实录
在实际操作中,你肯定会遇到各种问题。下面是一个速查表:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 无法通过ST-Link连接芯片 | 1. 读保护已设置为Level 1或Level 2。 2. 调试端口被选项字节禁用。 3. 硬件连接问题。 | 1. 确认是否已设置保护。如果是Level 1,需执行全片擦除才能恢复连接(程序会丢失)。 2. 检查选项字节中 nSWDIO等配置。如需恢复,必须在设置保护前通过程序或工具保留一个可用的通信接口(如串口IAP)来修改选项字节。3. 检查SWD接线(SWDIO, SWCLK, GND, 3.3V)是否可靠。 |
| 设置读保护后,程序功能异常 | 1. 程序中有通过调试接口或自身访问Flash的非法操作。 2. 中断向量表定位错误。 | 1. 检查代码中是否存在试图通过*(uint32_t*)addr方式直接读取Flash代码区的操作(在保护开启后,这类操作可能被禁止或返回错误数据)。2. 确保在设置保护后,如果发生了复位,中断向量表地址已正确重映射(通常由启动文件完成,但需确认)。 |
| UID绑定验证总是不通过 | 1. 用于生成密钥的UID与运行时读取的UID不一致。 2. 算法实现有误,端序问题。 3. 存储的密钥在Flash中损坏。 | 1.最关键一步:在目标芯片上,用调试器或一个临时小程序,亲自读取并记录其UID,用于生成密钥。不同批次的芯片UID范围可能不同。 2. 检查算法中的字节序(大端/小端)。STM32是小端模式,确保你的生成器程序和单片机程序端序一致。 3. 在程序中添加读取并打印UID和计算结果的调试代码,与PC生成器输出对比。 |
| 写保护设置失败 | 1. 尝试保护的扇区当前正在执行代码。 2. 未正确解锁Flash和选项字节。 | 1.绝对不能对当前正在执行代码的扇区进行写保护操作。这会导致立即硬件错误或死机。设置写保护的操作代码必须放在其他未被保护的扇区(如RAM中执行,或由Bootloader设置)。 2. 严格按照 HAL_FLASH_Unlock()->HAL_FLASH_OB_Unlock()的顺序解锁。 |
| 量产时,个别芯片保护功能异常 | 1. 芯片Flash或选项字节存在瑕疵。 2. 烧录器电源或信号不稳定。 3. 烧录脚本流程有误。 | 1. 更换芯片测试,排除芯片个体问题。 2. 检查烧录夹具的接触和供电。劣质电源可能导致编程电压不稳,影响选项字节写入的可靠性。 3. 在烧录脚本中,在“设置保护”操作后,增加一个“验证”步骤,回读选项字节确认是否设置成功。 |
我个人在实际操作中体会最深的一点是:安全是一个链条,最薄弱的一环决定了整体强度。给STM32加上读保护,就像给家门装了把好锁。但如果你的程序逻辑里,把“钥匙”(比如解密密钥)明晃晃地放在门口脚垫下(比如一个固定的全局常量),那锁再结实也没用。因此,硬件保护必须与软件层面的巧妙设计(如UID动态绑定、代码混淆)结合起来,形成纵深防御。在项目初期就规划好安全方案,远比后期修补要容易和有效得多。这个“加密保护实验”例程,正是提供了这样一个从硬件到软件的完整起点,值得每一个严肃的嵌入式开发者仔细研究和实践。
本文还有配套的精品资源,点击获取