Files
lofi-12xt-firmware/ghidra/gen_vtable_csv.py
T
2026-09-30 22:48:06 +02:00

59 lines
1.8 KiB
Python

#!/usr/bin/env python3
"""Offline helper (runs HERE, stdlib-only): cluster vtable candidates.
A vtable = run of >=3 consecutive LE32 words in rodata, each pointing
into the code sect. Output CSV is consumed by the Ghidra-side script.
Usage:
python3 ghidra/gen_vtable_csv.py "Lofi-12XT_v1.5.205/.../Lofi-12 XT.bin" /tmp/vtables-1.5.205.csv
"""
import os
import struct
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'tools'))
from lofi_image import parse_image, find_sect_by_role
def main():
image, out = sys.argv[1], sys.argv[2]
d = open(image, 'rb').read()
info = parse_image(d)
code = find_sect_by_role(info, 'code')
ro = find_sect_by_role(info, 'rodata')
print("code sect%d %08X..%08X" % (code['index'], code['addr'], code['end']))
print("rodata sect%d %08X..%08X" % (ro['index'], ro['addr'], ro['end']))
p = ro['payload']
words = [struct.unpack('<I', p[i:i + 4])[0]
for i in range(0, len(p) - 3, 4)]
is_code_ptr = [code['addr'] <= w < code['end'] for w in words]
runs = []
i = 0
n = len(words)
while i < n:
if is_code_ptr[i]:
j = i
while j < n and is_code_ptr[j]:
j += 1
if j - i >= 3:
runs.append((i * 4, j - i, words[i:j]))
i = j
else:
i += 1
with open(out, 'w') as f:
f.write("rodata_off,load_addr,nentries,entries\n")
for off, cnt, ws in runs:
f.write("%d,%08X,%d,%s\n"
% (off, ro['addr'] + off, cnt,
";".join("%08X" % w for w in ws)))
to_code = sum(is_code_ptr)
print("rodata LE words -> code: %d, vtable runs(>=3): %d -> %s"
% (to_code, len(runs), out))
if __name__ == '__main__':
main()