aboutsummaryrefslogtreecommitdiffstats
path: root/psp_emu/harness/tsu_parser_diff.py
blob: 67e77533946ef731a54d63654f52b766b7fee35c (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
84
85
86
87
88
89
90
91
#!/usr/bin/env python3
"""Run the REAL rom_parse_directory in the emulator, and differential it against
psp_out/ifwi_parse_model.py.

Why this is in scope: no hardware, no flash, no card. CONSTRAINTS.md bars issuing
flash commands to the card; this executes firmware under Unicorn.

Why it matters: ifwi_parse_model.py is a host-side reimplementation of the
pre-auth IFWI parser written to validate crafted images, and it has never been
checked against the firmware it models. The TSU disclosure chain (ANALYSIS-PSP-
CODE-EXEC-AVENUES.md 4.1b/4.1c) rests on that parser's wrapping bounds checks,
so the model has to be right before any crafted input means anything.

Address note: the v53 symbol map's TSU addresses do NOT match this image -- they
have zero callers, while the ANALYSIS/ifwi_parse_model set has 2/1/2/1. This
uses the latter, which is the set that matches the code.

    rom_parse_directory(ctx, img) -> err        @ 0xE16964
"""
import struct, sys
sys.path.insert(0, "/home/user/dev/amd-psp-re/psp_emu")
sys.path.insert(0, "/home/user/dev/amd-psp-re/psp_out")

from pspemu import sysdrv_env
import importlib.util
_s = importlib.util.spec_from_file_location(
    "ifwi", "/home/user/dev/amd-psp-re/psp_out/ifwi_parse_model.py")
IFWI = importlib.util.module_from_spec(_s); _s.loader.exec_module(IFWI)

RPD = 0xE16964
FLASH = "/home/user/dev/amd-psp-re/psp_out/flash_BIOS2_KNOWNGOOD_20260917.bin"

sd = sysdrv_env.make()
sysdrv_env.run_init(sd)

img = open(FLASH, "rb").read()
print("known-good flash image: %d bytes" % len(img))

# place the image somewhere the guest can read
IMG_VA = 0x10000000
sd.uc.mem_map(IMG_VA, (len(img) + 0xFFF) & ~0xFFF)
sd.uc.mem_write(IMG_VA, img)
assert bytes(sd.uc.mem_read(IMG_VA, 4)) == img[:4], "image not readable at IMG_VA"
print("image mapped at %#x, first4 = %s" % (IMG_VA, img[:4].hex()))
ctx = sd.alloc(0x400)


def firmware_parse(image_bytes):
    """Run the real parser over image_bytes; return its error code."""
    sd.uc.mem_write(IMG_VA, image_bytes.ljust(len(img), b"\x00")[:len(img)])
    sd.write(ctx, b"\x00" * 0x400)
    try:
        return sd.run_function(RPD, (ctx, IMG_VA, 0, 0), count=20_000_000)
    except Exception as e:
        return "FAULT:%s" % type(e).__name__


def model_parse(image_bytes):
    try:
        IFWI.parse(image_bytes)
        return 0
    except IFWI.Fail as e:
        return "Fail:%s" % str(e)[:44]


print("\n=== CONTROL: known-good image ===")
fw = firmware_parse(img)
md = model_parse(img)
print("   firmware rom_parse_directory -> %s" % (hex(fw) if isinstance(fw, int) else fw))
print("   ifwi_parse_model             -> %s" % md)
print("   both accept: %s" % (fw == 0 and md == 0))
if fw != 0:
    print("\n   Firmware rejects the known-good image: either the entry point or the")
    print("   calling convention is wrong. Nothing below would mean anything.")
    print("   RESULT NOT REPORTABLE")
    raise SystemExit(2)

print("\n=== negative control: corrupted magic ===")
bad = bytearray(img); bad[0:4] = b"\xde\xad\xbe\xef"
print("   firmware -> %s" % (lambda v: hex(v) if isinstance(v, int) else v)(firmware_parse(bytes(bad))))
print("   model    -> %s" % model_parse(bytes(bad)))

print("\n=== wrapping inputs (the 4.1b claim) ===")
for name, off, val in (("fw_tail slot0 ptr = -0x10", 20, (-0x10) & 0xFFFFFFFF),
                       ("fw_tail slot0 ptr = 0xFFFFFFF0", 20, 0xFFFFFFF0),
                       ("fw_tail slot0 ptr = 0x1FFFF0", 20, 0x1FFFF0)):
    t = bytearray(img)
    struct.pack_into("<I", t, off, val)
    f = firmware_parse(bytes(t)); m = model_parse(bytes(t))
    print("   %-30s firmware=%-14s model=%s"
          % (name, hex(f) if isinstance(f, int) else f, m))