38 lines
1.7 KiB
Python
38 lines
1.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Heuristic function-start table for the SotS exe (no symbols): scan .text for MSVC prologues
|
|
and call targets. Output: funcs.txt with one 'ghidra_addr' per line, sorted."""
|
|
import struct, sys
|
|
data = open(sys.argv[1], 'rb').read()
|
|
pe = struct.unpack_from('<I', data, 0x3c)[0]
|
|
nsec = struct.unpack_from('<H', data, pe + 6)[0]
|
|
optsz = struct.unpack_from('<H', data, pe + 20)[0]
|
|
imgbase = struct.unpack_from('<I', data, pe + 0x34)[0]
|
|
secs = []
|
|
for i in range(nsec):
|
|
o = pe + 24 + optsz + i * 40
|
|
name = data[o:o+8].rstrip(b'\0').decode(errors='replace')
|
|
vsz, va, rsz, rp = struct.unpack_from('<IIII', data, o + 8)
|
|
secs.append((name, va, vsz, rp, rsz))
|
|
text = [s for s in secs if s[0] == '.text'][0]
|
|
_, tva, tvsz, trp, trsz = text
|
|
lo, hi = imgbase + tva, imgbase + tva + trsz
|
|
code = data[trp:trp+trsz]
|
|
starts = set()
|
|
# call rel32 targets inside .text
|
|
for i in range(len(code) - 5):
|
|
if code[i] == 0xE8:
|
|
rel = struct.unpack_from('<i', code, i + 1)[0]
|
|
tgt = lo + i + 5 + rel
|
|
if lo <= tgt < hi:
|
|
starts.add(tgt)
|
|
# prologues: push ebp; mov ebp,esp (55 8B EC) preceded by ret/int3/nop/padding
|
|
for i in range(1, len(code) - 3):
|
|
if code[i] == 0x55 and code[i+1] == 0x8B and code[i+2] == 0xEC and code[i-1] in (0xC3, 0xCC, 0x90, 0xC2, 0x00) :
|
|
starts.add(lo + i)
|
|
# also CC-padded starts of any kind: prev byte CC and this byte not CC
|
|
if code[i-1] == 0xCC and code[i] != 0xCC and code[i] in (0x55, 0x53, 0x56, 0x57, 0x83, 0x81, 0x8B, 0x6A, 0x68, 0xB8, 0x33, 0xE9, 0xA1, 0x51, 0x52):
|
|
starts.add(lo + i)
|
|
with open('funcs.txt', 'w') as f:
|
|
for a in sorted(starts):
|
|
f.write('%08x\n' % a)
|
|
print('imgbase %x text %x-%x funcs %d' % (imgbase, lo, hi, len(starts)))
|