1 #! /usr/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0-or-later
7 from binascii
import crc32
8 from dataclasses
import dataclass
9 from itertools
import cycle
10 from typing
import List
13 def xor(data
: bytes
) -> bytes
:
14 passphrase
= "Seek AGREEMENT for the date of completion.\0"
15 pw
= cycle(bytearray(passphrase
.encode('ascii')))
16 return bytearray(b ^
next(pw
) for b
in data
)
19 def add_fw_header(data
: bytes
, magic
: int, hwid
: int, build_id
: int,
20 offsets
: List
[int]) -> bytes
:
23 unknown_3
= 0x00000000
24 unknown_4
= 0x01000000
25 file_crc
= crc(data
, 0)
27 header_struct
= struct
.Struct('>QIBBHIIIIII' + 'I' * len(offsets
))
28 header_size
= header_struct
.size
29 file_size
= header_size
+ len(data
)
31 header_offsets
= map(lambda x
: x
+ header_size
, offsets
)
33 header_data
= header_struct
.pack(magic
, file_size
, unknown_1
, len(offsets
),
34 unknown_2
, hwid
, build_id
, unknown_3
,
35 build_id
, unknown_4
, *header_offsets
,
37 return header_data
+ data
40 def add_file_header(data
: bytes
, filename
: str, build_id
: int) -> bytes
:
43 file_crc
= crc(data
, 0)
45 header_struct
= struct
.Struct(">16sIIIII")
46 file_size
= header_struct
.size
+ len(data
)
48 header_data
= header_struct
.pack(filename
.encode('ascii'), file_size
,
49 unknown1
, build_id
, unknown2
, file_crc
)
50 return header_data
+ data
53 def crc(data
: bytes
, init_val
: int) -> int:
54 return 0xffffffff ^
(crc32(data
, 0xffffffff ^ init_val
))
65 Partition(name
='kernel', size
=2048 * 1024),
66 Partition(name
='root', size
=9216 * 1024),
67 Partition(name
='userdisk', size
=3076 * 1024),
70 parser
= argparse
.ArgumentParser(prog
='moxa-encode-fw',
71 description
='MOXA IW firmware encoder')
72 parser
.add_argument('-i', '--input', required
=True, type=str, help='Firmware file')
73 parser
.add_argument('-o', '--output', required
=True, type=str, help="Output path for encoded firmware file")
74 parser
.add_argument('-m', '--magic', required
=True, type=lambda x
: int(x
,0), help="Magic for firmware header")
75 parser
.add_argument('-d', '--hwid', required
=True, type=lambda x
: int(x
,0), help="Hardware id of device")
76 parser
.add_argument('-b', '--buildid', required
=True, type=lambda x
: int(x
,0), help="Build id of firmware")
77 args
= parser
.parse_args()
79 with
open(args
.input, 'rb') as input_file
:
80 firmware
= bytearray(input_file
.read())
85 firmware_seg
= bytearray()
87 for partition
in partitions
:
88 part_data
= firmware
[pos_input
:pos_input
+ partition
.size
]
90 # just to make sure that no partition is empty
91 if len(part_data
) == 0:
92 part_data
= bytearray([0x00])
94 header
= add_file_header(part_data
, partition
.name
, args
.buildid
)
95 firmware_seg
+= header
97 offsets
.append(pos_output
)
98 pos_input
+= partition
.size
99 pos_output
+= len(header
)
101 moxa_firmware
= add_fw_header(firmware_seg
, args
.magic
, args
.hwid
, args
.buildid
, offsets
)
103 encrypted
= xor(moxa_firmware
)
104 with
open(args
.output
, 'wb') as output_file
:
105 output_file
.write(encrypted
)
108 if __name__
== '__main__':