1. 项目概述为什么要禁止SWD和ISP升级1.1 核心需求解析先把这个标题拆开说清楚。SWD是STM32的串行调试接口通过四根线SWDIO、SWCLK、GND、VCC就能连接目标板实现程序下载、在线调试、寄存器读写、内存查看。ISP则是系统存储器引导STM32出厂时在系统存储区烧录了一段Bootloader只要把BOOT0引脚拉高再复位芯片就会进入系统存储器模式通过USART1、USB或者CAN等接口就能把固件写进Flash。这两条通道本质上是给开发者留的“后门”方便产品在研发阶段调试、在生产阶段烧录、在售后阶段升级。但问题在于这两条通道对产品的实际使用者同样是敞开的。任何一个拿到产品的人只要拆开外壳接上SWD调试器就能把芯片里的固件读出来或者把BOOT0引脚拉高通过串口就能查看或篡改Flash内容。对于投入了大量研发成本的产品来说这是最致命的软肋——固件一旦被读出核心算法、通信协议、加密密钥全部暴露仿制者甚至可以抄作业式地复制整个产品。所以标题里说的禁止SWD和ISP升级实际上是要做两件事一方面禁止外部通过SWD读取/调试另一方面禁止外部通过ISP重新烧录/篡改。而实现这两种禁止的核心手段就是操作STM32 Option Bytes中的RDPRead Protection位也就是俗称的读保护。HAL_FLASH_Program这个函数在HAL库中负责Flash编程它不仅能写主Flash区还能写Option Bytes区是设置读保护的标准入口。这个项目适合谁我觉得至少三类人会用到一是做商用产品的嵌入式工程师产品有实际的量产和防抄板需求二是在做毕业设计或竞赛项目的同学作品需要拿出来展示不想被别人轻易读出代码三是给客户做定制开发的外包开发者交付时需要保护自己的代码不被对方的其他合作方拷贝。不管属于哪一类读完这篇都能拿回去直接用。1.2 防护思路与整体方案读完保护这个需求第一反应可能是那我禁用调试接口就行了。但实际操作过就知道单纯禁用某个调试接口只是表层手段更不能指望把SWD引脚复用成普通GPIO这种方式——因为调试接口的使能在芯片复位后由Option Bytes决定应用程序里关闭调试接口一旦复位调试接口又会因为Option Bytes的配置重新恢复可用。真正能从根本上把口子堵死的办法只有一个设置Flash的读保护等级。读保护分为三个等级Level 0表示无保护Level 1是标准读保护Level 2是最高级保护且不可逆。对于绝大多数产品Level 1够用——它禁止通过SWD或者ISP读取Flash内容也禁止在调试模式下访问内存但允许通过修改Option Bytes降级回Level 0代价是芯片会执行全片擦除。也就是说哪怕保护被解除固件数据也已经没了。Level 2更极端直接永久禁止所有调试接口连解保护这条路都焊死芯片变成只写不读的封闭体适用于对安全要求极高的行业。但Level 2一旦设置就等于放弃了后续所有的在线调试和升级通道量产前的功能验证必须在设置Level 2之前完成否则芯片只能返厂换新。本项目的实现方案是用HAL库函数对Option Bytes执行编程把RDP设置为Level 1并配合复位加载使保护在芯片下次启动后即刻生效。成品模式下SWD和ISP这两条路就同时被封死了。2. STM32调试接口与ISP通道的原理拆解2.1 SWD调试接口的工作机制SWD全称Serial Wire Debug相对于传统的JTAGJoint Test Action Group接口它只占用两根信号线SWDIO负责数据双向传输SWCLK负责时钟同步。STM32内部集成了一套调试访问接口单元调试器通过SWD协议可以对内核寄存器、内存映射、Flash、外设寄存器进行访问。在调试模式下调试器可以先暂停内核然后通过AHB-APAdvanced High-performance Bus Access Port直接读写目标地址空间。这意味着什么意味着只要SWD连接可用调试器就能用一条读命令把Flash中从0x08000000开始的全部数据拷出来甚至可以使用批量读取模式几秒钟就能把几十KB固件dump到本地。更麻烦的是这种读取行为在用户程序运行时也是可行的——调试器随时把内核halt住此时CPU被暂停不会执行任何代码固件读取就不会受到应用程序主动设置的软件防读逻辑干扰。所以只要RDP不是Level 1或以上SWD读取就只是接四根线的问题。有的开发者会想我不接调试器给用户的产品不引出SWD接口不就行了这确实是基本的PCB防护手段但并不充分。一是PCB上往往因为生产烧录需要保留SWD测试点或排针二是即便没有引出维修人员用电表一探就能找到或者使用读芯片的方式直接飞线。对于终端用户而言拆外壳飞线可能还有门槛但对于竞品公司或者批量仿制者这完全不是障碍。RDP这种芯片级的保护才是从源头上堵住读取途径的措施。另外SWD还有一个容易被忽略的细节即使不在调试模式下调试接口也会响应调试器的IDCODE查询。也就是说接上调试器至少能读出芯片的IDCODE标识芯片型号的编号这本身就可能暴露产品选用的芯片型号。如果客户不希望竞对知道自己用的是哪一颗MCU设置读保护后IDCODE读取同样被禁止调试器只能返回0x00000000。2.2 ISP系统存储器引导的工作机制ISP在STM32上指的是System Memory Bootloader。芯片出厂时ST在系统存储区通常位于0x1FFF0000附近不同系列地址不同烧录了一段独立的引导程序。当芯片上电或复位时如果BOOT0引脚为高电平BOOT1可配置芯片的启动代码就会跳到系统存储区执行这段Bootloader。Bootloader启动后会通过串口/USB/DFU等外设接口监听主机命令主机使用ST官方的Flash Loader软件或者自写的协议工具就可以对芯片主Flash进行读、写、擦除、校验等操作。这段Bootloader是只读的用户无法修改也无法删除所以只要BOOT0引脚还有机会拉到高电平芯片就永远存在一条物理后门。哪怕产品固件里做了再多的软件防读逻辑只要进入系统存储器模式Bootloader在用户程序运行之前就已经接管了芯片用户所有防御代码都来不及执行。举个例子一款产品正常加电运行固件但维修人员把芯片的BOOT0引脚强拉到3.3V再复位芯片启动后进入系统存储器模式此时串口接上STM32的USART1主机就能发命令读取Flash内容。即使产品PCB上没引出BOOT0只要用镊子直接拨动芯片相邻引脚或者从靠近芯片的走线飞线一样可以达到同样效果。所以防ISP单靠PCB设计和引脚复用也不够必须从Option Bytes层面禁止对Flash的读访问。当RDP为Level 1时系统存储器Bootloader会对读Flash和写Flash命令执行权限检查允许擦除Flash允许写入新固件这个很有讲究FT引脚出厂保护需要保留防止产品因为变砖失去远程升级能力但不允许读取固化在Flash中的固件内容。这就在保留失效恢复能力和防止固件被抄之间取了平衡。如果一台产品需要返厂维修工程师可以在不知道原有固件内容的前提下直接擦除并重写固件只是原固件数据已经被擦除拿不到了。2.3 为什么软件层面的防读不靠谱时常有客户问我我在代码里加一个判断如果检测到调试器就死循环或者把Flash配置成写保护能不能达到同样效果答案是能撑一会儿但撑不住。原因有三第一软件断言的保护逻辑只在用户程序运行期间有效。调试器可以在程序启动早期甚至在Reset向量处就把内核halt住用户的启动代码可能还没执行到防御逻辑内核已处于停摆状态此时调试器连CPU主频、Flash等待周期都不需要设置直接通过调试接口读取Flash即可。第二Flash写保护WRP保护的是写入而不是读取。WRP的初衷是防止程序意外覆盖关键区域它并不禁止调试器从Flash读取数据所以设置WRP对防读毫无意义。很多工程师把写保护和读保护混淆实际动手后发现固件依然能被dump出来就是这个原因。第三一旦BOOT0拉高进入系统存储器用户代码压根不会被执行无论软件防御逻辑写的多严密都是空转。所以结论很明确能同时对SWD和ISP两路通道生效、且从芯片硬件层面执行的防护只有RDP读保护。3. 方案落地Option Bytes与HAL_FLASH_Program的完整实操3.1 读懂Option Bytes在Flash中的位置Option Bytes是芯片中独立于主Flash的一块存储区域存放的是芯片的行为配置。以STM32F4系列为例Option Bytes分布在0x1FFFC000附近不同系列有差异F1系列是0x1FFFF800附近L4系列是0x1FFF7800附近包含RDP读保护级别、WRP写保护、BOR_LEV掉电复位阈值、硬件看门狗设置、BOOT配置等字段。芯片的Flash接口控制器在启动阶段会加载Option Bytes如果配置有非法值Flash控制器会报告错误甚至导致芯片无法启动。设置读保护本质就是把Option Bytes中的RDP字段从0xAALevel 0修改为其他值Level 1例如0xCCLevel 2例如0xEE各系列具体值以参考手册为准。HAL库封装了专门的宏例如F4系列提供OB_RDP_LEVEL_0、OB_RDP_LEVEL_1、OB_RDP_LEVEL_2F1系列提供OB_RDP_LEVEL_0和OB_RDP_LEVEL_1开发者不用记裸值直接用宏更安全。一个常见误区是直接在HAL_FLASH_Program传入Option Bytes的Flash地址然后写数据这个流程在不同系列上是不同的。在F1系列上Option Bytes是按地址映射的方式操作的写RDP时ByteAddress传Option Bytes区域的RDP字节偏移地址即可但在F4系列上Option Bytes的操作走的是寄存器FLASH_OPTCRHAL库内部会先把用户配置组装成OPTCR寄存器格式再触发Option Bytes编程请求。所以最稳妥的做法是优先使用HAL库提供的Option Bytes编程专用流程而不是直接照搬主Flash编程的方式。3.2 标准操作流程设置Level 1读保护这里给出一段可以直接复用的代码基于STM32F4系列和HAL库目标是把芯片设置为Level 1读保护随后复位使保护生效。核心思路是解锁Flash、解锁Option Bytes、组装RDP配置、发起Option Bytes编程、触发加载、复位。#include stm32f4xx_hal.h /** * brief 设置RDP Level 1读保护 * note 调用后芯片复位读保护立即生效。 * 生效后SWD调试接口无法读取FlashISP模式下无法读取Flash内容。 */ void Set_ReadProtection_Level1(void) { FLASH_OBProgramInitTypeDef ob_config; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); /* 读取当前Option Bytes配置 */ HAL_FLASH_OB_GetConfig(ob_config); /* 修改RDP级别为Level 1 */ ob_config.RDPLevel OB_RDP_LEVEL_1; /* 其余Option Bytes配置保持原值 */ /* 注意如果没有读取现有配置直接清零可能会误改其他选项 */ /* 将配置写入Option Bytes */ HAL_FLASH_OB_Program(ob_config); /* 触发Option Bytes加载芯片自动复位 */ HAL_FLASH_OB_Launch(); /* 保险起见如果Launch没有触发复位手动复位 */ NVIC_SystemReset(); }有几个关键细节要重点说明。一是解锁顺序。Flash和Option Bytes是两套独立的锁必须先解锁Flash再解锁Option Bytes顺序反了会返回HAL_ERROR。在F4上HAL_FLASH_Unlock会操作FLASH-KEYR写入解锁密钥序列HAL_FLASH_OB_Unlock会操作FLASH-OPTKEYR写入另一组密钥序列。执行完HAL_FLASH_OB_Program之后建议立即调用HAL_FLASH_OB_Lock与HAL_FLASH_Lock避免后续其他误操作。二是组合式的OB_Program。F4系列的Option Bytes编程不是按单个字节写入的而是通过FLASH_OPTCR寄存器一次性写多个位包括RDP、BOR电平、IWDG配置、nRST_STOP、nRST_STDBY等。HAL_FLASH_OB_Program会读取用户传入的FLASH_OBProgramInitTypeDef结构体把除了RDP之外的其他字段比如BORLevel、UserConfig、WRPArea等一并写入OPTCR。所以这里最忌讳的就是直接给结构体清零再调用那样会把BOR阈值、硬件看门狗等配置也改掉可能导致芯片启动行为异常。正确做法是先HAL_FLASH_OB_GetConfig把当前值读出来再修改你要改的字段。三是OB_Launch的作用。Option Bytes的修改不是即时生效的必须由软件触发Flash接口执行一次加载Option Bytes的操作芯片才会按新配置重新初始化。在F4中HAL_FLASH_OB_Launch会设置OPTCR的OPTSTRT位触发Option Bytes的重新加载并自动产生系统复位。这个过程会有短暂的延迟有些HAL版本实现里会等待加载完成有些不会所以最好在Launch之后加一句NVIC_SystemReset确保新配置无论如何都会在复位后生效。3.3 F1、F4、L4等主流系列的差异虽然HAL库把Option Bytes编程的API统一成了HAL_FLASH_OB_Program但底层细节在各系列上仍有讲究。用F1和F4做对比就看得出来。F1系列的Option Bytes被组织成独立字节有专门的字节地址比如RDP字节地址默认是0x1FFFF800。在这个系列上如果只想改RDP甚至可以用最原始的裸寄存器方式解锁Flash后直接写FLASH-OB和FLASH-RDP但HAL库不推荐绕过API。HAL在F1上会通过HAL_FLASHEx_OBProgram旧库或HAL_FLASH_OB_Program新HAL把用户数据映射到Option Bytes地址空间操作方式接近编程主Flash但有不同的约束时长上要求OptionsBytes编程完成后需要等待OPBT忙标志清除。L4系列的结构和F4类似Option Bytes的加载同样通过触发FLASH_CR寄存器里相关位来实现但L4增加了双Bank架构Option Bytes同时包含Bank区域的配置操作时要注意WRP区域选择。L4系列的Flash是单电源电压架构PGA编程对电压有要求编程电压不匹配会返回HAL_ERROR。对大多数项目而言只要用HAL_FLASH_OB_GetConfig读取旧值、HAL_FLASH_OB_Program写入新值、HAL_FLASH_OB_Launch加载就能跨系列通用。这套三连操作比手动拼寄存器靠谱得多。3.4 解除读保护的操作流程与风险提示有设置就有解除。在量产返修或者代码迭代时工程师需要解除Level 1保护。解除Level 1的唯一合法途径是再次修改Option Bytes把RDP降回Level 0。这个动作有一个铁律芯片会先执行全片擦除Mass Erase把主Flash所有内容清空然后才允许RDP降级。也就是说解保护和清固件是绑定的不可能在不擦除Flash的情况下解除Level 1保护。解除操作的代码和设置类似但把RDPLevel改成OB_RDP_LEVEL_0。void Clear_ReadProtection(void) { FLASH_OBProgramInitTypeDef ob_config; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); HAL_FLASH_OB_GetConfig(ob_config); ob_config.RDPLevel OB_RDP_LEVEL_0; HAL_FLASH_OB_Program(ob_config); HAL_FLASH_OB_Launch(); NVIC_SystemReset(); }执行这个函数前心态要摆正该芯片上所有固件、校准数据、出厂参数、日志信息全部会被擦除。如果在生产线上测试固件时误触发了解保护那就只能重新烧录生产节拍会被拖慢。所以解除读保护的代码只应该在明确的返修指令下调用不能在出厂固件里留下自动解保护的入口。还有一个非常容易踩的坑在不同系列的Option Bytes编程机制里降级和升级的擦除范围并不完全一样。在F4上从Level 1降级到Level 0擦除的不只是主Flash还包括Option Bytes区本身这会连带把WRP配置、BOR配置等一并恢复为出厂默认而不是保留你之前设置的其他选项。所以在降级后如果产品还要继续批量复用需要重新配置一次完整的Option Bytes方案不能只在代码里写降级保护这么简单。关于Level 2我的建议是不要轻易碰。Level 2一旦设置SWD和ISP在芯片生命周期内彻底关闭而且这一操作完全不可逆。后续如果需要固件升级只能走用户程序自定义升级通道比如OTA、串口升级且必须在设置Level 2之前把升级引导程序固化好。设置Level 2前要反复斟酌我有客户为了追求绝对安全早早就设了Level 2结果产品发布后发现协议需要改OTA bootloader又没做只能把板子上的芯片整个换掉成本比预期高了一个数量级。4. 生产环境中的保护方案设计4.1 量产烧录流程中的保护时机读保护不是越早设置越好。在生产流程中读保护的设置时机要仔细排布。一个典型的量产流程是贴片完成后先用SWD烧录出厂固件此时芯片是无保护状态烧录速度快、方便在线校验然后执行一次设置Level 1的代码或者命令最后再验证读保护是否生效。如果验证通过产品才能装箱出厂。为什么不一开始就烧录带读保护的芯片因为一旦设置了Level 1后续SWD调试功能被禁止生产测试工具就无法通过SWD读取Flash中的校准值、序列号等数据。很多产品的校准流程需要边测边写参数这个步骤在Level 1状态下会变得非常别扭。所以常规做法是不受保护地完成所有烧录、校准、写入序列号的操作最后一次性开启读保护。生产烧录时有几种方案可以实现设置读保护。方案一出厂固件里预埋一段受控代码比如上电后检测某个特殊标志位用户按键组合、串口命令字等检测到后执行Set_ReadProtection_Level1并复位。这样做的好处是生产线上不需要额外的烧录器操作坏处是这段代码给产品留了一个软件后门如果被逆向分析出触发方式攻击者可能利用它做其他操作。一般来说量产固件不建议预留这种带条件的保护代码还是烧录器直接操作Option Bytes更干净。方案二使用支持Option Bytes编程的烧录工具在烧录完成后由工具下发一条设置RDP Level 1的指令。多数烧录器比如常见的J-Link、ST-Link、以及部分第三方脱机烧录器都支持通过命令行或者图形界面设置读保护。这种方案不需要在固件里加任何保护代码烧录流程完全可控推荐给有批量生产需求的团队使用。方案三开发一个专门用于产线的封板小工具通过串口与产品通信写入一段专门的保护命令产品收到后调用HAL_FLASH_OB_Program设置保护并复位。适合自定义产线软件的公司灵活性最高但需要额外开发量。我自己常用的路径是方案二为主、方案一为辅产线批量出活用烧录器设置保护工程样板或者样品演示时用固件内置命令设置保护两边互不冲突。4.2 设置保护后的验证与确认手段设置完读保护不能拍脑袋说应该生效了。一定要做一次冒烟验证否则等产品发到客户手里发现固件还能被读出来这个责任是要背的。验证手段有两种一种是硬件层面验证另一种是软件层面验证。硬件层面最简单的做法在设置保护后重新连接SWD调试器尝试读取Flash内容。如果保护生效调试器初始化时会报Could not connect to target或者 Cannot access memory在IDE里会显示连接失败或者目标无响应。对于ISP使用Flash Loader工具连接串口尝试读取FlashLevel 1下读命令会被拒绝。如果这两条路都读不出内容说明保护基本生效。软件层面的验证在设置保护之后的代码里调用HAL_FLASH_OB_GetConfig读取当前RDPLevel打印或者通过串口上报确认读到的确实是OB_RDP_LEVEL_1。这个验证方式无法百分百模拟外部攻击者的路径但至少能确认Option Bytes写入成功。我建议两种方式都做尤其是批量生产时可以在产线工位上加一道读保护验证的工序防止因为芯片批次或者烧录器脚本配置错误导致保护失效。还有一个细节检测SWD连接是否被禁止不建议用IDE连接作为唯一标准因为有些调试器在Level 1下仍然能够与芯片建立部分连接尤其是在某些系列和调试器组合下连接时内核能够halt只是不能访问Flash。更严谨的验证方式是尝试读取Flash地址0x08000000只要能读出非全0xFF的数据就说明保护没有完全生效。4.3 保护与升级之间的矛盾平衡设置读保护后产品就不能再通过ISP或者SWD进行在线升级了。但商业产品往往有远程升级需求这时候需要区分调试接口升级和应用层升级。读保护禁止的是通过调试接口/系统存储器访问Flash并不禁止用户应用程序自己操作Flash。也就是说只要固件里实现了OTA或者串口升级功能在Level 1保护下依然可以正常升级新固件——应用程序运行后自己通过HAL_FLASH_Program写入新的固件到主Flash区域这是完全合法的。所以读保护与OTA升级并不矛盾刹车要踩在外部调试口这一层而不是关闭应用自己更新Flash的能力。实际项目里我建议在产品软件架构中把启动加载程序Bootloader和应用固件App分离Bootloader里实现串口升级、OTA升级引导、固件校验等功能App在启动时正常跳转。量产时Bootloader在设置读保护之前烧录App则可以通过Bootloader在受保护状态下远程更新。这套架构在Level 1下运行稳定既能防抄板又保留了后续迭代的基础设施。5. 常见问题排查与避坑经验5.1 设置读保护后芯片无法复位的排查有些工程师反馈设置完读保护后代码执行完HAL_FLASH_OB_Launch没有自动复位程序卡死在原地调试器也连不上了。这种情况大概率是OB_Launch之后没有做复位等待。HAL_FLASH_OB_Launch会触发Option Bytes加载并请求系统复位但如果当前CPU主频较高、Flash接口繁忙加载过程耗时可能超过函数内部轮询的超时时间。解决方式是在Launch之后主动加一个延时或者直接调用NVIC_SystemReset。还有一种情况Option Bytes加载失败后芯片进入受保护但不完整的状态SWD接口被禁止程序又不在运行状态看起来像砖头。此时用调试器连接不上是正常的不要慌先检查BOOT0引脚是否被拉高、供电是否正常、NRST是否被拉低排除硬件层面的问题。如果确实是Option Bytes配置异常有些系列可以通过串口系统存储器模式重新擦除恢复恢复后Read Protection自动变为Level 0。5.2 HAL_FLASH_Program返回HAL_ERROR的排查在调用HAL_FLASH_OB_Program写Option Bytes时如果返回HAL_ERROR优先级最高的问题一般是编程电压和擦除电压不匹配。在部分STM32系列上尤其是F4Flash编程电压范围要求是2.7V~3.6V如果芯片供电低于2.7VHAL库内部的校验逻辑会拒绝编程。这个在量产工位上特别值得注意有些电源适配器在负载波动时电压会掉到2.6V左右导致编程失败。排查时先看电压再看解锁状态然后检查读取的Option Bytes配置中是否有其他错误标志。另一个常见原因是Flash没有正确解锁。HAL_FLASH_OB_Unlock失败时HAL库会返回HAL_ERROR而这个函数的内部实现会先判断FLASH_CR的LOCK位。如果之前调用过HAL_FLASH_Lock而当前代码又试图直接解锁Option Bytes但没有先解锁主Flash也会失败。总之遵循固定顺序HAL_FLASH_Unlock - HAL_FLASH_OB_Unlock - 操作 - HAL_FLASH_OB_Lock - HAL_FLASH_Lock。5.3 解保护后代码全部丢失的回答这个问题在售后咨询里出现频率非常高我只想解除读保护为什么程序全没了答案是这不是bug这是Level 1读保护的硬件行为。从Level 1降级到Level 0时芯片为了确保之前写入的数据不会在解除保护后仍被读取必须把主Flash全部擦除。这在芯片设计上是为了防止攻击者绕过读保护获取固件的保护机制无所谓好与不好产品在生产计划必须提前把这个全片擦除的成本算进去。我处理过一批需要返修的产品。技术员在产线上执行了解读保护操作结果发现返修完成后还需要重新校准工位流程从读旧数据-改参数-写回变成了重新烧录-重新校准多花了近两倍时间。后来我调整了返修SOP凡是需要解保护的设备一律默认固件需要重烧产线预先准备烧录文件返修时间缩短了不少。这就是在硬件限制下优化流程的典型思路。5.4 有没有办法不擦除Flash直接解Level 1这个问题经常出现在论坛和群聊里。我的回答是对商用产品来说不应当也不可能。绕过读保护的方法可能存在黑产和实验室环境下研究的成果但那超出了正常产品开发的范畴。作为嵌入式工程师应该做的是在流程上规避需要解保护又不想丢数据的尴尬局面比如产品数据结构上做好分区序列号、校准数据等关键信息在解保护前先外发备份或者采用数据冗余存储的方式确保重烧后能快速恢复出厂参数。客户有时会问我在同一颗芯片里烧两套固件一套带保护一套不带切换BOOT选择行不行这种方式在少数支持多启动区域的系列上确实可行但两套固件共用同一Flash区域保护状态只能全局设置不存在区分区域保护等级的能力。所以这类方案劳心费力收益不大不建议走。5.5 关于量产固件的安全底线最后说一个容易被忽视的问题。很多工程师设置读保护之后就认为万事大吉但读保护只是防住了普通用户和低门槛竞对对于专业逆向团队来说芯片解密是另一个层面的攻防。不要在产品里硬编码非常敏感的密钥不要依赖固件没人能读出来来保护核心资产。更合理的做法是硬件读保护防住常规手段重要算法和密钥用安全存储区或外部安全芯片保存关键通信使用一机一密、加密传输。安全没有绝对的只有一层一层叠读保护是叠出来的第一层也是性价比极高的一层。我在实际开发中的体会是设置读保护这件事看起来就几行代码但牵扯到量产流程、返修方案、OTA设计、芯片选型等多个环节需要提前规划而不是最后补一刀。有部署了读保护的产品在户外设备维护、PLC设备、智能家居网关等长时间无人值守的场景里表现出的安全性要远高于裸奔固件。这套方案做下来最直接的收益是同型号竞品想要抄你的逻辑至少要付出十倍以上的时间成本和专业工具成本绝大部分仿制者在这一步就会放弃。