作者:小黑猪(朱文哲)
wr886nv7%203.bin
系统固件通常会以一定的格式封装在固件升级包中。为了提取系统固件可以先使用Binwalk对wr886nv7.bin进行初步分析。
从上图可以看到,除了一个比较明显的uImage header头以外这个升级包中还有一大堆的LZMA数据信息。通过使用binwalk或直接使用dd命令能够对升级包中的uimage镜像进行提取。
dd if=wr886nv7.bin of=uboot0x366C.img bs=1 skip=13932 count=20852
使用binwalk继续分析uboot的镜像,可以发现这个uboot镜像的Data部分使用了LZMA进行压缩。
解压uboot的LZMA数据
dd if=ubootnew.img of=uboot.lzma bs=1 skip=64
由于uboot image中缺乏符号表且大小只有52K不到,因此此处暂时不对其进行进一步的分析。
在wr886nv7.bin中除了uImage外,还有一个2M多的有趣的lzma压缩数据。
从升级包中提取该0xA200压缩数据并解压。
dd if=wr886nv7.bin of=A200.lzma bs=1 skip=41472 count=757630
lzma -d A200.lzma
使用Binwalk查看解压出的数据后发现这个文件很可能就是路由器所运行的系统固件,VxWorks系统版本5.5.1.
在对0xA200地址之前的一些数据进行分析后可以看到一个疑似uimage header的数据段,其中有两处地址指向了0x80001000,这个地址也和很多同类路由器设备的固件加载地址相同,因此我们可以尝试使用该地址作为固件加载地址进行分析。
在使用IDA加载前,首先我们需要确定CPU架构及加载地址。CPU架构信息已经在升级包中的uimage header中进行了定义,同时我们也可以使用binwalk -Y 命令进行识别。
加载地址的话,在前面的分析中已经初步可以判定为0x80001000,下一步就是使用IDA对固件进行加载分析了。在IDA中加载VxWorks固件。
在IDA中填写固件加载地址信息0x80001000。
成功加载固件后的IDA界面大致如下图所示,可以看到默认加载的情况下还是有大量的函数。
通过初步对加载的VxWorks固件进行分析后发现,这个固件中并没有编入符号表,这对后续研究产生了很大的影响。再次对固件升级包中的内容进行分析后发现,有一个格式为data的文件很有趣。
binwalk -e wr886nv7.bin
cd _wr886nv6-20180123.bin.extracted
grep -r bzero ./
在对**FAB的内容进行查看后发现有惊喜,文件中保存的数据很像独立的符号表,其中找到了大量的VxWorks关键函数名。
在对这个文件进行了初步的分析后发现这个文件还是有很明显的特征的。
根据这个独立符号文件的特征,我们可以编写对应的ida_python脚本来对固件进行修复。
# coding=utf-8 import idc import idaapi import idautils sym_file = open("PATH OF SYM FILE", 'rb').read() table_data = sym_file[0x08:0x9f80] print(table_data[-8:].encode('hex')) string_table = sym_file[0x9f80:] def get_string(offset): string = "" while True: if string_table[offset] != '\x00': string += string_table[offset] offset += 1 else: break return string def get_sym_data(): sym_data = [] for offset in range(0, len(table_data), 8): table = table_data[offset: offset + 8] flag = table[0] # print('flag: %s' % flag) string_offset = int(table[1:4].encode('hex'), 16) # print('string_offset: %s' % string_offset) string = get_string(string_offset) # print('string: %s' % string) target_address = int(table[-4:].encode('hex'), 16) # print('target_address: %s' % hex(target_address)) sym_data.append([flag, string, target_address]) return sym_data def fix_idb(sym_data): for sym in sym_data: flag, string, target_address = sym idc.MakeName(target_address, string) if flag == '\x54': print("Start fix Function %s at %s" % (string, hex(target_address))) idc.MakeCode(target_address) idc.MakeFunction(target_address, idc.BADADDR) # print('flag: %s' % flag) # print('string: %s' % string) # print('target_address: %s' % hex(target_address)) if __name__ == '__main__': sym_data = get_sym_data() fix_idb(sym_data)
脚本运行后固件看起来就清晰了很多。
至此,固件分析的预处理工作就成功完成了,此时固件中依然还有很多很函数无法被正确被识别,还需要后续的一些操作进行手动修复。基本修复后的IDA界面会像下图所示, 具体的修复方法将会在后续的文档中进行说明。
根据固件及设备分析后修复的segments信息
本文作者:GalaxyLab
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/75635.html
厉害阿
请问固件进一步的函数恢复工作要怎么操作呢?跪求
@大罗 这部分可以利用函数执行错误的报错输出,来获取函数名。例如下面的图片中这样,可以利用这种方式来识别。[img=uploads/comments/2019/07/23/20190723090618_665.png][img=uploads/comments/2019/07/23/20190723090618_333.png]
@GalaxyLab 感谢您的回答,这篇文章对我启发很大,谢谢您.
可以提供下固件链接或者附件吗?网上没找到。 谢谢!