Files
dragon-hd/tools/pe_refs.py

80 lines
2.7 KiB
Python

import struct, sys
from capstone import *
exe = r"F:\steam\steamapps\common\The I of the Dragon\TheIOfTheDragon.exe"
data = open(exe, "rb").read()
# --- parse PE ---
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
assert data[e_lfanew:e_lfanew+4] == b"PE\x00\x00", "not PE"
coff = e_lfanew + 4
machine, nsec, tstamp, symptr, nsym, optsize, chars = struct.unpack_from("<HHIIIHH", data, coff)
opt = coff + 20
magic = struct.unpack_from("<H", data, opt)[0]
print("machine=%04x sections=%d magic=%04x" % (machine, nsec, magic))
# image base
imagebase = struct.unpack_from("<I", data, opt+28)[0]
print("imagebase=%08x" % imagebase)
sec_off = opt + optsize
sections = []
for i in range(nsec):
o = sec_off + i*40
name = data[o:o+8].rstrip(b"\x00").decode("latin1")
vsize, vaddr, rawsize, rawptr = struct.unpack_from("<IIII", data, o+8)
sections.append((name, vaddr, vsize, rawptr, rawsize))
print("sec %-8s VA=%08x VS=%08x RAW=%08x RS=%08x" % (name, vaddr, vsize, rawptr, rawsize))
def off2va(off):
for name, va, vs, rp, rs in sections:
if rp <= off < rp+rs:
return va + (off-rp)
return None
def va2off(va):
for name, sva, vs, rp, rs in sections:
if sva <= va < sva+max(vs, rs):
return rp + (va-sva)
return None
# find strings of interest
targets = [b"Data\\Misc\\Fonts.dat", b"Data\\Misc\\Strings.dat", b"Data\\Misc\\UnitDescription.dat",
b"Data\\Misc\\AIDescription.dat", b"UI\\Overhead\\Shrift_gb.tga"]
str_vas = {}
for t in targets:
idx = data.find(t)
while idx != -1:
va = off2va(idx)
print(f"str {t!r} at off {idx:#x} VA {va:#x}")
str_vas[t] = va
idx = data.find(t, idx+1)
# text section
text = next(s for s in sections if s[0] == ".text")
tname, tva, tvs, trp, trs = text
code = data[trp:trp+trs]
md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True
# scan for instructions whose operand references target VAs (or a nearby data VA block)
want = set(v for v in str_vas.values() if v)
# also collect a window around the font string table maybe
def scan_for(refs, label):
hits = []
for ins in md.disasm(code, tva):
for op in ins.operands:
if op.type == CS_OP_IMM and op.imm in refs:
hits.append(ins)
print(f"--- refs to {label}: {len(hits)} ---")
for ins in hits[:20]:
print(f" {ins.address:08x}: {ins.mnemonic} {ins.op_str}")
scan_for(want, "target strings")
# Print data around the string table (Strings.dat / Fonts.dat region) to see structure
for t in [b"Data\\Misc\\Strings.dat"]:
if t in str_vas:
va = str_vas[t]
off = va2off(va)
print("=== dump around", t, "===")
print(data[off-64:off+320])