一 引言
随着物联网设备的普及,物联网设备的安全问题也逐渐被“有心人”挖掘出来,如2017年9月,一位黑客通过分析ofo共享单车的固件,发掘了4种攻击方法,并控制了共享单车;2017年10月,LIFX智能灯泡也被成功入侵,因为其固件中泄露了密钥相关的信息;又如,ReCon BRX 2018会议上,来自美国东北大学的两位研究员逆向了小米物联网设备的内部固件,发现了整个小米生态存在的漏洞。
研究员和黑客这些“有心人”在研究物联网设备时,为什么要获取物联网设备固件,又是如何获取物联网设备固件的呢?这三个案例中提到的漏洞,都是因为物联网设备的固件被研究员获取到,进而逆向分析后才发现的。更多的案例,可以在ReCon、BlackHat会议的官网上找到。通过分析物联网设备遭受攻击的链条,笔者发现,他们获取固件,把固件逆向成汇编或C程序语言后,能分析出设备的运行流程和网络行为,还能找到安全加密相关的密钥相关的信息。如果这些“有心人”没能获取到固件信息,他们也很难发现这些漏洞。从这一点看,物联网设备的安全性,在很大程度上决定于其固件的安全性。
那有没有一些方式可以防止“有心人”获取固件?想要回答这个问题,必须得知道固件在物联网设备中是怎么存储的,还要理清物联网设备固件的获取方法有哪些,才能回答如何防止获取到固件的问题。接下来,笔者将围绕固件的存储方式、获取方法和防护方法来呈现物联网设备固件的安全性。
二 存储位置
从计算机体系结构(以冯·诺依曼体系结构为例)看,固件被存储在存储器中,以方便CPU寻址、读指令、译指令等。 “有心人”只要知道物联网设备的组成部分中,哪些元器件具备存储固件的能力,就能大体知道固件在物联网设备上的存储位置了。
仅仅知道固件在存储器中,也是不够的,还应该知道固件在存储器的哪个部分。因为存储器中的数据是根据地址来读取和写入的。 “有心人”必须知道物联网设备固件被厂家写入的地址,才算完整地获取到固件的位置信息。
既然固件被存储在存储器中,那一定可以在物联网设备的主板上找到固件的存储芯片,进而定位物联网设备的固件。一般,固件在主板上存储的方式,可以简单分成两类:集成式和分离式。分别如图2和图3所示。
集成式固件存储
当设备的功能比较简单,不需要运行复杂的操作系统或者集成复杂的协议栈,也不需要足够强的计算性能,一般厂商会把固件集成到他们的主控芯片中,以节约成本。这类设备我们暂且把它们称为弱设备。
弱设备和个人电脑的组成有很大的不同。一般而言,个人电脑会有非常大的硬盘(10G以上)来存储操作系统,以满足操作系统的正常运行;而几乎全部的弱设备只需要百K容量级别的固件即可正常运行。为了满足系统集成的需要,主控器厂商(如ST、NXP等)把系统正常运行所需的内存(RAM),硬盘(Flash等可重复擦写、掉电数据不丢失的存储器),CPU(计算核心)和外围接口(用于同外围设备通信)集成到一个芯片中,这个芯片由于通常用于对传感器、电机等外围设备的简单控制,又被称为微型控制单元(MCU),也就是我们常说的主控器。如图4所示。
从内部集成的32bit RISC CPU来看,其寻址能力可以达到2^32字节,也就是4GB。其内部的固件一定会被存放在ROM中,也就是FLASH或者EEPROM中,一般,设备的固件会被写入到FLASH中,EEPROM用于重要系统参数或数据存放。
如图5所示,在可寻址的4GB的空间内,FLASH区域的地址空间为0x00080000~0x00100000(512KB)。只要“有心人”能通过某种方法,读取到这段地址空间中的数据,固件也就得到了。这个方法笔者在下一章介绍,接下来先介绍分离式的情况。
分离式固件存储
当主控器内部的存储容量不能完全存储所需的固件或信息时,厂商一般会外接存储器到主控器上,一般,存储器会有下面3种,如图6所示(下面三个芯片),对应来看,上面的设备可以作为读取下面的存储器的模块来使用,插入电脑,利用配套软件即可读出固件,这时候,“有心人”无需知道这三类存储器在主控器或CPU中的地址映射是怎样的,直接把芯片取下来,对应模块读出即可。
但如果不取下这些芯片,有没有可以读出来的办法?那就通过bootloader或者利用JTAG/SWD等调试接口,把通过控制主控器的读写存储器流程,进而读出固件内容。利用这种方式读取固件内容,需要知道这类存储器的地址在主控器中被映射到了哪段固件在存储器中的位置,寻找方法类似JN5169的固件地址,这里不再赘述。
三 固件获取方法
1 网络升级截获(FTP、HTTP)
固件不仅可以在本地获取。当物联网设备进入升级流程时,可以通过抓取升级过程的流量信息,进而得到物联网设备通过网络升级固件的具体途径,进而复现固件升级流程而获取固件。
例如,在某摄像头的升级流程中,物联网设备通过请求FTP服务器,进而下载到固件,如图7所示。
再如,在某物联网设备升级流程中,物联网设备通过HTTP协议,向云端发起GET请求,进而获取到固件,如图8所示。
通过抓取网络升级过程的流量进而获取固件的方法,适合在设备购买回来第一时间进行抓包,因为有的设备通过静默升级的方式来升级固件,用户是感知不到的。如果设备采取的是静默升级的方式,而此时设备刚好是最新的固件版本,设备将不会进入升级流程,此时固件是没有办法通过升级来获取的。此时,“有心人”会通过本章其他小节中介绍的方式来获取固件。
2 直接读存储芯片
上一章我们介绍了分离式固件存储方式下,固件在物联网设备主板上所处的位置。本节介绍“有心人”是如何直接读取存储器中的数据,进而获取到固件的。
既然是直接把存储器通过焊接的方式取下来,必然需要用到焊接工具,如图9所示。攻击者将存储器焊接下来之后,将用到图11所示的编程器把固件从图10所示存储器中读取到。其他两种存储器的读取方式,和这个流程类似,此处不再赘述。
“有心人”除了有这个编程器以外,还会从卖家得到一个配套软件,运行在PC上,作为上位机读取nandflash中的内容,如。图12所示。利用该软件和编程器,将存储芯片和PC建立连接后,直接读出即可。
3 通过串口等通信总线读取
上节提到的方式简单粗暴,“有心者”获取固件的方式中,还有一种比较“温柔”的方式,那就是通过主板上暴露的UART接口,在PC与固化在主控器中的bootloader程序之间建立通信,进而通过控制主控器读取固件中指令的流程,把固件读取出来。此处以STM32单片机为例,介绍如何利用主控器内部的bootloader进而得到主控器内部的flash区域的代码。
关于bootloader,ST公司对其STM32系列产品有如下描述:
“自举程序存储在 STM32 器件的内部自举 ROM 存储器(系统存储器)中。在生产期间由 ST编程。其主要任务是通过一种可用的串行外设(USART、CAN、USB、I2C 等)将应用程序下 载到内部 Flash 中。每种串行接口都定义了相应的通信协议,其中包含兼容的命令集和序列。”
从官方介绍来看,这段bootloader的主要作用是方便芯片使用者(嵌入式软件开发工程师)下载固件程序到主控器的flash区域中。事实上,除了这个功能外,bootloader还具备读取固件的能力。这要从bootloader的整个运行流程谈起。
从启动流程上看,如果bootloader被激活运行(一般是通过硬件上的引脚状态设置),主控芯片一定会停留在一个等待命令的状态,这个状态允许另外一台主机(如通过串口总线连接主控器的PC)通过串口通信发送命令,以下载固件到存储器或读取存储器中的内容。如果主机传入RD命令,将会进入读取存储器内部数据的流程。
当从机从主机端接收到0x11+0xEE时,接下来,主机会给从机传输读取的数据所在地址以及读取的容量大小,从机就会把数据发送到主机端,此时,只要主机端把接收到的数据以二进制或16进制的形式存储成文件,即可得到固件。读取数据的流程如图15所示。
利用bootloader读取其他MCU的方法类似,参考官方文档对其bootloader程序的描述即可。如果官方的芯片手册中没有提供通过外部通信接口读取固件的通信流程和时序,就意味着两种情况:其一,生产该芯片的公司不会把通过bootloader读取固件的方法开放出来;其二,生产该芯片的公司没有把通过外部通信接口读取固件的电路设计到芯片中。无论是哪种情况,厂商已经把bootloader获取固件的方法封闭或禁止了。如瑞萨电子(MCU厂商)就是这样做的。如果官方提供的bootloader程序中没有读取固件的功能,但是芯片手册中提供了读取流程和通信时序,就可以参考芯片的官方手册写一个第三方bootloader来代替物联网设备中的bootloader,这样开发成本会大一些,不如选择利用硬件调试接口来读取存储在设备固件中的内容。
4 通过调试接口读取
有些产品的主板上,会暴露硬件开发调试时所用的接口,如JTAG/SWD接口。一般,如果只是利用串口,在开发调试阶段是没办法设置程序断点的,所以厂商实现了JTAG/SWD硬件模块,以特定的协议,通过一个硬件调试器作为通信协议适配器,方便开发者在PC上动态调试正在运行在芯片中的代码。利用JTAG/SWD的接口,使PC与主控器建立连接和适配后,相当于控制整个芯片或者设备。
以SWD接口为例。利用SWD接口和相配套的硬件调试器,将PC和物联网设备建立连接的接线方法,如图16所示。
当PC与主控芯片建立连接后,需要参考SWD接口的通信协议规范,把存储在flash中的数据读取出来,才能获取固件。与通过bootloader读取固件的方式不同,生产硬件调试器的厂商会提供一个配套软件作为主机端程序,如J-Link驱动程序等,使PC运行J-Link驱动中的程序后,可以向主控器的存储器中写入数据、读取数据,也可以运行指令、设置断点等,实现硬件调试的效果。
以J-Link驱动程序为例,J-Link驱动程序与J-Link硬件调试器配套。PC安装完J-Link的驱动程序之后,会带有如图17所示的软件。运行J-Link Commander和J-Flash ARM都可以建立PC与芯片之间的连接并读取出固件。J-Link GDB Server via SWD/JTAG可以建立一个GDB调试服务,使用平常调试用的gdb即可动态调试主控芯片。
四 如何防护
固件本是可以得到足够的防护的。前两章分别介绍了固件的位置以及读取方法。从这两方面的介绍来看,如果设备暴露了UART或者JTAG/SWD接口,固件有可能被读取到。如果固件被分开存储,甚至没办法在利用软件编程防止固件被读取。如果想要对固件做到足够的保护,最基本的应该做到防止“有心人”轻易读取。
1 加密传输与认证
为了防止固件被“有心人”在局域网中获取到,必须在产品的升级流程中加入加密传输和请求认证的功能。
如果产品请求的是FTP服务,可以改用SFTP服务代替FTP服务,并进行双向认证。这样他们看到的内容是加密的文件传输流量,如果没有解密所需信息,是无法对数据包进行解密的,同时,针对密钥的认证,也能防范中间人攻击。SFTP服务是基于SSH服务实现,所以可以通过设置SSH服务的方式来设置好双向认证流程,具体方法可以参考网络上的相关文档或教程,本文不再展开陈述。
如果产品请求的是HTTP服务,可以改用HTTPS服务代替HTTP服务,并进行双向认证。网络上有许多基于nginx和apache进行HTTPS加密通信和双向认证的设置,本文不再呈现设置方法。
但是,设备侧应如何实现传输加密和认证呢?笔者以kaa[7](版本0.10.0)开源物联网解决方案举例,来说明物联网设备端应如何设计,以实现安全认证和传输加密。
从kaa架构图上看,kaa为物联网设备生成了SDK,使用kaa的工程师可以从服务端下载SDK源码进行编译并运行,进而连接到kaa服务端。笔者分析了kaa SDK的源码,有以下结论:
1. kaa服务端和设备端的数据传输,是基于AES256加密的方法。
2. kaa服务端会对设备端认证,设备端对云端的认证,笔者没有找到。
3. 设备端是以AES密钥作为被签名的文本,采用的RSA签名方式。
kaa的加密方法足够强,目前看来,其签名认证采用的单向认证,如何把kaa的单向认证改为双向认证呢?在设备端,需要在建立TCP连接之后,对设备签名的信息传输之前,加入设备对服务端进行认证流程。认证方法也可以采用另一个RSA秘钥对,在生成SDK时,使设备带有服务端签名验证的密钥,进而对服务端认证。在服务端,可以独立于kaa服务开启另一个认证服务,专用于加密传输认证信息。
不过,需要注意的是,我们需要对该私钥做到足够的保护,可以把密钥放到芯片内部可以进行读保护的区域,利用读保护的方法防止攻击者获取云端密钥信息。设置读保护的方法,本章4.3 节会有案例说明。
2 隐藏接口
如何隐藏UART、JTAG等调试接口?笔者提供以下思路:
1. 隐藏主控芯片引脚和型号信息。
采用BGA封装的芯片,可以较好地隐藏芯片的引脚,以增加判断调试接口位置的难度。如果再把芯片的型号信息抹去,此时“有心人”是没有特定的文档做参考,以确定芯片型号,进而确定接口位置的。如果主板上留有这两类接口的焊盘,他们只能通过这些焊盘来盲目测试以确定接口位置。
2. 删除主板上这两类接口的焊盘和相关的丝印。
如果把主板上这两类接口相关的布线和焊盘删去,此时“有心人”除非把主控芯片焊接下来才有可能接触到BGA封装的芯片引脚。如果他们想读取出固件,必然要引出导线建立与PC的连接才可以。如果固件是分离式存储,把芯片取下来,是没有办法通过主控芯片读取外部存储器的固件的,因为即使PC和芯片建立连接,存储器和芯片之间的连接已经被断开了。此时只能读取到主控芯片内部的固件,或者直接把存储器取下来,直接读取存储器中的固件。
删除焊盘、丝印、芯片信息,隐藏芯片引脚,这两种方式已经把“有心人”读取固件的方法限定到两种方法:通过焊接去下存储器或主控芯片,利用其它工具获取固件的暴力方法了。
3 设置主控芯片的读保护
在主控芯片内设置读保护,可以防止“有心人”通过读取芯片内部flash区域的固件。以STM32主控器提供的RDP读保护功能为例。如果主控芯片内部设置了对内部存储区域的读保护操作,那必须要先解除读保护操作才可以读取内部存储区,而解除读保护的操作,会导致正片存储区域的擦除,事实上,如果解除了读保护机制,主控芯片内的固件已经被擦除了。所以,这种方法可以防止“有心人”暴力取下主控器,单独制板读取内部flash数据,进而读取到固件。
到此为止,集成式存储固件的产品,其固件可以受到保护,使“有心人”基本没有任何办法读取到主控器中的固件。“有心人”可利用的方法,现在只能针对分离式的产品,把存储器取下来,再利用编程器读取出来。
4 加密保护
如果不加入特定的保护电路,从软件上已经没办法防止“有心人”把外部存储器中的固件读取出来了。退一步讲,我们可以加密一部分固件,以防止他们从固件中获取有效的信息(如文件系统)。如加密除bootloader以外的代码,解密程序在bootloader中实现。解密密钥相关的信息完全可以放到主控器中,并加以读保护,这样,即便他们拿到了加密的固件,和bootloader中的算法,也没办法得到解密密钥,获取不到有用的信息。
综上所述,如果我们能综合利用加密技术、读保护技术,并在产品开发期间注意隐藏接口、芯片信息,“有心人”是很难读取到设备固件,或者利用固件获取到有效信息的。没有固件,没有接口能进入系统控制台的情况下,他们只能盲目地对设备进行测试,从而在一定程度上保证物联网设备本身的安全性。
一 引言
随着物联网设备的普及,物联网设备的安全问题也逐渐被“有心人”挖掘出来,如2017年9月,一位黑客通过分析ofo共享单车的固件,发掘了4种攻击方法,并控制了共享单车;2017年10月,LIFX智能灯泡也被成功入侵,因为其固件中泄露了密钥相关的信息;又如,ReCon BRX 2018会议上,来自美国东北大学的两位研究员逆向了小米物联网设备的内部固件,发现了整个小米生态存在的漏洞。
研究员和黑客这些“有心人”在研究物联网设备时,为什么要获取物联网设备固件,又是如何获取物联网设备固件的呢?这三个案例中提到的漏洞,都是因为物联网设备的固件被研究员获取到,进而逆向分析后才发现的。更多的案例,可以在ReCon、BlackHat会议的官网上找到。通过分析物联网设备遭受攻击的链条,笔者发现,他们获取固件,把固件逆向成汇编或C程序语言后,能分析出设备的运行流程和网络行为,还能找到安全加密相关的密钥相关的信息。如果这些“有心人”没能获取到固件信息,他们也很难发现这些漏洞。从这一点看,物联网设备的安全性,在很大程度上决定于其固件的安全性。
那有没有一些方式可以防止“有心人”获取固件?想要回答这个问题,必须得知道固件在物联网设备中是怎么存储的,还要理清物联网设备固件的获取方法有哪些,才能回答如何防止获取到固件的问题。接下来,笔者将围绕固件的存储方式、获取方法和防护方法来呈现物联网设备固件的安全性。
二 存储位置
从计算机体系结构(以冯·诺依曼体系结构为例)看,固件被存储在存储器中,以方便CPU寻址、读指令、译指令等。 “有心人”只要知道物联网设备的组成部分中,哪些元器件具备存储固件的能力,就能大体知道固件在物联网设备上的存储位置了。
仅仅知道固件在存储器中,也是不够的,还应该知道固件在存储器的哪个部分。因为存储器中的数据是根据地址来读取和写入的。 “有心人”必须知道物联网设备固件被厂家写入的地址,才算完整地获取到固件的位置信息。
既然固件被存储在存储器中,那一定可以在物联网设备的主板上找到固件的存储芯片,进而定位物联网设备的固件。一般,固件在主板上存储的方式,可以简单分成两类:集成式和分离式。分别如图2和图3所示。
集成式固件存储
当设备的功能比较简单,不需要运行复杂的操作系统或者集成复杂的协议栈,也不需要足够强的计算性能,一般厂商会把固件集成到他们的主控芯片中,以节约成本。这类设备我们暂且把它们称为弱设备。
弱设备和个人电脑的组成有很大的不同。一般而言,个人电脑会有非常大的硬盘(10G以上)来存储操作系统,以满足操作系统的正常运行;而几乎全部的弱设备只需要百K容量级别的固件即可正常运行。为了满足系统集成的需要,主控器厂商(如ST、NXP等)把系统正常运行所需的内存(RAM),硬盘(Flash等可重复擦写、掉电数据不丢失的存储器),CPU(计算核心)和外围接口(用于同外围设备通信)集成到一个芯片中,这个芯片由于通常用于对传感器、电机等外围设备的简单控制,又被称为微型控制单元(MCU),也就是我们常说的主控器。如图4所示。
从内部集成的32bit RISC CPU来看,其寻址能力可以达到2^32字节,也就是4GB。其内部的固件一定会被存放在ROM中,也就是FLASH或者EEPROM中,一般,设备的固件会被写入到FLASH中,EEPROM用于重要系统参数或数据存放。
如图5所示,在可寻址的4GB的空间内,FLASH区域的地址空间为0x00080000~0x00100000(512KB)。只要“有心人”能通过某种方法,读取到这段地址空间中的数据,固件也就得到了。这个方法笔者在下一章介绍,接下来先介绍分离式的情况。
分离式固件存储
当主控器内部的存储容量不能完全存储所需的固件或信息时,厂商一般会外接存储器到主控器上,一般,存储器会有下面3种,如图6所示(下面三个芯片),对应来看,上面的设备可以作为读取下面的存储器的模块来使用,插入电脑,利用配套软件即可读出固件,这时候,“有心人”无需知道这三类存储器在主控器或CPU中的地址映射是怎样的,直接把芯片取下来,对应模块读出即可。
但如果不取下这些芯片,有没有可以读出来的办法?那就通过bootloader或者利用JTAG/SWD等调试接口,把通过控制主控器的读写存储器流程,进而读出固件内容。利用这种方式读取固件内容,需要知道这类存储器的地址在主控器中被映射到了哪段固件在存储器中的位置,寻找方法类似JN5169的固件地址,这里不再赘述。
三 固件获取方法
1 网络升级截获(FTP、HTTP)
固件不仅可以在本地获取。当物联网设备进入升级流程时,可以通过抓取升级过程的流量信息,进而得到物联网设备通过网络升级固件的具体途径,进而复现固件升级流程而获取固件。
例如,在某摄像头的升级流程中,物联网设备通过请求FTP服务器,进而下载到固件,如图7所示。
再如,在某物联网设备升级流程中,物联网设备通过HTTP协议,向云端发起GET请求,进而获取到固件,如图8所示。
通过抓取网络升级过程的流量进而获取固件的方法,适合在设备购买回来第一时间进行抓包,因为有的设备通过静默升级的方式来升级固件,用户是感知不到的。如果设备采取的是静默升级的方式,而此时设备刚好是最新的固件版本,设备将不会进入升级流程,此时固件是没有办法通过升级来获取的。此时,“有心人”会通过本章其他小节中介绍的方式来获取固件。
2 直接读存储芯片
上一章我们介绍了分离式固件存储方式下,固件在物联网设备主板上所处的位置。本节介绍“有心人”是如何直接读取存储器中的数据,进而获取到固件的。
既然是直接把存储器通过焊接的方式取下来,必然需要用到焊接工具,如图9所示。攻击者将存储器焊接下来之后,将用到图11所示的编程器把固件从图10所示存储器中读取到。其他两种存储器的读取方式,和这个流程类似,此处不再赘述。
“有心人”除了有这个编程器以外,还会从卖家得到一个配套软件,运行在PC上,作为上位机读取nandflash中的内容,如。图12所示。利用该软件和编程器,将存储芯片和PC建立连接后,直接读出即可。
3 通过串口等通信总线读取
上节提到的方式简单粗暴,“有心者”获取固件的方式中,还有一种比较“温柔”的方式,那就是通过主板上暴露的UART接口,在PC与固化在主控器中的bootloader程序之间建立通信,进而通过控制主控器读取固件中指令的流程,把固件读取出来。此处以STM32单片机为例,介绍如何利用主控器内部的bootloader进而得到主控器内部的flash区域的代码。
关于bootloader,ST公司对其STM32系列产品有如下描述:
“自举程序存储在 STM32 器件的内部自举 ROM 存储器(系统存储器)中。在生产期间由 ST编程。其主要任务是通过一种可用的串行外设(USART、CAN、USB、I2C 等)将应用程序下 载到内部 Flash 中。每种串行接口都定义了相应的通信协议,其中包含兼容的命令集和序列。”
从官方介绍来看,这段bootloader的主要作用是方便芯片使用者(嵌入式软件开发工程师)下载固件程序到主控器的flash区域中。事实上,除了这个功能外,bootloader还具备读取固件的能力。这要从bootloader的整个运行流程谈起。
从启动流程上看,如果bootloader被激活运行(一般是通过硬件上的引脚状态设置),主控芯片一定会停留在一个等待命令的状态,这个状态允许另外一台主机(如通过串口总线连接主控器的PC)通过串口通信发送命令,以下载固件到存储器或读取存储器中的内容。如果主机传入RD命令,将会进入读取存储器内部数据的流程。
当从机从主机端接收到0x11+0xEE时,接下来,主机会给从机传输读取的数据所在地址以及读取的容量大小,从机就会把数据发送到主机端,此时,只要主机端把接收到的数据以二进制或16进制的形式存储成文件,即可得到固件。读取数据的流程如图15所示。
利用bootloader读取其他MCU的方法类似,参考官方文档对其bootloader程序的描述即可。如果官方的芯片手册中没有提供通过外部通信接口读取固件的通信流程和时序,就意味着两种情况:其一,生产该芯片的公司不会把通过bootloader读取固件的方法开放出来;其二,生产该芯片的公司没有把通过外部通信接口读取固件的电路设计到芯片中。无论是哪种情况,厂商已经把bootloader获取固件的方法封闭或禁止了。如瑞萨电子(MCU厂商)就是这样做的。如果官方提供的bootloader程序中没有读取固件的功能,但是芯片手册中提供了读取流程和通信时序,就可以参考芯片的官方手册写一个第三方bootloader来代替物联网设备中的bootloader,这样开发成本会大一些,不如选择利用硬件调试接口来读取存储在设备固件中的内容。
4 通过调试接口读取
有些产品的主板上,会暴露硬件开发调试时所用的接口,如JTAG/SWD接口。一般,如果只是利用串口,在开发调试阶段是没办法设置程序断点的,所以厂商实现了JTAG/SWD硬件模块,以特定的协议,通过一个硬件调试器作为通信协议适配器,方便开发者在PC上动态调试正在运行在芯片中的代码。利用JTAG/SWD的接口,使PC与主控器建立连接和适配后,相当于控制整个芯片或者设备。
以SWD接口为例。利用SWD接口和相配套的硬件调试器,将PC和物联网设备建立连接的接线方法,如图16所示。
当PC与主控芯片建立连接后,需要参考SWD接口的通信协议规范,把存储在flash中的数据读取出来,才能获取固件。与通过bootloader读取固件的方式不同,生产硬件调试器的厂商会提供一个配套软件作为主机端程序,如J-Link驱动程序等,使PC运行J-Link驱动中的程序后,可以向主控器的存储器中写入数据、读取数据,也可以运行指令、设置断点等,实现硬件调试的效果。
以J-Link驱动程序为例,J-Link驱动程序与J-Link硬件调试器配套。PC安装完J-Link的驱动程序之后,会带有如图17所示的软件。运行J-Link Commander和J-Flash ARM都可以建立PC与芯片之间的连接并读取出固件。J-Link GDB Server via SWD/JTAG可以建立一个GDB调试服务,使用平常调试用的gdb即可动态调试主控芯片。
四 如何防护
固件本是可以得到足够的防护的。前两章分别介绍了固件的位置以及读取方法。从这两方面的介绍来看,如果设备暴露了UART或者JTAG/SWD接口,固件有可能被读取到。如果固件被分开存储,甚至没办法在利用软件编程防止固件被读取。如果想要对固件做到足够的保护,最基本的应该做到防止“有心人”轻易读取。
1 加密传输与认证
为了防止固件被“有心人”在局域网中获取到,必须在产品的升级流程中加入加密传输和请求认证的功能。
如果产品请求的是FTP服务,可以改用SFTP服务代替FTP服务,并进行双向认证。这样他们看到的内容是加密的文件传输流量,如果没有解密所需信息,是无法对数据包进行解密的,同时,针对密钥的认证,也能防范中间人攻击。SFTP服务是基于SSH服务实现,所以可以通过设置SSH服务的方式来设置好双向认证流程,具体方法可以参考网络上的相关文档或教程,本文不再展开陈述。
如果产品请求的是HTTP服务,可以改用HTTPS服务代替HTTP服务,并进行双向认证。网络上有许多基于nginx和apache进行HTTPS加密通信和双向认证的设置,本文不再呈现设置方法。
但是,设备侧应如何实现传输加密和认证呢?笔者以kaa[7](版本0.10.0)开源物联网解决方案举例,来说明物联网设备端应如何设计,以实现安全认证和传输加密。
从kaa架构图上看,kaa为物联网设备生成了SDK,使用kaa的工程师可以从服务端下载SDK源码进行编译并运行,进而连接到kaa服务端。笔者分析了kaa SDK的源码,有以下结论:
1. kaa服务端和设备端的数据传输,是基于AES256加密的方法。
2. kaa服务端会对设备端认证,设备端对云端的认证,笔者没有找到。
3. 设备端是以AES密钥作为被签名的文本,采用的RSA签名方式。
kaa的加密方法足够强,目前看来,其签名认证采用的单向认证,如何把kaa的单向认证改为双向认证呢?在设备端,需要在建立TCP连接之后,对设备签名的信息传输之前,加入设备对服务端进行认证流程。认证方法也可以采用另一个RSA秘钥对,在生成SDK时,使设备带有服务端签名验证的密钥,进而对服务端认证。在服务端,可以独立于kaa服务开启另一个认证服务,专用于加密传输认证信息。
不过,需要注意的是,我们需要对该私钥做到足够的保护,可以把密钥放到芯片内部可以进行读保护的区域,利用读保护的方法防止攻击者获取云端密钥信息。设置读保护的方法,本章4.3 节会有案例说明。
2 隐藏接口
如何隐藏UART、JTAG等调试接口?笔者提供以下思路:
1. 隐藏主控芯片引脚和型号信息。
采用BGA封装的芯片,可以较好地隐藏芯片的引脚,以增加判断调试接口位置的难度。如果再把芯片的型号信息抹去,此时“有心人”是没有特定的文档做参考,以确定芯片型号,进而确定接口位置的。如果主板上留有这两类接口的焊盘,他们只能通过这些焊盘来盲目测试以确定接口位置。
2. 删除主板上这两类接口的焊盘和相关的丝印。
如果把主板上这两类接口相关的布线和焊盘删去,此时“有心人”除非把主控芯片焊接下来才有可能接触到BGA封装的芯片引脚。如果他们想读取出固件,必然要引出导线建立与PC的连接才可以。如果固件是分离式存储,把芯片取下来,是没有办法通过主控芯片读取外部存储器的固件的,因为即使PC和芯片建立连接,存储器和芯片之间的连接已经被断开了。此时只能读取到主控芯片内部的固件,或者直接把存储器取下来,直接读取存储器中的固件。
删除焊盘、丝印、芯片信息,隐藏芯片引脚,这两种方式已经把“有心人”读取固件的方法限定到两种方法:通过焊接去下存储器或主控芯片,利用其它工具获取固件的暴力方法了。
3 设置主控芯片的读保护
在主控芯片内设置读保护,可以防止“有心人”通过读取芯片内部flash区域的固件。以STM32主控器提供的RDP读保护功能为例。如果主控芯片内部设置了对内部存储区域的读保护操作,那必须要先解除读保护操作才可以读取内部存储区,而解除读保护的操作,会导致正片存储区域的擦除,事实上,如果解除了读保护机制,主控芯片内的固件已经被擦除了。所以,这种方法可以防止“有心人”暴力取下主控器,单独制板读取内部flash数据,进而读取到固件。
到此为止,集成式存储固件的产品,其固件可以受到保护,使“有心人”基本没有任何办法读取到主控器中的固件。“有心人”可利用的方法,现在只能针对分离式的产品,把存储器取下来,再利用编程器读取出来。
4 加密保护
如果不加入特定的保护电路,从软件上已经没办法防止“有心人”把外部存储器中的固件读取出来了。退一步讲,我们可以加密一部分固件,以防止他们从固件中获取有效的信息(如文件系统)。如加密除bootloader以外的代码,解密程序在bootloader中实现。解密密钥相关的信息完全可以放到主控器中,并加以读保护,这样,即便他们拿到了加密的固件,和bootloader中的算法,也没办法得到解密密钥,获取不到有用的信息。
综上所述,如果我们能综合利用加密技术、读保护技术,并在产品开发期间注意隐藏接口、芯片信息,“有心人”是很难读取到设备固件,或者利用固件获取到有效信息的。没有固件,没有接口能进入系统控制台的情况下,他们只能盲目地对设备进行测试,从而在一定程度上保证物联网设备本身的安全性。
举报