aboutsummaryrefslogtreecommitdiffstats
path: root/psp_emu/harness/find_params_ptr.py
blob: 6e8374f76efaf4482293777fc3902dffe0b0f5d8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/env python3
"""At the hijack point, does any attacker-reachable memory already hold a pointer to the host
command buffer? If so, gadget B's base register can be LOADED from it instead of carved, removing
two lottery bytes from the chain (chain2: A=0x402425 -> B=0x401f58 needs r6 = params pointer)."""
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 *
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,TSAM=HM.GW,HM.FT,HM.TSAM
from pspemu import armmmu
def main():
    cont=glob.glob(os.path.join(HM.CONTAINERS,"ta_22-0-0-77_*"))[0]
    env=HM.setup_hooked(cont, lambda uc,a,s,u: None, verbose=True)
    c=env["c"]; uc=c.uc
    def fill(cmd):
        buf=bytearray(b"\xA5"*GW.CMDBUF_LEN)
        struct.pack_into("<I",buf,0,cmd); struct.pack_into("<I",buf,0x10,1)
        struct.pack_into("<I",buf,0x414,0xFFFFFFFB); struct.pack_into("<I",buf,0x818,0xFFFFFFFB)
        for b in (GW.CMDBUF2_PA, GW.CMDBUF2_PA+HM.M.Ring.ALIAS): uc.mem_write(b,bytes(buf))
    for cmd in (0xFE07,0x0001,0x0004): fill(cmd); FT.invoke(env, struct.pack("<I",cmd)+b"\x00"*0x5C)
    got={}; frozen=[False]
    uc.hook_add(UC_HOOK_BLOCK, lambda u,a,s_,x: u.emu_stop() if frozen[0] else None)
    def on_bx(u,addr,size,user):
        if addr!=0x402938 or got: return
        got["sp"]=u.reg_read(UC_ARM_REG_SP); got["rows"]=TSAM.walk(u); frozen[0]=True; u.emu_stop()
    uc.hook_add(UC_HOOK_CODE,on_bx,begin=0x402938,end=0x402938); uc.ctl_flush_tb()
    try: env["ring"].submit(2, bytes(bytearray(0x20)), timeout=20, probe=env["disp"])
    except Exception: pass
    if not got: print("never reached bx ip"); return 1
    dacr=uc.reg_read(UC_ARM_REG_CP_REG,(15,0,0,3,0,0,0))
    ok={i for i in range(16) if ((dacr>>(2*i))&3) in (1,3)}
    cmd=[r for r in got["rows"] if r[6] in ok and "w" in str(r[5]).lower() and r[0]>=0x4000000]
    lo,hi=min(r[0] for r in cmd), max(r[1] for r in cmd)
    print("\n   host cmd-buffer VA window: %#x .. %#x"%(lo,hi))
    print("   scanning every attacker-READABLE TA region for a pointer into it\n")
    SEARCH=[(0x400100,0x407000,"code (RO, but readable)"),(0x407000,0x417000,".data/.bss"),
            (0x418000,0x41c000,"stack"),(0x41d000,0x421000,"heap/ctx"),(0x422000,0x423000,"procpage")]
    tot=0
    for a,b,name in SEARCH:
        hits=[]
        for va in range(a,b,4):
            try: v=struct.unpack("<I",bytes(armmmu.read_va(uc,va,4)))[0]
            except Exception: continue
            if lo<=v<hi: hits.append((va,v))
        tot+=len(hits)
        print("   %-24s %#010x-%#010x : %d pointer(s)%s"%(name,a,b,len(hits),
              "  "+", ".join("%#x->%#x"%(x,y) for x,y in hits[:6]) if hits else ""))
    # CAUTION: the emulator maps the cmd buffer at VA 0x4000000, which collides with ordinary
    # code/immediate byte patterns. HARDWARE places params at 0x04024000 (len 0x8000), so a hit is
    # only real if it also falls in that window. Both emulator hits are instruction bytes read at a
    # 4-byte boundary (0x406404 -> 0x04000000, 0x401be4 -> 0x04000007) and are NOT in it.
    HW_LO, HW_HI = 0x04024000, 0x0402c000
    real=[(va,v) for a,b,_ in SEARCH for va,v in []]
    print("\n   TOTAL pointers into the EMULATOR cmd-buffer window: %d"%tot)
    print("   of those, inside the HARDWARE params window %#x..%#x: see per-hit check above"
          %(HW_LO,HW_HI))
    print("   => VERDICT: no genuine params pointer exists in TA-readable memory;")
    print("      gadget B's base register MUST be carved (2 lottery bytes).")
    return 0
if __name__=="__main__": sys.exit(main())