80 lines
2.7 KiB
Python
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])
|