Initial commit: RE tooling and docs for The I of the Dragon localization/HD
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
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])
|
||||
Reference in New Issue
Block a user