查看: 60|回复: 0

​使用UUID进行程序加密

[复制链接]

主题

好友

422

积分

秀才

该用户从未签到

发表于 2019-4-2 08:37:33 |显示全部楼层
单片机FLASH中的程序可以被开盖获取,即便关闭调试接口,折断管脚,内部设置FLASH读保护也只是增加一点获取难度而已。
现在大多数MCU在制造过程中都加入的UUID,用UUID加密程序可以进一步提高程序的保密性。
为防止破解人员获取flash二进制数据后快速定位加密方法,二进制code中不能直接出现UUID相关信息,包括UUID值和地址。
下面的演示程序用UUID来制作一个密码本。
加密程序要单独放在一个文件内,方便确定地址。
/*****************************************************************************//*!
*
* @brief   jiami.
*   
* @param   1111
*
* @return  none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void JiaMiHandle(uint32_t rx0, uint32_t rx1, uint32_t rx2, uint32_t rx3)
{
    uint32_t mima_buff[32];
    uint32_t mima_now[4];
    uint32_t Index;
    volatile uint32_t addr;
    uint32_t u32t;
    uint32_t offset;
    NOP5();
    //此模板生成地址,防止编译器在程序尾部添加地址常量
    //程序中调用密码本时,密码本地址及密码偏移地址也应用此方法生成
    //MOV指令支持8bits立即数传递,超出范围的将数据集中放在函数尾部通过LDR指令加载。所以加密过程应尽量避免大于255的常数出现。地址生成过程禁止出现大于255的常数。
    addr = 0;
    addr += rx0 ? (UUID_ID1>>24)&0xFF : 12;
    addr <<= 8;
    addr += rx1 ? (UUID_ID1>>16)&0xFF : 13;
    addr <<= 8;
    addr += rx2 ? (UUID_ID1>>8)&0xFF : 15;
    addr <<= 8;
    addr += rx3 ? (UUID_ID1&0xFF) : 16;
    //读取UUID
    rx0 = ((~(*((volatile uint32_t *)(addr))))*3>>1)*13+23;
    addr+=4;
    rx1 = ((~(*((volatile uint32_t *)(addr))))*5>>1)*7+19;
    addr+=4;
    rx2 = ((~(*((volatile uint32_t *)(addr))))*7>>1)*11+5;
    rx3 = rx0 + rx1 + rx2;
    addr = 0;
    //计算密码
    //MiMaxGP1
    mima_buff[0] = (~rx0)*3 + 71;
    mima_buff[1] = (((((~rx1)*7 + 71)<<1)*5)>>7)*131+71;
    mima_buff[2] = (~rx2)*11 + 71;
    mima_buff[3] = usMBCRC16((uint8_t*)mima_buff, 6);
    mima_buff[3] = ((mima_buff[3]*3)<<16)+(mima_buff[3]);
    //MiMaxGP2
    mima_now[0] = (((((~rx0) * 31 + 3)>>3)*11)>>1)*3+5;
    mima_now[1] = (~rx1)  * 47 + 31;
    mima_now[2] = (~rx2)  * 71 + 39;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*11)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[4+Index] = mima_now[Index];
    //MiMaxGP3
    mima_now[0] =  (~rx0) * 5 + 11;
    mima_now[1] =  (((~rx1)  * 17 + 51)>>5)*41;
    mima_now[2] = (~rx2)  * 41 + 31;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*17)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[8+Index] = mima_now[Index];
    //MiMaxGP4
    mima_now[0] = (~rx0) * 23 + 23;
    mima_now[1] = (~rx1)  * 37 + 13;
    mima_now[2] = (((~(rx2+rx0))  * 53 + 29)>>7)*91+91;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*23)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[12+Index] = mima_now[Index];
    //MiMaxGP5
    mima_now[0] = (~rx0>>1)*3 + 71;
    mima_now[1] = (~rx1>>1)*7 + 71;
    mima_now[2] = (~rx2>>3)*11 + 71;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*3)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[16+Index] = mima_now[Index];
    //MiMaxGP6
    mima_now[0] = (~(rx0+rx1)>>3) * 31 + 3;
    mima_now[1] = ((((~(rx1+rx2)>>4)  * 47 + 31)*3)>>2)*5+13;
    mima_now[2] = (~rx2>>5)  * 71 + 39;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*11)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[20+Index] = mima_now[Index];
    //MiMaxGP7
    mima_now[0] = (~rx0>>1) * 5 + 11;
    mima_now[1] = (~rx3>>2)  * 17 + 51;
    mima_now[2] = (~rx2>>3)  * 41 + 31;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*17)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[24+Index] = mima_now[Index];
    //MiMaxGP8
    mima_now[0] = (~rx0>>2)  * 23 + 23;
    mima_now[1] = ((((~rx1>>3)  * 37 + 13)<<3)+11)*5+11;
    mima_now[2] = (~rx2>>4)  * 53 + 29;
    mima_now[3] = usMBCRC16((uint8_t*)mima_now, 6);
    mima_now[3] = ((mima_now[3]*23)<<16)+(mima_now[3]);
    for(Index=0;Index <=3;Index++)
        mima_buff[28+Index] = mima_now[Index];
    u32t = rx3;
    offset = ((rx3>>8)+(rx3>>16)+(rx3>>24)+(rx3))*3 & 0xff; //根据UUID生成0-255的偏移
    addr = (uint32_t)&MiMa;
    FLASH_Unlock();//解锁flash
    for(Index=0; Index < 512;Index++)
    {
            //生成密码本
            if(offset + 3 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[0]); //MiMaxGP1
            else if(offset + 7 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[1]);
            else if(offset + 13 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[2]);
            else if(offset + 23 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[3]);
            else if(offset + 37 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[4]);//MiMaxGP2
            else if(offset + 53 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[5]);
            else if(offset + 71 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[6]);
            else if(offset + 87 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[7]);
            else if(offset + 5 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[8]);//MiMaxGP3
            else if(offset + 11 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[9]);
            else if(offset + 17 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[10]);
            else if(offset + 29 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[11]);
            else if(offset + 41 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[12]);//MiMaxGP4
            else if(offset + 57 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[13]);
            else if(offset + 79 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[14]);
            else if(offset + 101 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[15]);
            else if(offset + 109 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[16]);//MiMaxGP5
            else if(offset + 126 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[17]);
            else if(offset + 147 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[18]);
            else if(offset + 173 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[19]);
            else if(offset + 191 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[20]);//MiMaxGP6
            else if(offset + 213 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[21]);
            else if(offset + 237 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[22]);
            else if(offset + 243 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[23]);
            else if(offset + 113 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[24]);//MiMaxGP7
            else if(offset + 131 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[25]);
            else if(offset + 153 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[26]);
            else if(offset + 187 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[27]);
            else if(offset + 207 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[28]);//MiMaxGP8
            else if(offset + 226 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[29]);
            else if(offset + 239 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[30]);
            else if(offset + 251 == Index)
                FLASH_ProgramWord(addr+Index*4, mima_buff[31]);
            else
            {
                //生成随机值
                if(Index > 3)
                    u32t *= u32t*Index*mima_buff[(Index-2)];
                else
                    u32t *= u32t*(Index+1);
                u32t+=rx3;
                FLASH_ProgramWord(addr+Index*4, u32t);
            }
    }
    FLASH_Lock();
    NOP40();
}
用KEIL和分散加载功能将加密程序放在指定的ROM区,假定我们放在0x0800E000处。
LR_IROM2 0x0800E000 0x00001000  {   ;存储分区2:加密代码
  JM_CODE 0x0800E000 0x00001000  {  ; load address = execution address
   jiami.o (+RO)
  }
}
可以用如下方法创建密码本,并销毁密码创建程序。主程序调用 JiaMiCreatCheck即可。
void JiaMiCreatCheck(void)
{   
        //检查加密程序是否已删除
        if(0x4770BF00 != *((const uint32_t *)(0x0800E000)))
        {
            uint32_t addr;
            JiaMiHandle(1,1,2,3);
            fmc_unlock();
            //删除加密程序
            fmc_page_erase(0x0800E000);
            fmc_page_erase(0x0800E400);
            fmc_page_erase(0x0800E800);
            fmc_page_erase(0x0800EC00);
            addr = 0x0800E000;
            //填充空指令
            for(;addr < 0x0800F000;addr+=4)
                fmc_word_program(addr, 0x4770BF00); //0x4770 [BX LR], 0xBF00 [NOP]   
            fmc_lock();
        }
}
下一步在应用程序运行过程中验证密码本,MiMaEn作为函数传入参数,其余均为局部变量。addr应用 volatile 修饰。
...应用子程序内部
        {
            //MiMaxGP6
            {
                addr = 0;
                addr += MiMaEn ? (UUID_ID1 >> 24) & 0xFF : 12;
                addr <<= 8;
                addr += MiMaEn ? (UUID_ID1 >> 16) & 0xFF : 13;
                addr <<= 8;
                addr += MiMaEn ? (UUID_ID1 >> 8) & 0xFF : 15;
                addr <<= 8;
                addr += MiMaEn ? (UUID_ID1 & 0xFF) : 16;
            }
            {
                //读取UUID
                rx0 = ((~(*((volatile uint32_t *)(addr)))) * 3 >> 1) * 13 + 23;
                addr += 4;
                rx1 = ((~(*((volatile uint32_t *)(addr)))) * 5 >> 1) * 7 + 19;
                addr += 4;
                rx2 = ((~(*((volatile uint32_t *)(addr)))) * 7 >> 1) * 11 + 5;
                addr = 0;
            }
            /*
            * 插入应用程序代码,验证过程不要连续。
            */
            {
                //获取密码本基地址
                addr += MiMaEn ? (0x08004000 >> 24) & 0xFF : 12;
                addr <<= 8;
                addr += MiMaEn ? (0x08004000 >> 16) & 0xFF : 13;
                addr <<= 8;
                addr += MiMaEn ? (0x08004000 >> 8) & 0xFF : 15;
                addr <<= 8;
                addr += MiMaEn ? (0x08004000 & 0xFF) : 16;
                //偏移地址基准数据
                rx3 = rx0 + rx1 + rx2;
                //合成新地址
                addr += (((rx3 >> 8) + (rx3 >> 16) + (rx3 >> 24) + (rx3)) * 3 & 0xff) * 4;
            }
            /*
            * 插入应用程序代码,验证过程不要连续。
            */
            {
                //计算密码
                mima_buff[0] = (~(rx0 + rx1) >> 3) * 31 + 3;
                mima_buff[1] = ((((~(rx1 + rx2) >> 4) * 47 + 31) * 3) >> 2) * 5 + 13;
                mima_buff[2] = (~rx2 >> 5) * 71 + 39;
                mima_buff[3] = usMBCRC16((uint8_t *)&mima_buff[0], 6);
                mima_buff[3] = ((mima_buff[3] * 11) << 16) + (mima_buff[3]);
            }
            /*
            * 验证
            */
            if (mima_buff[3] != *(uint32_t *)(addr + 243 * 4))
            {
                //验证失败在这里制造一个故障
            }
        }
...
关于最后的验证,可以使用数据叠加,而不用数据比较。比如系统中不常使用到的关键变量,可以在此处加上mima_buff中的计算值,而在使用的位置减去密码本中的对应值进行还原。
可以在程序多个位置验证不同的加密点,分散位置越多,被完全发现的概率越低。
另外不要在主循环中验证,容易被故障跟踪。可以在某些特定时刻验证,比如某一AD通道值的低8bits和运行总时间的低8bits值相同时执行一次验证。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /3 下一条

手机版|电路城

GMT+8, 2019-5-23 18:40 , Processed in 0.113194 second(s), 15 queries , MemCache On.

ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

苏公网安备 32059002001037号

Powered by Discuz!

返回顶部