"""Bounded instruction dataflow candidates; no process read or invocation."""
import bisect,json,struct
from pathlib import Path
import pefile
from capstone import Cs,CS_ARCH_X86,CS_MODE_64,CS_OP_MEM,CS_OP_REG

image=Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe')
pe=pefile.PE(str(image),fast_load=True);pe.parse_data_directories(directories=[3])
entries=sorted((e.struct.BeginAddress,e.struct.EndAddress,e.struct.UnwindData) for e in pe.DIRECTORY_ENTRY_EXCEPTION)
starts=[e[0] for e in entries]
def parent(rva):
    index=bisect.bisect_right(starts,rva)-1
    entry=entries[index]
    for _ in range(16):
        header=pe.get_data(entry[2],4)
        if not header[0]>>3&4:return hex(entry[0])
        entry=struct.unpack('<III',pe.get_data(entry[2]+4+((header[2]+1)&~1)*2,12))
    raise ValueError('Unwind bound')
dis=Cs(CS_ARCH_X86,CS_MODE_64);dis.detail=True
results=[]
for start,end,_ in entries:
    if not 0x3A00000<=start<0x3A50000:continue
    data=pe.get_data(start,end-start)
    if b'\x50' not in data:continue
    instructions=list(dis.disasm(data,start))
    for index,ins in enumerate(instructions):
        ops=ins.operands
        if ins.mnemonic!='mov' or len(ops)!=2 or ops[0].type!=CS_OP_REG or ops[1].type!=CS_OP_MEM or ops[1].mem.disp!=0x50:continue
        reg=ops[0].reg
        for later in instructions[index+1:index+20]:
            for op in later.operands:
                if op.type==CS_OP_MEM and op.mem.base==reg and op.mem.disp in (0x20,0x28,0x30):
                    results.append(dict(parent=parent(ins.address),configLoad=hex(ins.address),candidate=hex(later.address),
                        field=hex(op.mem.disp),context=[hex(row.address)+' '+row.mnemonic+' '+row.op_str for row in instructions[max(0,index-2):min(len(instructions),index+20)]]))
            if len(later.operands)>0 and later.operands[0].type==CS_OP_REG and later.operands[0].reg==reg and later.mnemonic not in ('cmp','test'):break
            if later.mnemonic in ('ret','jmp'):break
out=Path('artifacts/galaxy-query-research/power-outer-sources/background-config-consumers.json')
out.write_text(json.dumps(dict(scope='candidate dataflow only; not semantic closure',minimum='0x3A00000',maximum='0x3A50000',results=results),indent=2))
print(json.dumps([dict(parent=row['parent'],field=row['field'],candidate=row['candidate']) for row in results],indent=2))
