找回密码
 立即注册
查看: 56|回复: 0

带有CHIPID功能的芯片进行ID号加密示例,以及一些额外加密手段介绍

[复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:694
  • 最近打卡:2026-03-22 11:00:31
已绑定手机
已实名认证

130

主题

3385

回帖

8821

积分

版主

积分
8821
发表于 6 天前 | 显示全部楼层 |阅读模式
芯片加密,主要防止的就是其他人将程序读出后仿制,只要让其破解成本大于研发成本,则可以认为是有效的加密
这里主要讲下载由自己或者信任方控制,芯片下载好程序后才交付给客户或销售,如果需要下载过程也保密,可以参考(远程加密下载):

远程现场升级,自动生成您公司界面的升级软件,省电脑端开发人员,人工智能 - 远程现场升级 =【发布项目程序+程序加密后传输+USB下载】,ID号加密/通过ID号控制下载 国芯人工智能技术交流网站 - AI32位8051交流社区

STC芯片默认就没有留出读出程序的接口,所以芯片下好程序以后,只能通过开盖(磨掉芯片顶部塑封),然后使用墨水染色和电子显微镜观察FLASH后拿到一个二进制文件
这部分操作通常会消耗一万元左右,而CHIPID加密主要防止的就是开盖拿到原始程序以后,直接下载到其他芯片内运行的这一个步骤,使其复制出来的程序到其他芯片内也运行不了


这里顺便讲解一下其他的一些加密手段:
下载口令加密:可以设置一串密码,使其下次下载程序时,需要口令,这样可以避免其他人恶意下载空白/损坏的程序,然后诽谤这个芯片/程序有缺陷
这个密码建议设置的复杂一点(这里仅作演示)

截图202603211419104859.jpg

硬件选项加密:通常来讲,芯片会通过无字版本隐藏芯片型号,但是官方ISP的“检测选项”是可以检测到芯片型号的
此时可以通过选择“下次冷启动时,P32和P33都为0时才可下载程序”来阻止使用官方ISP软件检测
这样只要下载好程序后,第二次尝试下载(解密方)就需要这两个端口同时为低电平才能下载,可以在外部利用一个小电阻上拉到高电平,此时使用ISP软件查询芯片型号就会没有反应

从而避免解密方获取到芯片的真实型号
截图202603211421503513.jpg

然后是ID号加密,这里需要明确的是,ID号加密仍然可以被破解。因为开盖从FLASH内读取出二进制程序后,是可以转换成汇编代码的
此时如果ID号验证比较简单,就可能会被查找到关键词然后通过修改汇编指令绕过去。所以这里能做的就是使用多种不同手段进行ID号验证,因为原程序中加验证只是复制粘贴,但是找汇编指令就非常繁琐了
同时,这里推荐ID验证不通过后,依然正常运行,延迟固定时间+随机时间后在关键位置搞破坏/主动死机,创造一种程序运行不稳定的假象,可以有效骗过解密方。因为大部分解密的一看功能都正常就不会继续寻找了ID锁了



首先介绍最简单的ID号验证方式
需要ISP软件勾选ID号加密

截图202603211432264740.jpg
这里为了简单演示,就只选择纯加法加密,实际建议同时使用加减,乘除一个奇数(偶数的话和移位区别不大),移位2位及其以上,异或
同时,上下两个部分的加密需要保持不一致,也就是会出现两个加密内容。存储地址是绝对地址。
不能放在太靠前的地方(程序起始位置有启动和中断跳转指令,占用后会有问题),但是也不能放在超出用户程序的区域(太过于明显),这里因为仅作演示,就使用了一些比较规律的地址(0x100和0x200)
(如何查看用户程序空间?在程序文件选项卡左下角,鼠标放在代码长度上即可显示十进制的程序实际占用字节)

截图202603211447236108.jpg
然后,在程序内通过_at_方式定义数组进行占用,程序下载完毕后,ISP软件会将加密后的ID号写入对应的地址(需要留出7个byte的空间)
这里建议这个ID号前后也利用_at_定义一些数组,填入一些无意义的数据来进行混淆。这样在汇编代码中,因为没有注释,会非常的难找这部分内容。
程序部分代码:

  1. //<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
  2. // 在此添加用户全局变量定义、用户宏定义以及函数声明  
  3. // ID存储占位数组(ISP软件会将加密后的ID数据写入这两个地址)
  4. // 使用_at_关键字指定绝对地址,地址分别为0x0100和0x0200,各占用7字节
  5. unsigned char code ID_At100[7] _at_ 0x0100;  // 存储原始CHIPID每个字节+4的结果
  6. unsigned char code ID_At200[7] _at_ 0x0200;  // 存储原始CHIPID每个字节+8的结果
  7. // ID验证结果标志(全局变量,1表示验证成功,0表示失败)
  8. unsigned char success;
  9. //<<AICUBE_USER_GLOBAL_DEFINE_END>>
  10. ////////////////////////////////////////
  11. // 项目主函数
  12. // 入口参数: 无
  13. // 函数返回: 无
  14. ////////////////////////////////////////
  15. void main(void)
  16. {
  17.     unsigned char i;          // 循环计数器
  18.     unsigned char chipid_byte; // 当前读取的CHIPID字节值
  19.     unsigned char encrypted100; // 从0x100地址读取的加密值
  20.     unsigned char encrypted200; // 从0x200地址读取的加密值
  21.     //<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
  22.     // 在此添加用户主函数初始化代码  
  23.     //<<AICUBE_USER_MAIN_INITIAL_END>>
  24.     SYS_Init();  // 系统初始化(包含串口等)
  25.     //<<AICUBE_USER_MAIN_CODE_BEGIN>>
  26.     // 在此添加主函数中运行一次的用户代码  
  27.    
  28.     // 初始化验证标志为成功(假设通过,遇到失败再置0)
  29.     success = 1;
  30.     // 依次验证7个字节,验证规则:
  31.     // 地址0x100的值 = CHIPID原始值 + 4
  32.     // 地址0x200的值 = CHIPID原始值 + 8
  33.     for (i = 0; i < 7; i++) {
  34.         // 根据循环索引读取对应的CHIPID字节
  35.         switch (i) {
  36.             case 0: chipid_byte = CHIPID0; break;
  37.             case 1: chipid_byte = CHIPID1; break;
  38.             case 2: chipid_byte = CHIPID2; break;
  39.             case 3: chipid_byte = CHIPID3; break;
  40.             case 4: chipid_byte = CHIPID4; break;
  41.             case 5: chipid_byte = CHIPID5; break;
  42.             default: chipid_byte = CHIPID6; break;
  43.         }
  44.         // 从预留地址读取加密数据
  45.         encrypted100 = ID_At100[i];
  46.         encrypted200 = ID_At200[i];
  47.         // 检查是否符合加密规则
  48.         if ((encrypted100 != (chipid_byte + 4)) ||
  49.             (encrypted200 != (chipid_byte + 8))) {
  50.             success = 0;  // 任一字节不匹配,验证失败
  51.             break;        // 立即退出循环,无需继续检查
  52.         }
  53.     }
  54.                 delay_ms(1000);//防止用户打开串口过慢,看不到信息
  55.     // 根据验证结果输出对应信息(仅输出成功/失败,不输出具体数值)
  56.     if (success) {
  57.         printf("ID验证成功!\r\n");
  58.     } else {
  59.         printf("ID验证失败!\r\n");
  60.     }
  61.     //<<AICUBE_USER_MAIN_CODE_END>>
  62.     while (1)
  63.     {
  64.         //<<AICUBE_USER_MAIN_LOOP_BEGIN>>
  65.         // 在此添加主函数中用户主循环代码  
  66.         //<<AICUBE_USER_MAIN_LOOP_END>>
  67.     }
  68. }
复制代码

运行结果:

截图202603211501587052.jpg
如果未勾选任意一个ID号加密,则验证失败
截图202603211502396469.jpg
以下是可以编译运行的CHIPID加密示例程序,已在Ai8051U-32Bit模式下测试通过
CHIPID_CHECK.zip (403.33 KB, 下载次数: 3)

下面演示一些更为高级的ID号加密验证方式

1.分散验证,每次只验证一个byte,在程序多个地方验证,给解密制造麻烦
2.间接使用CHIPID地址,防止被地址查询得到(可以通过指针变量运算得到想要的地址),然后再使用,汇编中就不会出现对应的地址
3.验证失败后随机时间后爆出异常,不会立刻异常
以下是正常运行结果
截图202603211517453227.jpg
以下是异常运行结果
截图202603211521251669.jpg
以下是间接使用CHIPID地址,可以看到汇编代码中没法找到CHIPID0地址0x7EFDE0
截图202603211519404385.jpg

以下为高级CHIPID加密办法的完整程序和代码文件
  1. //<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
  2. // 在此添加用户全局变量定义、用户宏定义以及函数声明  
  3. // ID存储占位数组(ISP软件会将加密后的ID数据写入这两个地址)
  4. // 使用_at_关键字指定绝对地址,地址分别为0x0100和0x0200,各占用7字节
  5. unsigned char code ID_At100[7] _at_ 0x0100;  // 存储原始CHIPID每个字节+4的结果
  6. unsigned char code ID_At200[7] _at_ 0x0200;  // 存储原始CHIPID每个字节+8的结果
  7. // ID验证结果标志(全局变量,1表示验证成功,0表示失败)
  8. unsigned char verify_failed = 0;       // 0=成功, 1=失败
  9. // 用于间接访问CHIPID的指针(避免汇编直接锁定操作)
  10. // 指针初始化时通过两个volatile变量相加得到地址,防止编译器优化为常量
  11. unsigned char volatile far *chipid_ptr;
  12. // 地址拆分部分(volatile防止编译时常量折叠,确保汇编中呈现加法运算)
  13. volatile unsigned long addr_high = 0x7EFD00;   // 地址高段
  14. volatile unsigned long addr_low  = 0xE0;       // 地址低段
  15. //<<AICUBE_USER_GLOBAL_DEFINE_END>>
  16. ////////////////////////////////////////
  17. // 项目主函数
  18. // 入口参数: 无
  19. // 函数返回: 无
  20. ////////////////////////////////////////
  21. void main(void)
  22. {
  23.     unsigned char i;          // 循环计数器
  24.     unsigned char chipid_byte; // 当前读取的CHIPID字节值
  25.     unsigned char encrypted100; // 从0x100地址读取的加密值
  26.     unsigned char encrypted200; // 从0x200地址读取的加密值
  27.     unsigned int random_delay;   // 随机延迟时间(毫秒)
  28.     //<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
  29.     // 在此添加用户主函数初始化代码  
  30.     //<<AICUBE_USER_MAIN_INITIAL_END>>
  31.     SYS_Init();  // 系统初始化(包含串口等)
  32.     //<<AICUBE_USER_MAIN_CODE_BEGIN>>
  33.     // 在此添加主函数中运行一次的用户代码  
  34.     // ----- 1. 初始化间接访问指针(通过运行时加法得到CHIPID地址,防止汇编中出现完整地址)-----
  35.     // 使用两个volatile变量相加,编译器必须生成加法指令,不会直接使用常量0x7EFDE0
  36.     chipid_ptr = (unsigned char volatile far *)(addr_high + addr_low);
  37.     // 用CHIPID的第一个字节作为随机数种子(保证每次上电随机序列不同)
  38.     srand(chipid_ptr[0]);
  39.     // ----- 2. 分散验证:每次只验证一个字节,共7个字节 -----
  40.     printf("开始ID分散验证(每次验证一个字节)...\r\n");
  41.     for (i = 0; i < 7; i++) {
  42.         // 通过指针间接读取CHIPID(避免汇编直接锁定操作)
  43.         chipid_byte = chipid_ptr[i];
  44.         // 读取ISP写入的加密数据
  45.         encrypted100 = ID_At100[i];
  46.         encrypted200 = ID_At200[i];
  47.         // 打印当前验证的字节索引和CHIPID原始值(演示用)
  48.         printf("验证字节[%d]: CHIPID=0x%02X, 期望0x100=0x%02X(实际0x%02X), 期望0x200=0x%02X(实际0x%02X)\r\n",
  49.                i, chipid_byte,
  50.                chipid_byte + 4, encrypted100,
  51.                chipid_byte + 8, encrypted200);
  52.         // 验证规则:0x100地址的值 = CHIPID原始值 + 4
  53.         //         0x200地址的值 = CHIPID原始值 + 8
  54.         if ((encrypted100 != (chipid_byte + 4)) || (encrypted200 != (chipid_byte + 8))) {
  55.             verify_failed = 1;  // 标记验证失败
  56.             printf("  验证失败!字节[%d]不匹配。\r\n", i);
  57.             // 注意:失败后不break,继续验证后续字节(演示分散验证)
  58.         } else {
  59.             printf("  验证通过\xfd。\r\n");
  60.         }
  61.         // 模拟每个字节验证之间的小延时(体现过程)
  62.         delay_ms(50);
  63.     }
  64.     // 最终验证结果汇总
  65.     if (!verify_failed) {
  66.         printf("ID验证完全成功!程序正\xfd常运行。\r\n");
  67.     } else {
  68.         printf("ID验证失败!程序将继续运行,但将在随机延时后出现异常。\r\n");
  69.     }
  70.     // ----- 3. 模拟耗时的流水任务 -----
  71.     // 流水任务循环执行,每次打印流水信息并延时
  72.     // 如果验证失败,则在一定循环次数后触发随机延迟异常
  73.     while (1) {
  74.         // 流水任务:模拟处理数据、通讯等耗时操作
  75.         printf("流水任务执行中...\r\n");
  76.         delay_ms(500);  // 模拟耗时500ms
  77.         // 如果验证失败,则随机延迟100~1000ms后触发异常
  78.         if (verify_failed) {
  79.             // 产生100~1000ms之间的随机延迟
  80.             random_delay = (rand() % 901) + 100;  // rand()%901 得0~900,加100得100~1000
  81.             printf("验证失败,随机延时 %d ms 后将出现任务异常...\r\n", random_delay);
  82.             delay_ms(random_delay);
  83.             // 触发任务异常:打印错误信息并进入死循环(模拟系统崩溃)
  84.             printf("\r\n!!! 任务异常 !!! 系统发生不可恢复错误,请重新上电。\r\n");
  85.             while (1);  // 死循环,程序停止响应
  86.         }
  87.     }
  88.     //<<AICUBE_USER_MAIN_CODE_END>>
  89.     while (1)   // 实际不会执行到这里,因为上面while(1)已包含
  90.     {
  91.         //<<AICUBE_USER_MAIN_LOOP_BEGIN>>
  92.         // 在此添加主函数中用户主循环代码  
  93.         //<<AICUBE_USER_MAIN_LOOP_END>>
  94.     }
  95. }
复制代码
CHIPID_CHECK (拓展办法).zip (421.02 KB, 下载次数: 3)



回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-3-27 11:22 , Processed in 0.105275 second(s), 45 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表