Skip to content

Commit 6887f7b

Browse files
author
Chris Lyne
committed
2 parents 20ca854 + df4a94e commit 6887f7b

File tree

5 files changed

+330
-8
lines changed

5 files changed

+330
-8
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ There are three actions:
77
- **show_partitions** - will display all of the partitions found in an image file.
88
- **dump_partition** - will dump the raw bytes of a specified partition into a file.
99
- **create_elf** - reconstruct an ELF file from an 'app' partition (e.g. ota_0).
10+
- **dump_nvs** - will parse a specified NVS partition and dump its contents.
1011

1112
# Setup
1213
`pip install -r requirements.txt`
@@ -28,3 +29,12 @@ Dumps to ble_data.dump
2829
Converts ota_0 partition into ELF. Writes to ota_0.elf
2930

3031
`$ python3 esp32_image_parser.py create_elf espwroom32.bin -partition ota_0 -output ota_0.elf`
32+
33+
## Dump a specific NVS partition
34+
Dumps the nvs partition
35+
36+
`$ python3 esp32_image_parser.py dump_nvs esp32_flashdump.bin -partition nvs`
37+
38+
Dumps the nvs partition as a JSON
39+
40+
`$ python3 esp32_image_parser.py dump_nvs flashdump/esp32_flashdump.bin -partition nvs -nvs_output_type json`

esp32_firmware_reader.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,20 @@ def read_partition_table(fh, verbose=False):
3434
print_verbose(verbose, "reading partition table...")
3535
for i in range(0, 95): # max 95 partitions
3636
magic = fh.read(2)
37-
if(magic[0] != 0xAA or magic[1] != 0x50):
38-
print('Magic bytes are fudged')
37+
# end marker
38+
if(magic == b'\xff\xff'):
39+
data = magic + fh.read(30)
40+
if data == b'\xff'*32:
41+
print_verbose(verbose,"Done")
42+
return partition_table
43+
# md5sum
44+
elif(magic == b'\xeb\xeb'):
45+
data = magic + fh.read(30)
46+
print_verbose(verbose,"MD5sum: ")
47+
print_verbose(verbose,data[16:].hex())
48+
continue
49+
# is partition?
50+
elif(magic[0] != 0xAA or magic[1] != 0x50):
3951
return partition_table
4052

4153
print_verbose(verbose, "entry %d:" % (i))

esp32_image_parser.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#!/usr/bin/env python
22

33
# Convert an ESP 32 OTA partition into an ELF
4-
4+
import sys
5+
import json
56
import os, argparse
67
from makeelf.elf import *
78
from esptool import *
89
from esp32_firmware_reader import *
10+
from read_nvs import *
911

1012
def image_base_name(path):
1113
filename_w_ext = os.path.basename(path)
@@ -133,7 +135,7 @@ def image2elf(filename, output_file, verbose=False):
133135
segments = {
134136
'.flash.rodata' : 'rw',
135137
'.dram0.data' : 'rw',
136-
'.iram0.vectors': 'rx',
138+
'.iram0.vectors': 'rwx',
137139
'.flash.text' : 'rx'
138140
}
139141

@@ -206,12 +208,17 @@ def flash_dump_to_elf(filename, partition):
206208
fh.close()
207209
return part_table
208210

211+
def dump_partition(fh, part_name, offset, size, dump_file):
212+
print("Dumping partition '" + part_name + "' to " + dump_file)
213+
dump_bytes(fh, offset, size, dump_file)
214+
209215
def main():
210216
desc = 'ESP32 Firmware Image Parser Utility'
211217
arg_parser = argparse.ArgumentParser(description=desc)
212-
arg_parser.add_argument('action', choices=['show_partitions', 'dump_partition', 'create_elf'], help='Action to take')
218+
arg_parser.add_argument('action', choices=['show_partitions', 'dump_partition', 'create_elf', 'dump_nvs'], help='Action to take')
213219
arg_parser.add_argument('input', help='Firmware image input file')
214220
arg_parser.add_argument('-output', help='Output file name')
221+
arg_parser.add_argument('-nvs_output_type', help='output type for nvs dump', type=str, choices=["text","json"], default="text")
215222
arg_parser.add_argument('-partition', help='Partition name (e.g. ota_0)')
216223
arg_parser.add_argument('-v', default=False, help='Verbose output', action='store_true')
217224

@@ -226,7 +233,11 @@ def main():
226233
# parse that ish
227234
part_table = read_partition_table(fh, verbose)
228235

229-
if args.action in ['dump_partition', 'create_elf']:
236+
if args.action in ['dump_partition', 'create_elf', 'dump_nvs']:
237+
if (args.partition is None):
238+
print("Need partition name")
239+
return
240+
230241
part_name = args.partition
231242

232243
if args.action == 'dump_partition' and args.output is not None:
@@ -235,10 +246,10 @@ def main():
235246
dump_file = part_name + '_out.bin'
236247

237248
if part_name in part_table:
238-
print("Dumping partition '" + part_name + "' to " + dump_file)
239249
part = part_table[part_name]
240-
dump_bytes(fh, part['offset'], part['size'], dump_file) # dump_file will be written out
241250

251+
if args.action == 'dump_partition':
252+
dump_partition(fh, part_name, part['offset'], part['size'], dump_file)
242253
if args.action == 'create_elf':
243254
# can only generate elf from 'app' partition type
244255
if part['type'] != 0:
@@ -247,9 +258,22 @@ def main():
247258
if args.output is None:
248259
print("Need output file name")
249260
else:
261+
dump_partition(fh, part_name, part['offset'], part['size'], dump_file)
250262
# we have to load from a file
251263
output_file = args.output
252264
image2elf(dump_file, output_file, verbose)
265+
elif args.action == 'dump_nvs':
266+
if part['type'] != 1 or part['subtype'] != 2: # Wifi NVS partition (4 is for encryption key)
267+
print("Uh oh... bad partition type. Can only dump NVS partition type.")
268+
else:
269+
dump_partition(fh, part_name, part['offset'], part['size'], dump_file)
270+
with open(dump_file, 'rb') as fh:
271+
if(args.nvs_output_type != "text"):
272+
sys.stdout = open(os.devnull, 'w') # block print()
273+
pages = read_nvs_pages(fh)
274+
sys.stdout = sys.stdout = sys.__stdout__ # re-enable print()
275+
if(args.nvs_output_type == "json"):
276+
print(json.dumps(pages))
253277
else:
254278
print("Partition '" + part_name + "' not found.")
255279

0 commit comments

Comments
 (0)