RSS Feed
更好更安全的互联网

使用 IDA 处理 U-Boot 二进制流文件

2019-12-04

作者:Hcamael@知道创宇404实验室
时间:2019年11月29日

最近在研究IoT设备的过程中遇到一种情况。一个IoT设备,官方不提供固件包,网上也搜不到相关的固件包,所以我从flash中直接读取。因为系统是VxWorks,能看到flash布局,所以能很容易把uboot/firmware从flash中分解出来。对于firmware的部分前一半左右是通过lzma压缩,后面的一半,是相隔一定的区间有一部分有lzma压缩数据。而固件的符号信息就在这后半部分。因为不知道后半部分是通过什么格式和前半部分代码段一起放入内存的,所以对于我逆向产生了一定的阻碍。所以我就想着看看uboot的逻辑,但是uboot不能直接丢入ida中进行分析,所以有了这篇文章,分析uboot格式,如何使用ida分析uboot。

uboot格式

正常的一个uboot格式应该如下所示:

而这uboot其实还得分为三部分:

1.从0x00 - 0x346C是属于bootstrap的部分 
2.0x346C-0x34AC有0x40字节的uboot image的头部信息 
3.从0x34AC到结尾才是uboot image的主体,经过lzma压缩后的结果

那么uboot是怎么生成的呢?Github上随便找了一个uboot源码: https://github.com/OnionIoT/uboot,编译安装了一下,查看uboot的生成过程。

1.第一步,把bootstrap和uboot源码使用gcc编译成两个ELF程序,得到bootstrapuboot 
2.第二步,使用objcopy把两个文件分别转换成二进制流文件。

3.把u-boot.bin使用lzma算法压缩,得到u-boot.bin.lzma

4.使用mkimage,给u-boot.bin.lzma加上0x40字节的头部信息得到u-boot.lzming

5.最后把bootstrap.binu-boot.lzming合并到一起,然后根据需要uboot的实际大小,比如需要一个128k的uboot,在末尾使用0xff补齐到128k大小

使用ida处理bootstrap二进制流文件

在上面的结构中,需要注意几点:

1.Data Address: 0x80010000, Entry Point: 0x80010000 表示设备启动后,会把后续uboot通过lzma解压出来的数据存入内存地址0x80010000,然后把$pc设置为: 0x80010000,所以uboot最开头4字节肯定是指令。

2.uncompressed size: 161184 bytes,可以使用dd把LZMA数据单独取出来,然后使用lzma解压缩,解压缩后的大小要跟这个字段一样。如果还想确认解压缩的结果有没有问题,可以使用CRC算法验证。

接下来就是通过dd或者其他程序把二进制流从uboot中分离出来,再丢到ida中。先来看看bootstrap,首先指定相应的CPU类型,比如对于上例,则需要设置MIPS大端。

uboot01

随后我们暂时设置一下起始地址为0x80010000,通电以后CPU第一个执行的地址默认情况下我们是不知道的,不同CPU有不同的起始地址。设置如下图所示:

uboot02

bootstrap最开头也指令,所以按C转换成指令,如下图所示:

uboot03

跳转到0x80010400, 随后是一段初始化代码,下一步我们需要确定程序基地址,因为是mips,所以我们可以根据$gp来判断基地址。

uboot04

如上图所示,因为bootstrap的大小为0x3a3c bytes,所以可以初步估计基地址为0x9f000000,所以下面修改一下基地址:

uboot05

并且修改在Options -> General -> Analysis -> Processor specific ......设置$gp=0x9F0039A0

uboot06

0x9F0039A0地址开始属于got表的范围,存储的是函数地址,所以把0x9F0039A0地址往后的数据都转成word:

uboot07

到此就处理完毕了,后面就是存逆向的工作了,具体bootstrap代码都做了什么,不是本文的重点,所以暂不管。

使用ida处理uboot流文件

处理bootstrap,我们再看看uboot,和上面的处理思路大致相同。

1.使用dd或其他程序,把uboot数据先分离出来。 2.使用lzma解压缩 3.丢到ida,设置CPU类型,设置基地址,因为uboot头部有明确定义基地址为0x80010000,所以不用再自己判断基地址 4.同样把第一句设置为指令

uboot08

正常情况下,uboot都是这种格式,0x80010008为got表指针,也是$gp的值。

5.根据0x80010008的值,去设置$gp 6.处理got表,该地址往后基本都是函数指针和少部分的字符串指针。结尾还有uboot命令的结构体。

到此uboot也算基础处理完了,后续也都是逆向的工作了,也不是本文的关注的内容。

编写idapython自动处理uboot

拿uboot的处理流程进行举例,使用Python编写一个ida插件,自动处理uboot二进制流文件。

1.我们把0x80010000设置为__start函数

2.0x80010008是got表指针,因为我们处理了0x80010000,所以got表指针地址也被自动翻译成了代码,我们需要改成word格式。

3.把got表都转成Word格式,如果是字符串指针,在注释中体现出来

基本都这里就ok了,后面还可以加一些.text段信息,但不是必要的,最后的源码如下:


Paper

本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1090/

作者:吴烦恼 | Categories:安全研究 | Tags:

发表评论