From 0b78817b816286c8246b2feff4fdaed9a61bd634 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 9 Nov 2017 17:46:30 +1100 Subject: [PATCH] Tools: added apj_tool.py used to change embedded parameters in firmware --- Tools/scripts/apj_tool.py | 231 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100755 Tools/scripts/apj_tool.py diff --git a/Tools/scripts/apj_tool.py b/Tools/scripts/apj_tool.py new file mode 100755 index 0000000000..3f94f03287 --- /dev/null +++ b/Tools/scripts/apj_tool.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python +''' +tool to manipulate ArduPilot firmware files, changing default parameters +''' + +import os, sys, struct, json, base64, zlib, hashlib + +import argparse + +class embedded_defaults(object): + '''class to manipulate embedded defaults in a firmware''' + def __init__(self, filename): + self.filename = filename + self.offset = 0 + self.extension = os.path.splitext(filename)[1] + if self.extension.lower() in ['.apj', '.px4']: + self.load_apj() + elif self.extension.lower() in ['.abin']: + self.load_abin() + else: + self.load_binary() + + def load_binary(self): + '''load firmware from binary file''' + f = open(self.filename,'r') + self.firmware = f.read() + f.close() + print("Loaded binary file of length %u" % len(self.firmware)) + + def load_abin(self): + '''load firmware from abin file''' + f = open(self.filename,'r') + self.headers = [] + while True: + line = f.readline().rstrip() + if line == '--': + break + self.headers.append(line) + if len(self.headers) > 50: + print("Error: too many abin headers") + sys.exit(1) + self.firmware = f.read() + f.close() + print("Loaded abin file of length %u" % len(self.firmware)) + + def load_apj(self): + '''load firmware from a json apj or px4 file''' + f = open(self.filename,'r') + self.fw_json = json.load(f) + f.close() + self.firmware = zlib.decompress(base64.b64decode(self.fw_json['image'])) + print("Loaded apj file of length %u" % len(self.firmware)) + + def save_binary(self): + '''save binary file''' + f = open(self.filename, 'w') + f.write(self.firmware) + f.close() + print("Saved binary of length %u" % len(self.firmware)) + + def save_apj(self): + '''save apj file''' + self.fw_json['image'] = base64.b64encode(zlib.compress(self.firmware, 9)) + f = open(self.filename,'w') + json.dump(self.fw_json,f,indent=4) + f.truncate() + f.close() + print("Saved apj of length %u" % len(self.firmware)) + + def save_abin(self): + '''save abin file''' + f = open(self.filename,'w') + for i in range(len(self.headers)): + line = self.headers[i] + if line.startswith('MD5: '): + h = hashlib.new('md5') + h.update(self.firmware) + f.write('MD5: %s\n' % h.hexdigest()) + else: + f.write(line+'\n') + f.write('--\n') + f.write(self.firmware) + f.close() + print("Saved abin of length %u" % len(self.firmware)) + + def find(self): + '''find defaults in firmware''' + # these are the magic headers from AP_Param.cpp + magic_str = "PARMDEF" + param_magic = [ 0x55, 0x37, 0xf4, 0xa0, 0x38, 0x5d, 0x48, 0x5b ] + while True: + i = self.firmware[self.offset:].find(magic_str) + if i == -1: + return None + matched = True + for j in range(len(param_magic)): + if ord(self.firmware[self.offset+i+j+8]) != param_magic[j]: + matched = False + break + if not matched: + self.offset += i+8 + continue + self.offset += i + self.max_len, self.length = struct.unpack(" self.max_len: + print("Error: Length %u larger than maximum %u" % (length, self.max_len)) + sys.exit(1) + new_fw = self.firmware[:self.offset+18] + new_fw += struct.pack("