"""Compare only the two bounded CG candidates and their typed operands."""
import hashlib,importlib.util,json,re,struct
from pathlib import Path
from capstone.x86 import X86_OP_IMM,X86_OP_MEM,X86_REG_RIP
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
audit=importlib.util.module_from_spec(spec);spec.loader.exec_module(audit)
old=audit.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=audit.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
assert hashlib.sha256(old.raw).hexdigest()=='6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
assert hashlib.sha256(new.raw).hexdigest()=='ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63'
def printable(image,rva):
try:
b=image.data(rva,256).split(b'\0',1)[0]
return b.decode('ascii') if len(b)>=4 and all(32<=c<=126 for c in b) else None
except:return None
def target(image,rva):
row=dict(rva=hex(rva),section=image.section(rva),text=printable(image,rva),bytes=image.data(rva,24).hex())
if image.section(rva) not in ['.text',None]:
pointers=[]
for at in range(0,24,8):
p=struct.unpack('<Q',image.data(rva+at,8))[0]-image.base
if 0<=p<image.size:pointers.append(dict(offset=at,rva=hex(p),text=printable(image,p)))
row['pointers']=pointers
return row
rows=[]
for before in [0x3433090,0x3440E40]:
refs=audit.references(old,new,before,0x3433030,531)
rows.append(dict(oldCandidate=hex(before),references=[dict(instructionOldRva=hex(at),old=target(old,a),new=target(new,b)) for a,b,_,at in refs]))
result=dict(runtimeNeutral=True,gameCalls=0,gameProcessAccess=False,currentFunction='0x3433030',currentTypedVtable='0x511f9d0',candidates=rows)
callers=[]
for m in re.finditer(rb'\xe8....',old.text_bytes,re.S):
rva=old.text.VirtualAddress+m.start();to=rva+5+struct.unpack_from('<i',m.group(),1)[0]
if to not in [0x3433090,0x3440E40]:continue
fn=old.function(rva)
if not fn:continue
instructions=old.instructions(fn[0],fn[1]-fn[0])
if not any(i.address-old.base==rva and i.mnemonic=='call' for i in instructions):continue
callers.append(dict(callRva=hex(rva),target=hex(to),parent=hex(fn[0]),
window=[dict(rva=hex(i.address-old.base),instruction=i.mnemonic+' '+i.op_str) for i in instructions if rva-70<=i.address-old.base<rva+16]))
result['directCallers']=callers
path=Path('artifacts/galaxy-client-compatibility/community-goal-optin-candidate-comparison.json')
path.write_text(json.dumps(result,indent=2)+'\n',encoding='utf8');print(json.dumps(result,indent=2))
"""Compare only the two bounded CG candidates and their typed operands."""
import hashlib,importlib.util,json,re,struct
from pathlib import Path
from capstone.x86 import X86_OP_IMM,X86_OP_MEM,X86_REG_RIP
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
audit=importlib.util.module_from_spec(spec);spec.loader.exec_module(audit)
old=audit.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=audit.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
assert hashlib.sha256(old.raw).hexdigest()=='6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
assert hashlib.sha256(new.raw).hexdigest()=='ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63'
def printable(image,rva):
try:
b=image.data(rva,256).split(b'\0',1)[0]
return b.decode('ascii') if len(b)>=4 and all(32<=c<=126 for c in b) else None
except:return None
def target(image,rva):
row=dict(rva=hex(rva),section=image.section(rva),text=printable(image,rva),bytes=image.data(rva,24).hex())
if image.section(rva) not in ['.text',None]:
pointers=[]
for at in range(0,24,8):
p=struct.unpack('<Q',image.data(rva+at,8))[0]-image.base
if 0<=p<image.size:pointers.append(dict(offset=at,rva=hex(p),text=printable(image,p)))
row['pointers']=pointers
return row
rows=[]
for before in [0x3433090,0x3440E40]:
refs=audit.references(old,new,before,0x3433030,531)
rows.append(dict(oldCandidate=hex(before),references=[dict(instructionOldRva=hex(at),old=target(old,a),new=target(new,b)) for a,b,_,at in refs]))
result=dict(runtimeNeutral=True,gameCalls=0,gameProcessAccess=False,currentFunction='0x3433030',currentTypedVtable='0x511f9d0',candidates=rows)
callers=[]
for m in re.finditer(rb'\xe8....',old.text_bytes,re.S):
rva=old.text.VirtualAddress+m.start();to=rva+5+struct.unpack_from('<i',m.group(),1)[0]
if to not in [0x3433090,0x3440E40]:continue
fn=old.function(rva)
if not fn:continue
instructions=old.instructions(fn[0],fn[1]-fn[0])
if not any(i.address-old.base==rva and i.mnemonic=='call' for i in instructions):continue
callers.append(dict(callRva=hex(rva),target=hex(to),parent=hex(fn[0]),
window=[dict(rva=hex(i.address-old.base),instruction=i.mnemonic+' '+i.op_str) for i in instructions if rva-70<=i.address-old.base<rva+16]))
result['directCallers']=callers
path=Path('artifacts/galaxy-client-compatibility/community-goal-optin-candidate-comparison.json')
path.write_text(json.dumps(result,indent=2)+'\n',encoding='utf8');print(json.dumps(result,indent=2))