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()