aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/handover
ModeNameSize
-rw-r--r--FINDINGS.md10670logstatsplainblame
-rw-r--r--README.md6071logstatsplainblame
d---------elf311logstatsplain
-rw-r--r--import_symbols.py4391logstatsplainblame
-rw-r--r--mkelf.py1491logstatsplainblame
d---------probes639logstatsplain
d---------symbols221logstatsplain
-rw-r--r--vdiff2.py2923logstatsplainblame
-rw-r--r--vport2.py6629logstatsplainblame
-rw-r--r--vport_emit.py2986logstatsplainblame

AMD PSP (Navi31 / RX 7900 XTX) — Reverse-Engineering & Security Handover

Self-contained handover package for the AMD Platform Security Processor firmware review. Everything needed to reload the reverse-engineering work into IDA and to reproduce the hardware findings.

Start here

  • FINDINGS.md — the consolidated writeup: what was reversed, the full host-facing attack-surface characterisation, confirmed bugs and their true reach, rollback/version-diff results, the DRAM trust model, and retracted claims. Read this first.

The detailed per-topic docs referenced from FINDINGS.md live in the parent psp_out/ directory (46 markdown files). This package holds the summary plus the reusable artifacts.

Contents

handover/
  README.md                 this file
  FINDINGS.md               consolidated findings writeup
  import_symbols.py         re-apply all reversed symbols/structs to a fresh IDB
  elf/                      the six reversed module ELFs (the exact builds the
    sos2.elf sys_drv2.elf   symbols were extracted from — load these in IDA)
    asd2.elf hdcp2.elf dtm2.elf ras2.elf
    versions/               all archived firmware builds, per module, for
      sos/ sys_drv/         cross-version diffing & rollback (38 ELFs)
      asd/ hdcp/  VERSIONS.md
  symbols/
    sos.json  sys_drv.json  asd.json  hdcp.json  dtm.json  ras.json
                            exported names, prototypes, comments, segments, types
  probes/                   hardware probes & PoCs (see "Hardware" below)
  vport2.py vport_emit.py vdiff2.py   cross-version symbol-port diff tooling
  mkelf.py                  wrap a raw PSP image in a minimal ELF for IDA

The .i64 databases and many archived firmware versions are in psp_out/ and psp_out/elf/; this package bundles the six reversed ELFs plus the symbol JSONs so the RE is fully reloadable standalone.

ELF sha256 (the builds the symbols correspond to)

sos2.elf      81347067864b41114add1af94ca42c883877cc527d3e1b5904a3d6db64e47ec6
sys_drv2.elf  3ea10d443a571363859b9e7cbfbf84da6f2d50e3a175c7dc537de127fb80e8fc
asd2.elf      e0d0a344ea6378d696e816b6f577399f57c0d3346df8ff35d9f116b2d89b9bf9
hdcp2.elf     b2a7b5e007bd9b570396c34119230e1b0ad863fa4eb31310be1e4d8fb9d14d82
dtm2.elf      af22ef6243befdee4b85b0e427d9e269c13db39bc07e978eb5e57c6d3a139843
ras2.elf      0daf0c36ce0b0e45175ee13b263d0d43505b0b887a0327839c28d1ca7d2198ad

Live-firmware versions: sos/sys_drv 0.49.0.54, asd 33.0.1.21, hdcp 23.0.0.82, dtm/ras as shipped. The symbols were reversed against these; they re-apply to other builds too but addresses will differ (use vport_emit.py to re-map).

Reloading the symbols into IDA

The reversed work is captured as JSON, not .i64, so it re-applies to a fresh, clean database of any build.

  1. Open elf/<module>.elf in IDA (auto-detects ARM/Thumb from the ELF), let auto-analysis finish. To reverse a different build, wrap its raw image first: python3 mkelf.py <raw.bin> <base_hex> <module>.elf bases: sos 0x0, sys_drv 0xE00100, asd 0xD00100, hdcp/dtm/ras 0x400000 (hdcp 0x400100).
  2. File > Script file... -> import_symbols.py. It auto-detects the module from the root filename and applies the matching symbols/<module>.json: struct/enum types, segment names+perms, function names + prototypes + comments, data names, and line comments. Idempotent.

Applies ~3,470 function names, ~6,360 comments, and the custom structs across the six modules.

Headless: idat64 -A -Simport_symbols.py <module>.elf

Hardware reproduction

All hardware work ran through tinygrad's userspace AMD driver on an RX 7900 XTX over a USB4 / ASM2464PD PCIe bridge (DEV=AMD). Every probe is self-documenting (docstring explains the mechanism and the expected signal).

Always run probes/psp_recover.py first — it power-cycles the PCIe link, which the card needs after a machine restart or a PSP hang, and before any rollback/DMA experiment.

Key reproductions:

probe shows
test_rollback_51.py --fw51 boots firmware 0.49.0.51 from a host-supplied signed container
test_tmr_range_guard.py [--fw51] the TMR range guard differential (.54 rejects, .51 accepts)
probe_tmr_fence.py the TMR memory-controller fence blocks host reads/writes
probe_tmr_writes_real.py / probe_tmr_footprint.py the SETUP_TMR DMA writes 0xFF, mapping its footprint
probe_tmr_base_sweep.py --fw51 --base=... 4 GB base sweep: every reachable-safe target inert
probe_c2p_mailbox.py C2P mailbox is a runtime NAK stub (not reachable)
probe_ta_session_uaf.py TA session lifecycle is robust (no UAF/double-free)

Rollback needs a signed older container: fetch an older amdgpu/psp_13_0_0_sos.bin from linux-firmware git history (commit 36934f24c008 = 0.49.0.51), drop it where test_rollback_51.py expects it (/tmp/lfw/36934f24c008.bin).

Version-diff tooling

vport2.py <new.elf> <funcs.json> <old.elf> [--list] classifies every function as IDENT / RELOC / MOVED / MOVED~ / DIVERGED against an older build, using alignment-independent mnemonic matching plus a 0.90 fuzzy threshold. Two false-positive modes are fixed (narrow index window; inline literal pools). Caveat: it misses genuinely-new boilerplate (RPC stubs) that closely resemble siblings — use an opcode/constant census for templated functions. vport_emit.py writes a {name: {va, size, cls}} map for porting addresses to a target build; vdiff2.py <new> <funcs> <old> <func> prints an instruction diff of one diverged function.

Environment notes

  • Symbols exported from IDA 9.x (ida_typeinf types API; ida_struct is gone).
  • Do not rely on absolute addresses across builds — SYS_DRV grew +4 KB between .51 and .54; use vport_emit.py to re-map.
  • Do not fire destructive/error-injection commands at a live card without intent; probe_tmr_base_sweep.py has a safety rail refusing bases whose footprint reaches the 0xFFFD_FC000000 register block (brick risk).