-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfw_tool
142 lines (123 loc) · 4.04 KB
/
fw_tool
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
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
# does things with an Airconsole TFTP recovery image
# Thanks to jn for doing all of the IDA magic!
import struct
import binascii
import sys
import datetime
import json
import xtea
class AirConsoleFirmwareImage():
iv = b'AirConso' # well…
def __init__(self):
self.xtea = None
def data_decrypt(self):
if self.xtea == None:
self.xtea_key()
self.data = self.xtea.xtea_cbc_decrypt(self.image_data[64:])
def image_load(self, filename):
"""loads an image file"""
self.image_data = bytearray(open(filename, "rb").read())
self.header_unpack()
self.data_decrypt()
def data_encrypt(self):
if self.xtea == None:
self.xtea_key()
return self.xtea.xtea_cbc_encrypt(self.data)
def image_save(self, filename):
"""saves an image file"""
fd = open(filename, "wb")
fd.write(self.header_pack())
fd.write(self.data_encrypt())
fd.close()
def data_load(self, filename):
"""loads the unencrypted content of an image file"""
fd = open(filename, "rb")
self.data = fd.read()
fd.close()
def data_save(self, filename):
"""saves the unencrypted content of an image file"""
fd = open(filename, "wb")
fd.write(self.data)
fd.close()
def header_load(self, filename):
"""loads an unencrypted copy of the image file header"""
pass
def header_save(self, filename):
"""saves an unencrypted copy of the image file header"""
fd = open(filename, 'wb')
for elem in self.header:
print(elem)
print(json.dumps(struct))
def calculate_crc32_header(self):
h = self.header_pack()
h = self.header_replace(h)
return binascii.crc32(h)
def calculate_crc32_data(self):
return binascii.crc32(self.data)
def header_prettyprint(self):
print("File length: \nFrom header: {} byte \n Calculated: {} byte ".format(self.header['file_length'], len(self.data)))
print("Header CRC32:\nFrom header: 0x{:08x}\n Calculated: 0x{:08x}".format(self.header['header_crc32'], self.calculate_crc32_header()))
print("Data CRC32: \nFrom header: 0x{:08x}\n Calculated: 0x{:08x}".format(self.header['data_crc32'], self.calculate_crc32_data()))
print("Timestamp: {}".format(datetime.datetime.fromtimestamp(self.header['timestamp'])))
print("XTEA key: 0x{0[0]:08X} 0x{0[1]:08X} 0x{0[2]:08X} 0x{0[3]:08X}".format(self.xtea_key()))
def header_unpack(self):
s = struct.unpack(">4sIII4s4sI4s32s", self.image_data[:64])
d = {
'magic': s[0],
'fw_name': s[8],
'file_length': s[3],
'header_crc32': s[1],
'data_crc32': s[6],
'timestamp': s[2],
'unknown_0': s[4],
'unknown_1': s[5],
'unknown_2': s[7]
}
self.header = d
def header_pack(self):
h = struct.pack(">4sIII4s4sI4s32s",
self.header['magic'],
self.header['header_crc32'],
self.header['timestamp'],
self.header['file_length'],
self.header['unknown_0'],
self.header['unknown_1'],
self.header['data_crc32'],
self.header['unknown_2'],
self.header['fw_name']
)
return h
# not sure if this is actually needed - the software implements this function tho
def header_byteswap(self):
s = struct.unpack("<IIIIIII36s", self.header)
out = struct.pack(">IIIIIII36s", s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
return out
# does some replacement for the crc32(header) calculation
def header_replace(self, header):
header = bytearray(header)
header[0] = 0x27
header[1] = 0x05
header[2] = 0x19
header[3] = 0x56
header[4] = 0
header[5] = 0
header[6] = 0
header[7] = 0
return header
# gets the components of the encryption key out of the header struct
def xtea_key(self):
key = (self.header['header_crc32'], self.header['timestamp'], self.header['file_length'], self.header['data_crc32'])
if self.xtea == None:
self.xtea = xtea.XTEA(key, self.iv)
return key
if __name__ == '__main__':
if len(sys.argv) == 1:
print("Usage: {} [encrypted image]".format(sys.argv[0]))
exit(0)
fwimg = AirConsoleFirmwareImage()
fwimg.image_load(sys.argv[1])
fwimg.header_prettyprint()
fwimg.data_save("{}.decrypted".format(sys.argv[1]))
fwimg.data_load("{}.decrypted".format(sys.argv[1]))
fwimg.image_save("{}.encrypted".format(sys.argv[1]))