芯片UID绑定修改 加密与逆向修改思路分析

芯片UID绑定加密与逆向修改思路分析(MCU固件安全角度)

标签:芯片逆向 UID加密 固件防拷贝 STM32 固件patch 硬件安全

阅读须知:本文仅用于固件安全研究、白盒审计、设备自有固件调试学习 ,禁止用于破解、篡改、盗版他人商用设备,所有操作请在自有硬件授权范围内进行,违规操作后果自行承担。

1. 前言

在MCU产品开发中,最常见的防拷贝方案就是芯片UID绑定加密 。STM32、GD32、HC32、PIC、瑞萨等主流芯片内部都内置出厂固化的唯一UID(Unique ID,也叫Chip ID),存储在OTP只读区域,原厂设计上正常不可直接改写。

厂商做法:固件启动时读取芯片UID,通过哈希/AES/异或派生校验密钥,和预存的校验摘要比对;校验失败直接拒绝运行业务代码,以此阻止固件被直接dump后烧录到别的芯片上运行。

这也是逆向工程师高频遇到的一类保护方案:拿到dump固件,但是换芯片无法跑起来,核心卡点就是UID校验逻辑。

网上很多文章混淆了两个概念:

  1. MCU内置只读UID(本篇重点):OTP出厂烧录,硬件层面只读;
  2. 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校验"的逆向思路,也是工程里最常用的:

  1. 方案A:Patch固件,绕过UID校验逻辑(最常用)

    找到固件内UID读取、校验判断分支,修改跳转指令,强制校验结果恒为true,不再校验UID。

    优点:不需要改动硬件,只修改bin/hex固件,适合dump后移植调试;

    缺点:安全审计场景容易被检测,多校验点的固件需要逐个定位。

  2. 方案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读取代码

  1. 查芯片手册,拿到UID寄存器固定基地址;
  2. 在IDA/Ghidra中搜索该常量地址,快速定位读取函数;
  3. 交叉引用(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摘要:

  1. 不要把UID读取地址硬编码成固定常量,运行时动态计算地址;
  2. 增加随机挑战应答,而不是固定摘要比对;
  3. 密钥不要只依赖UID单一因子,叠加板载序列号、Flash唯一页、外部安全芯片;
  4. 分散校验、增加校验时机,避免只在启动阶段校验一次;
  5. 开启RDP、禁止调试,增加硬件破解成本;
  6. 定期更新固件,轮换校验算法。

6. 总结

  1. MCU原厂OTP UID硬件只读,不存在通用软件改写指令,网上所谓改芯片UID大多是劫持读取函数、补丁绕过校验;
  2. UID绑定是入门级固件防拷贝方案,弱算法很容易逆向patch;
  3. 逆向工程中,优先区分【MCU芯片UID】和【NFC卡片UID】,二者原理完全不同,不要混淆;
  4. 所有逆向操作严格限定在自己拥有完全版权、授权调试的硬件上,商用设备未授权逆向存在法律风险。

7. 相关推荐阅读

  • STM32参考手册:Unique device ID章节
  • Ghidra固件逆向静态分析入门
  • MCU RDP读保护原理与风险
  • SHA/AES在嵌入式密钥派生中的安全实践

版权声明:本文为安全研究分享,仅用于学习固件安全、逆向分析技术,请勿用于非法破解、盗版、侵权行为。

相关推荐
Liben2 小时前
Weex开发踩坑记录
android·前端·ios
救救孩子把4 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务
张小姐的猫5 小时前
【MySQL】事务
android·mysql·adb
你看我名字5 小时前
数眼智能 Search + Reader 实战:联网问答怎样避免“搜到了,却引用错了”
android·java·开发语言
恋猫de小郭6 小时前
最近 Android 有什么更新:Android Dev Summit 2026 技术解读
android·前端·flutter
Kapaseker6 小时前
删掉 1.1 万行 C 代码,Rust 正式接手 Android Binder
android·rust
嵌入式-老费6 小时前
esp32开发与应用(题外话之ADB和Android NDK开发)
android·adb
野生的码农6 小时前
能工智人,40 大寿,尚能班否?
android·ai编程
事圆则缓6 小时前
Android 串口通信:从 /dev/ttyS1到协议帧
android