为了防止大家的程序不被剽窃,本文给大家分享单片机加密的方法。

1. 加密方法一: Flash读保护

STM32读保护是通过设置RDP选项字节,然后在系统重新复位加载了新的RDP选项字节后启动的。

当保护字节被写入相应的值以后:

  • 通过从内置SRAM或FSMC执行代码访问主闪存存储器的操作,通过DMA1、DMA2、JTAG、SWV(串行线观察器)、SWD(串行线调试)、ETM和边界扫描方式对闪存的访问都将被禁止。
  • 只允许从用户代码中对主闪存存储器的读操作(以非调试方式从主闪存存储器启动)。
  • 第0~3页(小容量和中容量产品),或第0~1页(大容量和互联型产品)被自动加上了写保护,其它部分的存储器可以通过在主闪存存储器中执行的代码进行编程(实现IAP或数据存储等功能),但不允许在调试模式下或在从内部SRAM启动后执行写或擦除操作(整片擦除除外)。
  • 所有通过JTAG/SWD向内置SRAM装载代码并执行代码的功能依然有效,亦可以通过JTAG/SWD从内置SRAM启动,这个功能可以用来解除读保护。当读保护的选项字节转变为存储器未保护的数值时,将会执行整片擦除过程。
  • 可以使用系统启动程序解除读保护(此时只需执行系统复位即可重新加载选项字节),芯片自动擦除Flash所有内容。

HAL库实现Flash读保护的方式如下

void SysCodeProtection( void )
{
    FLASH_OBProgramInitTypeDef pOBInit = {0}; 
    HAL_FLASHEx_OBGetConfig( &pOBInit ); 
    if ( pOBInit.RDPLevel != OB_RDP_LEVEL_1 )
    {
        HAL_FLASH_Unlock();
        HAL_FLASH_OB_Unlock();
        pOBInit.OptionType = OPTIONBYTE_RDP;
        pOBInit.RDPLevel = OB_RDP_LEVEL_1;
        HAL_FLASHEx_OBProgram( &pOBInit );
        HAL_FLASH_OB_Launch();
        HAL_FLASH_OB_Lock();
        HAL_FLASH_Lock();
     } 
}

2. 主程序中使用设备ID保护

即使将Flash设置为读保护,破解者也可以通过IAP下载自己的一段小程序,从而读出Flash中的内容。

因此,还需要利用设备的唯一ID进行加密保护。在主程序中,加入对设备唯一ID的检测,这样即使破解者读出了芯片中的二进制码,也不能用这个二进制码去复制新的器件。

具体实现方法:

  • 在应用程序中定义1个(32位甚至更多)const变量,变量值全为0xFF。每次启动程序时,检查const变量值如果全为0xFF,就读器件的唯一ID,通过Flash编程写入该const变量中(因为全是0xFF,所以可以编程写入)。
  • 在程序中多个地方检查const变量,如果变量值不为0xFF并且与设备ID不一致,就执行与功能无关代码(比如自擦除)。

这样,即使破解者读出了芯片中的二进制码,因为这份二进制码包含了设备唯一ID,具有唯一性,所以不能复制到其他芯片中。

为了避免破解者利用反汇编,根据芯片ID数据在二进制文件中查找对应相同数据的位置从而破解,可以将ID拆散成不同的组合,并且写到不同且不连续的地方。更进一步,可在程序中检测多份这样的分散ID,以增加反汇编的难度。或者将CPUID进行加密,Flash中存储加密后的结果。

//加密后的CPUID
volatile const static uint32 CPUIDEncrypt = 0xFFFFFFFF;  
//写入加密数据  
{   
    //第一次烧写:将UID写入到Flash中
    if(CPUIDEncrypt==0xFFFFFFFF)
    {
        uint32_t CpuID[3];         
        //获取CPU唯一的ID
        CpuID[0]=*(vu32*)(UID_BASE);
        CpuID[1]=*(vu32*)(UID_BASE+4);
        CpuID[2]=*(vu32*)(UID_BASE+8);

        //加密算法,很简单的加密算法
        uint32_t EncryptCode=(CpuID[0]>>3)+(CpuID[1]>>1)+(CpuID[2]>>2);
        FLASH_Unlock();
        FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);  
        FLASH_ProgramWord((uint32_t)&CPUIDEncrypt, EncryptCode);  
        FLASH_Lock();  
    }
}
//判断加密
bool JudgeEncrypt(void)
{
    uint32_t CpuID[4];
    //获取CPU唯一的ID  
    CpuID[0]=*(uint32_t *)(UID_BASE);  
    CpuID[1]=*(uint32_t *)(UID_BASE+4);  
    CpuID[2]=*(uint32_t *)(UID_BASE+8);
    //加密算法,很简单的加密算法
    CpuID[3]=(CpuID[0]>>3)+(CpuID[1]>>1)+(CpuID[2]>>2);     
    //检查Flash中的UID是否合法
    return (CPUIDEncrypt == CpuID[3]);
}