【嵌入式】NXP/LPC的CRP功能的使用(代码加密/解密)

news/2024/6/17 6:15:55 标签: 嵌入式, lpc, nxp, 加密解密, iap

目录

一 J-Flash读取MCU内部程序

二 程序加密

三 程序解密


一 J-Flash读取MCU内部程序

        通过J-Flash可以读取到MCU内部的程序,步骤如下:

(1)进入J-Flash,选择指定的芯片之后,点击连接Target-Connect;

(2)根据需要选择Read back中的选项,分别是回写Project settings中预设的扇区区间(Selected sectors)、回写芯片全部的扇区(Entire chip)、回写指定范围的扇区(Range)。选择之后J-Flash会回写内存中的数据

(3)可以保存读取到的数据,建议选择HEX格式。这样就可以把回写得到的程序纳为己用,进行重复烧写。

二 程序加密

        经过上一章,如此简单就可以获得MCU中的程序,那有效地把自己的程序保护起来就显得尤为重要。以所使用的LPC1857为例,关于CRP,其说明书如下:

        总结概括为以下几点:

(1)LPC1857有三个CRP权限以及对应的安全码,分别为CRP1(0x12345678)、CRP2(0x87654321)、CRP3(0x43218765)。从这三个安全权限中选择所需的设置值写入到0x1A0002FC或者0x1B0002FC,即可实现ROM1或者ROM2的加密。

(2)无论设置哪一种权限,JTAG都是禁用的。谨慎选择CRP3,我这边选用的是CRP1级别。

(3)加密的时候,需要确认keil的option中,没有定义NO_CRP宏,否则加密失效:

        程序加密的方法如下:

(1)在main.c中加入下面的语句,作用在于预开辟出0x1A0002FC处的空间用作CRP功能:

const unsigned crp __attribute__((section(".ARM.__at_0x1A0002FC"))) = 0x12345678;

 (2)startup_LPC18xx.s中,关于CRP的部分修改如下:

;CRP address at offset 0x2FC relative to the BOOT Bank address
                IF      :LNOT::DEF:NO_CRP
                AREA    |.ARM.__at_0x1A0002FC|, CODE, READONLY
;                EXPORT  CRP_Key
CRP_Key         DCD     0x12345678
;                       0xFFFFFFFF => CRP Disabled
;                       0x12345678 => CRP Level 1
;                       0x87654321 => CRP Level 2
;                       0x43218765 => CRP Level 3 (ARE YOU SURE?)
;                       0x4E697370 => NO ISP      (ARE YOU SURE?)
                ENDIF

                AREA    |.text|, CODE, READONLY

 这样,即可以对程序进行CRP1级别加密,烧写MCU,重启之后代码保护生效,再通过JTAG烧写程序,或者通过J-Flash回写内存均不能成功。

三 程序解密

        程序加密之后,需要通过擦除或者修改0x1A0002FC处的值,来实现解密。主要分为下面两种方式。

(1)加密之后,程序不能通过JTAG口进行烧写,但是可以进入ISP模式,通过Flash Magic软件进行下载或者擦除操作(外部硬件进入ISP模式只对CRP1和CRP2有效;而IAP指令进入ISP模式对CRP1、CRP2、CRP3均有效);

(2)可以通过程序执行IAP指令擦除扇区0整块扇区,来清除0x1A0002FC处的加密值,重新上电之后,就可以恢复未加密的状态(加密或者解密程序下载到MCU之后,都必须重新上电才能生效);

        我这边采用的第二种方法,思路是通过串口接收特定的通讯指令(例如XX XX 12 34),当串口数据接收函数收到该指令时,则执行扇区清除

void exBufRcv(unsigned char *p_rcv_buf)
{
    int i = 0;
	int j = 0;

    //修改IAP与APP切换的标志位
    if((p_rcv_buf[2] == APP_CONFIG_SET_VALUE) || (p_rcv_buf[2] == APP_CONFIG_CLEAR_VALUE))
    {
        iap_init(BANK0);
        Iap_Write_Config_Value(p_rcv_buf[2]);

        __set_FAULTMASK(1);
        NVIC_SystemReset();
    }
    
    //收到解锁命令码之后,执行扇区清除操作
    if((p_rcv_buf[2] == 0x12) && (p_rcv_buf[3] == 0x34))
    {
        iap_init(BANK0);
        iap_erase_flash(0x1A0002FC, 4);
    }
}
/******************************************************************************************************  
** 函数名称:iap_erase_flash()  
** 函数功能:flash 擦除操作  
** 入口参数:addrDst       要擦除flash的起始地址  
**           length        要擦除flash的长度   
** 出口参数:IAP操作状态码  
**           IAP返回值(paramout缓冲区)  
*******************************************************************************************************/ 
uint32_t iap_erase_flash(uint32_t addrDst, uint32_t length)
{
	uint32_t state;
	uint32_t baseAddr;
	
	if (length == 0)
	{
		return 101;
	}
	
	if (g_bank_num)
	{
		baseAddr = 0x1B000000;
	} else
	{
		baseAddr = 0x1A000000;
	}
	
	if ((addrDst < baseAddr) || ((addrDst + length) > (0x7FFFF + baseAddr)))
	{
		return 100;
	}

	// 计算起始扇区号
	if (addrDst < (0x10000 + baseAddr))
	{
		gSecStart = (addrDst - baseAddr) / (8 << 10);
		if ((addrDst + length) < (0x10000 + baseAddr))
		{
			gSecEnd = (addrDst - baseAddr + length) / (8 << 10);
		} else
		{
			gSecEnd = 8 + (addrDst - baseAddr - 0x10000 + length) / (64 << 10);
		}
	} else
	{
		gSecStart = 8 + (addrDst - baseAddr - 0x10000) / (64 << 10);
		gSecEnd = 8 + (addrDst - baseAddr - 0x10000 + length) / (64 << 10);
	}

//	debugPrint("bank %d, len %d,sector start %d, end %d\r\n", g_bank_num, length, gSecStart, gSecEnd);
		 
	CLOSE_CORE_INT();	

	state = pre_sector(gSecStart, gSecEnd, g_bank_num);

	if (state == CMD_SUCCESS) 
	{
		state =  erase_sector(gSecStart, gSecEnd, g_bank_num);		
	
		if (state == CMD_SUCCESS) 
		{
			state = blank_check_sector(gSecStart, gSecEnd, g_bank_num);
		}  
	}		   
	OPEN_CORE_INT();

	return state;
}

        如果程序中IAP部分程序和APP部分程序是分开的话(【嵌入式】基于串口的IAP在线升级详解与实战1----IAP功能设计),需要对IAP程序和APP程序分开处理,在IAP程序中,初始化设置0x1A0002FC加密。在APP中,串口收到特定码解密

        总的来说,给程序加密,尽管可能会带来程序调试或者debug的不便,但是对于保护自己的劳动成果是非常重要的。


http://www.niftyadmin.cn/n/1295280.html

相关文章

【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录2----移植emWin

一 emWin移植准备 完成了前一文点亮LCD屏之后&#xff0c;考虑到将要设计较为复杂的界面GUI&#xff0c;光用一些基本的绘图、显示字符接口不能满足要求&#xff0c;所以琢磨着再移植一套emWin&#xff0c;用来辅助设计GUI&#xff08;由emWin的用户手册中可以看到&#xff0c;…

【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录1----点亮LCD屏

目录 一 SPI屏的接线 二 SPI屏驱动初始化 三 SPI屏点亮 四 附录 一 SPI屏的接线 SPI屏的特点在于接线简单&#xff0c;只需要四根SPI线以及几个GPIO口即可驱动工作&#xff0c;但是由于非并口的&#xff0c;所以当像素比比较大的时候&#xff0c;刷新速度可能会受到考验。我…

j2ee调用Oracle带数组参数和游标的存储过程方法

2019独角兽企业重金招聘Python工程师标准>>> 环境&#xff1a;Oracle 10g; jboss4.2.2;jdk1.6;hibernate 3.2 需求&#xff1a;有一个数据量比较大的表tableA,大概有几十万数据。里面存放用户手机号码&#xff0c;现在要求批量保存至少几百的的手机号码&#xff0c…

面试官:Java 是如何实现线程间通信的?

正常情况下&#xff0c;每个子线程完成各自的任务就可以结束了。不过有的时候&#xff0c;我们希望多个线程协同工作来完成某个任务&#xff0c;这时就涉及到了线程间通信了。 本文涉及到的知识点&#xff1a; thread.join(), object.wait(), object.notify(), CountdownLa…

【嵌入式总线】TIA Portal V15软件在Profibus上的操作使用1--组态

目录 一 设备列表 二 设备组态 三 更改从机设备地址 一 设备列表 主站CPU&#xff1a;西门子CPU 1212C DC/DC/DC 订货号6ES7 212-1AE40-0XB0 Profibus模块&#xff1a;西门子CM 1243-5 订货号6GK7 243-5DX30-0XE0 从站设备&#xff1a;总线板搭载VPC3芯片 设备名EASY4711 二…

J2EE知识体系思维导图

为什么80%的码农都做不了架构师&#xff1f;>>> J2EE知识体系思维导图&#xff0c;带红色箭头的里面还有详细内容&#xff0c;但是内让那个太多发不出来了。如果想看我在发出来&#xff0c;如果有需要的可以联系我&#xff0c;我给你们发邮箱。 同时也请前辈们&am…

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录

一 并口屏接线 之前整了一块串口屏&#xff0c;实际使用中&#xff0c;感觉整屏&#xff08;320*240&#xff09;的刷新速度还是有点偏慢&#xff0c;肉眼能够看到明显的刷屏动作&#xff0c;故而考虑改用并口屏来实现显示功能。 首先根据显示屏的接线图进行接线&#xff1a; 其…

windows XP下MySQL Cluster集群安装配置 .

1软件的下载 下载MySQL Cluster&#xff0c;地址&#xff1a;http://www.mysql.com/downloads/cluster/ 要Windows32位免安装版的&#xff0c;如图&#xff1a; <!--[endif]--> 2机子的配置及作用 准备3台电脑&#xff0c;机子的功能和配置见下表 Node IP Address Mana…