- UID
- 1029342
- 性别
- 男
|
本文目标是对运行于STM32的嵌入式代码程序进行加密,编译环境为Keil。
0 k1 |5 V }5 s5 X/ p' N- y
一.STM32Flash组织
4 d' H, D2 H5 A! U: g
STM32的Flash包括主存储器(HD版本,512KB)+信息块。信息块包括2KB的系统存储器(用于系统自举启动代码)和16字节的选项字节(8个字节数据+8个字节数据的反码)。( x6 p" ^8 r7 ?* r6 z$ m
二、STM32读保护
STM32读保护是通过设置RDP选项字节,然后在系统重新复位加载了新的RDP选项字节后启动的。当保护字节被写入相应的值以后:
●通过从内置SRAM或FSMC执行代码访问主闪存存储器的操作,通过DMA1、DMA2、JTAG、SWV(串行线观察器)、SWD(串行线调试)、ETM和边界扫描方式对闪存的访问都将被禁止。
2 G% M" G3 l. U! b: c3 W( U6 b
●只允许从用户代码中对主闪存存储器的读操作(以非调试方式从主闪存存储器启动)。
●第0~3页(小容量和中容量产品),或第0~1页(大容量和互联型产品)被自动加上了写保护,其它部分的存储器可以通过在主闪存存储器中执行的代码进行编程(实现IAP或数据存储等功能),但不允许在调试模式下或在从内部SRAM启动后执行写或擦除操作(整片擦除除外)。2 U4 H+ r( S M" z$ B- ~0 r4 Q3 k
●所有通过JTAG/SWD向内置SRAM装载代码并执行代码的功能依然有效,亦可以通过JTAG/SWD从内置SRAM启动,这个功能可以用来解除读保护。当读保护的选项字节转变为存储器未保护的数值时,将会执行整片擦除过程。
3 Z- k. J$ j5 A+ ?) I# {
●可以使用系统启动程序解除读保护(此时只需执行系统复位即可重新加载选项字节),芯片自动擦除Flash所有内容。
5 V& n- @% ]& M0 R0 U8 C
三.STM32的加密
: q! y/ Z$ j9 I2 M# U8 L
1.使用系统启动程序STM32 Flash Loader demonstrator将Flash设置为读保护。- n+ q6 T4 c' n8 j3 o
所有以调试工具、内置SRAM或FSMC执行代码等方式对主存储器访问的操作将被禁止,只允许用户代码对主Flash存储器的读操作和编程操作(除了Flash开始的4KB区域不能编程)。用户代码允许自主编程可以实现IAP或者数据存储等功能。, k7 e/ ?" C2 H }, O
这样破解者将不能用调试工具、内置SRAM或者FSMC执行代码等方式读出Flash中的代码。破解者也不能使用系统启动程序读取代码,因为要解除读保护,将执行整个芯片的擦除操作。4 I& V' N# ]* `* K7 V+ S& S
2.主程序中使用设备ID保护
即使将Flash设置为读保护,破解者也可以通过IAP下载自己的一段小程序,从而读出Flash中的内容。因此,还需要利用设备的唯一ID进行加密保护。在主程序中,加入对设备唯一ID的检测,这样即使破解者读出了芯片中的二进制码,也不能用这个二进制码去复制新的器件。具体实现方法:: o' o( e6 C/ D
(1)在应用程序中定义1个(32位甚至更多)const变量,变量值全为0xFF。每次启动程序时,检查const变量值如果全为0xFF,就读器件的唯一ID,通过Flash编程写入该const变量中(因为全是0xFF,所以可以编程写入)。
(2)在程序中多个地方检查const变量,如果变量值不为0xFF并且与设备ID不一致,就执行与功能无关代码(比如自擦除)。
( b: i4 \5 I3 X. i
这样,即使破解者读出了芯片中的二进制码,因为这份二进制码包含了设备唯一ID,具有唯一性,所以不能复制到其他芯片中。
为了避免破解者利用反汇编,根据芯片ID数据在二进制文件中查找对应相同数据的位置从而破解,可以将ID拆散成不同的组合,并且写到不同且不连续的地方。更进一步,可在程序中检测多份这样的分散ID,以增加反汇编的难度。或者将CPUID进行加密,Flash中存储加密后的结果。
四.程序加密的实现
//加密后的CPUID* Y; h- D9 o) a4 w; v. }5 T; O! Z0 f
volatile const static uint32 CPUIDEncrypt = 0xFFFFFFFF;
0 p1 q. S6 I, K4 F
//写入加密数据+ D, I D" {; ?$ X2 ?' }9 d6 ~/ g
void WriteEncrypt(void)
{
//第一次烧写:将UID写入到Flash中4 L; d) Q! y1 U" \5 @% o
if(CPUIDEncrypt==0xFFFFFFFF)
{
uint32_t CpuID[3];
//获取CPU唯一的ID: ^8 S9 v6 A/ Q; ]7 G: T
CpuID[0]=*(vu32*)(UID_BASE);
CpuID[1]=*(vu32*)(UID_BASE+4);
CpuID[2]=*(vu32*)(UID_BASE+8); . v$ a0 x1 e' O; N" F
5 Q6 _% V, R* N+ m; ~2 J7 i9 z
//加密算法,很简单的加密算法: O: e9 o" Q- o" E) P! B. ~
uint32_t EncryptCode=(CpuID[0]>>3)+(CpuID[1]>>1)+(CpuID[2]>>2);
FLASH_Unlock();6 D6 ^: }! S. H( Q
FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);0 W: p" X$ `7 e8 j& P) _4 \
FLASH_ProgramWord((uint32_t)&CPUIDEncrypt, EncryptCode);
FLASH_Lock();
}
} |
|