单片机程序代码烧写方法有专用编程器烧写、编程器/调试器烧写及ISP和IAP,简单介绍及个人总结如下:
专用编程器:裸片烧写,可同时烧写多个芯片,速度快,单片机烧写好程序后再焊接到电路板上,但编程器价格贵,适用于大批量生产
仿真器/调试器:通过电路板上安装的编程接口与单片机连接烧写程序,需要给电路板通电,操作麻烦,效率低。这种方式本来是给工程师调试程序用的,但是现在普通功能的仿真器/调试器已经很便宜了,而且一般也都有配套的编程软件可用,因此对于少量产品的程序烧写可以采用此种方式,比ISP方便。
ISP(In-System Programming)在系统编程:指芯片已经焊接在电路板上的情况下对其进行编程。厂家在单片机中内置了一段bootloader程序,上电时通过一个IO口状态选择是运行此bootloader程序还是运行flash中的用户程序。如果配置为运行此bootloader程序,那么此bootloader可对flash进行擦除、代码烧写,代码可通过UART或其他外设接口接收来自PC发送的数据,很多单片机都有此功能。此种方式不需要昂贵的编程器(早期的普通仿真器也是很贵的),是少量产品情况下低成本的代码烧写方式。
IAP(In-Application Programming)在应用编程:指在flash中运行的程序再对flash进行编程。ISP方式虽然好用,但也有其缺点:必须在上电时手动配置一个IO口状态(使用按键或跳线)使芯片进入ISP代码部分,编程完成后需要断电将IO口配置改回来以便上电时运行flash中的代码,而且ISP代码功能由厂家固定,且需配合PC端专用的ISP软件使用,用户无法修改,接收代码的端口也基本固定,不够灵活。IAP烧写方式刚好解决了这些问题,IAP编程是将单片机的flash划分为两部分,一部分的作用是给另一部分flash擦除、编程,这部分的代码称之为bootloader程序,另一部分flash存放实现产品功能的代码,称之为app程序。单片机上电后首先运行bootloader程序,通过自定义的标志位判断是否进行app程序编程,如果不编程则跳转到app程序运行,如果编程,则从自定义的数据接收通道接收数据并更新app程序,然后跳转到app程序运行。bootloader程序是用户编写的、对于是否执行app程序更新的标志位可以自定义(如在一定时间内是否接收到相关数据来确定),更新程序代码数据的接收通道也可以自定义(如UART、USB、SD卡等),灵活性很大,但是IAP有一个缺点就是必须占用一部分flash空间,对于小容量单片机的意义不大。IAP方式编程的实际用途是用于给单片机的app程序升级,bootloader程序功能单一,完成后基本不需要修改了,而app程序功能相对复杂,需要更改的可能性更大,一般通过上面其他几种方式将bootloader程序烧写到flash中,再通过IAP方式烧写/升级app程序,由于IAP方式用户自定义程度高,因此很容易做到不打开产品外壳就能给单片机升级app程序(如通过SD卡或产品功能需要所引出的通讯接口)。IAP方式编程的另一个目的就是可以将app二进制文件代码提供给客户,让客户自己升级程序,而不至于威胁到单片机代码的安全性,因为客户没有bootloader代码,他拿app代码烧写到空白芯片中是不能工作的。
单片机代码保护:为了防止别人窃取自己付出努力设计的单片机代码从而复制自己的产品,必须要对单片机代码加入保护措施,一般是指代码读保护,有时也包括关键flash位置的写保护。单片机都有此种读保护机制,不同厂家产品的保护机制各有不同,大概有如下两类:1、通过编程器配置专用的代码保护标志位或熔丝位,使flash程序代码不能从编程接口读出;2、在flash的固定位置放置密码,通过编程接口读写flash内容前需要先核对密码,瑞萨的R8C系列单片机和TI的TMS320F28x系列DSP就是这种类型的。
单片机代码保护的实际应用:在实际应用中,以上介绍的单片机自身提供的代码保护方式是最基本的,实际用途中往往还需要考虑得更多。
1、在产品功能较复杂,后续升级程序的可能性较大的情况下,就要考虑提供代码给用户或维修点自行升级的功能,这就需要用到上面介绍的IAP方式的bootloader。如果产品没法采用IAP方式,那么如果单片机的代码保护方式是如上介绍的第二种的话(密码验证),那还可以采用这种方式:给维修点提供一个自己开发的专用编程器,此编程器读取二进制程序代码后,找到其中的密码,将密码按照自定义的规则转换成另一组密码,然后连同程序代码烧写到目标芯片中,而程序中增加一个检查密码的机制,如果flash中的密码是转换后的密码,则正常运行,如果不是转换后的密码,则限制产品运行(限制部分功能,或累计工作一定时候后再限制产品正常工作,以迷惑解密者),用这种方式,就得控制好这个编程器,假如窃取者手上有一两个这种编程器而进行我们的产品的仿制,那么他也要承受编程器损坏而不能继续生产的风险。(此种方式本人在R8C单片机上实际使用过)
2、现在很多单片机内置了芯片唯一ID码,用户可以利用此ID进行软加密。程序运行时检查flash特定位置的内容是否为空,如果为空,则将ID码按照自定义规则转换存储到此位置,如果不为空,则判断此处的内容是否等于ID码按照自定义规则转换后的内容,如果相等,则程序正常运行,如果不相等,则限制产品功能。如果窃取者通过其他手段获取了芯片flash代码,那么将获取的代码烧写到新的芯片中是不能正常运行的。此种加密方式,ID码转换规则要尽量加ID码打散转换,并在程序多处检查确认,一旦发现不对,就要限制程序运行。
3、为了防止解密者反汇编flash程序代码避开ID码软加密,可以不使用单片机本身的ID码,使用一个存储在外部器件中的自定义唯一ID码,通过I2C或UART等端口读取,其他部分的做法跟使用芯片内部ID码软加密的做法一样,这样,解密者就不容易找到代码中判断ID码的地方并修改。对于没有内置芯片ID码的单片机,可以采用此种ID码软加密方法。在这种软加密方式中,存储自定义唯一ID码的芯片的保护措施很重要,而且此芯片要尽量“低调”,不要让别人轻易猜出其作用,最好是一个产品功能所需要的芯片,顺便再存储此ID码。
4、采用外部专用的加密芯片,此种方式解密难度大幅度提高,但是需要额外增加成本。
5、还有一种辅助加密手段:将芯片上的印字打磨掉,或者让芯片厂家印一些别人都看不懂的代码,不要印正常的信息在上面,这样解密者还需要花时间来判断芯片厂家及型号,提高了解密难度。
单片机加密是为了防止被解密,做加密那就得对解密手段有所了解,这样能更有助于增加我们所采用的加密措施的解密难度。
1、对于采用密码加密的单片机,可以采用密码穷举法来破解,通过计算机自动执行,只是多耗些时间而已。典型的就是TI的MSP430单片机,当配置了熔丝位保护代码后,可通过BSL方式(即ISP方式)验证密码后读取flash内容,密码是中断向量的32字节,而默认未使用的中断向量内容为0xffff,假如用户只用到复位中断向量,对于4K大小flash的产品,只需要最多尝试2000次密码就可破解。为应对这种破解方式,MSP430单片机可以将所有未使用的中断向量内容填写为随机内容
2、通过专用配置位锁定编程接口的单片机,破解者可能采用特定上电、复位时序或电磁干扰的方式避开锁定判断(如果芯片存在这种bug的话),从而可通过编程口将flash内容读出,应对此种方式的一种方法是通过外部高电压真正烧坏编程引脚,使其真正与内部电路断开。
3、破解者还可以采用解剖芯片的方式进行破解,芯片被解剖后,可采用在特定地方植入探针读取数据,或者加入干扰信号使之能从编程口读取程序代码,或者采用显微镜对flash拍照后分析导出flash代码内容,芯片厂家一般也都有采用防止芯片被解剖后读取代码的手段,只是可能早期的产品此种防护措施相对薄弱,而且出于成本考虑,不同厂家的防护措施也有差异。我们对此种破解方式基本上没有有效的防护手段,只能寄希望于芯片厂家,一般来说新出的芯片对这种措施的防护性会好,因此选择芯片的厂家和型号对防解密是相当重要的。
4、假如解密者通过以上方式获取了flash代码,那我们还有如上介绍的通过ID码做的软加密,但是解密者也了解我们这种软加密,他可以将flash代码进行反汇编,然后查找出与ID码相关的逻辑判断语句并修改,就可避开此种软加密,因此做软加密时要尽量将ID码打散,在读取、转换、存储、比较ID码时都不要一次完成,可单字节操作,单字节操作中间插入一些其他语句,用指针操作,以提高解密者反汇编解密的难度。
5、对于用ID码做软加密,还有一种解密方式就是修改芯片的唯一ID码。由于生产成本等因素,有些厂家单片机的器件唯一器件ID码是存储在特定的flash区域的,在工厂通过特定方式将ID码烧写进去,此烧写方法不公开,因此普通用户无法修改。但如果这种特定烧写方式被解密者知晓,如果解密者也获取了flash代码,那么将此flash代码烧写到新的芯片中,并将新的芯片的ID码都修改成破解flash代码的那片芯片的ID码,那么代码的软加密就被破解了。
从目前来看,没有解密不了的单片机,只是解密的成本不同而已,我们加密单片机保护自己的代码,只要能做到让解密成本有一定高度,那就可以认为代码是安全的了。
补充:当采用ID码做软加密时,由于要执行flash块擦除,用ID码转换后的数据要存储在单独的flash区块或EEPROM中,如果破解者通过解剖芯片获取了芯片代码,那么很容易找出这个单独的flash块,将其数据全部置为0xff即可。有两个方法可以提高破解难度:1、将此块剩余空位置放置一些无用的代码(但是用反汇编看起来又像是正常的程序代码);2、在存储ID码转换后的数据的位置预先存储自定义的数据,程序第一次上电先判断此数据(或者将ID码转换后的数据存储在外部EEPROM,而外部EEPROM此位置的初始自定义数据通过编程器单独写入)