当前位置: 首页 > news >正文

MCU程序加密保护(二)ID 验证法 加密与解密

STM32 微控制器内部具有一个 96 位全球唯一的 CPU ID,不可更改。开发者可利用此 ID 实现芯片绑定和程序加密,增强软件安全性。
ID 验证法就是利用这个 UID,对每颗芯片的身份进行识别和绑定,从而防止程序被复制。

实现方式:

  1. 在程序首次运行或出厂时,读取 CPU ID,经过不可逆算法(如 CRC32、SHA256)处理后生成一个校验值;

  2. 将该校验值写入 Flash;

  3. 每次程序启动时重新计算当前芯片 ID 的校验值,并与 Flash 中存储的值进行比较;

  4. 如果匹配,程序正常运行;否则程序终止,防止非法拷贝。

三、这种方法的优点:

优点说明
✅ 绑定芯片唯一身份因为 UID 不可修改,确保每个程序绑定一颗芯片
✅ 防止直接拷贝同样的程序拷贝到另一颗芯片上,会因 UID 不同而校验失败
✅ 可配合写保护存放校验值的 Flash 区域可以配合 WRP 写保护
✅ 可量产可以用上位机脚本自动读取 UID,计算校验值并写入 Flash

 下面只讲一种方法,方法肯定是不唯一的!

#define UID_BASE_ADDR       0x1FFFF7E8U
#define FLASH_CRC_ADDRESS   0x0800FC00U  // 最后一页(64KB Flash)起始地址
#define UID_WORD_COUNT      3
#define FLASH_PAGE_SIZE     1024U
#define CRC32_POLYNOMIAL    0xEDB88320Uint fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);return ch;
}void send_string(const char* str)
{HAL_UART_Transmit(&huart1, (uint8_t*)str, strlen(str), HAL_MAX_DELAY);
}void Print_UID(void)
{const uint32_t* uid = (const uint32_t*)UID_BASE_ADDR;char buf[64];snprintf(buf, sizeof(buf), "[STM32 UID] %08lX-%08lX-%08lX\r\n", uid[2], uid[1], uid[0]);send_string(buf);
}uint32_t Calculate_UID_CRC32(void)
{const uint32_t *uid = (const uint32_t *)UID_BASE_ADDR;uint32_t crc = 0xFFFFFFFF;for (int i = 0; i < UID_WORD_COUNT; i++) {crc ^= uid[i];for (int j = 0; j < 32; j++) {if (crc & 1)crc = (crc >> 1) ^ CRC32_POLYNOMIAL;elsecrc >>= 1;}}return crc ^ 0xFFFFFFFF;
}uint32_t Read_Stored_CRC(void)
{return *(const uint32_t *)FLASH_CRC_ADDRESS;
}void Verify_UID(void)
{uint32_t calc_crc = Calculate_UID_CRC32();uint32_t stored_crc = Read_Stored_CRC();if (calc_crc != stored_crc) {char buf[128];snprintf(buf, sizeof(buf), "[ERROR] UID CRC Mismatch! Calc: 0x%08lX, Stored: 0x%08lX\r\n", calc_crc, stored_crc);send_string(buf);Error_Handler();} else {send_string("[INFO] UID CRC Verified OK\r\n");}
}void Store_UID_CRC_To_Flash(void)
{uint32_t crc = Calculate_UID_CRC32();HAL_FLASH_Unlock();FLASH_EraseInitTypeDef EraseInitStruct = {.TypeErase   = FLASH_TYPEERASE_PAGES,.PageAddress = FLASH_CRC_ADDRESS,.NbPages     = 1};uint32_t PageError = 0;if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK) {send_string("[ERROR] Flash Erase Failed\r\n");HAL_FLASH_Lock();return;}if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_CRC_ADDRESS, crc) != HAL_OK) {send_string("[ERROR] Flash Program Failed\r\n");HAL_FLASH_Lock();return;}HAL_FLASH_Lock();send_string("[INFO] UID CRC Stored to Flash\r\n");
}void Auto_Bind_UID_CRC_If_Needed(void)
{uint32_t stored_crc = Read_Stored_CRC();if (stored_crc == 0xFFFFFFFF)  // 判断是否首次上电{send_string("[INFO] No UID CRC Stored. Binding current chip...\r\n");Store_UID_CRC_To_Flash();  // 写入当前芯片 UID 的 CRC}
}void SystemClock_Config(void);int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();Auto_Bind_UID_CRC_If_Needed();Print_UID();Verify_UID();while (1) {。。。。。。}
}

防止程序在“非原芯片”上运行

uint32_t calc_crc = Calculate_UID_CRC32();
uint32_t stored_crc = Read_Stored_CRC();

对 MCU 内置唯一的 UID 地址 0x1FFFF7E8 做了 CRC 校验,并将第一次计算的结果写入 Flash:

Store_UID_CRC_To_Flash(); // 用于首次存储 CRC
校验通过才能继续运行,否则 Error_Handler() 阻止执行。
这个机制可有效阻止程序被别人拷贝到其他 STM32 芯片后运行。

其次0xEDB88320U这个是干嘛的?

0xEDB88320UCRC32 多项式的反转形式(Reflected Polynomial),它是你代码中用于计算 CRC32 校验值的核心常数

什么是 CRC?

CRC(循环冗余校验)是一种常用于数据完整性校验的哈希算法,在嵌入式系统、通信、存储等领域被广泛使用。
它通过一个多项式除法运算对数据生成一个固定长度的校验值,用于检测数据在传输或存储中是否被篡改或损坏。

你用到的 0xEDB88320U 是什么?

这是标准 CRC32 算法中使用的 反射形式多项式

标准 CRC32 多项式(正向)

x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1

对应的二进制多项式值是:0x04C11DB7

反转版本(Reflected CRC):就是你代码中使用的

0xEDB88320\text{0xEDB88320}0xEDB88320

它等价于对 0x04C11DB7 的位反转处理。

STM32 的 HAL_CRC 或很多嵌入式代码实现中,都是按位反转(LSB first)方式计算 CRC,因此使用 0xEDB88320 是标准做法。

这种反转方式效率高,和实际硬件或工业协议(比如 Ethernet、ZIP 文件、PNG 图片、STM32 Bootloader 校验)保持一致。

此外上述代码已经验证,拷贝后在别的扳机程序无法运行!达到效果!

但是!加密完总得自己解密哈哈,相比于闪存加密的方法,这个方法还有很多漏洞的,毕竟是软件加密,如下为漏洞分析!

安全目标是否达成原因
防止程序拷贝到其他芯片运行✅ ✅ ✅UID 校验 OK
防止程序被他人反编译修改绕过校验没有开启 RDP,程序可读出
防止校验值被改写绕过校验没有开启写保护(WRP)
防止程序被整个二进制拷贝RDP 未开启

此为量产方式

方案需要写入 UID CRC是否支持量产烧录器是否支持自动化推荐场景
A(运行时绑定)不用提前写入,程序首次运行自动绑定✅ 支持全自动烧录最推荐,适合量产
B(编译时绑定)烧录前需写入 CRC❌ 烧录前需人工或脚本操作✅ 有产测平台时适用小批量

当然漏洞是有的,但是我们将闪存保护法跟id验证法放在一起,并且将下述方法加在一起,更多方法进行融合,达到真正的加密效果!

目前只是对 UID 本身做 CRC32,攻击者拿到程序和 CRC 算法很容易在自己芯片上重算并替换。
改进: 在计算 CRC 前,把 UID 跟一个“固化私钥”一起混合,再做 CRC。

#define SECRET_KEY_WORD   0xA5A5BEEFULuint32_t Calculate_Mixed_CRC32(void)
{const uint32_t *uid = (const uint32_t*)UID_BASE_ADDR;uint32_t buffer[UID_WORD_COUNT + 1] = {uid[0], uid[1], uid[2], SECRET_KEY_WORD};uint32_t crc = 0xFFFFFFFF;for (int i = 0; i < UID_WORD_COUNT + 1; i++) {crc ^= buffer[i];for (int j = 0; j < 32; j++) {if (crc & 1)crc = (crc >> 1) ^ CRC32_POLYNOMIAL;elsecrc >>= 1;}}return crc ^ 0xFFFFFFFF;
}

结合 Option Bytes 自动锁定

Auto_Bind_UID_CRC_If_Needed() 中一旦绑定完成,即可顺带触发 RDP Level1 和写保护:

void Secure_Lock_Device(void)
{FLASH_OBProgramInitTypeDef ob = {0};HAL_FLASH_Unlock();HAL_FLASH_OB_Unlock();HAL_FLASHEx_OBGetConfig(&ob);// 只在 Level0 时生效,避免重复擦除if (ob.RDPLevel == OB_RDP_LEVEL_0) {ob.OptionType = OPTIONBYTE_RDP | OPTIONBYTE_WRP;ob.RDPLevel   = OB_RDP_LEVEL_1;ob.WRPState   = OB_WRPSTATE_ENABLE;ob.WRPPage    = OB_WRP_PAGES60TO63;  // 保护 CRC 存储页HAL_FLASHEx_OBProgram(&ob);}HAL_FLASH_OB_Lock();HAL_FLASH_Lock();
}

使用硬件 CRC 外设或查表加速

软件 bitwise CRC32 性能偏低,可考虑:

STM32F1 的 CRC 外设(部分型号支持)

或者用 256 项的查表法,把 0xEDB88320 的查表数组预先生成,运算速度提升 5~10 倍。

static const uint32_t crc32_table[256] = { /* 预生成的 256 项 */ };uint32_t CRC32_Table(const uint8_t *data, uint32_t len) {uint32_t crc = 0xFFFFFFFF;while (len--) {crc = (crc >> 8) ^ crc32_table[(crc ^ *data++) & 0xFF];}return crc ^ 0xFFFFFFFF;
}

 

http://www.xdnf.cn/news/441289.html

相关文章:

  • SCDN如何有效防护网站免受CC攻击?——安全加速网络的实战解析
  • 深度强化学习 | 图文详细推导软性演员-评论家SAC算法原理
  • FPGA: Xilinx Kintex 7实现PCIe接口
  • 数据库基础复习笔记
  • 量子计算实用化突破:从云端平台到国际竞合,开启算力革命新纪元
  • 40:相机与镜头选型
  • 虚幻引擎5-Unreal Engine笔记之Qt与UE中的Meta和Property
  • 云图库和黑马点评的项目学习经验
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]: 获取macOS App的Bundle路径信息.
  • list 容器常见用法及实现
  • 基于运动补偿的前景检测算法
  • loss = -F.log_softmax(logits[:, -1, :], dim=1)[0, irrational_id]
  • 【C/C++】自定义类型:结构体
  • Seata源码—2.seata-samples项目介绍
  • 酒店行业冰与火:一边流拍,一边扩张
  • 大模型高效微调技术:从原理到实战应用
  • 深入理解Java适配器模式:从接口兼容到设计哲学
  • Python调用SQLite及pandas相关API详解
  • 解密企业级大模型智能体Agentic AI 关键技术:MCP、A2A、Reasoning LLMs-强化学习算法
  • 机器学习第十一讲:标准化 → 把厘米和公斤单位统一成标准值
  • 对抗系统熵增:从被动救火到主动防御的稳定性实战
  • R利用spaa包计算植物/微生物的生态位宽度和重叠指数
  • 序列化和反序列化hadoop实现
  • Math工具类全面指南
  • OpenCV CUDA 模块中用于在 GPU 上计算矩阵中每个元素的绝对值或复数的模函数abs()
  • 量子算法:开启计算新时代的技术密码
  • MATLAB实现振幅调制(AM调制信号)
  • Hadoop-HDFS-Packet含义及作用
  • 通用软件项目技术报告 - 术语词典
  • 【数据分析】从TCGA下载所有癌症的多组学数据