logo资料库

STM32Cube之ID加密.pdf

第1页 / 共12页
第2页 / 共12页
第3页 / 共12页
第4页 / 共12页
第5页 / 共12页
第6页 / 共12页
第7页 / 共12页
第8页 / 共12页
资料共12页,剩余部分请下载后查看
STM32Cube 学习之十六:ID 加密 在单片机产品开发完成后,可能有人通过工具读取 MCU 上 FLASH 中的代码,窃取我们的程序。 本文的通过一个完整的例程,演示如何利用 STM32 的 ID 对程序进行加密,使他人即使读到 FLASH 中的代码,也无法直接使用,从而保护自己的劳动成果。 先引用坛友“人生如梦 123” 2015 年 9 月 28 日在 ST 社区发布的帖子“[原创] STM32 防破解 ” 帖子链接:http://www.stmcu.org/module/forum/thread-603421-1-1.html 本人之前是做软加密破解的,后来良心发现,自觉做我们这行窃取他人劳动成果,和做贼没有区别, 所以果断转行了,首选声明我绝不是高手,只是想将我做破解的时候的一些经验和大家分享,以便 各位兄弟在做产品的时候重视加密,不要让别人轻而易举就窃取了您的劳动成果, STM32 系列从问世以来就以优异的性能和便宜的价格深得人心,但是不幸的是树大招风,问世 不就久就被解密公司破了,从 12 年的 12 万的解密价格,到 13 年的 6 万,14 年一万,再到现在的五 千,相信不用两年就会像 51 一样沦为几百块的白菜价,所以软加密对未来的 STM32 的工程师来说异 常重要,以下正式对各种方式的软加密和破解方法做一个总结,以便各位朋友在日后设计软加密的 时候不要给破解的人留下漏洞 1.最简单的软加密不用反汇编,直接在机器码中就可以先到 FF1FE8F7,因为 STM32 机器码是小端 格式,这个地址实际就是芯片 ID 地址,破解的人只需要在程序中找到一块空白的位置,然后将解密 的那个芯片的 ID 复制到这里,再将程序中出现的那个 1FFFF7E8 改为存放母片的那个 ID 就破解了, 这种方法和你程序采用的什么算法加密毫无关系,防破解处理方法是在程序加密的时候不要直接读 芯片 ID,应采用几个变量运算合成 ID 地址再间接的去读,注意不能用立即数合成,因为那样编译器 还是会给你优化成一个立即数的。 2.就算在程序中找不到明显的读 ID 指令也是可以破解的,方法就是仿真跟踪,仿真跟踪前需要 反汇编,THUMB2 的文档中每条指令生成的机器码有详细的说明,随便都可以找个做上位机的写个自 动反汇编工具,之后再人工修改下就可以了,其实还有一种更简单的方法,就是将机器码定义成 DCI XXXXH 这样的格式,导入 KEIL 编译能通过,然后仿真,KEIL 会自动的帮你反汇编,接下来就是单步 执行,延时类函数跳过,这时候要密切注视 R0 到 R15,不管你用什么方法得到的 ID 地址,最终一定 会出现再这几个寄存器中,防破解的方法一个是检验 ID 号的时候不要在开机就检验,要在特定的硬 件条件下才检验 ID,然后如果不合法程序就自毁,这样就只能通过 JTAG 硬件仿真了。所以产品上市 的时候切记将其它 IO 口转到 JTAG 口,这样就占用了 JTAG,仿真就不行了 3.是不是这样就安全了?不是的,你可以禁用 JTAG,人家同样可以修改指令开启 JTAG,最好的方 式是在程序关键的代码块做 CRC 检验,这样只要关键指令被修改过,就可以发现,剩下的自己看着 办…… 4.其实没有破不了的软加密,只是一个时间和成本的问题,但是也不能让人家那么轻易的就破 解了,除了上面的防破解的方法之外还可以在程序中特定条件下做多次检验,检验的时候不要用简 单的判断真假跳转,应该用检验的结果做程序下一步执行的参数,这样别人破出来的产品原以为没 问题了,但是用起来不稳定,或者性能差,或者老死机等。除了这些还可以外挂加密芯片,以增加 破解的成本,更多防破解的方法欢迎加我 QQ254915501 讨论
假设已经安装好 STM32CubeMX 和 STM32CubeF4 支持包。 Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F407ZETx。 Step2. 在 Pinout 界面下配置引脚功能。 根据电路使用 PF9,PF10 作 LED 控制,将 PF9,PF10 的功能配置为 GPIO_Output,幵把用户标签改为 LED0 和 LED1。 配置串口,作为信息输出接口。
Step3.在 Clock Configuration 界面配置时钟源。 配置时钟树,在此使用默认值, 内部 16MHz。 Step4.配置外设参数。 GPIO:使用默认的推挽即可。 USART:串口参数默认即可。
Step5.生成源代码。 点击生成源代码按钮。在设置界面中输入工程名,保存路径,工程 IDE 类型,点 OK 即可。 生成代码完成后可直接打开工程。 弹出如下对话框时,如果已经安装了 F4 的支持包,则点击 OK 关闭。如果没有安装,则点击界面中 的 www.keil.com/...链接,找到芯片的支持包,然后安装。
关闭后面的界面。 点击“是”,然后选择芯片型号。可以在搜索框中输入关键字,加快选择速度。 Step6.添加功能代码。 先在 main.c 文件用户代码区输入包含标准输入输出头文件。 在用户代码区 4 实现标准输出 printf()的底层驱动凼数 fputc(),功能是在 UART1 输出一个字符。 在 main 凼数中定义一个变量。 在/* USER CODE BEGIN 2 */和/* USER CODE END 2 */用户代码,添加如下代码:
ID 校验凼数 check_ID()在文件 encrypt.c 和 encrypt.h 中实现。 Step7.实现 ID 校验功能。 0x4000 /* Sector 3 size: 16 Kbytes */ encrypt.h 文件内容: #ifndef _ENCRYPT_H_ #define _ENCRYPT_H_ #include "stm32f4xx_hal.h" #define FLASH_SECTOR3_SIZE #define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */ // chipID 地址 #define UID0 0X1F #define UID1 0XFF #define UID2 0X7A #define UID3 0X10 // chipID 地址在 crc_tmp_data 中的存储坐标 #define UID0_OFFSET 0x23 #define UID1_OFFSET 0x33 #define UID2_OFFSET 0x43 #define UID3_OFFSET_1 0x53 #define UID3_OFFSET_2 0x63 #define UID3_OFFSET_3 0x73 // 通用密码 #define eID0 0xEF #define eID1 0x3E #define eID2 0x05 #define eID3 0x69 #define eID4 0xAB #define eID5 0xC7 #define eID6 0xE5 #define eID7 0x31 #define eID8 0x05 #define eID9 0x59 #define eID10 0xA3 #define eID11 0x07
// 通用密码在 crc_tmp_data 中的存储坐标 #define eID0_OFFSET 0x09 #define eID1_OFFSET 0x19 #define eID2_OFFSET 0x29 #define eID3_OFFSET 0x39 #define eID4_OFFSET 0x49 #define eID5_OFFSET 0x59 #define eID6_OFFSET 0x0A #define eID7_OFFSET 0x1A #define eID8_OFFSET 0x2A #define eID9_OFFSET 0x3A #define eID10_OFFSET 0x4A #define eID11_OFFSET 0x5A void calculate_eCODE(uint8_t *pUID, uint8_t *peCODE); void encrypt_code_save(void); void read_ID(uint32_t *pUID_dat32); HAL_StatusTypeDef check_ID(void); #endif encrypt.c 文件内容: #include "encrypt.h" // 密码存储区 const unsigned char encrypt_code[FLASH_SECTOR3_SIZE] __attribute__((at(ADDR_FLASH_SECTOR_3))) = { eID0, eID1, eID2, eID3, eID4, eID5, eID6, eID7, eID8, eID9, eID10, eID11, }; // 通用密码和 CRC 计算数据 const unsigned char crc_tmp_data[128]={ // 0 1 2 3 4 5 6 7 8 9 A B C D E F /*0*/ 0x39, 0x18, 0xbb, 0x5a, 0xdd, 0x21, 0xff, 0xee, 0x62, eID0, eID6, 0x14, 0x66, 0x77, 0x44, 0x55, /*1*/ 0x2b, 0x90, 0x21, 0x32, 0x42, 0x54, 0x67, 0x76, 0xea, eID1, eID7, 0x89, 0xfe, 0xef, 0x2c, 0xcd, /*2*/ 0x00, 0x21, 0x22, UID0, 0x44, 0x55, 0xc6, 0x77, 0x88, eID2, eID8, 0xbb, 0xcc, 0x3a, 0xce, 0x2c, /*3*/ 0x31, 0x10, 0x32, UID1, 0x55, 0x44, 0xf7, 0x66, 0x34, eID3, eID9, 0x01, 0xc3, 0x67, 0x54, 0x05, /*4*/ 0xaa, 0xbb, 0x88, UID2, 0xf7, 0xff, 0xcc, 0x8d, 0x33, eID4, eID10,0x00, 0xd7, 0x66, 0x55, 0xb4, /*5*/ 0x5b, 0xaa, 0x99, UID3, 0xdf, 0xf7, 0xdd, 0xfe, 0xff, eID5, eID11,0xcf, 0xbb, 0x3a, 0x99, 0x78, /*6*/ 0x88, 0xa9, 0xca,UID3+4,0xdc, 0x2d, 0xfe, 0x6f, 0x2c, 0xa1, 0x30, 0x20, 0x50, 0x25, 0x76, 0x3a, /*7*/ 0x48, 0x55, 0x87,UID3+8,0x08, 0x61, 0x22, 0x23, }; // 本机 ID 存储变量 volatile union { uint32_t dat32[3]; uint8_t dat8[12]; }mcuID; // 本机密码存储变量 volatile uint8_t eCODE[12]; /* 函数名称: calculate_eCODE 功 能: 计算本机密码 (本函数当前没有进行复杂的算法设计,只简单地进行按位取反) 输 入: pUID,UID 数组指针 输 出: peCODE,本机密码 返 回: 无 0x66, 0x2c, 0x4c, 0x55, 0x2c, 0x33, 0x00, 0x41,
*/ void calculate_eCODE(uint8_t *pUID, uint8_t *peCODE) { peCODE[0] = ~pUID[0]; peCODE[1] = ~pUID[1]; peCODE[2] = ~pUID[2]; peCODE[3] = ~pUID[3]; peCODE[4] = ~pUID[4]; peCODE[5] = ~pUID[5]; peCODE[6] = ~pUID[6]; peCODE[7] = ~pUID[7]; peCODE[8] = ~pUID[8]; peCODE[9] = ~pUID[9]; peCODE[10]= ~pUID[10]; peCODE[11]= ~pUID[11]; } /* 函数名称: encrypt_code_save 功 能: 保存密码到 FLASH 中 输 入: 无 返 回: 无 */ void encrypt_code_save(void) { } /* 函数名称: read_ID 功 能: 读取 ID 输 入: 无 输 出: 读到的 UID 存入 pUID_dat32 指向的存储空间 返 回: 无 */ void read_ID(uint32_t *pUID_dat32) { uint32_t PAGEError = 0; uint32_t write_addr; uint32_t i; uint8_t *pbuf; FLASH_EraseInitTypeDef EraseInitStruct; EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Banks EraseInitStruct.Sector = FLASH_SECTOR_3; EraseInitStruct.NbSectors = 1; EraseInitStruct.VoltageRange= FLASH_VOLTAGE_RANGE_3; HAL_FLASH_Unlock(); if (HAL_OK == HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError)) { } HAL_FLASH_Lock(); write_addr = ADDR_FLASH_SECTOR_3; pbuf = (uint8_t *)eCODE; for (i=0; i<12; i++) { } uint32_t mcuID_add; HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, write_addr + i, pbuf[i]); = FLASH_BANK_1;
分享到:
收藏