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
|
#! /usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
import argparse
import struct
from binascii import crc32
from dataclasses import dataclass
from itertools import cycle
from typing import List
def xor(data: bytes) -> bytes:
passphrase = "Seek AGREEMENT for the date of completion.\0"
pw = cycle(bytearray(passphrase.encode('ascii')))
return bytearray(b ^ next(pw) for b in data)
def add_fw_header(data: bytes, magic: int, hwid: int, build_id: int,
offsets: List[int]) -> bytes:
unknown_1 = 0x01
unknown_2 = 0x0000
unknown_3 = 0x00000000
unknown_4 = 0x01000000
file_crc = crc(data, 0)
header_struct = struct.Struct('>QIBBHIIIIII' + 'I' * len(offsets))
header_size = header_struct.size
file_size = header_size + len(data)
header_offsets = map(lambda x: x + header_size, offsets)
header_data = header_struct.pack(magic, file_size, unknown_1, len(offsets),
unknown_2, hwid, build_id, unknown_3,
build_id, unknown_4, *header_offsets,
file_crc)
return header_data + data
def add_file_header(data: bytes, filename: str, build_id: int) -> bytes:
unknown1 = 0x01000000
unknown2 = 0x00000000
file_crc = crc(data, 0)
header_struct = struct.Struct(">16sIIIII")
file_size = header_struct.size + len(data)
header_data = header_struct.pack(filename.encode('ascii'), file_size,
unknown1, build_id, unknown2, file_crc)
return header_data + data
def crc(data: bytes, init_val: int) -> int:
return 0xffffffff ^ (crc32(data, 0xffffffff ^ init_val))
@dataclass
class Partition:
name: str
size: int
def main():
partitions = [
Partition(name='kernel', size=2048 * 1024),
Partition(name='root', size=9216 * 1024),
Partition(name='userdisk', size=3076 * 1024),
]
parser = argparse.ArgumentParser(prog='moxa-encode-fw',
description='MOXA IW firmware encoder')
parser.add_argument('-i', '--input', required=True, type=str, help='Firmware file')
parser.add_argument('-o', '--output', required=True, type=str, help="Output path for encoded firmware file")
parser.add_argument('-m', '--magic', required=True, type=lambda x: int(x,0), help="Magic for firmware header")
parser.add_argument('-d', '--hwid', required=True, type=lambda x: int(x,0), help="Hardware id of device")
parser.add_argument('-b', '--buildid', required=True, type=lambda x: int(x,0), help="Build id of firmware")
args = parser.parse_args()
with open(args.input, 'rb') as input_file:
firmware = bytearray(input_file.read())
offsets = []
pos_input = 0
pos_output = 0
firmware_seg = bytearray()
for partition in partitions:
part_data = firmware[pos_input:pos_input + partition.size]
# just to make sure that no partition is empty
if len(part_data) == 0:
part_data = bytearray([0x00])
header = add_file_header(part_data, partition.name, args.buildid)
firmware_seg += header
offsets.append(pos_output)
pos_input += partition.size
pos_output += len(header)
moxa_firmware = add_fw_header(firmware_seg, args.magic, args.hwid, args.buildid, offsets)
encrypted = xor(moxa_firmware)
with open(args.output, 'wb') as output_file:
output_file.write(encrypted)
if __name__ == '__main__':
main()
|