60 lines
1.9 KiB
Python
60 lines
1.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Scan firmware regions for consecutive function-pointer tables.
|
||
|
|
|
||
|
|
Finds runs of >= min_run consecutive little-endian dwords that all fall in a
|
||
|
|
given code range — the shape of MCU command-handler dispatch tables.
|
||
|
|
|
||
|
|
Usage: scan_tables.py <region.bin> <code_lo> <code_hi> [min_run] [tolerance]
|
||
|
|
tolerance = max absolute non-code dwords allowed inside a run (0/NULL counted).
|
||
|
|
"""
|
||
|
|
import struct
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
path = Path(sys.argv[1])
|
||
|
|
lo, hi = int(sys.argv[2], 0), int(sys.argv[3], 0)
|
||
|
|
min_run = int(sys.argv[4]) if len(sys.argv) > 4 else 8
|
||
|
|
tol = int(sys.argv[5]) if len(sys.argv) > 5 else 0
|
||
|
|
|
||
|
|
d = path.read_bytes()
|
||
|
|
base = 0
|
||
|
|
# If the region file corresponds to a load address, pass that as lo bound
|
||
|
|
# via argv; region offsets here are file offsets, so also print file offset.
|
||
|
|
words = struct.unpack_from('<%dI' % (len(d) // 4), d)
|
||
|
|
run_start = None
|
||
|
|
run = []
|
||
|
|
tables = []
|
||
|
|
|
||
|
|
|
||
|
|
def flush(end):
|
||
|
|
global run, run_start
|
||
|
|
code = sum(1 for w in run if lo <= w < hi)
|
||
|
|
if len(run) >= min_run and code >= min_run:
|
||
|
|
tables.append((run_start, end, len(run), code))
|
||
|
|
run = []
|
||
|
|
run_start = None
|
||
|
|
|
||
|
|
|
||
|
|
for i, w in enumerate(words):
|
||
|
|
ok = lo <= w < hi or w == 0
|
||
|
|
if ok:
|
||
|
|
if run_start is None:
|
||
|
|
run_start = i * 4
|
||
|
|
run.append(w)
|
||
|
|
else:
|
||
|
|
if run_start is not None and tol:
|
||
|
|
run.append(w) # tolerate, next check trims
|
||
|
|
if sum(1 for x in run[-3:] if not (lo <= x < hi or x == 0)) >= 2:
|
||
|
|
flush(i * 4)
|
||
|
|
else:
|
||
|
|
flush(i * 4)
|
||
|
|
|
||
|
|
for t in tables:
|
||
|
|
start_off, end_off, n, code = t
|
||
|
|
print('table @ file+0x%x..0x%x (%d words, %d code ptrs)'
|
||
|
|
% (start_off, end_off, n, code))
|
||
|
|
vals = words[start_off // 4:end_off // 4]
|
||
|
|
for j, w in enumerate(vals[:64]):
|
||
|
|
mark = '' if lo <= w < hi else ('(0)' if w == 0 else '(?)')
|
||
|
|
print(' [%2d] 0x%08x %s' % (j, w, mark))
|