上文,我们讨论了固件逆向分析过程中的部分工具和策略,这篇我们接着介绍如何分析被加密的固件以及分析策略。
hex editor的使用
hex editor是一款使用简单的十六进制编辑工具,能快速对数字进行十六进制转换操作,它运行于windows平台,它能够编辑xml文件进行修改二进制数据。软件支持Undo、插入、覆盖、搜索、比较等编辑模式。
十六进制转换操作是个复杂的过程,在此我们推荐hex editor,这对固件逆向分析可能很有用。说实话,至今我都还没能找到我理想中的十六进制编辑工具。我希望有一些分析内容可以直接被显示为二进制文件,为任何给定的公共架构找到有效的字节码,可视化熵(包括字节码),允许做笔记和高亮显示,列表字符串等等。
在实践中,我倾向于使用HxD来做一些基本的工作,使用wxHexEditor来做笔记和高亮显示,如下所示:
目前市场上有很多十六进制编辑工具,所以你选择使用哪一个将取决于个人偏好,建议你多试几个。
固件中的文件是否被加密?
你可能会遇到以某种方式加密的固件文件,遇到这种情况,首先,你需要了解是整个文件被加密,还是某个数据库被加密。如果文件是加密的,你可能还想快速了解它的加密执行情况。
这些分析工作是很难在字符行中一行一行手动分析的,你最好尝试从更高层全面了解文件的字符行。
计算出熵变是了解任何给定字节序列的压缩或加密方式的一种非常好的方法,如果计算的结果是高熵,则文件可能被加密或压缩。如果计算的结果是低熵,则文件是不会被加密或压缩。但是,即使熵变被计算出来,你也不容易区分出来。
在此,建议你使用可视化工具,这有助于快速查看熵,以下是一些我用过的非常有用的策略。
binwalk – e
binwalk有一个内置的熵计算器,它可输出一个2D图形,能很好地将熵可视化。但是,由于它是2D图形格式,可数据的一些细微差别可能会被丢失。
这是STM32F4系统引导加载程序的熵图。第一个16kb主要是裸机Thumb字节码(以及它可能是经过特别压缩和优化的),这使得熵计算相对较高。但它离1还很远,显然没有加密。最后的12kb主要是0xFF字节——因此计算出的熵非常低,甚至接近于0。
以上是一个大型(大于100mb)固件文件的熵图,在某种程度上它是加密的。文件熵的计算结果非常接近1,这说明它是高熵,不过由于无法达到1,可能表明加密不够理想。中国菜刀
binvis.io
binvis.io是一款不错的全彩二进制可视化工具,只要你不介意把它们上传到别人的服务器上,它在较小的文件上运行得非常好。
Binvis的脱机模式
还有一个(非常不发达的)C#项目也被称为binvis ,它似乎在很久以前就停止研发了。它与上面说的与binvis.io没有任何关系,但对于离线分析和大型文件,它确实能更好地进行分析。
特别是,RGB绘图。尽管不像binvis.io输出那样华丽,但对于快速查看文件中是否存在任何明显的重复模式或较低熵的区域非常有用。
以上的示例是针对某个大型路由器品牌的加密固件文件的分析过程,很明显,“加密”可能没有达到应有的“加密”程度。如果你看到固件中的重复模式被假定为以某种方式加密,你实际上可能正在查看XOR,其密钥部分可以通过统计分析相对容易地得出。如果你不太幸运,你可能是在ECB模式下查看类似AES的内容。
加密还是压缩?
已经有人写过关于《使用“数学”方法区分加密和压缩结构》的文章,你可以详细阅读。
裸机?
上述大多数工具都非常适合使用适用于具有适当操作系统的SoC的固件,但有时你会分析裸机MCU的固件。在这种情况下,你需要考虑其他策略来分析固件。天空彩
数据表
如果你能得到数据表,只恭喜你,你的分析基本上就算成功了。不要低估一个不错的数据表的价值。数据表可能会包含1000多页的编程以及核心处理器的内容。
有时数据表是不公开的,有时是被加密的,有时它们是放在芯片制造商的官方网站上,下载即可。用于查找数据表的资源可能是:
· Google:没有什么比Google更好的搜索引擎了。
· 类似Yandex.ru的网站:Yandex是俄罗斯网络拥有用户最多的网站,不要低估非英语搜索引擎和论坛的威力。
· Alibaba:大部分芯片在这里有卖的,一些列表将链接到你可能无法在其他任何地方找到的数据表,你也可以通过直接与供应商交谈来获取数据表。
如果找不到数据表,至少需要找到入口点和固件应加载的地址。你可以通过谷歌搜索、阅读论坛、阅读源代码或其他一些意想不到的角落找到答案。如果你的目标芯片有工具链,那么其中也可能有源代码,这也可能导致你朝着正确的方向前进。
将裸机二进制文件加载到IDA中
分析裸机时,IDA仍然是最有用的(也是最通用的)逆向分析程序。关于如何使用裸机二进制文件,相关文章很多。但是,通常情况下,分析裸机二进制文件时,你需要知道加载地址和入口点。
加载地址是正在执行二进制文件的内存中的地址,入口点是处理器开始执行的二进制文件中的位置。
同样,你需要记住正在分析的固件的环境,了解内核及其特性对分析会有很大帮助。如果你正在查看运行在Cortex-M上的某些内容,你可能会发现自己非常熟悉的infocenter.arm.com网站。
以下,我将使用裸机ARM二进制文件为例进行说明,因为裸机ARM非常常见。
让我们看看STM32F405的系统引导加载程序,如果你已经提前安装好了,就可以无拘无束的进行内存访问了。由于STM32数据表都是在线的发布的,所以我们可以通过google找到STM32F405的数据表。
数据表显示,引导加载程序位于“系统内存”中,但具体是在哪里?
根据上图的提示,系统内存位于0x1fff0000。
因此,将0x1fff0000到0x1fff77ff之间的所有内存逆向分析到一个文件之后,我们就可以进行一些快速的完整性检查,顺带检查一下里面有没有字符串。
$ strings -n5 stm32f405.bin s F 1`hC1hA rAh CA`b{ pGZHJ !1Ccs […snip…] a`hK!Aa`h QAarH@h h@$@! beta1
相当多字符串都是无用的,只有以下的这些对分析有用。
$ strings -el stm32f405.bin @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg @OTP Memory /0x1FFF7800/01*512 e,01*016 e @Option Bytes /0x1FFFC000/01*016 e @Device Feature/0xFFFF0000/01*004 e STMicroelectronics STM32 BOOTLOADER STM32F2STM32 "11
我们还可以检查代码编译的目的是什么,以及什么是字节顺序。为此,我们可以再次使用binwalk。
binwalk有一个内置的操作码扫描器,它使用-Y标志激活。它不执行常规的binwalk魔术字节扫描活动。相反,它只是使用Capstone引擎检查所有主要体系结构的有效指令并报告它找到的内容,包括有多少连续有效指令,体系结构和字节顺序。如果你只想对正在查看的某些固件进行快速的检查,这是非常有用的。
以下显示的就是binwalk运行在一个STM32F405引导加载程序的过程:
$ binwalk -Y stm32f405.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 ARM executable code, 16-bit (Thumb), little endian, at least 1079 valid instructions
但是,需要注意的是,binwalk -Y只是负责检查文件是否包含有效字节码的一种简单方法。它没有告诉你任何关于固件本身的有用信息,只会告诉你里面有没有有效的字节码。
唯一的有用信息是让我们知道,字节码是从偏移量0处开始的,Capstone引擎可以将字节读取为有效的LITTLE-ENDIAN(低字节序)Thumb代码。本示例中,我们得到了1079条有效指令,这是相当多的代码。然后,我们可以将这个逆向分析装载到IDA中。
译者注:Capstone是一个轻量级的多平台多架构支持的逆向分析框架。支持包括ARM,ARM64,MIPS和x86/x64平台。
LITTLE-ENDIAN(小字节序、低字节序),即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,与之对应的是:BIG-ENDIAN(大字节序、高字节序)。
在IDA中打开文件,你将看到常用的弹出窗口。此时IDA不会为你解决任何问题,因此你需要将“处理器类型”更改为“ARM Little-endian [ARM]”,然后单击“设置”。
如果你想删除ARM代码,并且只使用Thumb代码,你可以执行以下操作:
点击“处理器选项”,弹出“ARM特定选项”窗口。
点击“编辑ARM架构选项”按钮:
在“ARM架构选项”窗口中,将“ARM指令”设置为“否”。保留Thumb说明,按下“OK”“OK”“OK”,此时,IDA将再次提示你,这次要求你配置内存。
由于我们已经知道引导加载程序的地址是0x1FFF0000,因此我们可以在ROM起始地址字段中输入0x1FFF0000。另外,我们还需要设置“加载地址”来反映这一点,所以你也应该把0x1FFF0000放在那里。
单击“OK”,可能会弹出一个小窗口,提示你可以使用Alt+G组合键在ARM和Thumb指令之间切换。有时候这也是一个很好的技巧,但不是很直观,我现在给你示范一下。
加载二进制文件后,如果单击一个地址(例如,在此示例中,它是0x1FFF0000处的二进制基数),然后按Alt + G,将弹出“Segment Register Value”窗口。
0x1的“值”表示这一地址之后的代码(0x1FFF0000)将被视为Thumb代码,这意味着,ARM处理器状态寄存器中的“T”标志,会将“T”标志设置为1,以表示正在执行Thumb代码,并且标志0表示正在执行ARM代码。
注意:CODE16也会放在那个地址中,其作用就是提示你。如果将它设置为0x0,它将被视为ARM处理器状态。在这种情况下,CODE32也将会放在那个地址中。
你可以稍后在文件中单击任何其他地址并将值设置为0x0,并在该地址之后的代码将被视为ARM。但无论如何,我们不希望这样做,我们希望所有内容都被视为Thumb,所以它被设为0x1。
入口点在哪里?
这一步,我们也要用到IDA。
我们可以通过查看中断向量表来确定入口点,只需要知道表中的哪个元素是重置向量即可,因为重置向量就是我们的入口点。重置向量是当设备重置时,CPU将开始执行代码的地址。二四六
STM32F405具有Cortex-M4内核,因此我们可以使用Google查找Cortex-M4向量表。在Cortex-M4设备通用用户指南中,我们可以发现以下内容。
还有这个:
这很有趣,让我们先记住它们。
这个有用的表显示偏移量0x0处的指针是初始堆栈指针位置,偏移量0x4处的指针是重置向量,偏移量0x8处的指针是不可掩码中断的,重置向量是处理器在启动时开始执行代码的地址。
在IDA中,我们可以将地址0x0、0x4、0x8等处的数据定义为“ DWORD”。DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。右键单击地址0x1FFF0000处的字节,然后单击“ DWORD”,最后在0x1FFF0004处执行相同的操作。
看看我们从下图中得到了什么,我自己也添加了评论。这些看起来很合理。0x0偏移处的初始堆栈指针值指向一个SRAM的内存块。我们了解该内存块,是因为我们在数据表中读取了它。
重置向量0x1FFF3DA1也有意义,现在让我们跳到IDA中的那个位置。要么高亮显示并按下“Enter”,要么右键单击并选择“跳转到操作数”。
请注意:在Thumb模式下,重置向量的最小有效位加了1。所以,实际的重置向量要减去 1。
注意,此时IDA“stripe”(在顶部)完全是芥末色的,这意味着到目前为止文件中没有任何内容被定义。所以,我们的目的就是要对它们进行定义。
点击复位向量减1后的地址,按“C”,IDA将开始逆向分析。
这看起来很合理,已经定义了许多子例程,所以在 IDA “stripe”中有相当多的蓝色。对于向量表中的其他惟一指针,继续相同的操作,你将看到更多的蓝色出现。
现在,你可以开始了解固件复杂的内部工作原理。
让我们快速地看一下这个子例程,以帮助你继续分析。在这个子例程中,你可以看到值0x40023C04被加载到R0中,然后0x45670123和0xCDEF89AB被依次写入0x40023C04的内存中。
图中红色突出显示的地址指的是将要访问的内存,它没有映射到IDA文件中。因此,当前IDA文件中没有映射0x40023C04的内存。其实你不需要映射它,但可能需要在数据表中查找它的用途。
通过查看STM32F4数据表,我们可以看到0x40023C04指的是Flash接口寄存器内存段中的某个地址。
并且0x40023C00基址的偏移量0x4指的是闪存密钥寄存器(FLASH_KEYR):
可以看到,数据表中有0x45670123和0xCDEF89AB值,它们是解锁Flash控制寄存器的密钥。这就是这个子例程的作用。
IDA脚本
在逆向分析期间,你可能会需要一些有用的IDA脚本,你可以在使用IDA中的嵌入式固件时使用它们。IDA Python嵌入式工具包就是比较著名的IDA脚本,它可以让固件的逆向分析变得更容易。但一般来说,在谷歌上搜索你正在使用的特定芯片可能会比使用脚本工具来得更直接高效一些,这会为你节省了很多时间。有很多特殊的代码,可以帮助你重新构建各种奇怪芯片的固件。
总结
本文讲了逆向分析的很多方法,但不管哪种方法,你都要先搞清楚你正在使用的固件的环境。不要什么都依赖binwalk。如果有疑问,请使用不同的搜索引擎。最后,你要精通对十六进制字节的分析。