59 lines
1.8 KiB
Python
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()
|