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
|
#!/usr/bin/env python3
"""Is any fuse/debug function reachable from a SYS_DRV RPC opcode?
fuzz_sysdrv's sample (634 blocks) reached none of them, but it only sweeps its
ACL ranges and never touches the crypto dispatcher. This sweeps the opcode space
directly through the real entry (sub_E087E4), with the same validated hooks.
Controls: the dispatcher/allowlist/entry hooks must fire, and the crypto
dispatcher must fire too this time (low opcodes are included), otherwise the
sweep is not covering what it claims.
"""
import collections, json, sys
sys.path.insert(0, "/home/user/dev/amd-psp-re/psp_emu")
from pspemu import sysdrv_env
from unicorn import UC_HOOK_CODE, UC_HOOK_BLOCK
import struct
syms = json.load(open("/home/user/dev/amd-psp-re/psp_out/v53/sys_drv_53_symbols.json"))["map"]
ENTRY = 0xE087E4
CONTROLS = {"high disp E105A8": 0xE105A8, "allowlist E0AB44": 0xE0AB44,
"entry E087E4": ENTRY, "crypto disp E1DC30": 0xE1DC30}
TNAMES = ("fuse_program_bits", "fuse_write_field", "fuse_write_thunk",
"debug_unlock_apply", "debug_policy_value", "debug_allowed",
"debug_port_enabled", "secure_boot_state", "dma_transfer",
"fuse_read_config", "fuse_read_flag", "fuse_feature_check",
"fuse_decode_flags", "debug_disabled", "debug_unlock_apply",
"spirom_write_checked", "spirom_read_checked", "rom_parse_directory",
"secure_channel_setup", "fuse_read_pair")
TARGETS = {n: syms[n]["va"] for n in TNAMES if n in syms}
sd = sysdrv_env.make()
sysdrv_env.run_init(sd)
hits = collections.Counter()
first_op = {}
cur = {"op": None}
for n, va in {**CONTROLS, **TARGETS}.items():
def mk(nm):
def h(u, a, s, x):
hits.update([nm])
first_op.setdefault(nm, cur["op"])
return h
sd.uc.hook_add(UC_HOOK_CODE, mk(n), begin=va, end=va)
blocks = set()
sd.uc.hook_add(UC_HOOK_BLOCK, lambda u, a, s, x: blocks.add(a), begin=0xE00100, end=0xEB7000)
sd.uc.ctl_flush_tb()
req = sd.alloc(0x100)
cfg = sd.alloc(0x400); sd.write(cfg, b"\x00" * 0x400)
buf = sd.alloc(0x2000); sd.write(buf, b"\x00" * 0x2000)
ops = list(range(0x0000, 0x0100)) + list(range(0x1000, 0x2000))
print("sweeping %d opcodes through the real entry..." % len(ops))
for n, op in enumerate(ops):
cur["op"] = op
d = [0] * 31
d[0] = op; d[1] = 2; d[2] = buf; d[3] = 0x100; d[4] = buf; d[5] = 0x100; d[18] = cfg
sd.write(req, b"\x00" * 0x100)
sd.write(req, struct.pack("<31I", *d))
try:
sd.run_function(ENTRY, (req, 2, 0, 0), count=3_000_000)
except Exception:
pass
if n % 512 == 0:
print(" ...%d/%d, blocks=%d" % (n, len(ops), len(blocks)), flush=True)
print("\nCONTROLS:")
for n in CONTROLS:
print(" %-24s %s" % (n, hits[n] or "NOT REACHED"))
print("\nTARGETS:")
any_hit = False
for n in sorted(TARGETS):
if hits[n]:
any_hit = True
print(" %-24s %-8d first reached by opcode %#06x" % (n, hits[n], first_op[n]))
else:
print(" %-24s -" % n)
print("\nSYS_DRV blocks covered: %d" % len(blocks))
print("RESULT: %s" % ("SOME FUSE/DEBUG FUNCTION IS RPC-REACHABLE" if any_hit
else "none reachable from any swept opcode"))
|