Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
112 changes: 97 additions & 15 deletions parse-uboot-dump.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,101 @@
#!/usr/bin/env python3
#
# 20241026 original from matt brown, modified by jens heine <binbash@gmx.net>
#
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()