芯片UID绑定加密与逆向修改思路分析(MCU固件安全角度)
标签:
芯片逆向UID加密固件防拷贝STM32固件patch硬件安全阅读须知:本文仅用于固件安全研究、白盒审计、设备自有固件调试学习 ,禁止用于破解、篡改、盗版他人商用设备,所有操作请在自有硬件授权范围内进行,违规操作后果自行承担。
1. 前言
在MCU产品开发中,最常见的防拷贝方案就是芯片UID绑定加密 。STM32、GD32、HC32、PIC、瑞萨等主流芯片内部都内置出厂固化的唯一UID(Unique ID,也叫Chip ID),存储在OTP只读区域,原厂设计上正常不可直接改写。
厂商做法:固件启动时读取芯片UID,通过哈希/AES/异或派生校验密钥,和预存的校验摘要比对;校验失败直接拒绝运行业务代码,以此阻止固件被直接dump后烧录到别的芯片上运行。
这也是逆向工程师高频遇到的一类保护方案:拿到dump固件,但是换芯片无法跑起来,核心卡点就是UID校验逻辑。
网上很多文章混淆了两个概念:
- MCU内置只读UID(本篇重点):OTP出厂烧录,硬件层面只读;
- RFID M1卡UID(CUID/UID卡):带后门指令可改写扇区0卡号,属于NFC卡片,和MCU芯片UID不是同一类东西。
2. UID绑定加密的常规实现原理
以STM32F1为例,UID基地址固定:0x1FFFF7E8,一共96bit(12字节),上电通过寄存器读取。
典型链路:
芯片上电 → 固件读取OTP区域UID → 送入密钥派生函数KDF(MD5/SHA256/AES) → 生成校验Token
→ 和Flash分区内预置的校验值对比
→ 匹配:正常跑业务;不匹配:死循环/跳转异常/擦除业务区
简易伪代码示意:
c
// 读取芯片UID
uint8_t chip_uid[12];
read_chip_uid(chip_uid);
// 基于UID派生密钥
uint8_t check_sum[16];
sha256_calc(chip_uid,12,check_sum);
// 和出厂写入的摘要对比
if(memcmp(check_sum,flash_save_digest,16)!=0){
while(1); // UID校验失败,停机
}
常见加密实现分类(逆向识别重点)
- 【弱方案】简单异或、CRC32、累加校验:算法简单,逆向极易还原、直接patch跳转即可绕过;
- 【中等方案】SHA256/MD5摘要绑定:无密钥泄露前提下,无法伪造摘要;
- 【强方案】AES密钥派生+非对称签名:UID作为密钥种子,固件里放公钥,校验签名;
- 【进阶方案】Bootloader校验 + App二次校验 + 多个位置分散校验(防单点patch)。

#mermaid-svg-mN0P6SkCdQO8cLjc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mN0P6SkCdQO8cLjc .error-icon{fill:#552222;}#mermaid-svg-mN0P6SkCdQO8cLjc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mN0P6SkCdQO8cLjc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mN0P6SkCdQO8cLjc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mN0P6SkCdQO8cLjc .marker.cross{stroke:#333333;}#mermaid-svg-mN0P6SkCdQO8cLjc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mN0P6SkCdQO8cLjc p{margin:0;}#mermaid-svg-mN0P6SkCdQO8cLjc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster-label text{fill:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster-label span{color:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster-label span p{background-color:transparent;}#mermaid-svg-mN0P6SkCdQO8cLjc .label text,#mermaid-svg-mN0P6SkCdQO8cLjc span{fill:#333;color:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc .node rect,#mermaid-svg-mN0P6SkCdQO8cLjc .node circle,#mermaid-svg-mN0P6SkCdQO8cLjc .node ellipse,#mermaid-svg-mN0P6SkCdQO8cLjc .node polygon,#mermaid-svg-mN0P6SkCdQO8cLjc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mN0P6SkCdQO8cLjc .rough-node .label text,#mermaid-svg-mN0P6SkCdQO8cLjc .node .label text,#mermaid-svg-mN0P6SkCdQO8cLjc .image-shape .label,#mermaid-svg-mN0P6SkCdQO8cLjc .icon-shape .label{text-anchor:middle;}#mermaid-svg-mN0P6SkCdQO8cLjc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mN0P6SkCdQO8cLjc .rough-node .label,#mermaid-svg-mN0P6SkCdQO8cLjc .node .label,#mermaid-svg-mN0P6SkCdQO8cLjc .image-shape .label,#mermaid-svg-mN0P6SkCdQO8cLjc .icon-shape .label{text-align:center;}#mermaid-svg-mN0P6SkCdQO8cLjc .node.clickable{cursor:pointer;}#mermaid-svg-mN0P6SkCdQO8cLjc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mN0P6SkCdQO8cLjc .arrowheadPath{fill:#333333;}#mermaid-svg-mN0P6SkCdQO8cLjc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mN0P6SkCdQO8cLjc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mN0P6SkCdQO8cLjc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mN0P6SkCdQO8cLjc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mN0P6SkCdQO8cLjc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mN0P6SkCdQO8cLjc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster text{fill:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc .cluster span{color:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mN0P6SkCdQO8cLjc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mN0P6SkCdQO8cLjc rect.text{fill:none;stroke-width:0;}#mermaid-svg-mN0P6SkCdQO8cLjc .icon-shape,#mermaid-svg-mN0P6SkCdQO8cLjc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mN0P6SkCdQO8cLjc .icon-shape p,#mermaid-svg-mN0P6SkCdQO8cLjc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mN0P6SkCdQO8cLjc .icon-shape .label rect,#mermaid-svg-mN0P6SkCdQO8cLjc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mN0P6SkCdQO8cLjc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mN0P6SkCdQO8cLjc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mN0P6SkCdQO8cLjc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一致
不一致
芯片上电
读取OTP UID 12字节
哈希算法派生校验摘要
摘要比对
业务固件正常运行
校验失败,停机/复位
3. 误区:能不能直接修改芯片硬件UID?
先说结论:绝大多数商用MCU原生OTP UID寄存器,没有改写接口,无法通过软件指令直接修改硬件UID 。
OTP = One Time Programmable,出厂一次性熔断写入,没有开放写命令,这是芯片硬件安全设计。
很多新手逆向踩坑:
想写一段代码往UID地址写数据,发现写入无效,读出来还是原厂UID。
那行业里两种"等效修改UID校验"的逆向思路,也是工程里最常用的:
-
方案A:Patch固件,绕过UID校验逻辑(最常用)
找到固件内UID读取、校验判断分支,修改跳转指令,强制校验结果恒为true,不再校验UID。
优点:不需要改动硬件,只修改bin/hex固件,适合dump后移植调试;
缺点:安全审计场景容易被检测,多校验点的固件需要逐个定位。
-
方案B:Hook读取UID的函数,软件劫持返回自定义UID(推荐研究)
不修改校验判断逻辑,在
read_uid底层函数打补丁,不管硬件读到什么,直接返回我们指定的UID数组。优势:上层校验代码完全不动,算法、摘要校验流程原样执行,兼容性更强,不容易触发厂商的反调试检测。
补充:极少数特殊非标芯片、工程样片、定制流片版本会预留OTP写接口,市面量产消费级MCU基本不存在这个后门。
4. 逆向定位UID校验逻辑的实操步骤
4.1 前置准备
工具链:J-Link/ST-Link、OpenOCD、IDA Pro/Ghidra、binwalk、objcopy、串口调试器。
目标:自有固件bin文件 + 同款开发板。
4.2 定位UID读取代码
- 查芯片手册,拿到UID寄存器固定基地址;
- 在IDA/Ghidra中搜索该常量地址,快速定位读取函数;
- 交叉引用(xref),追踪读取到UID之后,数据流向哪里:哈希函数、memcmp、分支判断。
特征标记(逆向识别特征):
- 固定OTP地址常量;
- 读取一段12/8/7字节数据送入hash函数;
- 紧随memcmp、BNE、BEQ条件跳转指令;
- 失败分支:while(1)死循环、看门狗复位、跳转到异常处理。

4.3 两种Patch实战思路
思路1:修改条件跳转(简单校验)
反汇编示例:
asm
BL read_uid
BL sha256_hash
CMP R0, #0
BNE fail_check ; 不相等跳转到失败
B success
fail_check:
B . ; 死循环
修改:把BNE fail_check改成NOP或者直接无条件跳转到success分支,屏蔽校验。
思路2:Hook UID读取函数(推荐,隐蔽)
替换read_uid函数,函数出口直接拷贝自定义UID到缓冲区:
c
void read_uid(uint8_t *buf)
{
// 注释原有硬件读取代码
// __read_otp(0x1FFFF7E8, buf, 12);
// 返回自定义UID
uint8_t fake_uid[12]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC};
memcpy(buf,fake_uid,12);
}
重编译或者直接在固件对应函数位置做inline patch,适合AES/哈希强绑定场景。

5. 正向视角:如何让UID绑定加密更难逆向(开发参考)
如果是做产品固件防护,不建议只用简单UID摘要:
- 不要把UID读取地址硬编码成固定常量,运行时动态计算地址;
- 增加随机挑战应答,而不是固定摘要比对;
- 密钥不要只依赖UID单一因子,叠加板载序列号、Flash唯一页、外部安全芯片;
- 分散校验、增加校验时机,避免只在启动阶段校验一次;
- 开启RDP、禁止调试,增加硬件破解成本;
- 定期更新固件,轮换校验算法。
6. 总结
- MCU原厂OTP UID硬件只读,不存在通用软件改写指令,网上所谓改芯片UID大多是劫持读取函数、补丁绕过校验;
- UID绑定是入门级固件防拷贝方案,弱算法很容易逆向patch;
- 逆向工程中,优先区分【MCU芯片UID】和【NFC卡片UID】,二者原理完全不同,不要混淆;
- 所有逆向操作严格限定在自己拥有完全版权、授权调试的硬件上,商用设备未授权逆向存在法律风险。
7. 相关推荐阅读
- STM32参考手册:Unique device ID章节
- Ghidra固件逆向静态分析入门
- MCU RDP读保护原理与风险
- SHA/AES在嵌入式密钥派生中的安全实践
版权声明:本文为安全研究分享,仅用于学习固件安全、逆向分析技术,请勿用于非法破解、盗版、侵权行为。

