File size: 6,588 Bytes
ed365c0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #!/usr/bin/env python3
"""Recover native clip filtering and finite trace-selection decision slices."""
import gzip
import itertools
import json
from pathlib import Path
import re
from analyze_chat_crosshair import bits,number
from analyze_engine_control import control_flow,windows
from analyze_qvm import write_json
from analyze_vm_extensions import native_functions,verified
def probe(graph,start,stops,memory,registers,limit=128):
"""Restricted integer gates/finite COMISS, with supplied native word memory."""
ins={x['address']:x for x in graph['instructions']};order={x['address']:i for i,x in enumerate(graph['instructions'])}
m=dict(memory);regs=dict(registers);pc=start;equal=ge=False;trace=[]
def value(arg):
if arg.startswith('$'):return int(arg[1:],0)&0xffffffff
if arg.startswith('%'):
if arg not in regs:raise ValueError('unknown collision register')
return regs[arg]
match=re.fullmatch(r'(0x[0-9a-f]+)?\((%\w+)\)',arg)
if not match or match[2] not in regs:raise ValueError('unsupported collision address')
address=regs[match[2]]+int(match[1] or '0',0)
if address not in m:raise ValueError('unknown collision word')
return m[address]
def store(arg,v):
if arg.startswith('%'):regs[arg]=v&0xffffffff;return
match=re.fullmatch(r'(0x[0-9a-f]+)?\((%\w+)\)',arg)
if not match or match[2] not in regs:raise ValueError('unsupported collision destination')
m[regs[match[2]]+int(match[1] or '0',0)]=v&0xffffffff
while pc not in stops:
if pc not in ins or len(trace)>=limit:raise ValueError('collision probe escaped bound')
x=ins[pc];trace.append(pc);op=x['opcode'];args=x['operands'].split('#')[0].strip()
i=order[pc];pc=graph['instructions'][i+1]['address'] if i+1<len(graph['instructions']) else None
if op in ['movl','movss']:
a,b=args.split(', ');store(b,value(a))
elif op in ['cmpl','testl']:
a,b=args.split(', ');left,right=value(a)&0xffffffff,value(b)&0xffffffff
equal=(left==right) if op=='cmpl' else (left&right)==0
elif op=='comiss':
a,b=args.split(', ');source,destination=number(value(a)),number(value(b))
equal=source==destination;ge=destination>=source
elif op in ['je','jne','jmp','jae']:
taken=op=='jmp' or (equal if op=='je' else not equal if op=='jne' else ge)
if taken:pc=int(args.split()[0],16)
else:raise ValueError('unsupported collision probe opcode '+op)
return {'trace':trace,'stop':pc,'memory':m,'registers':regs}
def sites(graph):
rows=graph['instructions']
start=next(i for i,x in enumerate(rows) if x['opcode']=='movl' and x['operands']=='0x74(%rbx), %eax')
passed=next(x['address'] for x in rows[start:] if x['opcode']=='movl' and x['operands']=='0x1b0(%rbp), %r9d')
# Skip destination comes from the explicit candidate-number comparison.
cmp=next(i for i in range(start,len(rows)) if rows[i]['opcode']=='cmpl' and rows[i]['operands']=='(%r12), %eax')
skipped=int(rows[cmp+1]['operands'].split()[0],16)
merge_start=next(x['address'] for x in rows if x['opcode']=='movl' and x['operands']=='0x1030(%rsp), %r10d')
fraction=next(i for i,x in enumerate(rows) if x['opcode']=='comiss' and x['operands']=='0x44(%rbx), %xmm0')
if rows[fraction+1]['opcode']!='jae':raise ValueError('fraction decision changed')
return {'filter_start':rows[start]['address'],'filter_pass':passed,'skip':skipped,
'merge_start':merge_start,'replace':rows[fraction+2]['address']}
def main():
out={'native':{},'limitations':'Verified static Linux ELF64 and restricted supplied-memory instruction '
'probes, not original runtime/native callee execution. Entity/owner/content fields and trace result '
'words/fractions are supplied fixtures. Float probes reject nonfinite values. Full copy/merge, '
'collision brush/patch/capsule geometry, producer invariants and other-platform parity remain open.'}
for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
r=json.loads(Path(f'docs/research/native/{label}.json').read_text())
verified((Path('.cache/UrbanTerror43')/r['source']['path']).read_bytes(),r['source']['sha256'])
a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode())
names=['SV_ClipMoveToEntities','CM_TraceThroughBrush','CM_BoxTrace','CM_TempBoxModel','CM_InlineModel']
graphs={n:control_flow(fs[n]) for n in names};g=graphs['SV_ClipMoveToEntities'];s=sites(g)
clip,entity,array,stack=0x10000,0x20000,0x30000,0x40000
registers={'%rbx':clip,'%rbp':entity,'%r12':array,'%rsp':stack}
filters=[]
for passing,owner,pass_owner,contents in itertools.product([1023,7,5,9],[1023,5,9,-1],[-1,9],[0,1,2,3]):
m={clip+0x74:passing,entity+0x200:owner,array:7,stack+0x2c:pass_owner,
clip+0x78:1,entity+0x1cc:contents}
t=probe(g,s['filter_start'],[s['filter_pass'],s['skip']],m,registers)
filters.append({'pass_entity':passing,'candidate_index':7,'candidate_field200':owner,
'supplied_pass_related_word':pass_owner,'content_mask':1,'candidate_contents':contents,
'accepted_at_model_gate':t['stop']==s['filter_pass'],'probe':t})
merges=[]
for status0,status4,fraction,old_fraction in itertools.product([0,1],[0,1],[0,.25,.5,1],[.5,1]):
m={stack+0x1030:status0,stack+0x1034:status4,stack+0x1038:bits(fraction),
clip+0x3c:0,clip+0x40:0,clip+0x44:bits(old_fraction),entity:7}
t=probe(g,s['merge_start'],[s['replace'],s['skip']],m,registers)
merges.append({'candidate_status0':status0,'candidate_status4':status4,'candidate_fraction':fraction,
'current_fraction':old_fraction,'replace_at_copy_gate':t['stop']==s['replace'],'probe':t})
out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],
'graphs':graphs,'sites':s,'filter_probes':filters,'merge_probes':merges,
'trace_result_windows':windows(fs['CM_Trace'],[r'0xcc\(',r'0xd0\(',r'0xd4\(',r'0xfc\(',r'<CM_TraceThrough'],before=3,after=6)}
write_json(Path('docs/research/native-collision.json'),out)
print('Recovered paired entity filters, model paths and 320 collision decision probes')
if __name__=='__main__':main()
|