☰
你的STM32设备安全吗?从UID出发,聊聊MCU防抄板与软件授权的几种野路子
2026/10/28 8:09:24 网站建设 项目流程

STM32设备安全实战:基于UID的防抄板与授权方案设计

在智能硬件创业的热潮中,许多中小团队都面临一个共同的痛点——产品刚上市不久,市场上就出现了功能雷同的山寨版本。去年接触过一个做环境监测设备的团队,他们的故事很有代表性:首批1000台设备投放市场三个月后,竞品以低于40%的价格推出了"复刻版",连传感器校准参数都原封不动地照搬。这种赤裸裸的抄袭不仅蚕食市场份额,更可能因为劣质仿制品影响品牌声誉。

1. UID的本质与应用价值

每片STM32芯片出厂时都被赋予了一个96位的唯一标识符(UID),这个存储在只读区域的编码就像芯片的"身份证号"。以STM32F4系列为例,UID存储在0x1FFF7A10开始的地址空间,包含三个32位字:

#define UID_BASE 0x1FFF7A10 uint32_t deviceUID[3]; deviceUID[0] = *(uint32_t*)(UID_BASE); // 低位字 deviceUID[1] = *(uint32_t*)(UID_BASE + 4); // 中位字 deviceUID[2] = *(uint32_t*)(UID_BASE + 8); // 高位字

UID的不可复制性来自半导体制造工艺——在晶圆切割阶段,厂商通过激光熔断或电子注入方式写入随机编码,这个物理过程无法被常规手段篡改。但要注意不同系列STM32的UID存储位置存在差异:

芯片系列UID起始地址长度
STM32F0/F10x1FFFF7AC96位
STM32F2/F40x1FFF7A1096位
STM32L00x1FF8005096位

实际项目中,推荐使用HAL库提供的标准接口读取UID,避免直接操作地址带来的兼容性问题:

uint32_t uid[3]; uid[0] = HAL_GetUIDw0(); // 等效于UID[0] uid[1] = HAL_GetUIDw1(); // 等效于UID[1] uid[2] = HAL_GetUIDw2(); // 等效于UID[2]

注意:某些STM32型号(如L0系列)的UID第三字存储在基地址+0x14处,而非连续的+0x08偏移,这是容易踩坑的地方。

2. 基础防复制方案设计与实现

2.1 UID校验法

最简单的防护是在固件启动时验证UID合法性。具体实现是在生产环节将合法UID列表烧录到设备Flash的特定位置,运行时进行比对:

#define VALID_UID_ADDR 0x0800F000 // 存储合法UID的Flash地址 void check_uid_validity(void) { uint32_t *valid_uid = (uint32_t*)VALID_UID_ADDR; uint32_t current_uid[3] = {HAL_GetUIDw0(), HAL_GetUIDw1(), HAL_GetUIDw2()}; if(memcmp(current_uid, valid_uid, 12) != 0) { HAL_NVIC_SystemReset(); // 验证失败则重启 } }

这种方案的优缺点非常明显:

  • 优点:实现简单,几乎不增加硬件成本
  • 缺点:UID以明文存储,破解者通过Hex编辑器即可提取并复制到仿制品

2.2 UID哈希签名方案

提升安全性的改进方案是采用哈希算法处理UID。推荐使用SHA-256等加密哈希函数,虽然STM32没有硬件加速,但软件实现仍具备可行性:

#include "mbedtls/sha256.h" void generate_uid_signature(uint8_t *output) { uint32_t uid[3] = {HAL_GetUIDw0(), HAL_GetUIDw1(), HAL_GetUIDw2()}; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); mbedtls_sha256_starts(&ctx, 0); mbedtls_sha256_update(&ctx, (uint8_t*)uid, 12); mbedtls_sha256_finish(&ctx, output); mbedtls_sha256_free(&ctx); }

生产时预计算哈希值并写入设备,运行时验证流程:

  1. 读取Flash中存储的预期哈希值
  2. 实时计算当前UID的哈希值
  3. 比对两者是否一致

提示:可以考虑在哈希计算时加入"盐值"(salt)增强安全性,例如将盐值与UID拼接后再哈希。

3. 进阶授权系统设计

3.1 动态激活码方案

为每个设备生成唯一的激活码,实现软件授权控制。典型实现流程:

  1. 生产端读取UID并生成激活码:

    # 生产端Python示例 import hashlib from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes def generate_activation_code(uid): key = b'your_secret_key_32bytes' cipher = Cipher(algorithms.AES(key), modes.ECB()) encryptor = cipher.encryptor() return encryptor.update(uid.ljust(16, b'\0')) + encryptor.finalize()
  2. 设备端验证逻辑:

    uint8_t verify_activation(uint8_t *input_code) { uint8_t uid[12], calculated_code[16]; memcpy(uid, (uint8_t[]){HAL_GetUIDw0(), HAL_GetUIDw1(), HAL_GetUIDw2()}, 12); // 此处实现AES加密逻辑 aes_encrypt(uid, calculated_code); return memcmp(input_code, calculated_code, 16) == 0; }

3.2 联网授权方案

对于具备网络连接的设备,可以实现更灵活的授权管理。系统架构包含三个组件:

  1. 设备端:收集UID和产品信息
  2. 授权服务器:验证请求并签发令牌
  3. 云端数据库:存储授权状态

典型交互流程:

sequenceDiagram 设备->>服务器: POST /auth {uid, product_id} 服务器->>数据库: 查询授权状态 数据库-->>服务器: 返回授权结果 服务器->>设备: 签发JWT令牌 设备->>设备: 验证令牌有效性

注意:虽然要求不能使用mermaid图表,但实际实现时应采用文字描述替代图示

4. 安全增强与反破解策略

4.1 代码混淆技术

通过修改代码结构增加逆向工程难度,常用手段包括:

  • 控制流扁平化:将嵌套逻辑转为switch-case结构
  • 虚假分支注入:插入永不执行的分支代码
  • 字符串加密:运行时动态解密关键字符串
// 混淆后的UID检查示例 void check_uid() { volatile uint32_t a = 0xDEADBEEF; uint32_t uid[3]; if(a % 2) { uid[0] = HAL_GetUIDw0() ^ 0x55AA55AA; } else { uid[0] = HAL_GetUIDw0(); } // 更多混淆代码... }

4.2 运行时自检机制

定期校验关键代码段的CRC值,防止固件被篡改:

uint32_t compute_crc(uint32_t start, uint32_t end) { uint32_t crc = 0xFFFFFFFF; uint32_t *ptr = (uint32_t*)start; while(ptr < (uint32_t*)end) { crc ^= *ptr++; for(int i=0; i<32; i++) crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1)); } return ~crc; } void self_check() { uint32_t expected_crc = *(uint32_t*)0x0800FF00; uint32_t actual_crc = compute_crc(0x08000000, 0x0800F000); if(expected_crc != actual_crc) { HAL_FLASH_Lock(); while(1); // 进入死循环 } }

4.3 硬件辅助方案

对于安全性要求更高的场景,可以考虑:

  • 配合加密芯片:如ATECC608A,将关键验证逻辑外置
  • 使用STM32 TrustZone:在安全隔离区运行验证代码
  • 启用Flash写保护:防止通过调试接口修改固件
// 启用Flash写保护示例 void enable_write_protection() { HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTERR | FLASH_FLAG_WRPERR); FLASH_OBProgramInitTypeDef ob; ob.OptionType = OPTIONBYTE_WRP; ob.WRPState = OB_WRPSTATE_ENABLE; ob.WRPPage = 0x7F; // 保护所有页面 HAL_FLASHEx_OBProgram(&ob); HAL_FLASH_Lock(); }

在项目实践中,这些方案往往需要组合使用。比如一个中型智能家居设备可能采用:UID哈希校验 + 联网激活 + 关键函数CRC检查的三重防护,既控制了成本又达到了商业级的安全需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询