STM32 软件加密另类方法(软件加密)

/ 3评 / 5

这里讨论一种软件加密方式,无需修改Option Byte,无需二次烧写序列号,只要按照普通流程一样,烧写HEX文件进去,整个加密就起效了.别人读出你的HEX文件,重新烧写到他的片子上,也无法使用.当然修改Option Byte会更好啦.组合一起就最好啦,比如读保护,开机就执行禁止Flash写,哪怕别人读出了你的Flash,也会因为写不了而...
首先要对96位UID动手,因为有了唯一UID,才能满足以下的情况.比如把UID通过一种运算组合,变成一个新的序列,或者低级一些,直接用UID加密,比如我这里用的就比较低级,如果上电发现UID和我指定位置的数据比对,如果不对,程序就不执行.比对如图两个红箭头处信息是否是对的.

其中0x1FFFF7AC位置有UID,ST出厂烧写了,0x0801F800下有我们的UID加密序列,正常来说,我们应该自己烧写,然后比对这两个地方对不对.UID的存放示意图.

好了,我们假设我们加密UID(其实就是UID拷贝)放到了0x0801F800,那么我们如此比对.

    if( *(__IO uint32_t *)(0x0801F800) == *(__IO uint32_t *)(0x1FFFF7AC))
    {
        if( *(__IO uint32_t *)(0x0801F804) == *(__IO uint32_t *)(0x1FFFF7B0))
        {
            if( *(__IO uint32_t *)(0x0801F808) == *(__IO uint32_t *)(0x1FFFF7B4))
            {
                while (1)
                {
                    LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_6);
                    LL_mDelay(25);
                }
            }
        }
    }

如果比对完全一样,LED就狂闪,25ms间隔啊.问题来了,谁给0x0801F800编程啊.是二次烧写,还是程序内嵌,肯定是前者靠谱啊.烧写方法特别简单,如此一气呵成.

        HAL_FLASH_Unlock();
        {
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F800, *(__IO uint32_t *)(0x1FFFF7AC)) == HAL_OK)
            {
                if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F804, *(__IO uint32_t *)(0x1FFFF7B0)) == HAL_OK)
                {
                    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F808, *(__IO uint32_t *)(0x1FFFF7B4)) == HAL_OK)
                    {
                    }
                }
            }
        }
        HAL_FLASH_Lock();

等等,为什么Flash可以不擦就写么,肯定不行啊,但是烧写时候,我们可以全片擦除嘛.但是还有一个新的BUG,如果是全片擦除,那么,如果是别人拷贝我们的Flash内容会怎样,明显就是一开机,0x0801F800位置就不可能是0xFFFFFFFF,因为我们的Flash内容上这部分肯定有内容的,我们一开机就编程了.所以整个逻辑变成了这样.

    if( *(__IO uint32_t *)(0x0801F800) == 0xFFFFFFFF)
    {
        HAL_FLASH_Unlock();
        {
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F800, *(__IO uint32_t *)(0x1FFFF7AC)) == HAL_OK)
            {
                if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F804, *(__IO uint32_t *)(0x1FFFF7B0)) == HAL_OK)
                {
                    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F808, *(__IO uint32_t *)(0x1FFFF7B4)) == HAL_OK)
                    {
                    }
                }
            }
        }
        HAL_FLASH_Lock();
    }
    if( *(__IO uint32_t *)(0x0801F800) == *(__IO uint32_t *)(0x1FFFF7AC))
    {
        if( *(__IO uint32_t *)(0x0801F804) == *(__IO uint32_t *)(0x1FFFF7B0))
        {
            if( *(__IO uint32_t *)(0x0801F808) == *(__IO uint32_t *)(0x1FFFF7B4))
            {
                while (1)
                {
                    LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_6);
                    LL_mDelay(25);
                }
            }
        }
    }

也有人疑问,不是直接执行HAL_FLASH_Program,然后返回FAIL不就知道了吗,确实没错,但是这样会浪费更多的时间,因为Flash编程失败是基于Flash的总线超时来做的.一点都不划算.我们把编程的程序烧写进去,发现狂闪,确实没错了.
打开ST-Link工具,查看对应位置,果然有内容了.

比对一下,发现跟序列号真的一样哦.

模拟一下情况,把Flash存的序列号写错.

断开仿真器,然后重新上电,发现执行不到快闪了.那批量时候怎么办呢,这好解决了.先把0x0801F800填FF.

然后看看程序实际大小.

提取到0x538那么长的程序就满足了.

然后来个全片擦.

然后再写刚才保存的.

LED就可以正常快闪了.当然,STLink的擦除是部分擦除,如果Flash本身不是空的,他也只擦掉需要编程的位置的数据.比如下面说一个极端情况.UID位置被写了其他内容.这时候烧写精简就没用了.

这时候需要烧写全片,或者执行一次全片擦除.问题解决.备份全片就是把SIZE调大一点,其实后面也是全0,但是告诉烧写工具,我要全片擦掉.

另一个方法就是调整设置.比如STLink的自动模式,就可以这么干.要来全片擦.

好好利用这个自动模式,还是非常好用的.加密程序主要参考代码,下面还有OB的代码,记住哦,保护2的意思是ST都没办法解除哦.

int main(void)
{
    SystemClock_Config();
    if( *(__IO uint32_t *)(0x0801F800) == 0xFFFFFFFF)
    {
        static FLASH_OBProgramInitTypeDef FLASH_OBProgramInitStruct;
        HAL_FLASH_Unlock();
        {
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F800, *(__IO uint32_t *)(0x1FFFF7AC)) == HAL_OK)
            {
                if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F804, *(__IO uint32_t *)(0x1FFFF7B0)) == HAL_OK)
                {
                    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0801F808, *(__IO uint32_t *)(0x1FFFF7B4)) == HAL_OK)
                    {
                    }
                }
            }
        }
        HAL_FLASH_OB_Unlock();
        HAL_FLASHEx_OBErase();
        FLASH_OBProgramInitStruct.WRPPage    = OB_WRP_ALLPAGES;
        FLASH_OBProgramInitStruct.WRPState    = OB_WRPSTATE_ENABLE;
        FLASH_OBProgramInitStruct.RDPLevel    = OB_RDP_LEVEL_1;
        FLASH_OBProgramInitStruct.OptionType    = OPTIONBYTE_WRP | OPTIONBYTE_RDP;
        HAL_FLASHEx_OBProgram(&FLASH_OBProgramInitStruct);
        HAL_FLASH_OB_Launch();
        HAL_FLASH_OB_Lock();
        HAL_FLASH_Lock();
    }
    if( *(__IO uint32_t *)(0x0801F800) == *(__IO uint32_t *)(0x1FFFF7AC))
    {
        if( *(__IO uint32_t *)(0x0801F804) == *(__IO uint32_t *)(0x1FFFF7B0))
        {
            if( *(__IO uint32_t *)(0x0801F808) == *(__IO uint32_t *)(0x1FFFF7B4))
            {
                while (1)
                {
                    LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_6);
                    LL_mDelay(25);
                }
            }
        }
    }
    while (1)
    {
        LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_6);
        LL_mDelay(1000);
    }
}
  1. 井底添蛙说道:

    方法确实不错,

  2. 梅先生说道:

    在程序中不要直接出现1ffff7e8,另外最好程序进行校验,可以加q聊聊,5225016

  3. 梅先生说道:

    三、利用id做软件加密
    1,如果板子上有外部存储器,可以先编写一个程序,利用算法把id计算得到一些值存入外部存储器,然后再烧写真正的程序,真正的程序去校验外部存储器的数据是否合法即可

    2,利用板子上按键组合,或是上电按住某些键,程序在这个时候利用算法把id计算得到一些值存入程序区(stm8为EE区),程序运行时去验证程序区数据是否正确

    3,轩微编程器有软件加密的功能,编程器会读芯片id,根据算法直接改写缓冲区,达到软件加密的作用

    4,读出的id通过一定算法,例如异或加上一个数,得到的数据存入flash(只运行一次,运行后标志位也存入flash),下次读到这个标志位,就不运行这个程序。//Q9272078

    四、做软件加密时注意
    1,不要在程序中直接出现id地址,例如STM32:1FFFF7E8 1FFFF7EC 1FFFF7F0 STM8: 0x4865~0x4870
    2, 利用校验和或是crc对程序区进行校验,防止改程序

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注