pre cw with probe and a bunch script to read game behavior
This commit is contained in:
1 parent
5120219ae2
commit
da8a32f60d
310 files changed
+219693
-872
No files matched your search
@@ -0,0 +1,65 @@
|
||||
"""Read-only disassembly of an explicit PE RVA range; no process attachment."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import pathlib
|
||||
import struct
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--binary', type=pathlib.Path, required=True)
|
||||
parser.add_argument('--rva', type=lambda s: int(s, 0), required=True)
|
||||
parser.add_argument('--size', type=lambda s: int(s, 0), required=True)
|
||||
parser.add_argument('--output', type=pathlib.Path, required=True)
|
||||
parser.add_argument('--containing-function', action='store_true', help='Use the AMD64 exception-table bounds containing the selected RVA')
|
||||
args = parser.parse_args()
|
||||
if not 0 < args.size <= 65536:
|
||||
raise SystemExit('Select a range of 1..65536 bytes')
|
||||
data = args.binary.read_bytes()
|
||||
if data[:2] != b'MZ':
|
||||
raise SystemExit('Expected PE image')
|
||||
pe = struct.unpack_from('<I', data, 0x3c)[0]
|
||||
if data[pe:pe+4] != b'PE\0\0' or struct.unpack_from('<H', data, pe+4)[0] != 0x8664:
|
||||
raise SystemExit('Expected AMD64 PE image')
|
||||
section_count = struct.unpack_from('<H', data, pe+6)[0]
|
||||
optional_size = struct.unpack_from('<H', data, pe+20)[0]
|
||||
def file_offset(address, length):
|
||||
for i in range(section_count):
|
||||
off = pe+24+optional_size+i*40
|
||||
_, rva, size, raw = struct.unpack_from('<IIII', data, off+8)
|
||||
if rva <= address and address+length <= rva+size and raw+address-rva+length <= len(data):
|
||||
return raw+address-rva
|
||||
raise ValueError('Range is not contained in a raw section')
|
||||
if args.containing_function:
|
||||
if struct.unpack_from('<H', data, pe+24)[0] != 0x20b:
|
||||
raise SystemExit('Expected PE32+ optional header')
|
||||
exception_rva, exception_size = struct.unpack_from('<II', data, pe+24+112+3*8)
|
||||
base = file_offset(exception_rva, exception_size)
|
||||
matches = []
|
||||
for off in range(base, base+exception_size, 12):
|
||||
begin, end, _ = struct.unpack_from('<III', data, off)
|
||||
if begin <= args.rva < end:
|
||||
matches.append((begin, end))
|
||||
if len(matches) != 1 or not 0 < matches[0][1]-matches[0][0] <= 65536:
|
||||
raise SystemExit('No unique bounded exception-table function entry')
|
||||
args.rva, end = matches[0]
|
||||
args.size = end-args.rva
|
||||
for i in range(section_count):
|
||||
off = pe+24+optional_size+i*40
|
||||
_, rva, size, raw = struct.unpack_from('<IIII', data, off+8)
|
||||
if rva <= args.rva and args.rva+args.size <= rva+size:
|
||||
start = raw+args.rva-rva
|
||||
if start+args.size > len(data):
|
||||
raise SystemExit('Truncated section')
|
||||
decoder = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||
scope = 'AMD64 exception-table function bounds' if args.containing_function else 'explicit range; function boundaries unproven'
|
||||
lines = [f'; SHA256 {hashlib.sha256(data).hexdigest()}',
|
||||
f'; RVA {args.rva:#x}, length {args.size:#x}; {scope}']
|
||||
lines += [f'{ins.address:x}: {ins.mnemonic} {ins.op_str}' for ins in decoder.disasm(data[start:start+args.size], args.rva)]
|
||||
args.output.write_text('\n'.join(lines)+'\n', encoding='utf-8')
|
||||
print(f'Wrote {len(lines)-2} instructions to {args.output}')
|
||||
return
|
||||
raise SystemExit('RVA range is not contained in a raw section')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user