硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2411|回复: 20
收起左侧

[脱机烧录] H7-TOOL脱机烧录的UID加密操作方法,支持一键生成目标板C代码,方便大家轻松操作(2024-12-20,已发布)

  [复制链接]

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2024-8-5 01:34:03 | 显示全部楼层 |阅读模式
UID加密使用比较方便,对应的C代码模板已经做好,使用TOOL上位机生成后,直接复制粘贴到自己的工程即可使用。返回1表示解密成功,返回0表示失败。

【UID加密原理】

1、烧录器在烧录芯片时,按照指定的算法将UID码编码为一个加密数据,并写入FLASH指定区域。
2、用户的程序必须增加一段UID校验程序比较UID和加密数据是否满足算法规则,不满足则停止运行或呈现随机错误。
3、用户程序只要进行了UID加密,就可以防止初级盗版者直接复制程序到其他芯片。
4、用户可以自行修改加密算法,以增加保密性。

点击上位机这个按钮可以查看了解:

1.png

【UID界面功能介绍】

2345.png

1、算法公式
当前做了三个
(1)encrypt_001.lua,  这个最安全,对UID做了各种加密,当前支持的一键C文件生成也是基于这个制作,大家配置的时候要选择这个选项
(2)encrypt_copy_uid.lua, 这个最简单,就是单纯的读取UID,未对UID做加密处理。
(3)encrypt_crc32_uid.lua, 这个是对UID做了简单的CRC32方式加密。

本贴的实现是基于第1中UID加密,实际应用也推荐用这种。

2、参数名A1 -A16,每个参数值范围1 - 16

  这里默认即可,一般无需修改

3、选择算法随机数

这个的对应关系是这样的,  一个选项对应一个:

23.png

4、算法函数用的常量

这里是8bit的hex,推荐配置8个值即可,  每个值范围0x00 - 0xFF

5、随机数

随机数1,随机数2,随机数3,随机数4设置都是一样的

以随机数1为例进行说明,可以设置跟随那个分区一起写入

231.png

这里的分区是跟前面这里分区对应的:

232.png

长度推荐就设置为默认的4,而写入地址,可以根据自己的需要设置。

6、UID加密启用

可以设置跟随那个分区一起写入,与前面第5步的含义一样

长度推荐就设置为默认的4,而写入地址,可以根据自己的需要设置。

【实际操作举例】

使用H7-TOOL给我们的STM32H7开发板加密为例进行说明

1、选择脱机烧录的芯片型号,优先配置UID,生成UID加密算法的C实现

123.png

2、点击这里生成C算法:

124.png

[C] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include <string.h>

/*
*********************************************************************************************************
*                                           定义
*********************************************************************************************************
*/
typedef struct {
    uint32_t enable;  // 使能位
    uint32_t address; // 存储地址
    uint32_t length;  // 随机数长度
} RNG_TAB_t;

uint8_t UidArray[16] = {0};
uint8_t EncryptUidArray[16] = {0};
uint8_t ReEncryptUidArray[16] = {0};

/*
*********************************************************************************************************
*                                 通过上位机更新,与上位机保持一致
*********************************************************************************************************
*/

/*--PC_DEFINE_BEGIN--*/

#define A1   1
#define A2   2
#define A3   3
#define A4   4
#define A5   5
#define A6   6
#define A7   7
#define A8   8
#define A9   9
#define A10   10
#define A11   11
#define A12   12
#define A13   13
#define A14   14
#define A15   15
#define A16   16

#define ENCRYPT_ADDR     0x08100000  //UID加密数据存储地址
#define ENCRYPT_LEN      8 //加密结果长度

#define ENCRYPT_RND_IDX  0   //加密用初值选择 0:固定数,1-4是随机数
#define RNG_CONST_DATA = "11 22 33 44 55 66 77 88"   //常量值
#define RNG_TAB_t RNG_TAB = {     //使能位, 存储地址, 随机数长度
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4}
};

#define UID_ADDR  0x1FF1E800
#define UID_BYTES 12


/*--PC_DEFINE_END--*/

/*
*********************************************************************************************************
*                                             UID相关API
*********************************************************************************************************
*/
static uint8_t stringToHexArray(const char *str, uint8_t *hexArray) 
{
    uint32_t  index = 0;

    while (*str != '\0') 
        {
        while (*str == ' ') 
        {
            str++;
        }
                
        if (*str == '\0') 
        {
            break;
        }

        sscanf(str, "%2hhx", &hexArray[index++]);

        str += 2;
    }
        
        return index;
}

/*
*********************************************************************************************************
*        函 数 名: encrypt
*        功能说明: 计算UID加密
*        形    参: ---
*        返 回 值: 无
*********************************************************************************************************
*/
static void encrypt(const uint8_t* _uid, const uint8_t* _rnd, uint8_t _rndsize, uint8_t* out) 
{
    uint8_t uid[16] = {0};
    uint8_t rnd[16] = {0};
    int i, j;

    if (_uid == NULL) 
        {
        return;
    }

    for (i = 0; i < UID_BYTES; i++) 
        {
        uid[i] = _uid[i];
    }

    j = 0;
    for (i = UID_BYTES; i < 16; i++) 
        {
        uid[i] = uid[j++];
    }

    if (_rnd != NULL) 
        {
        for (i = 0; i < _rndsize; i++) 
                {
            rnd[i] = _rnd[i];
        }
    }

    out[0] = uid[A1 - 1] ^ (uid[A2 - 1] >> 1) ^ (uid[A3 - 1] >> 1);
    out[1] = uid[A4 - 1] ^ (uid[A5 - 1] >> 0) ^ (uid[A6 - 1] >> 1);
    out[2] = uid[A7 - 1] ^ (uid[A8 - 1] >> 1) ^ (uid[A9 - 1] >> 3);
    out[3] = uid[A10 - 1] ^ (uid[A11 - 1] >> 2) ^ (uid[A12 - 1] >> 0);

    out[4] = uid[A1 - 1] ^ (uid[A10 - 1] << 1) ^ (uid[A2 - 1] >> 0);
    out[5] = uid[A4 - 1] ^ (uid[A7 - 1] << 1) ^ (uid[A5 - 1] << 2);
    out[6] = uid[A7 - 1] ^ (uid[A4 - 1] >> 0) ^ (uid[A8 - 1] << 0);
    out[7] = uid[A10 - 1] ^ (uid[A1 - 1] >> 2) ^ (uid[A11 - 1] << 1);

    out[8] = uid[A2 - 1] ^ (uid[A10 - 1] >> 1);
    out[9] = uid[A3 - 1] ^ (uid[A7 - 1] << 1);
    out[10] = uid[A6 - 1] ^ (uid[A4 - 1] << 1);
    out[11] = uid[A9 - 1] ^ (uid[A1 - 1] >> 2);

    out[12] = uid[A1 - 1] ^ (uid[A2 - 1] >> 1);
    out[13] = uid[A3 - 1] ^ (uid[A5 - 1] >> 0);
    out[14] = uid[A6 - 1] ^ (uid[A8 - 1] << 2);
    out[15] = uid[A9 - 1] ^ (uid[A11 - 1] >> 0);

    for (i = 0; i < 16; i++) 
        {
        out[i] ^= rnd[i % _rndsize];
    }
}

/*
*********************************************************************************************************
*        函 数 名: UidCompare
*        功能说明: 比较加密的UID
*        形    参: 1表示成功,0表示失败
*        返 回 值: 反
*********************************************************************************************************
*/
uint8_t UidCompare(void)
{
    uint8_t tempbuf[16] = {0};
    uint16_t i;
        uint8_t size;
        uint8_t index;        

    for(i = 0; i < UID_BYTES; i++)
    {
        UidArray[i] = *(volatile uint8_t*)(UID_ADDR+i);
    }
        
    for(i = 0; i < ENCRYPT_LEN; i++)
    {
        EncryptUidArray[i] = *(volatile uint8_t*)(ENCRYPT_ADDR + i);
    }        

    if(ENCRYPT_RND_IDX == 0)
    {
        size = stringToHexArray((const char *)RNG_CONST_DATA, tempbuf);

        encrypt(UidArray, tempbuf, size, ReEncryptUidArray);
    }
    else
    {
                index = (uint8_t)(ENCRYPT_RND_IDX - 1);
        for(i = 0; i < RNG_TAB[index].length; i++)
        {
            tempbuf[i] = *((uint8_t *)(uintptr_t)(RNG_TAB[index].address + i));
        }
        encrypt(UidArray, tempbuf, RNG_TAB[index].length, ReEncryptUidArray);        
    }

    for (i = 0; i < ENCRYPT_LEN; i++) 
    {
        if (ReEncryptUidArray[i] != EncryptUidArray[i]) 
        {
            return 0;
        }
    }

    return 1;
}



3、用户仅需调用UidCompare()即可,返回值是1表示验证成功,返回值是0表示验证失败

[C] 纯文本查看 复制代码
int main() 
{
        uint8_t re;

        re = UidCompare();
        
        if(re == 1)
        {
                printf("验证成功\r\n");
                
        }
        else
        {
                printf("验证失败\r\n");                
        }

    while(1);
}


4、修改一个bug的地方
特别注意,当前2.26版本生成的C算法这两个写错了,在2.27版已经解决

[C] 纯文本查看 复制代码
#define RNG_CONST_DATA = "11 22 33 44 55 66 77 88"   //常量值
#define RNG_TAB_t RNG_TAB = {     //使能位, 存储地址, 随机数长度
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4}
};


修改为如下即可:

[C] 纯文本查看 复制代码
uint8_t RNG_CONST_DATA[] = "11 22 33 44 55 66 77 88";   //常量值
RNG_TAB_t RNG_TAB[] = {     //使能位, 存储地址, 随机数长度
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4},
    {0, 0x08000000, 4}
};


然后编译生成hex文件。添加到TOOL上位机进行下载:

123.png

为了方便查看验证是否正常,我们这里简单做了个串口答应,成了就提示验证成功:

QQ20240820-022806.png

5、实际验证是没问题的,完整的测试例子如下,方便大家参考:

H7-TOOL UID加密.7z (1.28MB)

评分

参与人数 1金币 +20 收起 理由
suozhang + 20

查看全部评分

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-8-20 02:34:38 | 显示全部楼层
更新完毕。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-9-22 00:18:20 | 显示全部楼层
2024-09-22 记录一个帮客户解决STM8L052R8的UID加密烧录问题。

这个芯片容量是64KB的,烧写固件(主要是EEPROM的烧写影响,不烧写EEPROM没这个问题)和UID后

2345.png

解决办法,在地址0xF000再任意加载一个文件,并设置UID下载到扇区3,地址0xF000

2.png
回复

使用道具 举报

5

主题

107

回帖

122

积分

初级会员

积分
122
发表于 2024-9-23 17:00:04 | 显示全部楼层
下载.png

2.png

3.png

硬汉哥,您好,我在使用UID加密L475的时候,显示已经烧录成功,但是读取UID加密存储位置的时候读出来的都是FF,麻烦您指导一下,谢谢!

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-9-23 17:15:35 | 显示全部楼层
wangyao 发表于 2024-9-23 17:00
硬汉哥,您好,我在使用UID加密L475的时候,显示已经烧录成功,但是读取UID加密存储位置的时 ...

这里生成一个全0xFF的1KB文件

12.png

将生成的这个文件加载到第2行,设置烧写地址

14.png

设置UID加密,烧写到2区

15.png

然后你看看读取是否正常
回复

使用道具 举报

5

主题

107

回帖

122

积分

初级会员

积分
122
发表于 2024-9-24 14:10:08 | 显示全部楼层
eric2013 发表于 2024-9-23 17:15
这里生成一个全0xFF的1KB文件



硬汉哥,按照你上面说的配置,读出来还全是 0xFF;
另外我还做了一个测试:将UID加密功能关闭,手动修改1K_0xFF.bin 文件里面的前两个字节,然后烧录在读出,依然全是0xFF
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-9-24 14:35:07 | 显示全部楼层
wangyao 发表于 2024-9-24 14:10
硬汉哥,按照你上面说的配置,读出来还全是 0xFF;
另外我还做了一个测试:将UID加密功能关闭,手动 ...

手头有多余的测试板吗,发我们一套,测试完毕后返还给你。

我让我们公司这边测试下,看看是怎么回事,这个现象不正常,这样的话,没法同时烧录两个固件了。看看你用的这款芯片是怎么回事.
------------
收获地址:孟东华,86-13638617262,湖北省 武汉市 武昌区 水果湖街街道 中北路世纪彩城E区世纪大厦15楼1501
发货的时候带个纸条注明:TOOL脱机烧录STM32L475的UID到地址0x0801FC00,没有正确执行,另外留下您的QQ,旺旺或者微信联系方式,方便我们工作人员联系你。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-9-24 14:53:51 | 显示全部楼层
wangyao 发表于 2024-9-24 14:10
硬汉哥,按照你上面说的配置,读出来还全是 0xFF;
另外我还做了一个测试:将UID加密功能关闭,手动 ...

我手头有个L431,这个和你用的L475的Flash算法是一样的,区别是一个256KB,一个512KB,我这里测试了没问题。

剩下就是你那边方便的话,发个你的测试板子,楼上贴地址了,我们这边验证下,看看怎么回事。

1235.png
回复

使用道具 举报

5

主题

107

回帖

122

积分

初级会员

积分
122
发表于 2024-9-24 16:56:06 | 显示全部楼层
eric2013 发表于 2024-9-24 14:53
我手头有个L431,这个和你用的L475的Flash算法是一样的,区别是一个256KB,一个512KB,我这里测试了没问 ...

感谢硬汉哥的解答,我这边看到你使用256K测试成功,也将1K_0xFF.bin 写入到 256K的最后一页上(0x0803FC00),发现可以正常读取的;这个不知道是读取这边有bug还是写入有bug 这个目前是公司样品 我也没有多余的板子,暂时没法寄给您这边帮忙分析呢。


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-9-24 18:21:07 | 显示全部楼层
wangyao 发表于 2024-9-24 16:56
感谢硬汉哥的解答,我这边看到你使用256K测试成功,也将1K_0xFF.bin 写入到 256K的最后一页上(0x0803FC0 ...

我让我们公司这边帮你测试了,用的L476,这个和L475的Flash是一模一样的,完全没问题,烧写地址0x0807FC00。

剩下,你看下是你的芯片问题还是你板子问题。有需要的话,可以发你的板子,我们帮你排查,这个问题就这样吧

1.png


2.png



回复

使用道具 举报

5

主题

107

回帖

122

积分

初级会员

积分
122
发表于 2024-9-25 08:45:15 | 显示全部楼层
eric2013 发表于 2024-9-24 18:21
我让我们公司这边帮你测试了,用的L476,这个和L475的Flash是一模一样的,完全没问题,烧写地址0x0807FC0 ...

好的 谢谢回复
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2024-12-20 09:41:34 | 显示全部楼层
2.27版本解决2.26版本生成时的bug。
回复

使用道具 举报

77

主题

223

回帖

454

积分

高级会员

积分
454
发表于 2025-3-10 11:38:21 | 显示全部楼层
本帖最后由 薪火相传 于 2025-3-10 11:41 编辑

1.png

2.png

华芯微特这个uid的地址是不是搞错了?

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2025-3-11 05:24:51 | 显示全部楼层
薪火相传 发表于 2025-3-10 11:38
华芯微特这个uid的地址是不是搞错了?

确实错了,谢谢反馈。你可以这样修改:

1、双击这里打开文件

1.png

2、打开后,修改这个UID地址,修改后保存并关闭这个文件
2.png

3、点击这里同步到eMMC,然后关闭上位机,关闭TOOL,都重新打开即可测试

2345.png


回复

使用道具 举报

77

主题

223

回帖

454

积分

高级会员

积分
454
发表于 2025-3-11 08:22:21 | 显示全部楼层
eric2013 发表于 2025-3-11 05:24
确实错了,谢谢反馈。你可以这样修改:

1、双击这里打开文件

ok
回复

使用道具 举报

77

主题

223

回帖

454

积分

高级会员

积分
454
发表于 2025-3-11 14:44:37 | 显示全部楼层
eric2013 发表于 2025-3-11 05:24
确实错了,谢谢反馈。你可以这样修改:

1、双击这里打开文件

硬汉兄,不知为啥,改了这个uid地址之后,一直检验不过,秘钥的地址有没有什么要求,这个秘钥地址我是放在程序的后面,搞不懂
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2025-3-12 09:22:14 | 显示全部楼层
薪火相传 发表于 2025-3-11 14:44
硬汉兄,不知为啥,改了这个uid地址之后,一直检验不过,秘钥的地址有没有什么要求,这个秘钥地址我是放 ...

这样试试,两个地方排查下

1、地方一:

这里选择复制UID,这种情况仅仅是将UID复制到指定的地方

234.png

下载后,读取芯片,这里直接读取,看看读取到的正常不,不正常的话,你的程序里面也读取UID地址打印出来看下

23.png

2、第1步确认没问题后,估计是我们的制作的这个C代码再你的这个芯片运行结果确实和STM32之类的芯片有些差异,可以看看是不是芯片优化等级或者什么问题。


回复

使用道具 举报

77

主题

223

回帖

454

积分

高级会员

积分
454
发表于 2025-3-12 14:29:45 | 显示全部楼层
本帖最后由 薪火相传 于 2025-3-12 14:31 编辑
eric2013 发表于 2025-3-12 09:22
这样试试,两个地方排查下

1、地方一:

找到问题所在,似乎是这款芯片的UID不支持单字节寻址,得32位地址才行,总不会是我mdk o1优化的问题吧
QQ20250312-142828.png
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2025-3-12 14:46:14 | 显示全部楼层
薪火相传 发表于 2025-3-12 14:29
找到问题所在,似乎是这款芯片的UID不支持单字节寻址,得32位地址才行,总不会是我mdk o1优化的问题吧:di ...

有这种情况,有些芯片确实不支持单字节寻址,非常感谢告知最终原因。
回复

使用道具 举报

26

主题

136

回帖

214

积分

高级会员

积分
214
发表于 2025-3-22 19:18:31 | 显示全部楼层
本帖最后由 ifree 于 2025-3-22 19:21 编辑

硬汉哥,这个UID加密方法一点疑问,不知想的对不对。

假如破解者通过某种方法拿到了整个Flash的内容。然后他通过分析知道了(猜到了)存放UID加密结果的地址(比如0x08040000)。
我烧录时使用您H7TOOL提供的encrypt_001.lua这个加密算法(没有修改自己的版本),破解者也同样使用H7TOOL选择这个encrypt_001.lua算法,填入保存地址0x08040000,他是不是就相当于破解了这个UID固件加密。
虽然我可以选择使用随机数,但是随机数是存入Flash中的,破解者是不是只要猜到了随机数存放的地址,它采用同样的配置,也可以得到相同效果的UID加密。

如此说来,是不是选择使用常量模式相对更安全一点,虽然它可以逆向固件来在代码里找这个”常量”,但是难度是不是要大一些。

又或者需要自己来写一个encrypt_002.lua加密算法文件,才相对更安全一些?

另外,如果它逆向代码了,UID比较匹配的地方,它直接暴力修改指令,使得无论如何都返回通过,UID加密也就被破解了?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
 楼主| 发表于 2025-3-24 16:00:03 | 显示全部楼层
ifree 发表于 2025-3-22 19:18
硬汉哥,这个UID加密方法一点疑问,不知想的对不对。

假如破解者通过某种方法拿到了整个Flash的内容。然 ...

这种情况可以来点套路

【加强反破解的策略】

1、读取UID的语句不要用明码地址,访问地址可通过运算后得到。避免通过静态分析hex就轻松攻克

2、关键的校验函数和善后处理语句可以分段组合后动态加载到内存中执行

3、UID校验和善后处理程序可以在程序中多个地方实现

4、UID校验的执行时刻可以随机、定时或硬件满足某个条件再执行
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-4-26 01:52 , Processed in 0.827607 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表