forked from johnwaynehacker/rwd-xray
-
Notifications
You must be signed in to change notification settings - Fork 37
/
rwd-xray.py
executable file
·133 lines (115 loc) · 4.2 KB
/
rwd-xray.py
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python2.7
import os
import sys
import struct
import gzip
import binascii
import operator
import itertools
import importlib
def get_checksum(data):
result = -sum(map(ord, data))
return chr(result & 0xFF)
def write_firmware(data, file_name):
with open(file_name, 'wb') as o:
o.write(data)
print('firmware: {}'.format(file_name))
def read_file(fn):
f_name, f_ext = os.path.splitext(fn)
f_base = os.path.basename(f_name)
open_fn = open
if f_ext == ".gz":
open_fn = gzip.open
f_name, f_ext = os.path.splitext(f_name)
with open_fn(fn, 'rb') as f:
f_data = f.read()
return f_data
def get_part_number_prefix(fn, short=False):
f_name, f_ext = os.path.splitext(fn)
f_base = os.path.basename(f_name)
part_num = f_base.replace('-','').replace('_', '')
prefix = part_num[0:5] + '-' + part_num[5:8]
if not short:
prefix += '-' + part_num[8:12]
return prefix
def main():
f_name = sys.argv[1]
f_dir = os.path.dirname(f_name)
f_base = os.path.basename(f_name).split('.')[0]
f_raw = read_file(f_name)
f_type = "x" + binascii.b2a_hex(f_raw[0])
f_module = importlib.import_module("format.{}".format(f_type))
f_class = getattr(f_module, f_type)
fw = f_class(f_raw)
print(fw)
# write out encrypted firmware
fenc_name = os.path.join(f_dir, f_base + '.enc')
with open(fenc_name, 'wb+') as fenc:
for fe in fw.firmware_encrypted:
fenc.write(fe)
# attempt to decrypt firmware (validate by searching for part number in decrypted bytes)
part_number_prefix = get_part_number_prefix(f_name)
firmware_candidates = fw.decrypt(part_number_prefix)
if len(firmware_candidates) == 0:
# try with a shorter part number
print('failed on long part number, trying truncated part number ...')
part_number_prefix = get_part_number_prefix(f_name, short=True)
firmware_candidates = fw.decrypt(part_number_prefix)
if len(firmware_candidates) == 0:
print("decryption failed!")
print("(could not find a cipher that results in the part number being in the data)")
exit(1)
checksums = {
"39990-TV9-A910": [
(0x01f1e, 0x07fff),
(0x08000, 0x225ff),
(0x23200, 0x271ff),
(0x27200, 0x295ff),
],
}
if len(firmware_candidates) > 1:
print("multiple sets of keys resulted in data containing the part number")
firmware_good = list()
idx = 0
for fc in firmware_candidates:
# concat all address blocks to allow checksum validation using memory addresses
firmware = ''
for block in xrange(len(fc)):
start = fw.firmware_blocks[block]["start"]
# fill gaps with \x00
if len(firmware) < start:
firmware += '\x00' * (start-len(firmware))
firmware += fc[block]
# validate known checksums
if f_base in checksums.keys():
print("firmware[{}] checksums:".format(idx))
match = True
for start, end in checksums[f_base]:
sum = ord(get_checksum(firmware[start:end]))
chk = ord(firmware[end])
print("{} {} {}".format(hex(chk), "=" if chk == sum else "!=", hex(sum)))
if sum != chk:
match = False
if match:
print("checksums good!")
firmware_good.append(firmware)
else:
print("checksums bad!")
else:
# no checksums so assume good
firmware_good.append(firmware)
idx += 1
# sometimes more than one set of keys will result in the part number being found
# hopefully the checksums narrowed it down to a single candidate
if len(firmware_good) > 1:
print("which firmware file is correct? who knows!")
idx = 1
# write out decrypted firmware files
for f_data in firmware_good:
start_addr = fw.firmware_blocks[0]["start"]
f_addr = hex(start_addr)
f_out = os.path.join(f_dir, f_base + '.' + f_addr + '.bin')
write_firmware(f_data[start_addr:], f_out)
idx += 1
if __name__== "__main__":
main()