TP-Link wr886nv6 固件解析

前言

最近看了@小黑猪的一篇关于TP-Link wr886nv7固件初步分析的文章,由于之前很少分析基于VxWorks系统的固件,所以按照文章的思路动手重现了一下整个过程。

使用binwalk 初步分析

从TP-Link官网下载wr886的固件,由于没有找到v7版本的固件,所以下载的是v6版本的固件。对下载的压缩包进行解压,然后使用binwalk对文件wr886nv6.bin进行分析,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ binwalk wr886nv6.bin 

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
12656 0x3170 U-Boot version string, "U-Boot 1.1.4 (May 8 2016 - 07:42:47)"
12704 0x31A0 CRC32 polynomial table, big endian
13932 0x366C uImage header, header size: 64 bytes, header CRC: 0x773178DD, created: 2016-05-08 14:42:48, image size: 20788 bytes, Data Address: 0x80010000, Entry Point: 0x80010000, data CRC: 0x983BDABA, OS: Linux, CPU: MIPS, image type: Firmware Image, compression type: lzma, image name: "u-boot image"
13996 0x36AC LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 52148 bytes
41472 0xA200 LZMA compressed data, properties: 0x6E, dictionary size: 8388608 bytes, uncompressed size: 2365008 bytes
791104 0xC1240 LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 1731 bytes
792301 0xC16ED LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 7272 bytes
793897 0xC1D29 LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 200 bytes
794124 0xC1E0C LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 247 bytes
794391 0xC1F17 LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 313 bytes
794600 0xC1FE8 LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 12213 bytes
796301 0xC268D LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 493 bytes
... # some data omitted
1270120 0x136168 LZMA compressed data, properties: 0x5A, dictionary size: 8388608 bytes, uncompressed size: 3965 bytes

从上述结果可知,除了开始部分的”U-Boot version string”、”CRC32”和”uImage header”之外,其余部分均为采用lzma进行压缩后的数据。其中,注意到偏移0xa200处的数据大小为2M多,猜测其可能包含一些有用的数据,提取该部分并使用lzma命令进行解压缩。

1
2
3
4
5
6
# 使用dd命令进行提取
$dd if=wr886nv6.bin of=a200.lzma bs=1 skip=41472 count=749632 # 749643=791104-41472

# 使用lzma命令进行解压
$ lzma -d a200.lzma
lzma: a200.lzma: Compressed data is corrupt

在进行解压时提示”Compressed data is corrupt”。使用hexdump打开wr886nv6.bin,定位到0xc1240处,如下所示。发现0xc120更像是处于lzma压缩数据的中间,而不像是lzma压缩数据的结尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ hexdump -s 0xc1100 -n 512 -C wr886nv6.b
000c1100 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
000c1200 4d 49 4e 49 46 53 00 00 00 00 00 00 00 00 00 00 |MINIFS..........|
000c1210 00 00 00 02 00 00 00 86 00 01 f4 00 00 07 55 7c |..............U||
000c1220 92 1c 64 4e ae 88 d9 d2 2d b4 48 ce 5c eb 69 51 |..dN....-.H.\.iQ|
000c1230 00 00 04 8d 00 00 00 20 00 00 06 c3 00 00 00 00 |....... ........|
000c1240 5a 00 00 80 00 c3 06 00 00 00 00 00 00 00 16 e9 |Z...............|
000c1250 0c 89 39 ad 0e c6 50 fb 60 3c ae 25 25 14 4e 75 |..9...P.`<.%%.Nu|
000c1260 25 2b ba d7 8c 1c 83 9e 9c d8 85 63 21 54 28 e1 |%+.........c!T(.|
000c1270 37 82 ac 9b 67 de 30 9f 31 9d a1 cc f8 9f 48 35 |7...g.0.1.....H5|
000c1280 95 d1 36 f2 6a 08 8c 7c 3f 20 25 b2 8c d0 62 4e |..6.j..|? %...bN|
000c1290 e6 3e 8d 3b 41 f4 ff b5 5c 7f e0 73 6c f4 a0 03 |.>.;A...\..sl...|

从偏移0xc1240处往上寻找偏移0xa200处压缩数据的结尾,发现可能是在0xc04b1处,如下。

1
2
3
4
5
6
$ hexdump -s 0xc04a0 -n 512 -C wr886nv6.bin 
000c04a0 03 0b ba 40 fb 20 27 05 c2 c4 64 d9 fe 98 78 de |...@. '...d...x.|
000c04b0 be 68 ff ff ff ff ff ff ff ff ff ff ff ff ff ff |.h..............|
000c04c0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
000c06a0

dd命令中的count参数进行修改后,即可成功提取并解压。

1
$dd if=wr886nv6.bin of=a200.lzma bs=1 skip=41472 count=746162 # 746162=0xc04b1-0xa200+1

说明:也可以运行命令binwalk -e wr886nv6.bin,解压目录中的A200文件与通过上述方法得到的一致。

再次利用binwalk对得到的a200文件进行分析,如下。猜测这个文件很有可能就是路由器所运行的系统文件,版本为5.5.1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ binwalk a200 

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
1846404 0x1C2C84 Certificate in DER format (x509 v3), header length: 4, sequence length: 4
1853692 0x1C48FC Certificate in DER format (x509 v3), header length: 4, sequence length: 4
1898688 0x1CF8C0 VxWorks operating system version "5.5.1" , compiled: "Sep 20 2017, 09:16:35"
1967556 0x1E05C4 Copyright string: "Copyright(C) 2001-2011 by TP-LINK TECHNOLOGIES CO., LTD."
1997244 0x1E79BC VxWorks WIND kernel version "2.6"
2042360 0x1F29F8 HTML document header
2042425 0x1F2A39 HTML document footer
2062192 0x1F7770 PEM certificate
2062248 0x1F77A8 PEM RSA private key
2071532 0x1F9BEC Base64 standard index table
2106544 0x2024B0 CRC32 polynomial table, big endian
2107568 0x2028B0 CRC32 polynomial table, big endian
2108592 0x202CB0 CRC32 polynomial table, big endian
2109616 0x2030B0 CRC32 polynomial table, big endian
2130316 0x20818C XML document, version: "1.0"
2149736 0x20CD68 SHA256 hash constants, big endian
2247821 0x224C8D StuffIt Deluxe Segment (data): f
2247852 0x224CAC StuffIt Deluxe Segment (data): fError
2247933 0x224CFD StuffIt Deluxe Segment (data): f

使用IDA Pro分析

由于IDA Pro无法识别该文件,提示为”Binary file”,因此需要确定CPU的架构及加载基址。在”uImage header”部分已经有一些信息,如下,可以看到CPU架构为MIPS

1
2
3
4
5
6
7
$ binwalk wr886nv6.bin 

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
12656 0x3170 U-Boot version string, "U-Boot 1.1.4 (May 8 2016 - 07:42:47)"
12704 0x31A0 CRC32 polynomial table, big endian
13932 0x366C uImage header, header size: 64 bytes, header CRC: 0x773178DD, created: 2016-05-08 14:42:48, image size: 20788 bytes, Data Address: 0x80010000, Entry Point: 0x80010000, data CRC: 0x983BDABA, OS: Linux, CPU: MIPS, image type: Firmware Image, compression type: lzma, image name: "u-boot image"

在uImage header中有一个Entry Point地址0x80010000,这个地址可能是uBoot程序的加载基址,而不是a200文件的加载基址。采用该地址作为加载基址,虽然也能识别出2000多个函数,但在后续导入符号表时会对不上。

也可以使用binwalk命令来查看CPU的架构,如下。可以看到,CPU架构为MIPS,同时为大端格式。

1
2
3
4
5
$ binwalk -Y a200 

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 MIPS executable code, 32/64-bit, big endian, at least 1013 valid instructions

在对偏移0xa200之前的一些数据进行分析时,可以看到一个疑似uImage header的数据段,其中有两处地址指向了0x80001000,这个地址也和devttys0博客中提到的加载基址相同,因此尝试使用该地址作为文件的加载地址。

在选择MIPS大端处理器以及设置加载基址后,IDA Pro的初步分析结果如下,可以看到共识别出6149个函数。

在对该文件进行分析时,发现IDA Pro中的ImportsExports都是空的,可能是没有导入符号表的缘故。

符号表导入

使用binwalk直接提取该固件,在提取后的目录中搜索包含VxWorks中的某个关键函数如bzero的文件,如下。

1
2
$ grep -r bzero .
Binary file ./C2E3A matches

通过查看该文件,发现其中包含大量的VxWorks关键函数名,猜测可能是独立的符号表。在进行简单分析后,发现该文件存在比较明显的特征,如下。

根据这个符号文件的特征,编写idapython脚本来对文件的符号进行修复,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import idautils
import idc
import idaapi

symfile_path = './C2E3A'
symbols_table_start = 8
strings_table_start = 0x9d00

with open(symfile_path, 'rb') as f:
symfile_contents = f.read()

symbols_table = symfile_contents[symbols_table_start:strings_table_start]
strings_table = symfile_contents[strings_table_start:]

def get_string_by_offset(offset):
index = 0
while True:
if strings_table[offset+index] != '\x00':
index += 1
else:
break
return strings_table[offset:offset+index]


def get_symbols_metadata():
symbols = []
for offset in xrange(0, len(symbols_table),8):
symbol_item = symbols_table[offset:offset+8]
flag = symbol_item[0]
string_offset = int(symbol_item[1:4].encode('hex'), 16)
string_name = get_string_by_offset(string_offset)
target_address = int(symbol_item[-4:].encode('hex'), 16)
symbols.append((flag, string_name, target_address))
return symbols


def add_symbols(symbols_meta_data):
for flag, string_name, target_address in symbols_meta_data:
idc.MakeName(target_address, string_name)
if flag == '\x54':
idc.MakeCode(target_address)
idc.MakeFunction(target_address)


if __name__ == "__main__":
symbols_metadata = get_symbols_metadata()
add_symbols(symbols_metadata)

修复完成后IDA Pro中的函数列表如下所示。

导入符号之后,再对该文件进行分析就更方便了。

说明:直接使用binwalk提取固件,在解压得到的文件中,绝大部分都是文本文件,包括脚本、图片等,只有3个文件为二进制文件,其中36AC为uBoot文件,A200为系统运行文件,C2E3A为符号文件。

相关链接

附件下载

固件文件及脚本