前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >四个字节的安全 :一次固件加密算法的逆向分析

四个字节的安全 :一次固件加密算法的逆向分析

原创
作者头像
腾讯玄武实验室
修改2017-08-16 10:05:49
5.5K2
修改2017-08-16 10:05:49
举报

作者:马卓

导语 本研究由hyperchemma和saltzhou共同完成。这篇文章源自我们的一个检测项目,项目中我们需要对设备的固件进行分析,在整个固件分析的过程中我们克服了很多困难,最后完整解密了设备固件的内容,这里将相关的内容做个记录。

设备介绍

设备采用ATMEL SAMA5D2系列的SOC作为主控芯片。该芯片基于ARM Cortex-A5内核,支持SPI、I2C等总线。支持USB外部设备。同时该芯片内置基于硬件的算法加速器,支持SHA1、SHA256等哈希算法,支持AES,3DES等对称加密算法。

获取固件

设备主板如下。通过观察主板,我们并没有在主板上发现常见的UART,JTAG等调试接口,这使得我们无法动态观察设备固件的运行方式。随后我们发现设备组主板上发现一个flash芯片,型号是N25Q032A,这个flash的大小是4MB。通过阅读主控芯片的手册,我们发现,这个款芯片内部容量有限,多采用外置flash的方式存储代码,因此我们推测这个flash中应该包含设备的主要代码,接着我们使用编程器我们完整读取了flash芯片的内容。

[1502761829544_355_1502761830040.jpg]
[1502761829544_355_1502761830040.jpg]

分析固件

获得设备固件后,我们着手开始对固件文件进行分析。初步观察发现,flash固件大致分为几个区域:

代码语言:txt
复制
0x000000-0x020000 这个部分是设备的启动的代码
0x020000-0x070000 第一个加密块
0x070000-0x200000 第二个加密块
0x200000-0x400000 配置数据保存快

Flash中一共有两个加密的区域,推测这两个区域应该是整个固件的真正的代码,硬件厂商为了保护代码而对这部分代码进行了加密。下面就来探讨一下如何解密这两块加密的代码。

每个加密块的结构如下,其中有两个字段比较关键,一个是加密块的大小和加密后的数据。

代码语言:txt
复制
typedef struct _encryptblock{
DWORD MAGIC; //0xf4f3f2f1
BYTE dummy[8]; //0xCD
int Size;
BYTE encryptedData[0]; //encrypted data
} encryptblock;

加密块的结构中不存在密钥字段,因此我们需要弄清楚这部分数据是什么算法加密的,以及密钥在哪里。Flash的开头部分包含部分代码,这部分代码是设备的启动代码,从逻辑上应该存在这部分加密块的解密算法和密钥,因此我们尝试对这部分代码进行逆向分析。

设备启动代码是设备启动阶段执行的代码,这部分代码通常不具备文件格式,代码也是在一个预先设定好的绝对物理地址上执行的。因此在对这部分代码进行逆向分析之前,首先我们需要确定这部分代码的基址,通过阅读芯片手册和对代码进行检查,我们确定这部分代码执行的基址是0x200000。加载到IDA中,我们在入口看到了这样的代码:

[1502761947735_9123_1502761948059.png]
[1502761947735_9123_1502761948059.png]

入口处有几个跳转指令,第一条是跳转到启动代码的真实入口,其他是跳转是跳转到相应的中断处理函数上。

通过定位特征数据和跟踪代码执行流,我们定位到了解密函数,下面就这部分代码的流程进行分析:

[1502761977748_8901_1502761978186.png]
[1502761977748_8901_1502761978186.png]

首先程序会读取第一个加密块的头部四个字节,比较是否是0xF4F3F2F1,检查合法性。然后程序会读取加密块的大小,并检查大小是否大于0x30000,同时检查数据块大小是否是0x40的倍数。

[1502762000122_540_1502762000474.png]
[1502762000122_540_1502762000474.png]

经过上面的检查后,程序会加密的内容以0x40大小为单位复制到绝对地址0x20800000。

[1502762062845_2116_1502762063227.png]
[1502762062845_2116_1502762063227.png]

经过上面的操作后,程序会将加密的数据分成两部分,分别用两个密钥:key1和key2,使用AES算法进行解密。解密到物理地址0x23F00000。至此,解密过程就算完成了。但是对于解密算法的相关的细节我们还没完全弄清楚。下面我们来逐一对解密的细节进行分析。

首先我们先看看是如何确定加密算法的。通过逆向分析,我们发现程序调用了下面这样一段代码:

[1502762097860_2515_1502762098163.png]
[1502762097860_2515_1502762098163.png]

从代码上看,0xF002C000这个地址已经超过当前固件的地址范围了,那这个操作的含义是什么呢? 通过查找芯片手册 ,我们这个地址其实是芯片内部的寄存器,名称为AES_Control Register.

[1502762115989_7284_1502762116345.png]
[1502762115989_7284_1502762116345.png]

上面代码的含义就是重置AES硬件加密引擎。

在知道了加密算法是AES,我们需要确定该算法的两个要素,一个是初始化向量(IV),一个是密钥。我们先看看IV是从哪里来的。

通过分析,我们发现IV被保存在0x00209FA4这个地址。而对这个地址的赋值代码如下:

[1502762154420_4668_1502762154724.png]
[1502762154420_4668_1502762154724.png]

上面代码告诉我们,IV的长度是16字节,分成四个部分,分别保存在R1,R2,R3和R12中, IV="\x00\x10\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x20\x00\x00\x00"。

最后我们来分析一下解密密钥的来源。解密过程中共需要两个密钥key1和key2。这两个密钥来自同一个来源,因此我们这里对key1的来源着重进行分析。

生成密钥的时候首先调用了下面的函数:

[1502762177456_7149_1502762177790.png]
[1502762177456_7149_1502762177790.png]

函数中的0xF8030050同样是一个芯片内部寄存器,这个地址和0xF803004C分别保存了4个字节芯片序列号,共计8个字节,这8个字节的序列号是唯一的,对每个芯片都不一样。这个函数就是获取当前芯片的序列号,这里我们把得到的数据记作SN0+SN1。

在读取到芯片序列号后,程序会调用一个函数生成32字节的key1,然后再对序列号进行换位,再次用同样的函数生成32字节的key2。那么这个函数到底是什么怎样把序列号计算成密钥的呢? 经过初步分析,我们在函数中发现了SHA256算法中使用的到常量,但是这个函数的代码并未采用标准库的代码,因此确定这个函数是否是标准的SHA256算法需要对代码进行详细的比对,这个过程会很耗时,因此我们想到了另一种方法来验证代码是否为标准SHA256。

我们可以肯定这部分代码是个纯算法的代码 ,因此不涉及到任何与硬件交互和操作的代码。因此我们将这段代码在模拟执行环境中执行并检查结果,如果得到的结果与我们使用标准算法计算的结果一致则表明这段代码就是标准的SHA256算法。

我们选择的模拟执行环境是Unicorn,一个基于qemu修改的CPU模拟器,支持各种语言的开发,下面是我们用python编写的模拟执行的代码。

[1502762214090_1133_1502762214480.png]
[1502762214090_1133_1502762214480.png]

通过上面代码的输出结果,我们确定了上面的算法输出的结果与标准SHA256算法一致。

至此,我们就分析清楚了固件的解密算法以及密钥来源,但是还有一个遗留的问题,就是如何的获取生成密钥芯片序列号。

前面提到,芯片序列号是唯一的,我们无法使用一个设备的密钥解密另一个设备的固件。因此我们需要的获得当前设备主控芯片的序列号。

由于设备不存在可用的输出接口,因此我们能想到的唯一能获取芯片序列号的方法就是patch固件,同时我们还需要将获取到序列号输出到能够放访问到的地方,这里我们采用了写flash的方法。下面是我们写的对固件的patch代码。

[1502762253094_5329_1502762253456.png]
[1502762253094_5329_1502762253456.png]

我们将修改过的固件烧入flash中,并重新焊接回去,在设备上电执行一段时间以后,再取下flash,读取其中的内容,这样我们就获得了当前芯片的序列号。

获取序列号以后,我们就可以按照之前的分析的算法对固件内容进行解密了。

最后我们还遗留一个问题,就是如何解密其他设备的固件。其实这个问题很好解决,因为芯片生产厂商在为每个芯片设置序列号时并不是采用全随机的方式,序列号之间存在一定联系和规律,因此我们可以假设序列号中的前面四个字节是固定的,而后面四个字节是不同的,然后暴力穷举这四个字节的内容。实践中我们使用这个方法成功解密了另外两台设备的固件。

结论

在本文中我们逆向了一款设备固件的加密算法,同时采用了模拟执行的方式确认了固件中的算法。最后我们发现整个设备固件的安全体系只依赖于四个字节的数据,在了解了相关的算法后,这种加密保护方式很容易被攻破。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 设备介绍
  • 获取固件
  • 分析固件
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com