46 lines
1.8 KiB
Python
46 lines
1.8 KiB
Python
"""Exported functions of a DungeonSiege2.exe by (decorated) name, from its PE export table.
|
|
|
|
usage: python -I tools/ds2exports.py EXE [SUBSTRING] (lists matching exports with their addresses)
|
|
As a module: exports(exe) -> {decorated name: VA}; find(exe, substring) -> [(name, VA)].
|
|
The exe is not relocated (image base 0x400000), so these addresses are valid in the running game.
|
|
"""
|
|
import struct, sys
|
|
from functools import lru_cache
|
|
|
|
|
|
@lru_cache(maxsize=None)
|
|
def exports(exe):
|
|
d = open(exe, 'rb').read()
|
|
pe = struct.unpack_from('<I', d, 0x3c)[0]
|
|
base = struct.unpack_from('<I', d, pe + 0x34)[0]
|
|
nsec = struct.unpack_from('<H', d, pe + 6)[0]
|
|
opt = struct.unpack_from('<H', d, pe + 20)[0]
|
|
secs = [struct.unpack_from('<8sIIII', d, pe + 24 + opt + i * 40) for i in range(nsec)]
|
|
|
|
def off(rva):
|
|
for _, vsize, va, rsize, raw in secs:
|
|
if va <= rva < va + max(vsize, rsize):
|
|
return raw + rva - va
|
|
raise ValueError('rva %x outside sections' % rva)
|
|
|
|
erva = struct.unpack_from('<I', d, pe + 24 + 96)[0]
|
|
e = off(erva)
|
|
nfun, nnames, afun, anames, aord = struct.unpack_from('<5I', d, e + 20)
|
|
funcs = [struct.unpack_from('<I', d, off(afun) + 4 * i)[0] for i in range(nfun)]
|
|
out = {}
|
|
for i in range(nnames):
|
|
nrva = struct.unpack_from('<I', d, off(anames) + 4 * i)[0]
|
|
ordinal = struct.unpack_from('<H', d, off(aord) + 2 * i)[0]
|
|
o = off(nrva)
|
|
name = d[o:d.index(b'\0', o)].decode('latin1')
|
|
out[name] = base + funcs[ordinal]
|
|
return out
|
|
|
|
|
|
def find(exe, sub):
|
|
return sorted((n, a) for n, a in exports(exe).items() if sub in n)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
for n, a in find(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else ''):
|
|
print('%08X %s' % (a, n))
|