File size: 9,495 Bytes
710340d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
"""Bounded native plane/node/leaf payload conversion after supplied allocation."""
import gzip
import hashlib
import itertools
import json
from pathlib import Path
import re
from analyze_chat_crosshair import bits
from analyze_chat_paths import native_string
from analyze_collision_composition import evaluate,get_word,put_word
from analyze_engine_control import control_flow
from analyze_frame_timing import read_address
from analyze_native import elf
from analyze_qvm import write_json
from analyze_vm_extensions import native_functions,verified

NORMALS=list(itertools.product([-1.,0.,1.],repeat=3))+[(-0.,0.,0.),(.5,-.25,.75),(1.,-2.,3.),(0.,1.,-2.)]
COUNTS=list(range(1,10))+[16,17,31]


def digest(trace):return hashlib.sha256(json.dumps(trace,separators=(',',':')).encode()).hexdigest()


def main():
    out={'native':{},'limitations':'Synthetic native payload conversion after an explicitly supplied '
         'first allocation return; leaf auxiliary-allocation continuation also supplies a return. No allocation, error, original process or native callee executes; '
         'execution stops before epilogues/later allocations. Entry length/division gates '
         'is retained as static evidence, not executed. No actual BSP file is loaded by these probes. '
         'Complete validation/ownership/reset/endian/exceptional float and runtime/platform contracts remain open.'}
    source,output,plane_base=0x10000,0x30000,0x50000
    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());data=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());metadata,_=elf(data)
        graphs={n:control_flow(fs[n]) for n in ['CMod_LoadPlanes','CMod_LoadNodes','CMod_LoadLeafs','CM_LoadMap']};results={};routing={}
        caller=graphs['CM_LoadMap']['instructions']
        for name in ['CMod_LoadPlanes','CMod_LoadNodes','CMod_LoadLeafs']:
            i=next(i for i,x in enumerate(caller) if '<'+name+'>' in x['operands'])
            if caller[i-2]['opcode']!='leaq' or caller[i-2]['operands']!='0x10(%rsp), %rdi' or caller[i-1]['opcode']!='addq':raise ValueError('header routing changed')
            offset=int(caller[i-1]['operands'].split(',')[0][1:],0)
            if offset<8 or (offset-8)%8:raise ValueError('unexpected lump descriptor offset')
            t=evaluate(graphs['CM_LoadMap'],caller[i-2]['address'],[caller[i]['address']],{}, {'%rsp':0x90000},capture_registers=True)
            routing[name]={'header_relative_offset':offset,'lump_index':(offset-8)//8,'descriptor_pointer':t['registers']['rdi'],'trace':t['trace'],'call':caller[i]['address']}

        for name in ['CMod_LoadPlanes','CMod_LoadNodes','CMod_LoadLeafs']:
            g=graphs[name]
            rows=g['instructions'];alloc=[i for i,x in enumerate(rows) if '<Hunk_Alloc>' in x['operands']];first=alloc[0];start=rows[first+1]['address']
            end=rows[alloc[1]]['address'] if name=='CMod_LoadLeafs' else next(x['address'] for x in rows[first+1:] if x['opcode']=='popq')
            static_globals=[];seed={}
            for x in rows:
                if '(%rip)' not in x['operands']:continue
                address=int(re.search(r'#\s*0x([0-9a-f]+)',x['operands'])[1],16)
                static_globals.append({'address':address,'instruction':x['address'],'opcode':x['opcode'],'operands':x['operands']})
                if x['opcode']=='movss':
                    raw,_=read_address(data,metadata['sections'],address,4)
                    for i,v in enumerate(raw):seed[address+i]=v
            if name=='CMod_LoadNodes':
                slots={x['address'] for x in static_globals if x['opcode']=='addq'}
                if len(slots)!=1:raise ValueError('node plane-base slot changed')
                for slot in slots:put_word(seed,slot,plane_base,8)
            elif name=='CMod_LoadLeafs':
                slots={x['address'] for x in static_globals if x['opcode']=='cmpl'}
                if len(slots)!=2:raise ValueError('leaf running maxima changed')
                for slot in slots:put_word(seed,slot,0)
            if name=='CMod_LoadPlanes':allocation_start=next(x['address'] for x in rows[:first] if x['opcode']=='leal' and x['operands']=='0xc(%rbp), %ecx')
            elif name=='CMod_LoadLeafs':allocation_start=next(x['address'] for x in rows[:first] if x['opcode']=='leal' and x['operands']=='0x2(%rbp), %r9d')
            else:allocation_start=next(x['address'] for x in rows[:first] if x['opcode']=='movslq' and x['operands']=='%r14d, %rdi')
            probes=[]
            for count in COUNTS:
                m=dict(seed);disk=[];disk_stride={'CMod_LoadPlanes':16,'CMod_LoadNodes':36,'CMod_LoadLeafs':48}[name]
                native_stride={'CMod_LoadPlanes':20,'CMod_LoadNodes':16,'CMod_LoadLeafs':24}[name]
                for i in range(count):
                    if name=='CMod_LoadPlanes':words=[bits(v) for v in NORMALS[i%len(NORMALS)]]+[bits(i-.5)]
                    elif name=='CMod_LoadNodes':words=[[-1,0,1,2,7][i%5],-i-1,i+1]+[0x1100+i*16+j for j in range(6)]
                    else:words=[[-1,0,2,2,7][i%5],[0,3,1,0][i%4]]+[0x2200+i*16+j for j in range(6)]+[100+i,200+i,300+i,400+i]
                    disk.append([v&0xffffffff for v in words])
                    for j,v in enumerate(words):put_word(m,source+disk_stride*i+4*j,v)
                # Fill destination with sentinels, including native padding and spare records.
                for i in range(native_stride*(count+12)+2):m[output-1+i]=0xa5
                allocation=evaluate(g,allocation_start,[rows[first]['address']],m,{'%rbp':count,'%r14':0,'%r13':source},capture_registers=True)
                regs={'%rax':output,'%rbx':source,'%rbp':count,'%r12':count}
                t=evaluate(g,start,[end],m,regs,limit=4096,capture_registers=True)
                records=[]
                for i in range(count):
                    p=output+native_stride*i
                    if name=='CMod_LoadPlanes':records.append({'words':[get_word(t['memory'],p+4*j) for j in range(4)],'type':get_word(t['memory'],p+16,1),'signbits':get_word(t['memory'],p+17,1),'padding':[t['memory'][p+18],t['memory'][p+19]]})
                    elif name=='CMod_LoadNodes':records.append({'plane_pointer':get_word(t['memory'],p,8),'children':[get_word(t['memory'],p+8),get_word(t['memory'],p+12)]})
                    else:records.append([get_word(t['memory'],p+4*j) for j in range(6)])
                portal_allocation=None
                if name=='CMod_LoadLeafs':
                    continuation=dict(t['registers']);continuation['rax']=0x70000
                    portal_allocation=evaluate(g,rows[alloc[1]+1]['address'],[rows[alloc[2]]['address']],t['memory'],continuation,capture_registers=True)
                destination_writes=[w for w in t['writes'] if output<=w['address']<output+native_stride*(count+12)]
                outside=[w for w in t['writes'] if output-1<=w['address']<output or output+native_stride*count<=w['address']<output+native_stride*(count+12)]
                probes.append({'count':count,'allocation_bytes':allocation['registers']['rdi'],'allocation_trace':allocation['trace'],'input_words':disk,'records':records,'destination_writes':destination_writes,'writes_outside_records':outside,
                    'globals':{str(x['address']):get_word(t['memory'],x['address'],8 if x['opcode']=='movq' else 4) for x in static_globals if x['operands'].split('#')[0].strip().endswith('(%rip)') and x['opcode'] in ['movq','movl'] and all(x['address']+j in t['memory'] for j in range(8 if x['opcode']=='movq' else 4))},
                    'portal_allocation_bytes':portal_allocation['registers']['rdi'] if portal_allocation else None,'portal_allocation_trace':portal_allocation['trace'] if portal_allocation else None,'next_allocation_bytes':t['registers']['rdi'] if name=='CMod_LoadLeafs' else None,'trace':t['trace'] if count<=4 else None,'trace_length':len(t['trace']),'trace_sha256':digest(t['trace'])})
            errors=[]
            for i,x in enumerate(rows):
                if '<Com_Error>' not in x['operands']:continue
                fmt=next(y for y in reversed(rows[max(0,i-4):i]) if y['opcode']=='movl' and y['operands'].split('#')[0].strip().endswith(', %esi'))
                address=int(fmt['operands'].split(',')[0][1:],0)
                errors.append({'call':x['address'],'text':native_string(data,metadata['sections'],address)})
            maxima={}
            if name=='CMod_LoadLeafs':
                for role,reg in [('cluster','%edx'),('area','%eax')]:
                    maxima[role]=next(x['address'] for x in static_globals if x['opcode']=='cmpl' and x['operands'].split('#')[0].strip().endswith(', '+reg))
            results[name]={'maxima_slots':maxima,'start_after_supplied_allocation':start,'stop_before_epilogue_or_allocation':end,'global_references':static_globals,'error_requests':errors,'probes':probes}
        out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],'graphs':graphs,'lump_routing':routing,'loaders':results}
    write_json(Path('docs/research/bsp-loading.json'),out)
    print('Recovered 72 supplied-allocation plane/node/leaf conversion probes')


if __name__=='__main__':main()