aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/check_procswitch_headroom.py
blob: 4f468baa0002cd88ce0172e59d422c73940606fa (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
"""Machine-check the sos_proc_switch msg-headroom defect directly from firmware bytes.

Claim under test: sos_proc_switch (SVC 0xF2) validates the caller-supplied `msg`
pointer with only 0x28 bytes of headroom on its third window, while the callee
(SYS_DRV's sysdrv_message_handler) reads AND writes 0x7C bytes at that pointer.

This script extracts BOTH numbers from the actual images -- the slack constants
from sos2.elf and the access size from sys_drv2.elf -- and reports the mismatch.
Nothing is hardcoded from the write-up.
"""
import struct
from capstone import *
from capstone.arm import *

BASE = '/Users/user/dev/amd-psp-re/psp_out/handover/elf/'

def seg(path):
    raw = open(path, 'rb').read()
    ph = struct.unpack('<I', raw[28:32])[0]
    o, v, pa, f = struct.unpack('<IIII', raw[ph+4:ph+20])
    return raw[o:o+f], v

# ---- 1. slack constants inside sos_proc_switch -----------------------------
body, v = seg(BASE + 'sos2.elf')
FUNC, END = 0x22b4, 0x22b4 + 456
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB); md.detail = True
subs, adds, wins = [], [], []
for i in md.disasm(body[FUNC-v:END-v], FUNC):
    if i.mnemonic.startswith('sub') and len(i.operands) == 3 and i.operands[2].type == ARM_OP_IMM:
        subs.append((i.address, i.operands[2].imm))
    if i.mnemonic.startswith('add') and len(i.operands) == 3 and i.operands[2].type == ARM_OP_IMM:
        adds.append((i.address, i.operands[2].imm))
    if i.mnemonic.startswith('mov') and len(i.operands) == 2 and i.operands[1].type == ARM_OP_IMM:
        if i.operands[1].imm in (0x200000, 0x300000, 0x400000, 0xD00000, 0xE00000):
            wins.append((i.address, i.operands[1].imm))

print("=== sos_proc_switch @ %#x ===" % FUNC)
print("window constants  :", [(hex(a), hex(c)) for a, c in wins])
slack_4k  = [(a, c) for a, c in subs if c == 0x1000]
slack_x28 = [(a, c) for a, c in adds if c == 0x28]
print("msg headroom via 'sub #0x1000' (type5/type1) :", [hex(a) for a, _ in slack_4k])
print("msg headroom via 'add #0x28'   (third path)  :", [hex(a) for a, _ in slack_x28])

# ---- 2. access size inside SYS_DRV's message handler -----------------------
body2, v2 = seg(BASE + 'sys_drv2.elf')
H, HEND = 0xE087E4, 0xE087E4 + 0x90
md2 = Cs(CS_ARCH_ARM, CS_MODE_THUMB); md2.detail = True
sizes = []
for i in md2.disasm(body2[H-v2:HEND-v2], H):
    if i.mnemonic.startswith('mov') and len(i.operands) == 2 and i.operands[1].type == ARM_OP_IMM:
        if i.operands[1].imm == 0x7C: sizes.append(hex(i.address))
print("\n=== sysdrv_message_handler @ %#x ===" % H)
print("literal 0x7C (frame access size) at :", sizes)

# ---- 3. the mismatch -------------------------------------------------------
WIN_END, SLACK, ACCESS = 0xE00000, 0x28, 0x7C
maxmsg = WIN_END - SLACK
print("\n=== RESULT ===")
print("  validation permits msg <= %#x" % maxmsg)
print("  callee accesses [msg, msg+%#x) -> ends at %#x" % (ACCESS, maxmsg + ACCESS))
print("  bytes past the window end      : %d" % (maxmsg + ACCESS - WIN_END))
print("  msg values that PASS but overflow: %#x .. %#x (%d)"
      % (WIN_END - ACCESS + 1, maxmsg, maxmsg - (WIN_END - ACCESS + 1) + 1))
ok = bool(slack_4k) and bool(slack_x28) and bool(sizes) and (SLACK < ACCESS)
print("\n%s" % ("*** HEADROOM DEFECT CONFIRMED FROM FIRMWARE BYTES ***" if ok
                else "could not confirm -- check offsets"))