ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LPC1768代码读出保护(CRP)实测:五个常见说法验证与量产建议

LPC1768代码读出保护(CRP)实测:五个常见说法验证与量产建议 整理仓库里吃灰的LPC1768开发板时翻出了当年调Code Readout Protection代码读出保护存的几百个书签和聊天截图。那几年论坛上关于CRP的说法五花八门有说“开了CRP3芯片就变砖”有说“CRP能保证固件绝对读不出来”还有说“设置CRP必须上专用编程器”。最近手里有个量产项目要定固件保护策略我决定把这些说法全部重新验证一遍用实物一块块测结论跟当年论坛里的主流说法差得不是一点半点。这篇文章不是翻译数据手册是一次实打实的“Claims复盘”。我会先梳理CRP保护的真实边界然后逐条验证流传最广的五个说法再给出三个实测场景下的完整日志和恢复链路最后聊聊量产项目里CRP的正确打开方式和我踩过的坑。整个过程以NXP LPC17xx系列为主因为它在国内开发者手里存量最大但结论对LPC13xx、LPC15xx等同样适用。1. 先捋清楚CRP保护的边界到底划在哪1.1 四个CRP等级一张表说明白很多开发者对CRP的理解是“分三档档位越高越安全”其实NXP的LPC系列在最常见的CRP1、CRP2、CRP3之外还有一个NO_ISP模式一共四个可用配置。以LPC17xx为例这个安全等级不是烧在某个配置寄存器里而是直接固化在Flash的特定地址处具体位置是0x0000_02FC写入不同魔数就启用不同的保护等级。等级CRP字节值LPC17xx调试接口JTAG/SWDISPUART读FlashISP擦除/编程典型适用场景无CRP非上述值完全开放可读可擦可写开发调试阶段CRP10x12345678禁用禁止读擦除受限编程受限产品已量产仍需要现场维修CRP20x87654321禁用禁止读仅允许全片擦除交付客户前的基本保护CRP30x43218765禁用禁止读且ISP握手会返回异常仅允许全片擦除恢复防抄板、防固件提取的常规选择NO_ISP0x4E697370禁用彻底关闭ISP入口无极少用慎用注意表格里的“擦除/编程”一列CRP1和CRP2的行为差异很大。CRP1下ISP仍然允许执行一部分扇区擦除和编程操作只是禁止了读取命令CRP2会把擦除权限收紧到“只能整片擦除”编程同样被禁止。这意味着CRP1设备在产线返修时还有机会通过ISP部分更新固件CRP2就基本等于“要么全片抹掉重来要么返厂处理”。1.2 容易被忽略的细节CRP只是“读保护”不是“物理盾牌”我在很多项目评审里反复强调一句话CRP本质上是MCU内部bootrom和调试接口做的一层软件级访问控制它管的是“外部能不能通过标准调试口和ISP口把Flash内容读出来”这件事。它不管三件事第一不管物理攻击。攻击者把芯片decapsulation之后用电子显微镜直接读浮栅电荷或者用FIB聚焦离子束改线绕过保护逻辑这些跟CRP完全不在一个对抗层级。CRP防的是拿个J-Link插上SWD就开读的“低成本抄板客”不是国家级安全实验室。第二不管侧信道攻击。如果固件里有敏感的AES密钥攻击者可以通过功耗分析、电磁辐射分析把密钥提取出来。CRP不会对运行时的功耗曲线做任何混淆它保护的是静态存储的代码不是动态运行的秘密。第三不管“合法用户自己作死”。我见过不止一个工程师在产品调试阶段把CRP打开转头就忘了结果要改固件时发现SWD连不上货已经发出去几百片最后只能走ISP全片擦除再重新烧录——代价是Flash里原有的校准数据、序列号、产测日志全部清零。所以我的结论是CRP是一道非常实用的门槛但它不是保险箱。它真正的价值在于把“读取固件的门槛”从“插上调试器就能看”提高到“必须解焊芯片、上化学腐蚀、做物理分析”的级别。对绝大多数商业竞争场景来说这个门槛已经够用但如果你的产品里藏着价值极高的算法或者密钥CRP之外还要叠加加密存储和安全启动。2. 五个流传很广的说法我逐一实测验证2.1 说法一“CRP3之后芯片就废了”这个说法在论坛里流传最广也是很多开发者不敢开CRP3的直接原因。实测下来完全不是这么回事。CRP3虽然把调试接口和ISP的读写命令全部封死但NXP在bootrom里保留了一条恢复通道通过UART进入ISP模式后工具往芯片发送全片擦除命令芯片会接受并执行整片Flash的擦除操作。擦除完成后CRP字节也就没了芯片回到完全开放状态。我用Flash Magic实测的流程是这样先把P2.10拉低给目标板重新上电进入ISP模式Flash Magic选择LPC1768波特率设为115200连接后工具回读设备ID成功。然后直接选“Erase all flash”并执行Flash Magic提示擦除完成此时再连接J-LinkSWD已经能正常识别到内核重新烧录任意固件都没问题。所以正确的说法是CRP3不会让芯片“变砖”它会让你“丢数据”。恢复手段永远存在代价是Flash被全片抹掉。这个特性其实是NXP有意设计的——如果恢复通道被彻底封死工厂里一旦设置错误整批芯片就只能报废这不符合量产场景的工程逻辑。2.2 说法二“CRP能让固件绝对安全”这是我最想纠正的说法。CRP能做的是把固件从“公开”变成“受控”但它不是“绝对安全”。我实测的边界是这样的CRP3启用后通过SWD接口往芯片发连接请求调试器连target ID都拿不到更别说读内存了。通过UART进ISP发送Read Memory命令芯片直接返回错误码。这两条常规路径确实被堵死了。但如果你把芯片解焊下来上化学药水开封用探针台或者电子显微镜直接看Flash阵列CRP完全无能为力。还有一类更现实的攻击路径是故障注入——攻击者在芯片供电引脚上叠一个毛刺让CPU在检查CRP状态的指令上跳变出错理论上存在跳过保护检查的可能。这种攻击不需要顶级实验室设备一台可编程电压源加一个示波器就能开始尝试。我说这些不是要吓唬人而是想让大家建立正确的预期CRP是防护体系里的一环不是全部。你的固件如果值钱到值得对手花几万块钱做物理攻击那就应该在CRP之上再加加密固件、安全启动、运行时完整性校验而不是反过来指望一个读保护字节扛住一切。2.3 说法三“CRP1和CRP2只有擦除限制不同编程都能做”这个说法错得很典型。我在LPC1768上做了分组实验两组板子分别写入CRP1和CRP2然后通过ISP分别尝试Read、Erase、Program三类命令。结果是CRP1组Read全被拒绝Erase对指定扇区可以执行但擦完后读取依然被拒Program某些扇区可以执行。CRP2组Read全部拒绝Erase只能发全片擦除单扇区擦除直接返回错误Program命令一律失败。也就是说CRP2比CRP1严的不只是“擦除粒度”而是把所有写操作也一并收走了。CRP1还允许在不开锁的情况下做有限的现场更新CRP2就完全断了这是条路。如果你的产品有“手持维修工具通过UART升级固件”这种需求CRP2以上基本就不现实了老老实实留在CRP1或者单独做一套带鉴权的ISP升级协议。操作CRP1CRP2CRP3ISP读Flash禁止禁止禁止ISP擦除单个扇区允许部分扇区禁止禁止ISP全片擦除允许允许允许ISP编程允许部分扇区禁止禁止SWD/JTAG连接无法识别内核无法识别内核无法识别内核2.4 说法四“设置CRP必须用专用工具”这是纯误解。CRP不是一个寄存器也不是一条特殊命令它只是Flash里0x2FC地址处的一个32位值。任何能把Flash写进去的手段——J-Link、ISP串口、量产烧录器、甚至应用代码自己——都可以设置CRP。我常用的一种方式是在固件工程里直接定义一个常量放在链接脚本指定的地址编译烧录后CRP就生效了。Keil MDK下的做法类似在分散加载文件里给CRP字节划一个独立执行域。这里贴一个GCC环境下的简单做法Keil用户思路完全一致/* crp.c */ __attribute__((section(.crp), used)) const uint32_t crp_word 0x43218765; /* CRC3: 0x43218765 *//* linker script fragment */ .crp 0x00002FC : { KEEP(*(.crp)) } FLASH烧录时不需要做任何额外动作整个工程烧进去CRP3就跟着固件一起生效了。量产线如果想分步控制也可以先用烧录器把固件烧好不烧CRP字节最后单独写一个只包含CRP字节的镜像到0x2FC两段烧录都不需要专用工具。2.5 说法五“量产固件顺序无所谓先写固件再设CRP怎么来都一样”这个说法在特定条件下成立在量产线上就是不成立的。实测中我遇到过一次典型的翻车生产测试程序里有一道工序是“烧录固件之后通过UART读取Flash里的序列号打印到标签”然后下一道工序才写CRP。因为CRP是在固件之后才写入的读取序列号时Flash还处于开放状态所以前两道工序都没问题一切看起来顺理成章。但后面导入新机型时产测程序被改成“先写CRP字节再通过UART读取序列号校验”。结果芯片进入CRP状态后ISP的Read命令直接返回错误产线一口气卡了50片停线排查了半天。正确的量产顺序是所有需要在Flash里读出校验的数据必须在CRP生效之前完成CRP一旦写入就默认“里面的内容永远只能被动执行不能再被外部读取”。3. 实测记录三个真实场景下的CRP行为3.1 场景一CRP3激活后J-Link连接失败的完整日志我手头这块LPC1768已经通过上面的链接脚本写入了CRP3复位后试着用J-Link Commander连接日志如下SEGGER J-Link Commander V7.60 (Compiled Jun 27 2023 15:01:23) DLL version V7.60, compiled Jun 27 2023 15:01:22 Connecting to target via SWD Found SW-DP with ID 0x2BA01477 Error: Cannot connect to target. Target connection failed. The target may be secured.注意第4行“Found SW-DP with ID 0x2BA01477”很容易误导人——J-Link已经发现了SWD端口并且读到了DPIDR看起来好像“还能连”但紧接着的“Cannot connect to target”才说明真正的问题。SWD协议里访问DPDebug Port的某些寄存器可能不受CRP限制但一旦尝试访问APAccess Port下面的内存总线就会触发保护连接就建立不起来。在Keil MDK里这个现象表现为点击“Download”后弹窗“Cannot access target please verify lockup state...”很多工程师看到这行字以为内核跑飞或者锁死了其实是CRP在起作用。这个场景给我们的教训是看到“found SW-DP”不要慌这不代表芯片可以被读写。要确认是否被CRP锁住最可靠的方法是进ISP模式用Flash Magic回读设备ID如果连接正常但读取被拒基本可以判定CRP已经生效。3.2 场景二Flash Magic通过ISP完整恢复CRP3芯片恢复CRP3芯片的链路我完整跑了一遍流程比大多数人想象得简单。第一步硬件连接。LPC1768的UART0通过USB转TTL接到PCP2.10引脚拉低同时确保复位引脚可以手动控制。上电顺序是先按住P2.10低电平然后给板上电这样bootrom检测到ISP引脚为低才会进入ISP模式而不是正常运行用户代码。第二步Flash Magic连接。目标设备选LPC1768COM口选对应的虚拟串口波特率设115200。这里有个小技巧Flash Magic在“Advanced”选项里可以选择“Use DTR/RTS for ISP”如果目标板有自动复位和ISP引脚控制电路比如用CP2102做了电平转换并接了RTS控制就可以一键进入ISP否则只能手动拉低P2.10再通电。第三步回读验证。连接成功后Flash Magic会显示设备ID和boot版本我在“Read”标签下尝试读取地址0x0000处的数据返回错误。原因是CRP3已经封住了读命令。第四步全片擦除。切到“Erase”标签勾选“Erase all flash”点击执行。Flash Magic的日志窗口会依次输出擦除扇区0、扇区1……直到全部完成。这个过程中芯片没有响应任何读取或编程命令只接受擦除。擦除完成后我把P2.10释放再重新上电J-Link就能正常连接了Flash内容为空CRP字节也不存在了。整个过程大约2分钟芯片完全恢复出厂状态。3.3 场景三误开CRP后量产烧录线卡住的排查过程有段时间我给一家做工业控制器的小厂做技术顾问他们的产线用的是一个国产脱机烧录器烧录流程是“擦除→烧固件→校验→写CRP”。某天产线突然开始大量报错烧录器在执行“校验”步骤时频繁提示“verify failed”一开始以为是Flash不良换了几片新芯片也一样。我过去排查时没有直接怀疑CRP而是先让产线把烧录流程里的“写CRP”步骤去掉结果校验立刻通过。再把“写CRP”加回来又失败。反复几次后才意识到问题出在烧录顺序上脱机烧录器在写完CRP字节后会执行一次全片回读校验而此时CRP已经生效回读命令被芯片拒绝校验自然失败。处理方案有两个一是把烧录脚本改成“校验完再写CRP”把CRP放在最后一个步骤二是如果烧录器只能先写CRP后校验那就把校验范围排除掉0x2FC所在的扇区或者改用“CRC校验”代替“逐字节回读校验”。最终我们选了改脚本顺序一劳永逸。这个案例的普遍意义在于很多脱机烧录器的默认流程是“先整体校验后写最后几个字节”而CRP恰恰很容易被安排在最后几个字节里两者一碰撞就出问题。如果你的产线突然出现“校验失败”且集中在“加入CRP之后”优先检查烧录顺序不要急着换芯片或者怀疑Flash品质。4. 量产与返修CRP的正确打开方式和翻车预防4.1 在固件工程里设置CRP位的推荐做法我推荐的量产做法是“固件里默认不写CRP通过量产脚本单独写入”而不是把CRP写死在应用代码里。原因很实际应用代码里的CRP在每次烧录都会自动生效开发阶段如果忘了带ISP引脚每改一版固件都要经历一次“全片擦除再烧录”的流程效率低且容易把板子搞坏。更稳妥的方案是使用支持“附加数据区”的烧录工具。比如J-Flash里可以单独制作一个只包含0x2FC地址处CRP字节的镜像文件量产时先烧应用固件再烧CRP镜像两步之间由产测程序完成功能校验。如果产线上没有独立烧录两步的条件再退回到“链接脚本放CRP”的方式但必须确保ISP引脚LPC17xx上是P2.10在产品PCB上有可操作的测试点。另外量产前务必做一个“CRP恢复演练”。拿一块量产板烧录CRP3然后按照返修流程走一遍ISP全片擦除恢复再重新烧录固件记录整个过程需要的时间和工具。这样万一产品在客户现场被锁住售后人员至少有章可循而不是临时翻手册。4.2 四个真实事故案例与教训第一个案例CRP3锁死了产测数据。某表计类产品在出厂前需要读取Flash中保存的计量校准系数用于打印出厂合格证。工程师把CRP3写进了固件结果产线测试时发现无法通过UART读取校准系数整个产线卡住。最后只能全片擦除、重新校准、重新设置CRP等于所有校准过的表计全部返工。教训凡是需要在产线上从Flash里读出来的数据必须在CRP生效前读取并保存到外部存储或者放弃CRP3改用CRP1。第二个案例售后误擦除。某设备在客户现场故障售后工程师按经验用ISP对芯片执行了全片擦除再重写固件但设备里原有的一组传感器标定数据存在Flash里擦除时直接丢失。重新标定需要专用工装设备在客户现场躺了一周。教训CRP恢复路径是全片擦除使用这条路径意味着所有非固件数据都会丢。如果产品里有标定数据、序列号、运行日志必须设计独立存储外部EEPROM/SPI Flash或者自动备份机制。第三个案例P2.10被复用后无法进入ISP。某便携设备为了省GPIO把LPC1768的P2.10既当ISP使能脚又当了一个按键输入。客户反馈某些按键操作后设备再上电就异常。排查半天发现是按键在复位瞬间把P2.10拉低了导致芯片误入ISP模式看起来像固件崩溃。更麻烦的是后续想通过ISP恢复时因为按键和ISP使能共用引脚操作时序很难控制。教训尽量不要把ISP使能脚复用在关键功能上至少在量产板上要留出独立的ISP测试点否则返修时你会非常被动。第四个案例写CRP字节时只写了半个字。某烧录器配置的CRP地址偏移写错把0x2FC写成了0x2FD结果CRP字节没有正确写入产品出货后固件可以被任意读取。这个案例不算“事故现场惨烈”但它说明了一个容易被忽略的点CRP字节是32位的工具的地址配置、数据宽度、字节序都要和烧录脚本对齐量产前最好用“回读验证CRP是否生效”这步来兜底。4.3 一张决策表什么时候用CRP1、CRP2、CRP3、不用产品状态和需求推荐CRP等级理由开发调试阶段不用需要频繁烧录、调试CRP只会添乱小批量试产需要现场维护CRP1保留部分ISP擦写能力便于返修量产交付防普通抄板CRP2阻断读写恢复路径仅全片擦除固件价值高防针对性提取CRP3最严格的读保护常规工具无法读取存储标定数据且需要现场回读不用或CRP1保证产线和售后能读取/更新Flash数据产品有专门的安全升级通道CRP2/CRP3升级走独立鉴权协议不依赖ISP这张表不能覆盖所有场景但能帮你避免最常见的“选错等级导致产线/售后无法操作”的问题。我的经验是CRP等级的选择应该从“产品生命周期里谁需要访问Flash、在什么阶段访问”倒推而不是从“我想防止谁读”正推。想想出厂后你还需要对设备做什么再决定把门锁到几级。最后再分享一个小体会这次重新验证CRP相关说法最大的收获不是那些技术参数而是意识到一个项目的固件保护策略应该在画PCB之前就定下来。CRP占用哪个引脚、量产用什么工具写入、售后怎么恢复这些如果等板子贴片了再想往往已经晚了。选CRP等级之前先把你自己的返修流程走一遍你会对它到底该怎么用有完全不同的理解。
返回列表