#!/usr/bin/env python3 """Offline-only FE0A comparator discriminator for two proposed GCP params VAs. This DOES NOT measure a live command-buffer VA and MUST NOT be treated as permission to retry a hardware ROP chain. FE0A comparator REJECT (and accepted diagnostic selector 11) call an ASD→SYS_DRV fixed-fabric-write event path; this is NOT a read-only card probe. No device requests or keys are used. """ from __future__ import annotations import struct from pathlib import Path from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB ELF = Path(__file__).resolve().parents[1] / 'psp_out/versions_win/elf_all/gcp_22.0.0.77_7c426e7f.elf' OLD_BASE = 0x04024000 SHIFTED_BASE = 0x04035000 # fault-compatible HYPOTHESIS, not measured CODE_VA = 0x00404A74 # signed Thumb function prologue SECOND_CODE_VA = 0x00404A78 # unique follow-up word in signed ELF LOAD ZERO_VA = 0x00415A74 # zero bytes inside signed ELF LOAD COUNTER = 0x40000000 def load_bytes(image: bytes, va: int, size: int) -> bytes: assert image[:6] == b'\x7fELF\x01\x01' phoff = struct.unpack_from(' None: image = ELF.read_bytes() thumb = Cs(CS_ARCH_ARM, CS_MODE_THUMB) # FE0A pre-selector comparator: R1=params+0x10, R2=*(params+0x824), # ADD R1,R2 then LDRB [R1,#4]! => target=params+0x14+R2. for va, mnemonic in ((0x40483A, 'ldr.w'), (0x40484C, 'ldm.w'), (0x40486E, 'add'), (0x404870, 'ldrb.w'), (0x404886, 'ldrd.w'), (0x40488A, 'subs'), (0x404890, 'bhs')): ins = next(thumb.disasm(load_bytes(image, va, 4), va, count=1)) assert ins.address == va and ins.mnemonic.split('.')[0] == mnemonic.split('.')[0], (hex(va), ins.mnemonic) # FE0A REJECT is not passive: GCP invokes its signed error emitter 3x. # That wrapper's ASD 0x208 → SYS_DRV 0x1079 path can request a fixed # fabric write at 0x03B30600; no card-side endpoint effect is known. for va, mnemonic in ((0x40490A, 'ldr'), (0x404912, 'blx'), (0x404918, 'blx'), (0x404924, 'blx')): ins = next(thumb.disasm(load_bytes(image, va, 4), va, count=1)) assert ins.address == va and ins.mnemonic == mnemonic, (hex(va), ins.mnemonic) prologue = next(thumb.disasm(load_bytes(image, CODE_VA, 4), CODE_VA, count=1)) assert prologue.mnemonic == 'push' code_word = struct.unpack(' ACCEPT status 0x241; shifted base -> REJECT 0x100.') # GCP .77 sub_4062B4 puts E3, caller-code 3 and the passed 16-bit # status into ASD 0x208 msg[1]. ASD .163 reorders the three parts and # SYS_DRV 0x1079 filters only top bytes EE/EF (signed reports cited below). forwarded = [] for status in (0xF000, 0, 0x100): v = (status << 16) | (3 << 8) | 0xE3 packed = ((v & 0xff) << 24) | (((v >> 8) & 0xff) << 16) | ((v >> 16) & 0xffff) assert packed >> 24 == 0xE3 and packed >> 24 not in (0xEE, 0xEF) forwarded.append(packed) print('CAUTION: FE0A REJECT can request three SYS_DRV 0x1079 fixed-fabric writes ' 'at 0x03B30600, words '+', '.join(hex(v) for v in forwarded)) print('ACCEPT with selector 11 also emits three error RPCs (final status 0x241); ' 'both paths may touch the same fixed endpoint.') print('This is NOT a passive live read; the private gate and target effect are unmeasured.') print('This offline script alone cannot measure other bases or the live destination mapping; ' 'bounded non-ROP hardware observations are documented separately, not ROP authorization.') if __name__ == '__main__': main()