STM32F1系列的"Unique device ID"寄存器的地址为0x1FFFF7E8。

这个寄存器是只读的。
"Unique device ID"寄存器位于"System memory"中。"System memory"地址范围为"0x1FFF F000- 0x1FFF F7FF"。

所有STM32 MCU上都存在系统引导加载程序。顾名思义,它位于MCU的系统内存(ROM)区域。系统 bootloader 位于存储器的只读部分,并在制造阶段进行编程。在流行文献中,系统 bootloader 可能被称为 ROM bootloader。
"引导加载程序"存储在STM32设备的内部引导ROM(系统内存)中,并在生产过程中由ST编程。其主要任务是通过一种可用的串行外设(如USART、CAN、USB、I2C、SPI)将应用程序下载到内部闪存中。为每个串行接口定义了一个通信协议,具有兼容的命令集和序列。
结论:
通过查看各种资料,发现STM32的"Unique device ID"只读,不能修改,它只能由ST在生产时进行编程 。
CPU内部的"Unique device ID"是唯一的,且不可修改,因此,我们可对这ID进行加密,然后将加密结果保存到FLASH中。这样每次启动都会去比较加密数据是否匹配。正确,则运行;不正确则进入死循环。
STM32不同系列的ID起始地址不同,如下所示:
-
0x1FFFF7AC, 是STM32F0系列"Unique device ID"的起始地址
-
0x1FFFF7E8, 是STM32F1系列"Unique device ID"的起始地址
-
0x1FFF7A10, 是STM32F2系列"Unique device ID"的起始地址
-
0x1FFFF7AC, 是STM32F3系列"Unique device ID"的起始地址
-
0x1FFF7A10, 是STM32F4系列"Unique device ID"的起始地址
-
0x1FF0F420, 是STM32F7系列"Unique device ID"的起始地址
-
0x1FF80050, 是STM32L0系列"Unique device ID"的起始地址
-
0x1FF80050, 是STM32L1系列"Unique device ID"的起始地址
-
0x1FFF7590, 是STM32L4系列"Unique device ID"的起始地址
-
0x1FF0F420, 是STM32H7系列"Unique device ID"的起始地址
编程时,注意不要在程序中出现上述的"Unique device ID"寄存器的地址,否则,很容易被盗版。

例如下面这个加密就会被破解:
#include "stm32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "USART1.h"
//注意:"USART1.h"要放在 "stdio.h" 包含文件之后,如果这个位置颠倒了,业不能打印浮点数。
/*
CPU内部的"unique ID"是唯一的,且不可修改,因此,我们可对这ID进行加密,
然后将加密结果保存到FLASH中。这样每次启动都会去比较加密数据是否匹配。正
确,则运行;不正确则进入死循环。
STM32不同系列的ID起始地址不同,如下所示:
1. 0x1FFFF7AC, /STM32F0唯一ID起始地址
2. 0x1FFFF7E8, /STM32F1唯一ID起始地址
3. 0x1FFF7A10, /STM32F2唯一ID起始地址
4. 0x1FFFF7AC, /STM32F3唯一ID起始地址
5. 0x1FFF7A10, /STM32F4唯一ID起始地址
6. 0x1FF0F420, /STM32F7唯一ID起始地址
7. 0x1FF80050, /STM32L0唯一ID起始地址
8. 0x1FF80050, /STM32L1唯一ID起始地址
9. 0x1FFF7590, /STM32L4唯一ID起始地址
10. 0x1FF0F420; /STM32H7唯一ID起始地址
*/
#define SYSID 0X1FFFF7E8 //stm32f1系列单片机id起始地址
//直接使用0X1FFFF7E8地址,它会出现在FLASH中,还是可以改到的;
//使用RAM中的数据通过加减来得到ID起始地址,比较安全;
#define SYSID1 0X1FFFF7E8
#define SYSID2 0X1FFFF7E9
#define SYSID3 0X1FFFF7EA
#define SYSID4 0X1FFFF7EB
#define SYSID5 0X1FFFF7EC
#define SYSID6 0X1FFFF7ED
#define SYSID7 0X1FFFF7EF
#define SYSID8 0X1FFFF7F0
#define SYSID9 0X1FFFF7F1
#define SYSID10 0X1FFFF7F2
#define SYSID11 0X1FFFF7F3
#define SYSID12 0X1FFFF7F4
u8 FLASHIDbuf[12];
//保存加密数据
void Save_SystemID(void)
{
uint8_t i;
u8 id[12]; //12*8 = 96
u8 *dfu;
//读ID数据并保存加密数据///
printf("\r\n\r\nEncryptionID: ");
id[0] = *(u8*)(SYSID1);
id[1] = *(u8*)(SYSID2);
id[2] = *(u8*)(SYSID3);
id[3] = *(u8*)(SYSID4);
id[4] = *(u8*)(SYSID5);
id[5] = *(u8*)(SYSID6);
id[6] = *(u8*)(SYSID7);
id[7] = *(u8*)(SYSID8);
id[8] = *(u8*)(SYSID9);
id[9] = *(u8*)(SYSID10);
id[10] = *(u8*)(SYSID11);
id[11] = *(u8*)(SYSID12);
for(i =0;i < 12;i++) //读取ID
{
printf("%#X,",id[i]);
}
dfu=FLASHIDbuf;
for(i=0;i<12;i++)
{
*dfu=id[i];
dfu++;
}
}
//解密
void CheckSystemID(void)
{
uint8_t i;
u8 id[12]; //12*8 = 96
u8 *dfu;
u8 d;
printf("\r\n\r\nCheckSystemID");
//读ID数据并比较加密数据///
id[0] = *(u8*)(SYSID1);
id[1] = *(u8*)(SYSID2);
id[2] = *(u8*)(SYSID3);
id[3] = *(u8*)(SYSID4);
id[4] = *(u8*)(SYSID5);
id[5] = *(u8*)(SYSID6);
id[6] = *(u8*)(SYSID7);
id[7] = *(u8*)(SYSID8);
id[8] = *(u8*)(SYSID9);
id[9] = *(u8*)(SYSID10);
id[10] = *(u8*)(SYSID11);
id[11] = *(u8*)(SYSID12);
dfu=FLASHIDbuf;
for(i=0;i<12;i++)
{
d=*dfu;
if(d==id[i])printf("\r\nOK%u",i);//比对正确,则打印
else //ID校验失败
{
printf("\r\n\r\nError");
while(1)//死循环
{
;
}
}
dfu++;
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART1_Serial_Interface_Enable(115200);
printf("\r\nCPU reset\r\n");
while(1)
{
Save_SystemID();//保存加密数据
CheckSystemID();//检查加密数据
}
}
要想解决不容易被破解,就将"Unique device ID"寄存器的地址为0x1FFFF7E8放到RAM中,通过计算得到。
#include "stm32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "USART1.h"
//注意:"USART1.h"要放在 "stdio.h" 包含文件之后,如果这个位置颠倒了,业不能打印浮点数。
/*
CPU内部的"unique ID"是唯一的,且不可修改,因此,我们可对这ID进行加密,
然后将加密结果保存到FLASH中。这样每次启动都会去比较加密数据是否匹配。正
确,则运行;不正确则进入死循环。
STM32不同系列的ID起始地址不同,如下所示:
1. 0x1FFFF7AC, /STM32F0唯一ID起始地址
2. 0x1FFFF7E8, /STM32F1唯一ID起始地址
3. 0x1FFF7A10, /STM32F2唯一ID起始地址
4. 0x1FFFF7AC, /STM32F3唯一ID起始地址
5. 0x1FFF7A10, /STM32F4唯一ID起始地址
6. 0x1FF0F420, /STM32F7唯一ID起始地址
7. 0x1FF80050, /STM32L0唯一ID起始地址
8. 0x1FF80050, /STM32L1唯一ID起始地址
9. 0x1FFF7590, /STM32L4唯一ID起始地址
10. 0x1FF0F420; /STM32H7唯一ID起始地址
*/
#define SYSID 0X1FFFF7E8 //stm32f1系列单片机id起始地址
//直接使用0X1FFFF7E8地址,它会出现在FLASH中,还是可以改到的;
//使用RAM中的数据通过加减来得到ID起始地址,比较安全;
u8 FLASHIDbuf[12];
//保存加密数据
void Save_SystemID(void)
{
uint8_t i;
u8 id[12]; //12*8 = 96
volatile u32 UID_BASE;
UID_BASE = 0x20000007; //让逆向的人误以为是ram变量
UID_BASE -= 0x800;
UID_BASE -= 0x1F; //等于id的基地址0x1FFFF7E8
//读ID数据并保存加密数据///
printf("\r\n\r\nEncryptionID: ");
for(i =0;i < 12;i++) //读取ID
{
id[i] = *(u8*)(UID_BASE+i);
FLASHIDbuf[i]=id[i];
printf("%#X,",id[i]);
}
}
//解密
void CheckSystemID(void)
{
uint8_t i;
u8 id[12]; //12*8 = 96
u8 *dfu;
u8 d;
volatile u32 UID_BASE;
UID_BASE = 0x20000007; //让逆向的人误以为是ram变量
UID_BASE -= 0x800;
UID_BASE -= 0x1F; //等于id的基地址0x1FFFF7E8
printf("\r\n\r\nCheckSystemID");
//读ID数据并比较加密数据///
dfu=FLASHIDbuf;
for(i=0;i<12;i++)
{
id[i] = *(u8*)(UID_BASE+i);
d=*dfu;
if(d==id[i])printf("\r\nOK%u",i);//比对正确,则打印
else //ID校验失败
{
printf("\r\n\r\nError");
while(1)//死循环
{
;
}
}
dfu++;
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART1_Serial_Interface_Enable(115200);
printf("\r\nCPU reset\r\n");
while(1)
{
Save_SystemID();//保存加密数据
CheckSystemID();//检查加密数据
}
}
这样,在FLASH中找不到"Unique device ID"寄存器的地址,如:0x1FFFF7E8。
为了保险起见,我们可以将ID读出后按照某个算法得到另外一个数值保存到FLASH中,就会更加安全。
只要盗版者无法修改的"Unique device ID"寄存器的内容,程序就不会被盗版。
如果你有好的破解方法或者是工具,请留言。