uid:unique ID
每个单片机芯片出厂后唯一的ID,不会重复。 uid有很多用途,我们可以用来作为mac地址,可以用来记录唯一的日志,也可以用来防止固件被拷贝使用。 本文所描述的是固件被拷贝使用的问题,即:固件从单片机中通过Jlink回读出来,然后再烧到其他芯片中运行,以atmel的samv71犀利为例。
1、定义uid校验码存放区
在链接文件xxx.ld文件中定义我们要存放的uid校验码在flash中的区域: uid_encryption
MEMORY
{
rom (rx) : ORIGIN = 0x00400000, LENGTH = 0x00020000
ram (rwx) : ORIGIN = 0x20400000, LENGTH = 0x00060000
}
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : 0x4000;
__ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;
HEAP_SIZE = DEFINED(HEAP_SIZE) ? HEAP_SIZE : 0x4000;
SECTIONS
{
.text :
{
. = ALIGN(4);
_sfixed = .;
KEEP(*(.vectors .vectors.*))
*(.text .text.* .gnu.linkonce.t.*)
*(.glue_7t) *(.glue_7)
*(.rodata .rodata* .gnu.linkonce.r.*)
*(.ARM.extab* .gnu.linkonce.armextab.*)
......
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
. = ALIGN(4);
KEEP (*(SORT(.uid_encryption.*)))
KEEP (*(.uid_encryption))
. = ALIGN(4);
_efixed = .;
} > rom
......
2、定义uid校验码变量
定义32位校验码
__attribute__((used, section(".uid_encryption"))) static uint32_t uid_encry = 0xFFFFFFFF;
3、读取uid并处理后保存
初次运行时,将uid从芯片中读取,经处理后保存到flash中,本文采用的是crc32处理,其他可根据自身情况进行修改:
static void uid_save()
{
flash_read_unique_id(uid, sizeof(uid)/sizeof(uint32_t));
if (uid_encry == 0xFFFFFFFF) {
uint32_t crc;
uint32_t addr = &uid_encry;
crc = crc32((uint8_t*)uid, sizeof(uid));
flash_init(FLASH_ACCESS_MODE_128, 6);
flash_write(addr, &crc, sizeof(crc), 0);
}
}
4、认证
之后每次运行,都会从芯片中将uid读取出来,经处理之后,与存放在flash中的校验码进行对比验证,若不一致,则代码运行在不同的芯片上,不会继续往下运行正常的程序。
static bool uid_authentication()
{
uint32_t local_crc;
uint32_t auth_crc;
local_crc = uid_encry;
auth_crc = crc32((uint8_t*)uid, sizeof(uid));
if (auth_crc == local_crc) {
return true;
}
else {
return false;
}
}
5、效果
如此,代码如果从当前芯片中回读出来,然后烧录到其他芯片上,则会在认证阶段无法通过,就不会运行正常的功能程序。
本文只是简单的uid使用方法,当前还有其他的复杂更高效的使用方法,可学习和探索。
|