#!/usr/bin/env python3 """Recover the qagame cvar registry and authentication announcement callers.""" import gzip import json from pathlib import Path import struct from analyze_deferred_auth import partition_function, trace_slice from analyze_engine_control import control_flow from analyze_qvm import decode, write_json from analyze_vm_extensions import native_functions, verified def vm_string(data, header, address): length = header['data_length'] + header['literal_length'] if not 0 <= address < length: raise ValueError('string outside initialized VM memory') start, stop = header['data_offset']+address, header['data_offset']+length if stop > len(data): raise ValueError('truncated initialized VM memory') end = data.find(b'\0', start, stop) if end == -1: raise ValueError('unterminated VM string') return data[start:end].decode('ascii') def registry(data, header, base, count, stride=28): if stride != 28 or not 0 < count <= 4096 or base < 0 or base+count*stride > header['data_length']: raise ValueError('invalid cvar table bounds') rows = [] for index in range(count): at = base+index*stride offset = header['data_offset']+at if offset+stride > len(data): raise ValueError('truncated cvar table') pointer, name, default, flags, cache, word20, word24 = struct.unpack_from('<7I', data, offset) if pointer and not 0 <= pointer <= header['declared_memory_bytes']-272: raise ValueError('cvar mirror outside declared memory') text = vm_string(data, header, name) if not text: raise ValueError('empty cvar name') rows.append({'index': index, 'vm_address': at, 'file_offset': offset, 'mirror_address': pointer, 'name_address': name, 'name': text, 'default_address': default, 'default': vm_string(data, header, default), 'flags_raw': flags, 'modification_cache_initial': cache, 'word20_initial': word20, 'word24_initial': word24}) return rows def main(): report = json.loads(Path('docs/research/qvm/qagame.json').read_text()) data = verified(Path(report['source']['member']['output']).read_bytes(), report['sha256']) header, ins = decode(data) # Concrete identities are checked against the supplied registration/update loops. for at, value in [(2685, 4), (2783, 28), (2788, 3532), (2958, 4), (3843, 28), (3848, 3532)]: if ins[at]['opcode'] != 'CONST' or ins[at]['operand'] != value: raise ValueError('cvar loop identity changed') count = struct.unpack_from('