上一篇微头条中说道:
如果我们将ST提供的代码编译成二进制文件下载到国厂替换芯片中,
可以芯片内的二进制代码被读出,并与用完全一样的编译器编译出的代码进行二进制文件的比较。
应该会有大块二进制数据相同,根据相似性可以判断出使用了ST声称了版权的代码。
为了证实这一点,我找了两套软件代码进行比较。
一套是6年前编写的雷电预警的代码,没有bootloader,
启动文件startup_stm32f0xx.s以及系统初始化文件system_stm32f0xx.c与应用程序混在一起;
另一套是前几年编写的bootloader的代码,除了启动文件startup_stm32f0xx.s以及系统初始化文件system_stm32f0xx.c之外,仅有一些flash操作的代码。
一、比对bin文件
如下图,在keil的options中,设置编译之后运行程序生成bin文件。
设置生成bin文件
通过compare it软件比对两套代码生成的bin文件,可以发现有大块二进制数据相同。
大量二进制数据相同
那么这些相同的二进制数据是否就是ST声称版权的代码?
二、反汇编分析
打开keil的反汇编窗口(disassembly window),
在bootloader的项目中,找到SystemInit 函数的前面两条语句:
RCC->CR |= (uint32_t)0x00000001;
RCC->CFGR &= (uint32_t)0xF0FF0000;
我们可以看到这两条指令编译出来的机器码放置在地址为0x80002E6-0X80002F之间的存储空间。
反汇编窗口
对应的机器码为(考虑存储的大小端问题):
36 48 00 68 40 F0 01 00 34 49 08 60...
在生成的bin文件中找到了对应的代码块:
汇编生成的代码
再打开两个bin文件的比较窗口,果然看到该地址段的数据大量相同。
而相同的数据也就是ST提供的代码所编译生成的二进制数据。
画线部分的数据为ST提供系统初始化函数代码生成的二进制数据
三、该怎么做
为了防止二进制数据从芯片中被读出比对,我们可以使用芯片提供的读保护功能(国产替代芯片号称功能与ST完全一致,想必也有此功能)。
开启了读保护功能之后,用正当手段仅能读上来全为0xff的数据。
我们可以在代码中加上自动开启写保护功能的代码,如下:
void ReadProtect(void)
{
if(FALSE == FL_GetReadOutProtectionStatus())
{
FL_Unlock();
FL_ReadOutProtection(TRUE);
FL_Lock();
}
}
void FL_Unlock(void)
{
if((FLASH->CR & FLASH_CR_LOCK) != RESET)
{
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
}
void FL_Lock(void)
{
if((FLASH->CR & FLASH_CR_LOCK) == RESET){
FLASH->CR |= CR_LOCK_SET;
}
}
FLASH_Status FL_ReadOutProtection(U8 enable)
{
FLASH_Status status = FLASH_COMPLETE;
status = fnFL_Wait();
if(status == FLASH_COMPLETE)
{
/* Authorizes the small information block programming */
FLASH->OPTKEYR = FLASH_KEY1;
FLASH->OPTKEYR = FLASH_KEY2;
FLASH->CR |= CR_OPTER_Set;
FLASH->CR |= CR_STRT_Set;
/* Wait for last operation to be completed */
status = FL_Wait();
if(status == FLASH_COMPLETE)
{
/* if the erase operation is completed, disable the OPTER Bit */
FLASH->CR &= CR_OPTER_Reset;
/* Enable the Option Bytes Programming operation */
FLASH->CR |= CR_OPTPG_Set;
if(enable)
{
OB->RDP = 0x00;
}
else
{
OB->RDP = RDP_Key;
}
/* Wait for last operation to be completed */
status = FL_Wait();
if(status != FLASH_TIMEOUT)
{
/* if the program operation is completed, disable the OPTPG Bit */
FLASH->CR &= CR_OPTPG_Reset;
}
}
else
{
if(status != FLASH_TIMEOUT)
{
/* Disable the OPTER Bit */
FLASH->CR &= CR_OPTER_Reset;
}
}
}
/* Return the protection operation Status */
return status;
}
#define RDPRT_Mask ((U32)0x00000002)
U8 FL_GetReadOutProtectionStatus(void)
{
U8 read = FALSE;
if ((FLASH->OBR & RDPRT_Mask) != 0)
{
read = TRUE;
}
return read;;
}