152 lines
12 KiB
Python
152 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify real GOP display discovery and the unchanged original client factory."""
|
|
import argparse
|
|
import hashlib
|
|
import mmap
|
|
import struct
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'build/native-legacy-display'
|
|
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--profile',choices=['pitch','info','scale'],default='scale');args=parser.parse_args()
|
|
sys.path.insert(0,str(ROOT/'kits/XnuPrelink'))
|
|
from native_prelink import macho,backed_offset,unserialize_xml
|
|
def require(v,reason):
|
|
if not v:raise SystemExit(reason)
|
|
def sha(p):return hashlib.sha256(Path(p).read_bytes()).hexdigest()
|
|
build=json.loads((OUT/'build-report.json').read_text())
|
|
for name,digest in build['source_sha256'].items():require(sha(ROOT/name)==digest,'Exact current compiled source '+name)
|
|
revision=subprocess.check_output(['git','-C',ROOT/'drivers/GopFramebuffer','rev-parse','HEAD'],text=True).strip()
|
|
for name in ['NativeLegacyDisplay.cpp','NativeGOPPresenter.cpp','NativeGOPWire.h']:
|
|
data=subprocess.check_output(['git','-C',ROOT/'drivers/GopFramebuffer','show',revision+':'+name])
|
|
require(hashlib.sha256(data).hexdigest()==sha(ROOT/'drivers/GopFramebuffer'/name),'Exact committed driver source')
|
|
require(sha(OUT/'libNativeLegacyDisplay.late.a')==build['archive_sha256'] and
|
|
sha(OUT/'OriginalLegacyFramebufferABI.h')==build['generated_abi_sha256'],'Exact late archive and generated ABI')
|
|
require(build['virtual_entries']==358 and build['owned_entries']==[0,1,7,14,84,87,141,184,188,211,213,245,246,288,306,307,337,338,345],
|
|
'Exact qualified own overrides and remaining guarded leaves')
|
|
audit=json.loads((ROOT/'build/display-analysis/mobile-graphics-legacy-abi.json').read_text())
|
|
require(int(build['bindings']['__ZTV25IOMobileFramebufferLegacy'],16)==int(audit['legacy_vptr'],16)-16,
|
|
'Apple-kext qualified super calls bind the real original vtable header')
|
|
candidate=ROOT/('build/xnu-prelink/native-owner-legacy-display-'+args.profile+'-candidate.macho');plan=json.loads(candidate.with_suffix('.json').read_text())
|
|
raw=candidate.read_bytes();image=macho(raw)
|
|
require(sha(candidate)==plan['candidate_sha256'] and sha(ROOT/'build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf')==plan['native_sha256'],
|
|
'Exact current source kernel and original-family candidate')
|
|
for module,digest in plan['original_executable_sha256'].items():
|
|
extent=next(e for e in plan['extents'] if e['module']==module and e['segment']=='__TEXT_EXEC')
|
|
at=backed_offset(image['segments'],extent['va'],extent['size'],len(raw))
|
|
require(hashlib.sha256(raw[at:at+extent['size']]).hexdigest()==digest,'Whole original executable unchanged: '+module)
|
|
|
|
# Qualify the actual uint32 output / IOReturn slot-188 contract and its
|
|
# missing-property result, rather than giving an opaque method a guessed ABI.
|
|
for address,size,digest in [
|
|
(0xfffffff005798184,312,'6c161fa48818f254191d915cc99318e085a1555d7ddbd003b86ccd75034ba363'),
|
|
(0xfffffff005778168,56,'b925b4b5bb870637ace1f46bc14010bf5d473edbeb6eaecd9d898fedbc8ee465'),
|
|
(0xfffffff00577a554,88,'6d8ebdd3591475c418f3632d112cad88a12cc4a53fa6733190531aa0e56d46c8')]:
|
|
at=backed_offset(image['segments'],address,size,len(raw))
|
|
require(hashlib.sha256(raw[at:at+size]).hexdigest()==digest,'Exact original dot-pitch implementation/callers')
|
|
pitch_path=ROOT/('build/darwinboot-owner-hvf-legacy-display-start-'+args.profile+'-dotpitch/gdb-stop.json')
|
|
pitch=json.loads(pitch_path.read_text())
|
|
require(pitch['kernel_sha256']==plan['candidate_sha256'] and not pitch['bounded_timeout_interrupted'] and
|
|
int(pitch['pc'],16)==0xfffffff005778194 and int(pitch['x0'],16)==0xe00002f0,
|
|
'Actual original caller receives genuine missing dot-pitch error')
|
|
frame=pitch['frame_chain'][1];data=bytes.fromhex(frame['local_bytes'])
|
|
offset=int(pitch['frame_chain'][0]['fp'],16)+16-int(frame['local_address'],16)
|
|
require(data[offset:offset+4]==bytes(4),'Actual uint32 output is initialized to zero on missing metadata')
|
|
require(pitch['native_legacy_display']['client_calls']>=2 and not pitch['native_legacy_display']['guard_slot'],
|
|
'Real system clients reach the query without a backend guard')
|
|
|
|
frame_info_observation=None
|
|
if args.profile in ('info','scale'):
|
|
require(build['guarded_backend_leaves']==[211,245,246,306,307,337,338,345],
|
|
'Eight unclassified leaves remain guarded; optional frame tracing is explicitly unsupported')
|
|
for address,size,digest in [
|
|
(0xfffffff005778688,152,'1dda59338b977c6c2ea0556d397e5ae26ace30d6c8c86fdceea18036ffb5b4c7'),
|
|
(0xfffffff00577a718,88,'9ffaf7219e50c6134bdc72fe51eff26de43ed27de4fe37fd0a05fff68771dc84'),
|
|
(0xfffffff005f35bb0,24,'75e1cd2260e703a28d2a5e7cf2ed1bbe0b1d4b49359860c12d55634dc85a805b')]:
|
|
at=backed_offset(image['segments'],address,size,len(raw))
|
|
require(hashlib.sha256(raw[at:at+size]).hexdigest()==digest,'Exact original frame-info ABI and selector 74 dispatch')
|
|
# Original userspace enumerates fixed 64-byte key names with strndup;
|
|
# failure / zero count follows its real optional-capability path.
|
|
cache=ROOT/'.cache/firmware/target-volumes/systemos-mount/System/Library/Caches/com.apple.dyld'
|
|
wanted={0x1b63d53f8:(432,'6a6d646a0ffbbd9ff528d7ec73b7b451af4f33c765ceb02c06c6bffdad77d881'),
|
|
0x1b63d65b0:(172,'e506fe8d7ec92144364bcdbcc5bf519569da6f30b319e24ccfc9a44803a228af')}
|
|
found=set()
|
|
for path in sorted(cache.glob('dyld_shared_cache_arm64*')):
|
|
if path.suffix=='.symbols':continue
|
|
with path.open('rb') as stream,mmap.mmap(stream.fileno(),0,access=mmap.ACCESS_READ) as mapping:
|
|
require(mapping[:16].rstrip(bytes(1))==b'dyld_v1 arm64','Original ARM64 shared-cache format')
|
|
table,count=struct.unpack_from('<2I',mapping,16)
|
|
for index in range(count):
|
|
va,size,offset,_,protection=struct.unpack_from('<3Q2I',mapping,table+index*32)
|
|
for address,(length,digest) in wanted.items():
|
|
if va<=address and address+length<=va+size:
|
|
at=offset+address-va
|
|
require(protection&4 and hashlib.sha256(mapping[at:at+length]).hexdigest()==digest,
|
|
'Exact unchanged original userspace trace-key enumeration')
|
|
found.add(address)
|
|
require(found==set(wanted),'Both original userspace frame-info functions are present')
|
|
path=ROOT/('build/darwinboot-owner-hvf-legacy-display-start-'+args.profile+'-frameinfo-return/gdb-stop.json')
|
|
d=json.loads(path.read_text())
|
|
require(d['kernel_sha256']==plan['candidate_sha256'] and not d['bounded_timeout_interrupted'] and
|
|
int(d['pc'],16)==0xfffffff0057786e8 and int(d['x0'],16)==0xe00002c7,
|
|
'Original frame-info caller receives genuine unsupported status')
|
|
data=bytes.fromhex(d['stack_bytes']);require(data[16:24]==bytes(8),'Both actual frame-info scalar outputs are zero')
|
|
require(bytes.fromhex(d['frame_indirect_memory']['0:-20:40']['bytes'])==bytes(64),'Actual fixed-size trace-key output is cleared')
|
|
require(not d['native_legacy_display']['guard_slot'] and d['native_legacy_display']['client_calls']>=2,
|
|
'Optional frame-info query returns after actual system-client creation')
|
|
frame_info_observation=dict(checkpoint_sha256=sha(path),status=0xe00002c7,trace_keys_supported=False)
|
|
|
|
observations={}
|
|
for case,directory in [('client','start-'+args.profile+'-client'),('init','init-'+args.profile+'-control'),('default-off','default-off-'+args.profile+'-control')]:
|
|
path=ROOT/('build/darwinboot-owner-hvf-legacy-display-'+directory+'/gdb-stop.json');d=json.loads(path.read_text())
|
|
require(d['kernel_sha256']==plan['candidate_sha256'] and not d['bounded_timeout_interrupted'] and d['qemu_accelerator']=='hvf','Actual bounded same-candidate observation '+case)
|
|
regs={k:int(v,16) for k,v in d['system_registers'].items()}
|
|
require(d['native_4k_mmu_observed'] and (regs['TCR_EL1']>>14)&3==0 and (regs['TCR_EL1']>>30)&3==2 and
|
|
regs['SCTLR_EL1']&1 and regs['TTBR0_EL1'] and regs['TTBR1_EL1'],'Actual native 4K CPU translation')
|
|
state=d['native_legacy_display'];require(not state['guard_slot'],'No unclassified backend called before this boundary')
|
|
if args.profile=='scale':
|
|
require(state['unit_scale_calls']==(1 if case=='client' else 0) and
|
|
state['chosen_scale']==(1 if case=='client' else 0) and not state['chosen_rotation'] and
|
|
(state['unit_scale_status']==0 if case=='client' else state['unit_scale_status']!=0),
|
|
'Actual unit-scale metadata is published only after the explicit mapped GOP system experiment')
|
|
if case=='init':
|
|
require(state['stage']==8 and state['released']==1 and state['init_result']==state['parent_match']==1 and
|
|
state['object_size']==6320 and not state['registration_calls'] and not state['dt_attached'] and not state['client_calls'],
|
|
'Real initialized derived object releases through original free without publication')
|
|
elif case=='client':
|
|
require(d['stop_symbol']=='_native_legacy_client_checkpoint' and state['stage']==10 and
|
|
state['init_result']==state['start_result']==state['probe_match']==state['parent_match']==1 and
|
|
state['object_size']==6320 and state['registration_calls']==state['dt_attached']==1 and not state['released'],
|
|
'Original start completes and actual GOP subsystem is registered/attached')
|
|
video=d['actual_boot_video']['pe_video']
|
|
require(state['gop_map'] and state['gop_virtual'] and state['gop_physical']==video['v_baseAddr']==state['video_base'] and
|
|
state['gop_bytes']==state['video_bytes']==video['v_rowBytes']*video['v_height']==1920000 and
|
|
state['gop_width']==video['v_width']==800 and state['gop_height']==video['v_height']==600,
|
|
'Actual retained GOP mapping and original mode')
|
|
props=unserialize_xml(d['native_legacy_properties'].encode())
|
|
require(props['IOMFB Debug Info']['Global']['width']==800 and props['IOMFB Debug Info']['Global']['height']==600 and
|
|
'NativeGOPFramebufferMapping' not in props,'Real original property serialization with no unencodable mapping object')
|
|
require(state['client_calls']==1 and state['client_type']==state['client_status']==0 and state['client_pid']>1 and
|
|
state['client_task'] and state['client_object'] and d['native_legacy_client_class']=='IOMobileFramebufferUserClient',
|
|
'Original system process creates the genuine original userclient through original newUserClient')
|
|
else:
|
|
require(d['original_launchd_main_executed'] and d['followed_main_entry']['first_instruction_completed'] and
|
|
state['stage']==0 and state['meta_status']==-1 and not state['registration_calls'] and not state['dt_attached'] and not state['client_calls'],
|
|
'Default-off original launchd without display experiment')
|
|
if case!='init':
|
|
require(d['original_amfi_table_owner_observed'] and d['owner_root_binding_audit']['boot_root_accepted'] and
|
|
d['owner_root_binding_audit']['_native_owner_root_apple_autherr']==80 and
|
|
d['actual_signature_failures']==dict(_cs_validate_page_bad_hash=0,_cs_validate_page_no_hash=0),
|
|
'Original AMFI/Apple rejection and independent owner-root acceptance')
|
|
shifts=d['commpage_user64_shifts'] if case=='client' else d['direct_user_commpage_shifts']
|
|
require(set(shifts.values())=={12},'Actual native user page ABI')
|
|
observations[case]=dict(checkpoint_sha256=sha(path),state=state,actual_tcr=regs['TCR_EL1'])
|
|
(OUT/'system-client-checkpoint.json').write_text(json.dumps(dict(candidate_sha256=plan['candidate_sha256'],native_sha256=plan['native_sha256'],
|
|
driver_revision=revision,observations=observations,frame_info=frame_info_observation,dot_pitch_checkpoint_sha256=sha(pitch_path),missing_dot_pitch_return=0xe00002f0,actual_original_start=True,actual_system_client_created=True,
|
|
original_instructions_modified=False,swap_contract_complete=False,system_metal_device=False,original_desktop=False,
|
|
adreno_execution=False,hardware_tested=False),indent=2)+'\n')
|
|
print('PASS: real GOP subsystem discovery, original framebuffer start and original system-process userclient creation')
|
|
print('Init/release and default-off controls, original AMFI/root/native 4K pass; actual swaps, system Metal and desktop remain open')
|