非Linux固件加载地址分析

一、引言

在分析固件的时候,有时候会遇到binwalk无法解包的情况,查看信息发现它是非Linux文件系统,那么有可能固件是实时操作系统,关于实时操作系统,常见的有Vxworks、eCos、FreeRTOS等。这里以Zyxel的RGS200-12P_1.00(ABEP.0)C0固件为例,使用binwalk分析结果如下:

$ binwalk RGS200-12P.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             eCos kernel exception handler, architecture: MIPSEL, exception vector table base address: 0x80000200
128           0x80            eCos kernel exception handler, architecture: MIPSEL, exception vector table base address: 0x80000200
5444512       0x5313A0        Unix path: /home/remus/svn/ivs/IVSPL5-ZyXEL_New/src_0809/src/build/../build/obj/ecos/install/include/cyg/libc/stdlib/atox.inl
5444581       0x5313E5        eCos RTOS string reference: "ecos/install/include/cyg/libc/stdlib/atox.inl"
5511365       0x5418C5        eCos RTOS string reference: "ecos/install/include/cyg/libc/time/time.inl"
......

二、固件分析

1、反编译

由上面的分析结果可知固件为eCos实时操作系统,并且架构为MIPSEL,那么直接使用Ghidra反编译工具分析看看吧。

选择架构MIPS小端,点击OK后选择不进行自动分析。

通过参考链接[3] 我们可以知道实际基址为0x80040000,我们点击内存修改按钮修改基址,然后再进行自动分析,分析的过程比较长。

分析完后,选择菜单Windows-> Defined Strings,然后选择一些字符串进行查看

这里选择了“%-3d”字符串,然后找到对应的引用函数,点击进入

找到引用字符串的函数

多找几处类似的,这里截取部分代码查看:

80046804 57 80 04 3c     lui        a0,0x8057
80046808 21 28 00 02     move       a1,s0
8004680c 1b e7 01 0c     jal        FUN_80079c6c
80046810 50 cb 84 24     _addiu     a0=>s_%-3d_8056cb50,a0,-0x34b0      = "%-3d "
......
80111a90 5a 80 05 3c     lui        a1,0x805a
80111a94 21 20 60 02     move       a0,s3
80111a98 b0 28 a5 24     addiu      a1=>s_Error:_%s!_805a28b0,a1,0x28b0  = "Error: %s!\n"

汇编代码分析,以指令57 80 04 3c为例,因为是小端排序,实际的指令可以理解为3c 04 80 57,其中3c 04为操作码(lui a0),80 57为操作数(0x8057),其他的指令理解方法类似。

地址指令指令解释
0x8004680457 80 04 3clui a0,0x8057:
a0=0x80570000
0x8004681050 cb 84 24addiu a0,a0,-0x34b0:
a0=0x80570000 - 0x34b0=0x8056cb5 ("%-3d ")
0x80111a905a 80 05 3clui a1,0x805a:
a1=0x805a0000
0x80111a98b0 28 a5 24addiu a1,a1,0x28b0
a1=0x80580000 + 0x28b0=0x805a28b0 ("Error: %s!\n")

由上,可以得出:

函数中表达式里面的字符串地址 = 实际字符串地址

2、加法的规则

至于50 cb 84 24的操作数cb 50为啥变成负数-0x34b0,我们可以写一小段汇编测试一下:

lui $a0,0xa123
addiu $a0,$a0,0x0001
nop
lui $a0,0xa123
addiu $a0,$a0,0x7FFF
nop
lui $a0,0xa123
addiu $a0,$a0,0x8000
nop
lui $a0,0xa123
addiu $a0,$a0,0x8001
nop
lui $a0,0xa123
addiu $a0,$a0,0xFFFF

使用gcc编译,并用objdump进行反汇编

$ mipsel-linux-gnu-gcc -c test.s -o test.o
$ mipsel-linux-gnu-objdump -d test.o
test.o:     file format elf32-tradlittlemips


Disassembly of section .text:

00000000 <.text>:
   0:	3c04a123 	lui	a0,0xa123
   4:	24840001 	addiu	a0,a0,1
   8:	00000000 	nop
   c:	3c04a123 	lui	a0,0xa123
  10:	24847fff 	addiu	a0,a0,32767
  14:	00000000 	nop
  18:	3c04a123 	lui	a0,0xa123
  1c:	24848000 	addiu	a0,a0,-32768
  20:	00000000 	nop
  24:	3c04a123 	lui	a0,0xa123
  28:	24848001 	addiu	a0,a0,-32767
  2c:	00000000 	nop
  30:	3c04a123 	lui	a0,0xa123
  34:	2484ffff 	addiu	a0,a0,-1

可以清楚的看出操作数80 00以后就是负数,那么操作数80 00等价于-32768=-0x8000,操作数80 01等价于-32767=-0x7FFF,操作数FF FF等价于-0x1,计算方式为:

当操作数80 00 以后的值,实际数值=操作数-0x10000

3、结论

当基址正确时,实际字符串地址等价于基址 + 相对字符串地址:

函数中表达式里面的字符串地址 = 实际字符串地址 
                             = 基址 + 相对字符串地址

那么基址为0时,实际的基址计算方法如下:

基址 = 函数中表达式里面的字符串地址 - 相对字符串地址

有了上面的分析,很清楚的找到对应关系,如下图所示:

找到函数中表达式里面的地址(记作sif_addr)、相对字符串地址(记作sid_addr),当sif_addr有多次引用的时候,此地址大概率为实际的字符串地址,根据内存分布,相对字符串地址+基址一般不会改变末尾3位,即234,最终遍历全部的条件,排序出匹配次数较高的sif_addr,用sid_addr - sid_addr = 实际的基址。

三、自动化代码

(1)匹配大小端,找到mips汇编的常见代码,如jr $ra:如果08 00 e0 03那就是MIPS小端,反过来的就是MIPS大端。

(2)匹配字符串,找\x00开头和\x00结尾,中间均为可见字符就是字符串:

(3)匹配lui $a0, 0x????; addiu $a0, $a0, 0x????等表达式,取出其中的地址

(4)找所有可能的基址,使用函数中表达式里面的字符串地址 - 相对字符串地址,统计排序。

根据以上分析过程编写代码,执行代码分析基址,效果如下,可能的基址为以下地址(列出前10,根据采样量的情况都有可能为实际的基址),示例程序的真实的基址为0x80040000,刚好在前10列表中:

$ python findimgbase.py -f RGS200-12P.bin
[*] check endian
    mips little endian
[*] find string in Data
    5055
[*] find string in Func
    lui $a0, 0x????; addiu $a0, $a0, 0x???? 7050
    lui $a1, 0x????; addiu $a1, $a1, 0x???? 7481
    lui $a2, 0x????; addiu $a2, $a2, 0x???? 2835
    lui $a3, 0x????; addiu $a3, $a3, 0x???? 7772
    lui $v0, 0x????; addiu $v0, $v0, 0x???? 3481
    lui $v1, 0x????; addiu $v1, $v1, 0x???? 1738
    lui $t1, 0x????; addiu $t1, $t1, 0x???? 8844
    lui $t2, 0x????; addiu $t2, $t2, 0x???? 892
    lui $v0, 0x????; addiu $a0, $v0, 0x???? 466
[*] image base address: 2043
(01) 0x80040000 70
(02) 0x80064000 15
(03) 0x80007000 13
(04) 0x800e1000 13
(05) 0x800a5000 13
(06) 0x8007b000 13
(07) 0x80087000 12
(08) 0x8005e000 12
(09) 0x80032000 12
(10) 0x80076000 12

示例脚本见gitee:

https://gitee.com/zhiwanyuzhou/IoTools/blob/master/imagebase/findimgbase.py

四、参考链接

[1] https://zhuanlan.zhihu.com/p/483751376

[2] https://bbs.kanxue.com/thread-267719-1.htm

[3] https://www.anquanke.com/post/id/233361

留下评论

您的电子邮箱地址不会被公开。 必填项已用*标注