openwrt/scripts/moxa-encode-fw.py
Maximilian Martin 906e2a1b99 ath79: Add support for MOXA AWK-1137C
Device specifications:
======================

* Qualcomm/Atheros AR9344
* 128 MB of RAM
* 16 MB of SPI NOR flash
* 2x 10/100 Mbps Ethernet
* 2T2R 2.4/5 GHz Wi-Fi
* 4x GPIO-LEDs (1x wifi, 2x ethernet, 1x power)
* 1x GPIO-button (reset)
* 2x fast ethernet
  - lan1
    + builtin switch port 1
    + used as WAN interface
  - lan2
    + builtin switch port 2
    + used as LAN interface
* 9-30V DC
* external antennas

Flashing instructions:
======================

Log in to https://192.168.127.253/
   Username: admin
   Password: moxa

Open Maintenance > Firmware Upgrade and install the factory image.

Serial console access:
======================

Connect a RS232-USB converter to the maintenance port.
   Pinout: (reset button left) [GND] [NC] [RX] [TX]

Firmware Recovery:
==================

When the WLAN and SYS LEDs are flashing, the device is in recovery mode.

Serial console access is required to proceed with recovery.

Download the original image from MOXA and rename it to 'awk-1137c.rom'.
Set up a TFTP server at 192.168.127.1 and connect to a lan port.

Follow the instructions on the serial console to start the recovery.

Signed-off-by: Maximilian Martin <mm@simonwunderlich.de>
2023-06-25 12:59:26 +02:00

110 lines
3.5 KiB
Python
Executable File

#! /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()