diff --git a/README.md b/README.md index dd974af..77b3898 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ # firmwaretools a set of scripts and tools for various firmware analysis tasks + +## parse-uboot-dump.py + +parse-uboot-dump.py - Use this tool to parse the picocom output of a uboot memory dump and create a firmware.bin file from it. There is also little endian support (needed for fritz 7141 i.e.). I reccomend to create a binary from the parse-uboot-dump.py with: + +``` +pyinstaller parse-uboot-dump.py --onefile --clean +``` +You can find the static binary in the ./dist folder which I use in the following example. + +Let's say we dump some memory data like this from the uboot prompt and we have captured the output : + +``` +picocom -b 38400 -l -r /dev/ttyUSB0 --logfile `date +"%Y%m%d_%H%M%S"`_picocom.log +... +Eva_AVM >dm 0x90000000 1000 + +0x90000000: 0x40809000 0x40809800 0x401A6000 0x241BFFFE +0x90000010: 0x035BD024 0x3C1BFFBF 0x377BFFFF 0x035BD024 +0x90000020: 0x409A6000 0x40806800 0x24080003 0x40888000 +... +0x90000F60: 0x3C08A861 0x35081604 0x24090001 0xAD090000 +0x90000F70: 0x00000000 0x03E00008 0xBC800000 0x24044000 +0x90000F80: 0x24050010 0x3C068000 0x00C43821 0x00E53823 +0x90000F90: 0xBCC00000 0x14C7FFFE 0x00C53021 0x03E00008 +``` + +``` +parse-uboot-dump -h +Usage: parse-uboot-dump [options] + +Options: + -h, --help show this help message and exit + -i FILE, --infile=FILE + read data from FILE (required) + -o FILE, --outfile=FILE + write binary data to FILE (default: firmware.bin) + -l, --little-endian convert data to little endian (default:big endian) + -f, --force force overwrite existing outfile + -v, --verbose be verbose +``` + +We can now parse the picocom output and convert it to a nice firmware.bin file which has the correct little endian mapping already done: + +``` +parse-uboot-dump -i 20241026_232645_picocom.log -l +``` + +Now you can nicely binwalk through the binary "firmware.bin" or use xxd to analyze the data... + +``` +xxd firmware.bin +00000000: 0090 8040 0098 8040 0060 1a40 feff 1b24 ...@...@.`.@...$ +00000010: 24d0 5b03 bfff 1b3c ffff 7b37 24d0 5b03 $.[....<..{7$.[. +00000020: 0060 9a40 0068 8040 0300 0824 0080 8840 .`.@.h.@...$...@ +... +00000830: ffff 616e 6e65 7800 4200 7573 625f 6d61 ..annex.B.usb_ma +00000840: 6e75 6661 6374 7572 6572 5f6e 616d 6500 nufacturer_name. +00000850: 4156 4d00 7573 625f 7265 7669 7369 6f6e AVM.usb_revision +00000860: 5f69 6400 3078 3030 3030 0075 7362 5f64 _id.0x0000.usb_d +00000870: 6576 6963 655f 6964 0030 7830 3030 3000 evice_id.0x0000. +00000880: 5365 7269 616c 4e75 6d62 6572 0030 3030 SerialNumber.000 +00000890: 3030 3030 3030 3030 3030 3030 3000 5072 0000000000000.Pr +000008a0: 6f64 7563 7449 4400 4672 6974 7a5f 426f oductID.Fritz_Bo +000008b0: 785f 3731 3431 0048 5752 6576 6973 696f x_7141.HWRevisio +000008c0: 6e00 3130 3800 7265 7365 7276 6564 0000 n.108.reserved.. +000008d0: 626c 7565 746f 6f74 6800 0075 7362 5f72 bluetooth..usb_r +... +``` + +Happy coding, Jens diff --git a/parse-uboot-dump.py b/parse-uboot-dump.py index c67ec7f..add3f64 100755 --- a/parse-uboot-dump.py +++ b/parse-uboot-dump.py @@ -1,19 +1,101 @@ #!/usr/bin/env python3 +# +# 20241026 original from matt brown, modified by jens heine +# +import os import re import sys +from optparse import OptionParser +from pathlib import Path -infile = sys.argv[1] -outfile = sys.argv[2] - -i = open(infile,"r") -o = open(outfile,"wb") - -for line in i.readlines(): - line = line.strip() - if re.match(r'^[0-9a-f]{8}:',line): - line = line.split(":") - if len(line) == 2: - line = line[1] - line = line.replace(" ","")[:32] - data = bytes.fromhex(line) - o.write(data) + +def main(): + parser = OptionParser() + parser.add_option("-i", "--infile", dest="infile", + help="read data from FILE (required)", metavar="FILE") + parser.add_option("-o", "--outfile", dest="outfile", + help="write binary data to FILE (default: firmware.bin)", metavar="FILE") + parser.add_option("-l", "--little-endian", dest="little_endian", + help="convert data to little endian (default:big endian)", action="store_true") + parser.add_option("-f", "--force", dest="force", + help="force overwrite existing outfile", action="store_true") + parser.add_option("-v", "--verbose", + action="store_true", dest="verbose", default=False, + help="be verbose") + + (options, args) = parser.parse_args() + + infile = None + if options.infile: + infile = options.infile + + outfile = "firmware.bin" + if options.outfile: + outfile = options.outfile + + if infile is None: + print("Error: Missing argument, try -h for help") + sys.exit(1) + + _little_endian = False + if options.little_endian: + _little_endian = options.little_endian + + _force = False + if options.force: + _force = options.force + + _verbose = False + if options.verbose: + _verbose = options.verbose + + if not _force and Path(outfile).is_file(): + print("Error: outfile '" + str(outfile) + "' already exists.") + answer = input("Overwrite (y/N)? : ") + print() + if answer != 'y': + sys.exit(0) + else: + os.remove(outfile) + + i = open(infile, "r") + o = open(outfile, "wb") + + data_count = 0 + for line in i.readlines(): + line = line.strip() + if re.match(r'^(0x)*[0-9A-Fa-f]{8}:', line): + line = line.split(":") + if len(line) == 2: + line = line[1] + line = line.replace("0x", "") + memory_address_contents = line.split(" ") + for memory_address_content in memory_address_contents: + if len(memory_address_content) == 0: + continue + if _verbose: + try: + decoded = bytes.fromhex(memory_address_content).decode(encoding="ascii") + except Exception as e: + decoded = '..' + print('Offset ' + str(data_count).rjust(8) + ' in : ' + str(memory_address_content) + + ' : ' + decoded) + if _little_endian: + memory_address_content = (memory_address_content[6:8] + memory_address_content[4:6] + + memory_address_content[2:4] + memory_address_content[0:2]) + data = bytes.fromhex(memory_address_content) + if _verbose: + try: + decoded = bytes.fromhex(memory_address_content).decode(encoding="ascii") + except: + decoded = '..' + print('Offset ' + str(data_count).rjust(8) + ' out : ' + str(memory_address_content) + + ' : ' + decoded) + o.write(data) + data_count += len(data) + print() + print(str(data_count) + ' bytes written to ' + outfile) + + +if __name__ == '__main__': + main()