vxworks固件分析

前言

vxworks 的固件分析流程

1.用binwalk查看固件基本信息并解压固件
2.获取固件相关信息, cpu架构,大小端
3.确定固件的加载地址
4.用IDA加载固件,并修复符号表
5. 分析固件

实战分析

一道CTF题

分析固件

用到的例子

http://www.icsmaster.org/wp-content/uploads/2018/01/2018013004153995.zip

首先用 binwalk 扫描下固件的信息

$ binwalk ctf_vxworks.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
901           0x385           Zlib compressed data, default compression

发现就是 zlib 压缩的数据, 用 binwalk 直接解开, 然后用 binwalk 对解开后的文件扫描,发现 vxworks 关键信息, 于是拿到 vxworks 的固件。

$ binwalk 385

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2054252       0x1F586C        EST flat binary
2088936       0x1FDFE8        HTML document header
2108532       0x202C74        HTML document footer
2110048       0x203260        HTML document header
2115564       0x2047EC        HTML document footer
2119528       0x205768        XML document, version: "1.0"
2119796       0x205874        XML document, version: "1.0"
2119912       0x2058E8        XML document, version: "1.0"
2192512       0x217480        Base64 standard index table
2192580       0x2174C4        Base64 standard index table
2211604       0x21BF14        VxWorks WIND kernel version "2.5"
2225264       0x21F470        Copyright string: "Copyright Wind River Systems, Inc., 1984-2000"
2321952       0x236E20        Copyright string: "copyright_wind_river"
3118988       0x2F978C        Copyright string: "Copyright, Real-Time Innovations, Inc., 1991.  All rights reserved."
3126628       0x2FB564        Copyright string: "Copyright 1984-1996 Wind River Systems, Inc."
3153524       0x301E74        VxWorks symbol table, big endian, first entry: [type: function, code address: 0x1FF058, symbol address: 0x27655C]

同时还扫到了符号表的位置

计算固件加载地址

固件加载地址的计算可以通过vxworks固件中的符号表计算得出的。

固件的符号表一般在固件的最后,有明显的规律。

16个字节 为一组数据:

  • 4个字节 符号对应字符串的内存地址

  • 4个字节 符号的内存地址

  • 4个字节 特征数据

  • 4个字节 0x00, 标识一组数据的结尾

计算加载地址的方法就是

加载地址 = 符号表中字符串的地址 - 相应字符串在固件中的偏移

这里还有一个小 tips

字符串表里面的最后一个字符串 在 符号表的第一项被引用

首先根据特征找到符号表, 根据 binwalk -A 可知固件为大端,所以 第一个符号表项对应字符串在内存中的地址为 0x27656c

然后找到字符串表中最后一个字符串所在偏移为 0x26656c

所以加载地址为

$ python -c "print hex(0x27656c - 0x26656c)"
0x10000

恢复符号表

拿到加载地址后,把固件用 IDA 加载起来,然后用 idapython 的脚本恢复即可、

from idaapi import *
from idc import *

loadaddress = 0x10000
eaStart = 0x301e64 + loadaddress
eaEnd = 0x3293a4 + loadaddress

ea = eaStart
eaEnd = eaEnd
while ea < eaEnd:
    create_strlit(Dword(ea), BADADDR)
    sName = get_strlit_contents(Dword(ea))
    print sName
    if sName:
        eaFunc = Dword(ea + 4)
        MakeName(eaFunc, sName)
        MakeCode(eaFunc)
        MakeFunction(eaFunc, BADADDR)
    ea = ea + 16

就是遍历符号表项,在指定位置命名 + 设置函数。

参考

http://www.icsmaster.org/archives/ics/784
http://www.freebuf.com/vuls/177036.html

下载固件

网上可以搜到

http://www.drvsky.com/TP-Link/TL-WR886N.htm

分析固件

首先 binwalk 看看信息

iMQJu6.png

这里 binwalk 只识别到了 u-boot 镜像,和一大堆 lzma 压缩的数据。通过 u-boot 的信息可以知道固件的加载地址为 0x80010000

发现其中有个 lzma 压缩的数据大小和其他的不在一个数量级, 把它拿出来解析。

41472         0xA200          LZMA compressed data, properties: 0x6E, dictionary size: 8388608 bytes, uncompressed size: 2365616 bytes

然后用 dd 把它 拿出来

dd if=wr886v6.bin of=large.lzma bs=1 skip=41472 count=749632

然后用 binwalk 解出来

$ binwalk _large.lzma.extracted/0
0     0.7z  
$ binwalk _large.lzma.extracted/0

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1846464       0x1C2CC0        Certificate in DER format (x509 v3), header length: 4, sequence length: 4
1853752       0x1C4938        Certificate in DER format (x509 v3), header length: 4, sequence length: 4
1899120       0x1CFA70        VxWorks operating system version "5.5.1" , compiled: "Oct 20 2017, 16:17:22"
1968188       0x1E083C        Copyright string: "Copyright(C) 2001-2011 by TP-LINK TECHNOLOGIES CO., LTD."
1997876       0x1E7C34        VxWorks WIND kernel version "2.6"
2042936       0x1F2C38        HTML document header
2043001       0x1F2C79        HTML document footer
2062828       0x1F79EC        PEM certificate
2062884       0x1F7A24        PEM RSA private key
2072188       0x1F9E7C        Base64 standard index table
2107248       0x202770        CRC32 polynomial table, big endian
2108272       0x202B70        CRC32 polynomial table, big endian
2109296       0x202F70        CRC32 polynomial table, big endian
2110320       0x203370        CRC32 polynomial table, big endian
2130920       0x2083E8        XML document, version: "1.0"
2150332       0x20CFBC        SHA256 hash constants, big endian
2248421       0x224EE5        StuffIt Deluxe Segment (data): f
2248452       0x224F04        StuffIt Deluxe Segment (data): fError
2248533       0x224F55        StuffIt Deluxe Segment (data): f

可以看到有 vxworks 的字符串,前面已经拿到了固件的基地址,用 ida 加载。

恢复符号表

使用 grep 来找符号表

$ binwalk  -Me wr886v6.bin
........
$ cd _wr886v6.bin.extracted/
$ grep -r bzero
Binary file C2E3A matches

然后打开 C2E3A 看看

iMl2sx.png

开头 8 字节表示文件大小和符号表大小, 然后就是符号表了, 0x9d00 为字符串表的位置。

然后用脚本恢复

# 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)

参考

https://www.secpulse.com/archives/75635.html
posted @ 2018-09-26 14:44  hac425  阅读(...)  评论(...编辑  收藏