|
单片机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值相同时执行一次验证。 |
|