#!/usr/bin/env python3 """Qualify unchanged inline math pools and actual-image prelink refusal controls.""" import hashlib import json import math from pathlib import Path import struct import sys ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / 'kits/XnuPrelink')) from native_prelink import backed_offset, macho, unserialize_xml from emit_prelink import instruction_inventory, pc_relative_target, MATH_LITERAL_POOLS source = ROOT / '.cache/firmware/kernelcache.macho/kernelcache.release.ipad7.decompressed' data = source.read_bytes() expected = 'f18b4df865405f2a287b84977d7aca745e056d656d9fc9df1662970ce2969de7' if hashlib.sha256(data).hexdigest() != expected: raise SystemExit('Require the unchanged supplied original kernelcache') parent = macho(data)['segments'] info = next(s for s in parent if s['name'] == '__PRELINK_INFO') at = backed_offset(parent, info['va'], info['filesize'], len(data)) entries = {e['CFBundleIdentifier']: e for e in unserialize_xml(data[at:at + info['filesize']])['_PrelinkInfoDictionary']} plan = json.loads((ROOT / 'build/display-analysis/mobile-graphics-plan.json').read_text()) modules = {name: macho(data, backed_offset(parent, entries[name]['_PrelinkExecutableLoadAddr'], 32, len(data))) for name in plan['components']} module = modules['com.apple.kec.Libm'] text = next(s for s in module['segments'] if s['name'] == '__TEXT_EXEC') at = backed_offset(parent, text['va'], text['filesize'], len(data)) blob = data[at:at + text['filesize']] ranges = [(0x350, 0xbc0), (0xf60, 0x1440), (0x1884, 0x2110), (0x21e8, 0x2628), (0x280c, 0x28e0), (0x3010, 0x3048)] records = [] for (start, end), (prefix, size, digest) in zip(ranges, MATH_LITERAL_POOLS): pool = blob[start:end] if len(pool) != size or not pool.startswith(bytes.fromhex(prefix)) or hashlib.sha256(pool).hexdigest() != digest: raise SystemExit('Complete original math-pool identity differs') previous = blob[start - 4:start] if previous != bytes.fromhex('c0035fd6') or blob[end:end + 4] != bytes.fromhex('fd7bbfa9'): raise SystemExit('Require original RET/data/next-function boundaries') references, incoming_branches = [], [] for offset in range(0, len(blob) - 3, 4): if any(a <= offset < b for a, b in ranges): continue word, = struct.unpack_from(' 1 or any(a >= b for a, b in zip(highs, highs[1:])): raise SystemExit('Original 128-row power-of-two lookup highs differ from independent arithmetic') positive = instruction_inventory(data, parent, modules) identified = [r for r in positive['known_inline_data'] if r['module'] == 'com.apple.kec.Libm'] if len(identified) != 6: raise SystemExit('Actual-image positive must identify exactly six original math pools') controls = [] for start, end in ranges: for offset in (start, end - 1): changed = bytearray(data) changed[at + offset] ^= 1 try: instruction_inventory(changed, parent, modules) except ValueError as error: if 'External PC-relative encoding' not in str(error): raise controls.append(dict(offset=offset, refusal=str(error))) else: raise SystemExit('A modified known pool must not be excluded by its prefix') changed = bytearray(data) struct.pack_into('