首先要清楚,单片机程序破解不仅仅是获得了源程序,即使没有获得源程序,获得了可执行文件并且可以随意复制使用,这种情况也算是被破解了。
所以单片机程序加密,也分两个方面:第一,不能让别人获得你的bin文件或者hex文件;第二,即使获得了bin文件或者hex文件,烧写进新的单片机也运行不了。
第一方面的解决办法常见的是借用软件对程序存储flash区进行读保护,要想读取这部分flash区域,只能先擦除,这样就防止别人轻易读取我们的程序。比如flash loader demonstrator就是针对stm单片机的专用烧写、读取程序,进行程序区读保护的软件。但借助更高级的工具,依然可以p破解这种读保护,从而把程序读取出去,所以我们要进行第二方面的加密。
第二方面是借用软件方法来实现的,今天介绍的就是这种方法的基本操作方法。
每一片单片机的id号是全球唯一的,在单片机第一次运行程序的时候,把单片机id号读出来,再进行加密,把加密后的内容烧写到程序区的空白处,和程序混合在一起。这样单片机里的程序其实已经融合了单片机的id号,是绑定了这个id号的单片机专用程序。在主程序的某些地方加入验证程序,验证读取到的id号加密后和加密区的内容是否一致,如果一致,说明程序是单片机的原程序,如果不一致,说明这个程序是从别的单片机复制过来的。我们再加入自宫程序,就是验证出不是单片机原程序,那就执行擦除代码区的指令。这样单片机就成了一个空机,防止了别人复制我们的程序。
目录
一、读取单片机ID并加密
stm32f407的id号是96位,由三个32位地址来存放。存放的地址为0x1FFF7A10、0x1FFF7A14、0x1FFF7A18。所以我们需要到这三个地址去读取。
需要注意的是,在程序中不能直接出现这三个地址,因为这些地址是公开的,破解者可以在bin文件或者hex文件中,通过搜索这三个地址找到读取id号和加密的代码位置,很容易破解出加密后的内容存放地址。假如破解者把加密内容清空,这样程序就变成了一个未运行过的原始程序,没有绑定id号,就可以随意复制到其他单片机。
所以,在声明这3个地址的时候要进行偏移,读取id号的时候再把偏移过的地址进行反偏移计算出真正的地址。
1、读取id并加密程序
volatile u32 ID_Shifted_Add=0x1FF367C4;//偏移过的id号存放首地址
const u32 ID_Encrypted[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};//加密后的内容存放地址
void Id_Encrypt(void)
{
//如果内容为0xFFFFFFFF,则未写入过加密内容,则执行写入加密内容代码
if(McuIDEncrypt[0]==0xFFFFFFFF)
{
uint32_t Stm32_ID[3];
uint32_t ID_Encrypted_value;
//反偏移计算出真正的id存放地址
Stm32_ID[0]=*(vu32*)(ID_Shifted_Add+791116);
Stm32_ID[1]=*(vu32*)(ID_Shifted_Add+791116+4);
Stm32_ID[2]=*(vu32*)(ID_Shifted_Add+791116+8);
//简单加密
ID_Encrypted_value=(Stm32_ID[0]>>8)+(Stm32_ID[1]>>2)+(Stm32_ID[2]>>9);
//加密后的内容存放到flash内
STMFLASH_Write((u32)ID_Encrypted,(u32*)&ID_Encrypted_value,1);
//写操作需要一定时间,所以延时一定时间,保证写入成功
delay_ms(3000);
}
}
2、写入flash函数
STMFLASH_Write()是写入flash函数,是库函数stmflash.c中的函数。
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
FLASH_Status status = FLASH_COMPLETE;
u32 addrx=0;
u32 endaddr=0;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;
FLASH_Unlock();
FLASH_DataCacheCmd(DISABLE);
addrx=WriteAddr;
endaddr=WriteAddr+NumToWrite*4;
if(addrx<0X1FFF0000)
{
while(addrx<endaddr)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)
{
status=FLASH_EraseSector(STMFLASH_GetFlashSector(addrx),VoltageRange_3);
if(status!=FLASH_COMPLETE)break;
}else addrx+=4;
}
}
if(status==FLASH_COMPLETE)
{
while(WriteAddr<endaddr)
{
if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)
{
break;
}
WriteAddr+=4;
pBuffer++;
}
}
FLASH_DataCacheCmd(ENABLE);
FLASH_Lock();
}
3、stmflash.h文件
#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"
//
#define STM32_FLASH_BASE 0x08000000
#define ADDR_FLASH_SECTOR_0 ((u32)0x08000000)
#define ADDR_FLASH_SECTOR_1 ((u32)0x08004000)
#define ADDR_FLASH_SECTOR_2 ((u32)0x08008000)
#define ADDR_FLASH_SECTOR_3 ((u32)0x0800C000)
#define ADDR_FLASH_SECTOR_4 ((u32)0x08010000)
#define ADDR_FLASH_SECTOR_5 ((u32)0x08020000)
#define ADDR_FLASH_SECTOR_6 ((u32)0x08040000)
#define ADDR_FLASH_SECTOR_7 ((u32)0x08060000)
#define ADDR_FLASH_SECTOR_8 ((u32)0x08080000)
#define ADDR_FLASH_SECTOR_9 ((u32)0x080A0000)
#define ADDR_FLASH_SECTOR_10 ((u32)0x080C0000)
#define ADDR_FLASH_SECTOR_11 ((u32)0x080E0000)
u32 STMFLASH_ReadWord(u32 faddr);
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite);
void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead);
#endif
二、验证程序
bool Check(void)
{
u8 i;
uint32_t Stm32_ID[3];
uint32_t ID_Encrypted_value;
u32 datatemp[3]={0};
//读id号
Stm32_ID[0]=*(vu32*)(ID_Shifted_Add+791116);
Stm32_ID[1]=*(vu32*)(ID_Shifted_Add+791116+4);
Stm32_ID[2]=*(vu32*)(ID_Shifted_Add+791116+8);
//计算加密结果
ID_Encrypted_value=(Stm32_ID[0]>>8)+(Stm32_ID[1]>>2)+(Stm32_ID[2]>>9);
//读取存入加密区内容,存入到datatemp中
STMFLASH_Read((u32)ID_Encrypted,(u32*)datatemp,1);
//判断加密结果和存入的内容是否一致,并返回比较的结果
return (datatemp[0]==ID_Encrypted_value);
}
三、自宫程序
void Erase(void)
{
FLASH_Unlock();
FLASH_DataCacheCmd(DISABLE);
FLASH_EraseAllSectors(VoltageRange_3);
FLASH_DataCacheCmd(ENABLE);
FLASH_Lock();
}
注意,本部分所用的函数都在官方库函数stm32f4xx_flash.c中。
四、主程序中调用
1、在程序运行开始调用ReadId_Encrypt()
2、在程序多个地方调用验证程序并对不同结果进行处理
if(Check()==0)//如果验证不通过
{
Erase();//执行自宫程序
}
(全文结束)