1. 固件被别人读走很多时候就是从一个小疏忽开始的玩STM32的工程师应该都有过这种体会好不容易把产品调稳定了PCB打样、贴片、烧录、出货忙完一阵子刚想歇口气结果没过多久市场上出现了一个功能几乎一模一样的东西。拆开一看主控是同一颗STM32板子走线风格相似连固件行为都完全一致。这时候你大概率会意识到一个问题——固件被读走了。很多人误以为程序烧进单片机就安全了实际上对于没有做任何保护的STM32用ST-LINK连接SWD接口打开ST-LINK Utility或STM32CubeProgrammer点一下连接再点一下读取整个Flash内容分分钟就能被导出来。拿到bin文件之后反汇编、逆向逻辑、移植到自己的板子上成本比你想象中低很多。做产品防抄设置读写保护RDP是最基础、也最应该养成习惯的一道防线。这篇文章就围绕我自己在实际项目里用ST-LINK Utility给STM32设置读写保护的完整流程展开包括保护等级的选择、具体操作步骤、解除方法以及我在帮同事处理芯片被锁死问题时总结出的一些排查经验。适合所有在做STM32产品开发的工程师尤其是画完板子、写完程序之后对固件安全还没有特别重视的朋友。RDPReadout Protection读保护是STM32内部选项字节Option Bytes里的一个关键配置。它决定了一个核心问题别人通过调试接口能不能直接把你的Flash内容读出来。ST-LINK Utility是老牌官方工具虽然现在ST主推的是STM32CubeProgrammer但ST-LINK Utility在设置选项字节和批量处理时依然有大量工程师在用路径熟悉、操作直观。我接下来的实操会基于ST-LINK Utility 4.6.0版本新老项目都能参考。2. 固件泄露的路径和RDP保护等级你得先知道自己防的是什么2.1 四种常见的固件泄露路径在动手设置保护之前先搞清楚威胁从哪里来这决定了你要防到什么程度。第一种是SWD/JTAG调试接口直接读取。这是最粗暴也最常见的路径只要芯片没有设置读保护任何一根ST-LINK连上SWDIO和SWCLK就能通过调试接口读取Flash。整个读取过程不需要知道你的程序逻辑也不需要破解什么完全是芯片默认开放的权限。第二种是Boot模式引导攻击。STM32支持从系统存储器System Memory启动也就是内置Bootloader。如果用户没有对这方面做限制攻击者可以把BOOT0拉高让芯片进入系统Bootloader模式然后通过串口或USB尝试读取Flash。好在STM32的系统Bootloader本身对读保护状态是敏感的——当RDP Level 1开启之后系统Bootloader的读操作也会被禁止所以RDP对这条路径同样有效。第三种是物理攻击。比如用探针直接测量内部总线、用FIB聚焦离子束修改芯片结构、用侧信道分析等方式提取数据。这类手段成本极高通常只有专业机构才会做普通产品遇到的概率不大。应对这种威胁需要更高的防护等级也就是Level 2。第四种其实是自己人泄露比如离职工程师带走固件、工厂端烧录环节拷贝了hex文件。这种情况靠芯片层面的保护解决不了所有问题但设置好Level 1保护至少能增加一道门槛让文件不能随随便便被读出来二次分发。2.2 Level 0、Level 1、Level 2三档保护的取舍逻辑RDP等级的语义在不同型号的STM32上略有差异但大框架是一样的。Level 0就是没有任何读保护出厂默认状态。Flash内容可以随意通过调试接口读写这是大多数开发板和自制产品的默认状态。如果你做的是批量出货的产品一直停在Level 0相当于把固件裸奔在市场里。Level 1是最常用的保护等级也是我推荐绝大多数产品选择的档位。开启之后调试接口SWD/JTAG无法再读取Flash内容也无法通过系统Bootloader的读命令获取固件。但芯片仍然允许通过调试接口进行擦除和重新烧录只不过每次从Level 1降回Level 0都需要先执行一次全片擦除。这意味着攻击者即使想暴力降级也只能得到一颗空片拿不到原有固件。Level 2是最高等级保护一旦设置芯片的调试接口会被永久禁用读保护功能本身也无法再被关闭。更关键的是从Level 2无法再回到Level 1或Level 0相当于把芯片变成了一个黑盒。程序再也无法通过调试器读取或更新只能通过应用程序自身的IAP升级逻辑来更新功能。这个等级适合固件价值极高、且确认后续不需要调试的产品。如果贸然设置Level 2后期想改bug就得换芯片。我做产品时的默认习惯是开发调试阶段保持Level 0出样机和量产前的最后一版固件切到Level 1。只有客户明确要求最高防护、且软件已经冻结不再调试时才考虑Level 2。3. 解读ST-LINK Utility在保护操作中的角色为什么这个老工具仍然好用3.1 工具选型ST-LINK Utility、STM32CubeProgrammer和J-Flash怎么选ST官方现在的通用编程工具是STM32CubeProgrammer功能确实更全支持OTA包生成、图形化选项字节配置等。但ST-LINK Utility一直没有完全退出大家的视野原因主要有几个。第一ST-LINK Utility界面极度简洁核心功能一目了然。打开软件、连接芯片、配置选项字节、保存hex所有高频操作都在一级菜单里不需要在多级界面里翻找。第二选项字节的读写非常直接。在做RDP设置这种需要频繁切换等级的场景下Utility的Option Bytes对话框里直接列出RDP级别选项和各个选项字节位改起来非常流畅。STM32CubeProgrammer虽然也支持但界面布局更复杂很多老工程师还是习惯Utility的操作节奏。第三对于ST-LINK/V2这种老款调试器ST-LINK Utility的兼容性反而更好。有些第三方ST-LINK或者老版本固件在CubeProgrammer下会报错但在Utility里依然能稳定连接。当然如果你用的是STM32H7系列或者新型号芯片ST-LINK Utility可能不支持部分新功能这时候建议用CubeProgrammer。我自己的习惯是老型号芯片、快速设置RDP用Utility新型号、需要配置复杂选项字节或做量产编程时用CubeProgrammer。3.2 连接前的准备工作驱动、硬件与状态确认工欲善其事必先利其器。在用ST-LINK Utility设置读写保护之前有几个细节容易被忽略。驱动是第一个坎。插上ST-LINK之后如果电脑设备管理器里显示的是未知设备或者黄色感叹号说明驱动没装好。ST-LINK Utility安装包自带驱动也可以在ST官网下载STSW-LINK009这个独立驱动包。我用过很多次装完驱动后务必重启一下软件否则工具可能仍然识别不到调试器。硬件连接上SWD接口只需要四根线SWDIO、SWCLK、GND、3.3V如果目标板有独立供电也可以不接VCC但必须共地。有些朋友只接了两根数据线不接地结果连半天连不上其实就是共地问题。设置RDP过程中芯片供电一定要稳定建议使用外部稳压电源或确保ST-LINK的3.3V输出电流足够避免在写选项字节时电压跌落导致写入失败。连接之前还要确认目标芯片没有被其他软件占用。比如Keil里打开了调试会话并占用着SWD口这时候再用ST-LINK Utility连接可能会提示无法建立连接。关闭Keil的调试界面拔插一次ST-LINK再重新连接基本都能解决。4. 设置读写保护实操从连接芯片到确认状态每步都不能省4.1 第一步连接目标芯片并读取当前选项字节打开ST-LINK Utility点击工具栏最左侧的Connect图标或者通过菜单Target - Connect。软件会弹出连接配置对话框这里有几个关键配置需要看清。如果ST-LINK Utility能自动识别出芯片型号Connection Mode选择Normal即可。Mode下拉框里有Normal、Under Reset、Hot Plug等选项。Normal模式适用于芯片正常供电、且当前没有启用Level 2保护的情况Under Reset模式会在连接过程中强制拉低复位引脚适合读取保护已经开启、或者程序里关了SWD引脚的情况。我遇到过不少芯片程序初始化时把SWDIO和SWCLK引脚重映射成了普通GPIO导致Normal模式连不上。这时候选Under Reset模式在芯片上电后、程序运行前的一小段时间窗口内抓住调试接口就能连上。连接成功之后软件右侧会显示芯片的Flash内容左侧的信息栏会列出芯片型号、Flash大小、UID等。此时先别急着操作点击Target - Option Bytes查看当前RDP级别。新出厂的芯片默认是Level 0如果你拿到的是二手芯片或者别人调过的板子有可能是Level 1甚至Level 2这时候根据实际情况决定操作方向。4.2 第二步将RDP从Level 0切换为Level 1在Option Bytes对话框里找到Read Out Protection这个区域里面有一个下拉选择框选项是AALevel 0和BBLevel 1之类的编码。不同芯片显示方式略有差别有些直接显示Level 0/Level 1原理都一样。选择Level 1之后点击OK或Apply按钮ST-LINK Utility会提示即将写入选项字节。确认写入后软件会执行一次擦除操作并写入新的选项字节配置。注意一个细节从Level 0切换到Level 1Flash中的数据会保留不会自动擦除。这也是为什么我们可以在程序烧录完成之后再单独设置读保护不会影响已有固件。写入完成后建议断开连接然后重新上电再次连接芯片并查看Option Bytes确认RDP已经变成Level 1。养成这个回读确认的习惯非常重要我见过不少同事设置了保护但没有验证结果后来发现根本没写进去白忙一场。4.3 第三步验证保护是否真正生效验证保护生效最直观的方法就是尝试用ST-LINK Utility读取Flash。连接上Level 1保护的芯片后软件会弹出提示说明当前芯片处于读保护状态无法读取Flash内容。如果你执行Read操作读取出来的数据会全是0xFF或者直接报错。这恰恰说明保护已经生效了。还有一个验证维度是查看能不能擦除。Level 1状态下调试接口仍然允许执行全片擦除操作Erase但擦除之后RDP会自动回到Level 0Flash内容也全部清空。这也解释了为什么很多破解行为最终只能得到一颗空片——攻击者可以擦除你的芯片但拿不到你的固件。如果你的产品需要在现场升级固件那就要考虑另一种情况Level 1保护开启后程序自身的IAPIn-Application Programming功能不受影响你可以在App里通过串口或网络接收新固件并写入内部Flash。因为IAP是CPU自己执行程序去写Flash跟调试接口无关所以RDP不会限制这部分功能。5. 解除读写保护的正确方法与不可逆风险5.1 解除Level 1保护的标准流程需要解除读保护的场景也不少见。比如产品退回维修、旧板子要重新烧录新固件或者你自己忘了芯片已经上锁想重新调试。解除Level 1保护的方法和设置过程类似。连接芯片后打开Option Bytes对话框在Read Out Protection下拉框里选择Level 0点击Apply。ST-LINK Utility会弹出一个警告框内容大意是关闭读保护将触发Flash全片擦除所有数据都会丢失。确认之后软件先执行擦除再写入Level 0的选项字节配置。这个全片擦除行为是芯片硬件层面的设计不是软件能绕过的。从安全角度看这是必须的如果允许不擦除就降级到Level 0那设置读保护还有什么意义攻击者只要降级就能读出固件了。所以每次解除保护都意味着固件清空之后需要重新烧录程序。解除完成后芯片恢复为完全开放状态调试接口可以正常读写Flash。在开发过程中如果你需要反复修改固件一般不需要每次烧录前都解除保护——直接使用编程功能即可ST-LINK Utility在检测到Level 1保护时会自动提示先擦除擦除后RDP自动归零再烧录新程序整个过程说白了一次搞定。5.2 Level 2保护不存在后悔药Level 2解除这个问题我必须非常明确地说从Level 2回到Level 1或Level 0是不可能的。这是芯片的硬件熔断机制不是软件没设计好。具体表现是当芯片处于Level 2状态时调试接口所有功能都被禁用包括连接、读取、擦除、选项字节修改。你在ST-LINK Utility里点连接软件根本识别不到芯片更别说改RDP等级了。可能有人会说通过串口系统Bootloader能解除吗答案也是不能。Level 2状态下系统Bootloader同样被禁用。所以我的建议非常保守不要在生产过程中轻易设置Level 2。除非你有严格的软件冻结流程、固件永久不再更新、且备货量足够。否则一旦产品上市后发现一个严重bug而芯片又是Level 2保护那你只能换芯片板子上的旧芯片等于报废。这个代价有时候比固件泄露还大。5.3 解除保护之后必须做的两件事第一件事是重新烧录完整固件。因为降级RDP必然擦除Flash包括选项字节里的其他配置比如独立看门狗设置、硬件看门狗配置等也会被恢复成默认值。烧录程序之后建议把其他选项字节也一并检查一遍确保和量产配置一致。第二件事是更新目标文件记录。我习惯在项目目录里维护一个烧录记录表格记录每块板子的芯片UID、烧录日期、固件版本号、RDP等级。解除保护这种操作尤其是返修板一定要在记录里标注清楚避免后续不知道这块板子的保护状态。6. 实战避坑误锁、连不上、保护不起效的排查链路6.1 设置完保护后发现连不上芯片了别急着重装驱动这是一个高频问题也是最容易让人慌的场景。一次我给一块STM32F103板子设置Level 1保护后重新上电结果ST-LINK Utility报错无法连接。我当时第一反应是芯片坏了后来排查了一圈才发现问题出在芯片的调试引脚复用配置上。具体背景是那个程序在初始化里把SWDIO和SWCLK两个引脚重映射成了普通GPIO功能。在Level 0状态下即使程序把SWD引脚复用掉了调试器依然可以抢占连接但某些STM32型号在设置RDP之后调试接口的连接时序变得更加严格程序跑起来之后很快就切断了调试通道。解决办法很简单连接模式改用Under Reset。ST-LINK Utility会在复位期间尝试连接芯片在程序正式运行之前抓住调试接口。具体操作是Connect对话框里把Mode改成Under Reset然后重新连接。一般情况下都能成功之后再按正常流程解除保护或进行擦除。6.2 排查链路连接失败的五个检查点当你在ST-LINK Utility里遇到连接失败不要上来就怀疑芯片锁死。我总结了一个固定排查顺序基本能解决90%的问题。第一看ST-LINK的驱动是否正常。设备管理器里如果能看到STMicroelectronics STLink dongle之类的设备驱动就正常如果显示未知设备先重装驱动。第二看硬件连接。SWDIO、SWCLK、GND三根线是不是都接好了地线虚焊是常见的隐蔽问题。用万用表量一下ST-LINK和目标板的GND是否导通。第三看目标板供电。触摸一下芯片表面有没有明显发热用万用表量VDD对GND电压是否在正常范围。芯片没供电或者供电异常时连接必然失败。第四看连接模式。Normal连不上就试Under Reset和Hot Plug。Hot Plug模式适合目标板已经上电的情况而Under Reset模式可以在芯片复位瞬间抢占连接。第五看是否被其他软件占用。Keil、IAR、CubeProgrammer如果正在使用同一个ST-LINKUtility是抢不到设备的。关掉其他软件、拔插一次ST-LINK再试。按照这个顺序排查大部分死锁其实都能救回来。6.3 保护层级与开发流程的配合最后说一个我踩过坑之后的体会读保护一定要和项目的开发流程、生产流程绑定而不是当成一个孤立的操作。开发阶段我会把RDP保持Level 0方便反复调试和烧录。出测试样机时切换为Level 1同时完整验证一次IAP升级流程确保程序在保护状态下能够通过应用层升级固件。量产阶段工厂烧录时默认烧录固件后再设置Level 1这一步可以写进生产文档里让产线测试人员按步骤执行避免漏设。如果你用ST-LINK Utility做批量设置可以先把整个操作流程录成操作视频或者写一个带截图的标准作业指导书。掌握几百片板子逐一去设置RDP很枯燥也容易出错。实测下来用ST-LINK Utility连接-设置RDP Level 1-断开每片板子大概20到30秒这个效率对于小批量生产完全能接受。大批量生产则可以考虑ST官方或第三方的离线烧录器直接把保护配置写进烧录脚本里。有一段经历让我印象很深一个客户把产品外包给我们做程序调通后他们没有设置任何RDP保护就出货了。三个月后市场出现了完全模仿的产品连串口打印信息都一样。后来我们紧急帮他们产出一版开启Level 1保护的固件重新烧录出货。但已经流出去的旧设备是没办法追回的只能靠后续产品迭代来弥补。这就是我为什么反复强调——RDP保护应该从第一版产品就要做而不是等出事之后才想起来。
阅读完成 · 觉得有帮助?