"""Bounded static x64 member-offset evidence; no process access or invocation."""
import argparse
import bisect
import json
import struct
from pathlib import Path
import pefile
from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_OP_MEM

parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('displacements',nargs='+',type=lambda value:int(value,0))
parser.add_argument('--minimum',type=lambda value:int(value,0),required=True)
parser.add_argument('--maximum',type=lambda value:int(value,0),required=True)
parser.add_argument('--output',type=Path,required=True)
parser.add_argument('--image',type=Path,default=Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
args=parser.parse_args()
image=args.image
pe=pefile.PE(str(image),fast_load=True)
pe.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXCEPTION']])
disassembler=Cs(CS_ARCH_X86,CS_MODE_64);disassembler.detail=True
results=[]
for entry in pe.DIRECTORY_ENTRY_EXCEPTION:
    start,end=entry.struct.BeginAddress,entry.struct.EndAddress
    if not args.minimum<=start<args.maximum:continue
    data=pe.get_data(start,end-start)
    if not any(struct.pack('<i',offset) in data for offset in args.displacements):continue
    for instruction in disassembler.disasm(data,start):
        for operand in instruction.operands:
            if operand.type==CS_OP_MEM and operand.mem.disp in args.displacements:
                results.append(dict(range=hex(start),rva=hex(instruction.address),text=instruction.mnemonic+' '+instruction.op_str,bytes=instruction.bytes.hex()))
output=dict(scope='Offline instruction operands only',image=image.name,memberOffsets=[hex(value) for value in args.displacements],references=results)
args.output.write_text(json.dumps(output,indent=2),encoding='utf-8')
print(json.dumps(output,indent=2))
