资讯动态

嵌入式安全防抄板:固件保护、安全启动与密钥管理实战

发布时间:2026/8/27 4:55:20 来源:尧图企业网站定制
做嵌入式产品最怕遇到什么不是功能做不出来而是方案好不容易定型、量产出货三个月市场突然出现一个和你几乎一模一样的板子固件读出来连串口日志都一样。做MCU开发这些年类似的故事我见过不少很多团队前期一门心思扑在功能实现上等到被抄了才开始补安全课大概率已经晚了。这篇文章想聊的是嵌入式安全与IP保护说人话就是三个字防抄板。围绕固件保护、安全启动、硬件防护、量产密钥管理这几条线展开把我自己在实际项目中的踩坑记录和那套能落地的方案完整拆出来。内容面向嵌入式软件工程师、硬件工程师和技术管理者只要你做的产品里有MCU、有固件、有算法就值得花十分钟看完。1. 先说清楚嵌入式设备到底在怕什么1.1 从一次真实的固件风波说起我有一个做工业控制器的朋友前后两年时间打磨了一套运动控制算法产品上市后客户反馈很好。然而第三个月市场上出现了另一家厂商的控制器外观、接口、上位机协议几乎一致甚至接上老客户的软件都能直接跑通。他们当时还以为是内部有人泄露了源码最后拆机检查发现对方用了同样的MCU型号直接把Flash里的固件读出来逆向分析不仅代码逻辑被看光了连算法库也被抄了个彻底。这不是什么高端黑客手段说白了就是利用调试接口、读保护配置遗漏以及固件里明文存放的字符串把整个产品裸奔在别人眼皮底下。你说固件被抄了能怎么样功能一样、价格更低你的客户流失了积累的Know-how变成了别人的免费午餐。这背后暴露出的核心问题就是很多嵌入式团队压根没有把安全和IP保护当作一项设计输入来做而是当成了“如果有空再搞”的可选项。1.2 嵌入式安全威胁模型哪些资产最值钱要谈防护先得回答一个问题你的产品里到底什么最值钱我通常把嵌入式设备的资产分成四层每一层都有对应的威胁方式资产层典型内容主要威胁固件代码业务逻辑、状态机、算法实现直接读出Flash、反汇编分析核心算法PID参数、加密密钥、AI模型权重从固件中提取常量、模式匹配通信协议私有协议帧格式、认证握手流程通过串口/总线抓包分析硬件设计原理图、PCB布局、器件选型拆机抄板、再流焊替换这四层里最容易丢的是固件代码因为只要MCU的读保护没开任何人都可以通过烧录器把Flash内容完整读出来然后加载到反汇编工具里慢慢分析。最致命的是核心算法很多人习惯把密钥、校验向量、算法表直接define在头文件里编译进固件这些常量在二进制文件里就是一个个明晃晃的十六进制串配合一张常量地址表逆向者很快就知道哪个数组是S盒、哪个是查表系数。所以做嵌入式IP保护之前你先要做一次资产盘点明确哪些是必须死守的哪些泄露了顶多损失一点面子。这决定了后面花多少钱、用多大力气来防护。1.3 安全与成本不是线性的再聊一个容易掉进去的误区以为越贵越安全。嵌入式安全防护的成本曲线是阶梯状的——开个读保护几乎零成本但只能挡住初级抄板者叠加启动校验和字符串加密能挡住大多数逆向爱好者成本也依然很低到了用外置安全芯片、算法动态混淆、硬件防侧信道这一档成本才开始明显上升。我的建议是绝大多数商业产品做到第二档就够用了也就是“默认开启读保护 固件签名校验 关键信息加密”。把省下来的防护预算投到产品迭代上比买一堆昂贵安全元件更划算。真正需要上第三档的是那些算法本身有很高商业价值的场景比如专利授权计费、版权保护类的嵌入式模块。2. 硬件层防护把攻击者挡在门外2.1 读保护等级怎么选才不给自己挖坑读保护Read Protection是MCU芯片自带的安全特性不同厂商叫法可能不一样比如STM32里叫RDPNXP有些系列叫FSL或Read-out Protection但思路都是类似的通过配置选项字节来限制调试接口和Bootloader对Flash的访问。以我用得最多的STM32为例读保护分三个等级Level 0无保护调试器可以随意读写Flash和RAM这是开发阶段默认状态。Level 1Flash不可通过调试接口读出但芯片仍可正常跑程序也可以设置新的选项字节。这时候你还能用SWD连接芯片只是读不了代码区域。Level 2最高等级调试端口完全禁用芯片基本变成一个只能跑程序的“黑盒”而且这个状态不可逆不能再降级回Level 0或Level 1。这里有个非常容易踩的坑很多人觉得直接上Level 2最安全结果发现量产之后固件出了Bug需要现场调试但芯片已经没法连接了。更尴尬的是某些MCU进了Level 2之后连读ID都受限返修时工程文件里的Flash内容也没法通过调试器备份。所以我的经验是研发阶段用Level 0试产阶段用Level 1只有确认最终量产版本之前再切换到Level 2并且切换前一定要跟生产确认三遍。那Level 1就够用了吗说实话对于防抄板场景Level 1足够了因为它挡掉了最基础的“直接读Flash烧到另一颗同型号芯片”这种复制方式。大多数抄板者遇到Level 1要么放弃要么得去搞硬件级攻击那就不是普通小厂愿意干的事了。2.2 调试接口和启动引脚的物理处理读保护只能管住“通过调试器读取”但它管不住另一条路Bootloader。很多MCU出厂自带Bootloader比如STM32的System Memory模式可以通过串口或USB往Flash里写程序也能在某些条件下把Flash内容读出来。如果不把启动引脚处理好芯片的引导模式就会成为一个后门。量产版我一般建议这样做把BOOT0引脚和BOOT1引脚都固定接低电平然后在PCB上不给任何方便飞线的焊盘直接过孔连接到地。千万不要在量产板上留一个“方便升级”的跳线帽那是给抄板者搭的桥。调试接口同理——量产板的SWDIO和SWCLK引脚建议串接0欧姆电阻程序烧录完做ICT测试时把这两个电阻贴掉让调试口彻底断连。有一个细节容易忽略有些硬件工程师喜欢把调试引脚同时复用为GPIO控制LED。这看起来省了引脚实际上如果你在代码里把SWD引脚当普通IO用那么即使你开了Level 1读保护调试器连接时也可能因为引脚状态冲突导致芯片死机最后你得花半天排查为什么“保护没开好”。所以量产的板子调试引脚就专职做调试别省那点IO。2.3 外置安全芯片要不要上外置安全芯片大概是嵌入式安全里争议最大的一类器件。它的原理是在MCU旁边放一颗独立的安全元件比如ATECC608A、ATSHA204A这类里面存密钥支持硬件算法加速MCU自己是不保存关键密钥的只在运行时通过I2C或SPI请求安全芯片做签名、解密、生成随机数等操作。这样一来就算攻击者把MCU的Flash完全读走也没有密钥没法在另一颗MCU上复制出同样逻辑。这颗芯片的优势是抗物理攻击能力强普通电钻开盖、探针读总线对它基本无效。劣势也很明显一是每颗芯片都要提前注入密钥或者使用厂商预置的证书生产流程复杂了一些二是增加了物料成本和PCB面积对价格敏感的小家电产品有点伤三是调试时如果I2C总线和安全芯片通信出了问题你很难判断是软件Bug还是芯片没配对。我的判断标准是这样单价超过500块、里面算法有独特商业价值、且竞争对手抄板成本低于500块的产品值得上安全芯片。至于那些几十块钱的消费类产品先用好MCU自带的读保护再叠一层固件校验性价比远高于外置安全芯片。芯片本身不会骗你但方案选型会。2.4 芯片唯一ID低成本的硬件指纹另一个成本几乎为零的手段是使用MCU内置的唯一ID简称UID。每一颗芯片在出厂时都有一个不会重复的标识有些是96位有些更长存放在固定地址的ROM区域。你可以把这个UID参与固件加解密过程比如把UID和某个固定盐值做一个Hash生成一个设备序列号后续所有重要的数据都用这个序列号来加密存储。这个方案的脆弱点是UID值本身是可以从芯片里间接读取的只要攻击者拿到了你用来计算的算法就能把序列号算出来。所以它只能作为第一道门槛挡一挡那些静态复制粘贴的初级抄板者而不是终极防御。我通常把它和读保护配合使用读保护挡住Flash内容UID和序列号绑定让即使读出了Flash也动态校验失败两道坎叠加大部分抄板者就被劝退了。3. 固件层防护提高逆向成本是核心3.1 把关键逻辑藏起来函数分块与分支混淆如果读保护被攻破或者攻击者本身就有同型号开发板做对比测试那固件代码就相当于摊在了桌面上。这时候固件层的软件防护就显得非常重要。核心思路不是一个不留地藏住所有代码而是大幅提高逆向分析的时间成本让抄板的人觉得“不划算”。我最常用的手段是函数分块和分支混淆。拿运动控制里的插补算法举例正常情况下你会写一个Interpolate()函数几百行代码把直线插补、圆弧插补、加减速规划全塞进去。逆向者只要定位到函数入口顺着调用关系就能把算法梳理得很清楚。但如果你把加减速规划拆成AccelPlan、DecelPlan、SmoothPlan三个小函数再故意在不同的编译单元里声明配合一些无实际意义的condition判断让编译器没法把它们挨在一起逆向者对函数边界的识别就会变得困难。配合链接脚本把每个模块的.text段随机打散到Flash的不同位置反汇编出来的代码看起来就像一座迷宫。这里要强调的是软件混淆不是银弹它挡不住一个耐心足够好的逆向工程师但它能把破解时间从3天拉到3周。对于一个迭代速度极快的消费市场3周窗口已经足够你更新一版固件让旧版破解方法失效。3.2 字符串和常量加密别把底牌露在明面上说个我见过很多次的低级失误代码里明晃晃地写着日志字符串、密钥常量、APIKey编译进固件后就是一串可读ASCII。像“LicenseKey12345678”这种你在固件里用strings命令一搜就出来了。更绝的是有些调试日志把整个通信协议的握手流程都打印出来等于把协议设计白送了。正确做法是所有敏感字符串不出现在固件的只读数据段里而是运行时通过解密函数还原到一个临时缓冲区用完立刻清掉。比如把字符串按字节打乱编译期使用一个宏来做混淆或者直接用简单异或加密存进Flash运行时解出来。我自己的工程里会写一个ConfidentialString工具类把密钥、APIKey、FTP账号这类信息用AES-CTR加密后存成常量只有对应的解密函数能还原。这样即便固件被读出来静态分析也看不到任何有用信息。需要提醒的是任何在MCU内部能解密的算法原则上敌人都能复制一个类似的运行环境来执行所以字符串加密挡不住高水平攻击者只能挡掉90%只会用strings和IDA的初学者。安全防御本来就不是追求绝对而是把门槛抬高。3.3 链接脚本与代码布局随机化链接脚本Linker Script在大多数工程里是被忽视的默认生成后就不动了。但它对安全分析的影响其实很大如果每次构建出来的固件布局都固定那么攻击者只要分析一次后续每个版本的地址关系都是已知的逆向效率会大幅提升。所以我会在release版本里刻意修改链接脚本让关键模块的加载地址随机化。具体做法是在链接脚本里用一个伪随机种子生成偏移比如将CrcModule的.text段地址从0x08020000移动到0x0802A000把LFSR校验函数循环展开成若干片段散布在不同地址。这样攻击者对照两个版本的diff时大部分地址都变了分析工作要重来一遍。当然代码布局随机化有个代价升级固件时bootloader里的跳转地址要动态解析或者你要在编译时生成一个地址映射表存到固定位置。这个工作量不大但对于使用默认工程的团队来说是一个新概念需要一点额外学习时间。好在现在GCC的链接脚本行为足够灵活用几个段属性和VMA/LMA设置就能实现。4. 安全启动与升级链路的完整设计4.1 启动流程与签名校验让固件可信固件防护做得好只是挡住了静态分析。真正要防的是攻击者写一段恶意固件刷进你的设备让设备执行他们的代码。这时候就需要安全启动来把关核心思路是系统上电后运行在不可变ROM里的Bootloader先对App固件做完整性校验和签名验证校验通过才跳转到应用代码失败则停在一个恢复状态拒绝执行。一个精简的校验流程大概是这样的Bootloader读取App固件头部的签名信息放在固定偏移比如App起始地址0x100处。Bootloader用存储在OTP区域或安全Flash区域的公钥对App固件哈希做RSA或ECDSA验签。验签通过再用SHA-256计算整个App固件的哈希值和头部存储的参考哈希比对确认没有被篡改。全部通过跳转到App入口任何一步失败进入错误处理循环。这里的公钥可以放在OTP一次性可编程区域因为OTP写一次之后就永远不能改能有效防止攻击者篡改公钥替换成自己的。私钥则留在开发环境或构建服务器里永远不进入固件包。每次发布固件时构建流水线自动用私钥对固件签名你手里的私钥安全等级直接决定了整个安全启动的可信度。4.2 升级链路中的防回滚设计启动校验能挡住恶意固件但还有一类攻击叫版本回滚攻击攻击者拿着一个存在已知漏洞的旧版本固件刷进去因为旧版本是正版签名的校验也能通过但漏洞依然可以利用。要防这一手必须在固件里加版本号并且Bootloader校验时增加一条规则——新固件的版本号必须大于或等于当前Flash里的版本号否则拒绝升级。这个防回滚逻辑做起来不难但生产环境里有个坑如果某一次现场升级失败BootLoader会要求重新烧录而产线上烧录工具如果没有同步更新版本号可能会把同版本固件覆盖上去触发防回滚拒绝导致设备变成砖。我的习惯是在产线烧录脚本里增加一个“允许覆盖同版本”的开关只在出厂阶段使用真正的远程升级服务则严格执行版本递增。4.3 远程升级的安全通道很多产品支持OTA升级这个功能既是刚需也是一扇很大的后门。如果升级通道不加密攻击者可以截获升级包篡改后重新发给设备。所以OTA升级包本身要满足三个要求机密性用对称密钥加密固件包、完整性带哈希校验、可验证性私钥签名。我一般先做对称加密来保护固件内容再叠加签名验签来保证包来源可信。密钥怎么安全地分发到设备上是个难点。常见的做法是在出厂时通过安全烧录工具把对称解密密钥写进设备的OTP区域同时Device Key可以基于UID推导出来这样每台设备的解密密钥都不一样一个设备的密钥泄露不会影响整个产品线。升级服务端也需要维护一个密钥台账不同批次设备用不同密钥表泄露后的影响面可控。5. 量产阶段的安全管理5.1 烧录方案选型离线烧录还是产线全自动烧录安全设计再完善如果量产环节出了纰漏一切白搭。比如你辛苦设计了密钥注入流程结果产线为了图方便先用一个公共固件把整批芯片烧录一遍再通过测试工装补充烧录密钥那固件和密钥就是分开存储的攻击者只要盯住密钥存储的位置就行完全不用分析固件。我见过做得比较靠谱的产线方案是产线配备离线烧录器烧录器内部直接连接安全的加密芯片或加密卡固件和密钥在烧录器内部完成合并再一次性写入MCU。这样从烧录器到MCU的整个过程中明文固件不会出现在任何中间文件里。如果使用支持Multi-Core或TrustZone的MCU还可以把密钥写入Secure Enclave区域应用内核完全无法访问。5.2 密钥注入与UID绑定流程量产的时候我会在产线测试的最后一个工位做密钥注入与验证流程如下读取MCU的96位UID通过产线工控机发给密钥管理系统。密钥管理系统生成该设备专属的密钥对或者用UID派生一个对称密钥。通过SWD接口把派生数据写进OTP区域或片内Secure Flash。产线测试软件重新上电检查设备能否用这个密钥正常解密一段测试向量验证密钥和UID绑定是否成功。这个流程的关键在于第2步和第3步之间不能有密钥明文落盘。如果产线工控机安装了杀毒软件或者文件备份服务器把这些数据存到了网盘上密钥就泄露了。所以有条件的话这步操作最好用一台不联网的专用工控机完成。5.3 产线一致性检查与防呆产线上容易出的低级错误非常多忘记开读保护、密钥写入失败但不报错、批次号和固件版本不匹配。所以我会在产线测试程序里增加几条强制检查检查RDP等级如果不是预期等级直接判定FAIL。检查签名信息读回固件头部的一个隐藏标记确认烧录的是当前发布版本。检查UID绑定值计算一个Challenge结合UID用设备密钥加密后上报服务端解密对比。这些检查看起来不起眼但真的能避免“整批设备出厂后发现密钥全配错”的灾难。我自己就遇到过某次产线调整烧录工位顺序导致一批设备用错误密钥烧录由于产线测试漏了校验出货后客户反馈设备无法激活最后不得不安排二次人工返工损失一堆售后成本。6. 实际项目中的典型问题与排查实录6.1 开启读保护后调试器连不上的问题这是新手最容易慌的场景在STM32CubeProgrammer里勾选了Level 1读保护然后烧录退出再连调试器不识别了以为是芯片锁死了。其实不是锁死而是读保护Level 1状态下的调试连接行为发生了变化你依然可以用Flash Loader或者标准烧录工具复位一下芯片然后全片擦除读保护就会解除。但注意全片擦除意味着Flash里的固件和所有数据都没了所以量产阶段的板子千万别没事点全片擦除来解除保护。对应的经验是研发板可以在调试配置里把“Connect under Reset”选项勾上这样连接时强制复位芯片能绕开很多因为选项字节配置变化导致的连接失败。6.2 外置安全芯片引发的产线集体卡死某次调试带ATECC608A的产品开机自检一直过不去查了一整天才发现是I2C总线上传感器的地址和安全芯片冲突了。安全芯片的地址是0x60而传感器默认地址也是0x60两边打架导致总线通信崩溃。排查过程很折磨人因为两个芯片都能单独工作凑在一起就异常。后来我们把安全芯片的地址改为0x61问题消失。这个案例提醒我设计阶段就要检查I2C地址分配不要等到产线出问题再来排查那个时间成本高得离谱。另外安全芯片初始化时最好加一个超时机制防止芯片故障导致整个设备死等毕竟安全芯片本身也可能有质量不良。6.3 固件升级时签名校验失败设备变砖远程OTA最怕遇到的情况是固件下载到一半网络闪断设备重启后Bootloader发现App不完整签名校验失败直接停摆。更糟的是有些方案里Bootloader没有设计恢复模式设备就彻底无法启动了。为这个问题我在升级流程里专门增加了一个“双Bank A/B备份”机制固件写到备用Bank校验通过后切Boot设置再重启到新App一旦新App起不来Bootloader自动切回旧Bank。这样就算升级包下载了一部分就断网也不会影响当前版本运行。这个设计多花了50%的Flash空间但换来的是远程升级的安全性值得。6.4 检查清单交付量产前过一遍这十项根据这些年积累的经验我给自己定了一个量产前安全自查清单每次发布版本都会过一遍读保护等级是否设置到目标值Level 1或Level 2。调试接口引脚是否已断开或串了跳线电阻。BOOT引脚是否固定为Flash启动。固件中是否还有明文密钥或敏感字符串。固件是否带合法签名版本号是否正确。Bootloader能否处理升级失败并回滚。安全芯片的地址是否与I2C总线上其他设备冲突。产线烧录流程中是否存在密钥明文落盘的环节。量产测试程序是否包含签名、UID绑定、读保护等级检查。返修的设备在进行Flash擦除后能否重新走一遍密钥注入流程。每一条看着都简单但真正逐条落实下来设备被抄袭的概率会大幅下降。很多团队不是不懂安全而是没有一个强制检查机制等产品出了问题才想起来。7. 分享几个长期实践后的心得做了几年嵌入式安全防护最大的感受是安全不是一道锁而是很多道门。读保护是第一道门固件签名是第二道字符串加密是第三道密钥管理是第四道每一道都挡不住一个铁了心要抄你的人但每一道都能劝退一批只想来碰碰运气的人。商业世界里大多数抄板者并不想花三个月死磕你的固件他们只会挑软柿子捏。此外对中小团队来说与其上来就引入复杂的TrustZone方案和安全芯片不如先把最基础的固件保护做到位。开个读保护、做个启动校验、把敏感信息藏起来这三件事加起来成本极低却能让产品从“裸奔”变成“穿了衣服”。等产品真正卖出了量再考虑更硬核的防物理攻击方案这个节奏我认为更适合大多数公司。最后再分享一个小技巧每当你发布一个新版本不妨亲自扮演一次攻击者用strings、IDA、烧录器去检查一遍自己的固件看看能读出什么、能定位到什么。你会发现很多平时觉得理所当然的安全假设在攻击者视角下压根靠不住。把自己当成最大的敌人才会老老实实地把门一道道关好。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价