// GCP TA command surface, decompiled with restored names, prototypes and types. // Source: psp_out/versions_win/ta_22-0-4-50_81b082b4.bin // Handlers resolved from fw/cmdmaps/gcp.json; loc_* labels mapped to their // containing function. Generated by fw/ta_surface.py. // ================ cmd 0x3 @ 0x4025f8 ================ int __fastcall gcp_cmd03_ActivateDRMCertAuthSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned __int64 *p_arg10; // r4 unsigned int arg10; // r6 int v5; // r4 struct gcp_drm_session *v6; // r6 unsigned int *p_state; // r7 struct gcp_ctx *v8; // r4 unsigned __int8 *body; // r0 int v10; // r2 int v11; // r3 int v12; // r6 _DWORD dst[65]; // [sp+8h] [bp-108h] BYREF int v15; // [sp+10Ch] [bp-4h] v15 = dword_405CE4; p_arg10 = ¶ms->arg10; arg10 = params->arg10; gcp_log(level: 0, fmt: aAmdTaGcpActiva); if ( arg10 < 0x20 ) { v6 = &ctx->drm[arg10]; p_state = &v6->state; if ( v6->state == 1 ) { gcp_lib_zero_fill_words(dst, len: 0x100u); v8 = (struct gcp_ctx *)((char *)p_arg10 + 4); sub_402820(a1: (int)v8, a2: (int)v8, a3: 256); v5 = gcp_cmdFE08_KeyExchangeAuthenticatedSession__h1(ctx: v8, params: (struct ta_params *)0x100); sub_402820(a1: (int)dst, a2: (int)dst, a3: 256); if ( v5 == 0 ) { body = v6->body; v10 = dst[1]; v11 = dst[2]; v12 = dst[3]; *(_DWORD *)body = dst[0]; *((_DWORD *)body + 1) = v10; *((_DWORD *)body + 2) = v11; *((_DWORD *)body + 3) = v12; *p_state = 2; } } else { v5 = 1046; } gcp_log(level: 0, fmt: aAmdTaGcpActiva_0); } else { gcp_log(level: 0, fmt: (const char *)off_402714); v5 = 1043; } if ( dword_405CE4 != v15 ) { ((void (__fastcall *)(int))loc_405CE8)(a1: dword_405CE4 - v15); JUMPOUT(0x4026B0); } return v5; } // ================ cmd 0x1 @ 0x402718 ================ // COPY AUDIT: 1 call(s) with a non-literal length. // sub_405084(a1: 0, a2: off_4027C4, v9) len=v9 // // Guards seen in this function: // if ( *(_DWORD *)(a2 + 16) <= 1u ) /*0x40272a*/ // if ( *v4 == 0 ) /*0x402736*/ // if ( v7 == 0 ) /*0x402756*/ // if ( v9 != 0 ) /*0x402786*/ // // Heuristic lead, not a verdict: a non-literal length may still be bounded elsewhere (HDCP carries bounded ubfx and unbounded ldrb paths in one function). int __fastcall gcp_cmd01_AllocateDRMSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int *p_state; // r7 unsigned int i; // r6 int v6; // r4 unsigned int asd_refcount; // r1 int v9; // r0 int v10; // r5 gcp_log(level: 0, fmt: aAmdTaGcpAlloca); if ( LODWORD(params->arg10) <= 1 ) { p_state = &ctx->drm[0].state; for ( i = 0; i != 32; ++i ) { if ( *p_state == 0 ) { asd_refcount = ctx->asd_refcount; ctx->asd_refcount = asd_refcount + 1; if ( asd_refcount == 0 ) { v9 = gcp_asd_rpc_305(arg: 1u); if ( v9 != 0 ) { v10 = v9; gcp_log(level: 0, fmt: (const char *)off_4027C4); return v10 | 0x20000000; } } *p_state = 1; *(p_state - 1) = params->arg10; params->out1950 = i; v6 = 0; gcp_log(level: 0, fmt: aAmdTaGcpAlloca_0); LABEL_10: gcp_log(level: 0, fmt: aAmdTaGcpAlloca_1); return v6; } p_state += 6; } v6 = 1049; goto LABEL_10; } return 1050; } // ================ cmd 0x5 @ 0x402878 ================ // COPY AUDIT: 1 call(s) with a non-literal length. // sub_405084(a1: 0, a2: (const char *)dword_402980, v8) len=v8 // // Guards seen in this function: // if ( v2 > 0x1F ) /*0x402888*/ // if ( *(_DWORD *)(v5 + 4) == 0 ) /*0x40289c*/ // if ( v6 != 0 ) /*0x4028b2*/ // if ( v7 != 0 ) /*0x4028bc*/ // if ( v8 != 0 ) /*0x4028da*/ // // Heuristic lead, not a verdict: a non-literal length may still be bounded elsewhere (HDCP carries bounded ubfx and unbounded ldrb paths in one function). int __fastcall gcp_cmd05_CloseDRMSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r5 int v4; // r5 const char *v5; // r4 struct gcp_drm_session *v6; // r0 unsigned int asd_refcount; // r0 unsigned int v8; // r0 int v9; // r0 int v10; // r5 arg10 = params->arg10; gcp_log(level: 0, fmt: aAmdTaGcpClosed); if ( arg10 > 0x1F ) { v4 = 1043; v5 = aAmdTaGcpClosed_0; goto LABEL_12; } v6 = &ctx->drm[arg10]; if ( v6->state == 0 ) { gcp_log(level: 0, fmt: (const char *)off_402984); v4 = 1043; goto LABEL_8; } gcp_lib_zero_fill_words(dst: v6, len: 0x18u); gcp_log(level: 0, fmt: aAmdTaGcpClosed_1); asd_refcount = ctx->asd_refcount; if ( asd_refcount != 0 ) { v8 = asd_refcount - 1; ctx->asd_refcount = v8; if ( v8 != 0 ) { v4 = 0; LABEL_8: v5 = off_40297C[0]; goto LABEL_12; } } v9 = gcp_asd_rpc_305(arg: 2u); v5 = off_40297C[0]; if ( v9 != 0 ) { v10 = v9; gcp_log(level: 0, fmt: (const char *)dword_402980); v4 = v10 | 0x20000000; } else { v4 = 0; } LABEL_12: gcp_log(level: 0, fmt: v5); return v4; } // ================ cmd 0xfe09 @ 0x402988 ================ // COPY AUDIT: 3 call(s) with a non-literal length. // sub_405084(a1: 0, a2: off_402B28[0], v8, v11) len=v11 // sub_405084(a1: 0, a2: (const char *)dword_402B6C, v8, v11) len=v11 // sub_405084(a1: 0, a2: off_402B70[0], v8, v11) len=v11 // // Guards seen in this function: // if ( v4 >> 28 == 0 ) /*0x4029a4*/ // if ( v6 > 0xFFFFFFFB || v6 + 4 <= v7 ) /*0x4029d2*/ // if ( v9 != 1 ) /*0x4029f2*/ // if ( *(_DWORD *)(v14 + 792) == 3 ) /*0x402a14*/ // if ( sub_4052AC() == 0 ) /*0x402a22*/ // if ( v15 <= *(_DWORD *)(v14 + 840) ) /*0x402a70*/ // // Heuristic lead, not a verdict: a non-literal length may still be bounded elsewhere (HDCP carries bounded ubfx and unbounded ldrb paths in one function). int __fastcall gcp_cmdFE09_ConfigureAuthenticatedSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r0 int v5; // r6 unsigned int v6; // r7 unsigned int v7; // r10 unsigned int v8; // r2 int v9; // r8 int v10; // r11 int v11; // r3 const char *v12; // r1 bool v14; // cc char *v15; // r6 unsigned int v16; // r0 unsigned __int8 *v17; // r0 struct gcp_ctx *v18; // [sp+0h] [bp-4h] gcp_log(level: 0, fmt: (const char *)sub_402AF4); arg10 = params->arg10; if ( arg10 >> 28 == 0 ) { v6 = *(_DWORD *)¶ms->data20[2056]; v7 = *(_DWORD *)¶ms->data20[2040]; v8 = *(_DWORD *)¶ms->data20[1012]; v9 = *(_DWORD *)¶ms->data20[2048]; v10 = *(_DWORD *)¶ms->data20[2052]; v11 = *(_DWORD *)¶ms->data20[2060]; if ( v6 > 0xFFFFFFFB || v6 + 4 <= v7 ) { if ( v9 != 1 ) { v15 = (char *)ctx + 80 * HIBYTE(arg10); if ( *((_DWORD *)v15 + 198) == 3 ) { v18 = (struct gcp_ctx *)(v15 + 792); if ( gcp_VerifyOMAC( ctx: (struct gcp_ctx *)(v15 + 792), params: (struct ta_params *)((char *)¶ms->arg10 + 4)) == 0 ) { v16 = *((unsigned __int8 *)¶ms->arg10 + v10 + 4) | (*((unsigned __int8 *)¶ms->arg10 + v10 + 5) << 8) | ((*((unsigned __int8 *)¶ms->arg10 + v10 + 6) | (*((unsigned __int8 *)¶ms->arg10 + v10 + 7) << 8)) << 16); if ( v16 <= *((_DWORD *)v15 + 210) ) { gcp_log(level: 0, fmt: aAmdTaGcpErrorI); v5 = 560; goto LABEL_7; } *((_DWORD *)v15 + 210) = v16; if ( v9 != 2 ) { if ( v9 == 3 ) { v5 = gcp_ConfigureCryptoSession(ctx, params); if ( v5 != 0 ) goto LABEL_7; } else if ( v9 != 4 ) { gcp_log(level: 0, fmt: (const char *)off_402B68); v5 = 561; goto LABEL_7; } } v5 = 0; v17 = ¶ms->data20[v6 + 1016]; *v17 = 0; v17[3] = 0; v17[2] = 0; v17[1] = 0; gcp_lib_db5482d6_100(a1: (int)¶ms->out1950, a2: (int)¶ms->data20[1016], a3: 1024); if ( gcp_GenerateOMAC(ctx: v18, params: (struct ta_params *)¶ms->out1950) == 0 ) { v5 = 0; *(_DWORD *)¶ms[1].data20[984] = v7; } goto LABEL_7; } gcp_log(level: 0, fmt: (const char *)off_402B2C); v5 = 562; } else { gcp_log(level: 0, fmt: (const char *)off_402B28); v5 = 610; } LABEL_7: gcp_log(level: 0, fmt: (const char *)off_402B74); return v5; } v14 = v11 + 4 > v8; if ( v11 + 4 <= v8 ) v14 = *(_DWORD *)¶ms->data20[2064] + 4 > v8; if ( !v14 ) { v5 = gcp_ConfigureInitialize(ctx, params); goto LABEL_7; } v12 = (const char *)dword_402B6C; } else { v12 = (const char *)off_402B70; } gcp_log(level: 0, fmt: v12); v5 = 256; goto LABEL_7; } return 256; } // ================ cmd 0xfe07 @ 0x402e94 ================ int __fastcall gcp_cmdFE07_CreateAuthenticatedSession(struct gcp_ctx *ctx, struct ta_params *params) { int v4; // r7 struct gcp_auth_session *auth; // r6 unsigned int *p_asd_refcount; // r0 int v7; // r4 __int64 v8; // kr00_8 unsigned int v9; // r1 unsigned __int64 arg10; // kr08_8 unsigned int handle; // r0 int v12; // r0 v4 = 0; gcp_log(level: 0, fmt: aAmdTaGcpCreate); auth = ctx->auth; p_asd_refcount = &ctx->asd_refcount; while ( 1 ) { if ( v4 == 0x10000000 ) { v7 = 529; goto LABEL_10; } if ( *(_DWORD *)&auth->body[16] == 0 ) break; ++auth; v4 += 0x1000000; } *(_DWORD *)&auth->body[16] = 1; auth->state = params->arg18; v8 = *(_QWORD *)p_asd_refcount; v9 = ctx->unk1E04 + 1; arg10 = params->arg10; ++*p_asd_refcount; ctx->unk1E04 = v9; handle = HIDWORD(v8) & 0xFFFFFF | v4; *(_QWORD *)&auth->body[8] = arg10; auth->handle = handle; if ( (_DWORD)v8 == 0 ) { v12 = gcp_asd_rpc_305(arg: 1u); if ( v12 != 0 ) { v7 = v12 | 0x20000000; goto LABEL_10; } handle = auth->handle; } v7 = 0; params->out1950 = handle; gcp_log(level: 0, fmt: aAmdTaGcpCreate_0); LABEL_10: gcp_log(level: 0, fmt: (const char *)off_402F8C); return v7; } // ================ cmd 0xfe0c @ 0x402f90 ================ int __fastcall gcp_cmdFE0C_CreateCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { int v4; // r7 struct gcp_crypto_session *crypto; // r5 unsigned int *p_asd_refcount; // r6 int v7; // r4 unsigned int v8; // r2 unsigned int v9; // r3 unsigned __int64 arg18; // kr00_8 unsigned int handle; // r0 int v12; // r0 v4 = 0; gcp_log(level: 0, fmt: aAmdTaGcpCreate_2); crypto = ctx->crypto; p_asd_refcount = &ctx->asd_refcount; while ( 1 ) { if ( v4 == 0x40000000 ) { v7 = 785; goto LABEL_10; } if ( crypto->state == 0 ) break; v4 += 0x1000000; ++crypto; } gcp_lib_zero_fill_words(dst: crypto, len: 0x58u); crypto->state = 1; crypto->key = params->arg10; v8 = *p_asd_refcount; v9 = p_asd_refcount[2]; arg18 = params->arg18; ++*p_asd_refcount; p_asd_refcount[2] = v9 + 1; handle = v9 & 0xFFFFFF | v4; crypto->field10 = arg18; crypto->handle = handle; if ( v8 == 0 ) { v12 = gcp_asd_rpc_305(arg: 1u); if ( v12 != 0 ) { v7 = v12 | 0x20000000; goto LABEL_10; } handle = crypto->handle; } params->out1950 = handle; v7 = 0; gcp_log(level: 0, fmt: aAmdTaGcpCreate_3); LABEL_10: gcp_log(level: 0, fmt: aAmdTaGcpCreate_4); return v7; } // ================ cmd 0xfe0b @ 0x4030a4 ================ int __fastcall gcp_cmdFE0B_DestroyAuthenticatedSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r0 int v5; // r4 char *v6; // r1 unsigned int asd_refcount; // r0 unsigned int v8; // r0 int v9; // r0 gcp_log(level: 0, fmt: aAmdTaGcpDestro); arg10 = params->arg10; if ( arg10 >> 28 == 0 ) { v6 = (char *)ctx + 80 * HIBYTE(arg10); if ( *((_DWORD *)v6 + 192) == arg10 ) { *((_DWORD *)v6 + 198) = 0; asd_refcount = ctx->asd_refcount; if ( asd_refcount == 0 || (v8 = asd_refcount - 1, ctx->asd_refcount = v8, v8 == 0) ) { v9 = gcp_asd_rpc_305(arg: 2u); if ( v9 != 0 ) return v9 | 0x20000000; } v5 = 0; } else { v5 = 256; } gcp_log(level: 0, fmt: aAmdTaGcpDestro_0); return v5; } return 256; } // ================ cmd 0xfe0d @ 0x403170 ================ // *** BUG: refcount decrement + ASD release happen on a REJECTED handle *** // // Naming, from this handler's own log string: 'DestroyCryptoSession'. It manages the stride-88 // crypto-session array at ctx+0x800. FE0B manages the stride-80 AUTH array at ctx+0x300, so the // two are NOT a same-type sibling pair: they are two destroy paths over different arrays, one of // which rolls back correctly and one of which does not. // // 0x4031a0 STREQ [R1,#0x820],R4 ; clear state -- success path ONLY // 0x4031a4 MOVNE R4,#0x100 ; error 256 // 0x4031a8 MOV.W R0,#0x1E00 ; <-- reached on BOTH paths // 0x4031b4 SUBS R0,#1 ; STR ; decrement even though the handle was rejected // 0x4031ba MOVS R0,#2 ; BL gcp_asd_rpc_305 ; ASD-side RELEASE // FE0B branches away first (0x4030d2 BNE loc_4030FA), skipping decrement and release. // // [HW] FE0D with handle 0x01234567 -> 0x2000002f (reached gcp_asd_rpc_305(2), which failed); // FE0B with an equally invalid handle -> plain 0x100, never reaching it. // // RESOLVED (previously recorded here as 'not yet shown'): the decrement from a POSITIVE count // with a live session IS now proven. That blocker was misdiagnosed as missing DRM key material; // ASD's 0x305 handler always returns 0 and cannot emit the 0x2f observed -- the real failure was // that ASD was never LOADED. With GFX_CMD_ID_LOAD_ASD issued first, FE0C creates a live session // and the full chain runs on silicon: FE0F zero-query returns the live handle, FE0D destroys it // (status 0), and the destroy repeats on the dead slot, taking this path each time. // See fw/FINDING-GCP-FE0D-REFCOUNT-DESYNC.md and fw/FINDING-GCP-FE0F-STALE-SLOT-LOOKUP.md. int __fastcall gcp_cmdFE0D_DestroyCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r0 int v5; // r4 char *v6; // r1 unsigned int asd_refcount; // r0 unsigned int v8; // r0 int v9; // r0 gcp_log(level: 0, fmt: aAmdTaGcpDestro_1); arg10 = params->arg10; if ( arg10 >> 30 != 0 ) return 256; v6 = (char *)ctx + 88 * HIBYTE(arg10); if ( *((_DWORD *)v6 + 512) == arg10 ) { v5 = 0; *((_DWORD *)v6 + 520) = 0; } else { v5 = 256; } asd_refcount = ctx->asd_refcount; if ( asd_refcount == 0 || (v8 = asd_refcount - 1, ctx->asd_refcount = v8, v8 == 0) ) { v9 = gcp_asd_rpc_305(arg: 2u); if ( v9 != 0 ) return v9 | 0x20000000; } gcp_log(level: 0, fmt: aAmdTaGcpDestro_2); return v5; } // ================ cmd 0xfe11 @ 0x4032d4 ================ int __fastcall gcp_cmdFE11_FinishKeyRefreshCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r0 int v5; // r4 unsigned int v6; // r0 char *v7; // r1 int i; // r2 char *v9; // r3 char *v10; // r0 unsigned int v11; // r1 unsigned int v12; // r0 unsigned int *p_out1950; // r1 gcp_log(level: 0, fmt: aAmdTaGcpFinish); arg10 = params->arg10; if ( arg10 >> 30 != 0 ) return 256; v6 = HIBYTE(arg10); v7 = (char *)ctx + 88 * (__int16)v6; if ( *((_DWORD *)v7 + 520) == 2 ) { for ( i = -16; i != 0; ++i ) { v9 = &v7[i]; v9[2100] ^= v9[2116]; } v10 = (char *)ctx + 88 * (__int16)v6; v11 = *((_DWORD *)v10 + 518); v12 = *((_DWORD *)v10 + 519); params->out1950 = v11; p_out1950 = ¶ms->out1950; v5 = 0; p_out1950[1] = v12; gcp_log(level: 0, fmt: aAmdTaGcpFinish_0); } else { v5 = 801; } gcp_log(level: 0, fmt: (const char *)off_4033C0); return v5; } // ================ cmd 0x4 @ 0x403704 ================ // HOST-REACHABLE ASD 0x202 DISCLOSURE -- the chain completes here. // // This handler is GCP command 4, reachable by GFX_CMD_ID_INVOKE_CMD (measured: // GCP loads and invokes on this Navi 31). // // Its second argument a2 IS the host's shared params buffer: gcp_dispatch is called // as (session, *a4) from the invoke entry, and v7 = *(a2+16) is a host-supplied // index (bounds-checked < 0x20 here). // // At 0x4037e8 it calls the ASD 0x202 thunk as: // gcp_asd_rpc_202_CANARY_LEAK(a1: v23, a2: v10, a3: a2 + 36, a4: &v21) // // ASD 0x202 (sub_D14948) copies 16 UNINITIALISED stack bytes to arg3 + 16 // (VULN-ASD-202-CANARY-LEAK.md). arg3 = a2 + 36, so the stale ASD stack lands at // host_params + 0x34 -- inside the page the host owns. // // So the disclosure is not confined to GCP memory; it is written straight back to the // caller. The standing 'NOT host-reachable' rating for ASD 0x202 rested on a scan of // ASD/HDCP/DTM/RAS only. // // NOT yet verified: (1) that the ASD build this card loads still copies at arg3+16; // (2) the value actually observed on hardware, incl. whether the stale slot holds the // canary without the 0x308 priming step. int __fastcall gcp_cmd04_GetContentKey(struct gcp_ctx *ctx, struct ta_params *params) { int v2; // r10 int *v3; // r8 unsigned __int64 *p_arg10; // r5 unsigned int arg10; // r7 int v7; // r5 int v8; // r5 unsigned __int8 *v9; // r0 int v10; // r3 int v11; // r5 int v12; // r6 int v14; // [sp+8h] [bp-3Ch] BYREF unsigned __int8 *body; // [sp+Ch] [bp-38h] int v16; // [sp+10h] [bp-34h] int a1; // [sp+14h] [bp-30h] BYREF int v18[2]; // [sp+18h] [bp-2Ch] BYREF _DWORD v19[4]; // [sp+20h] [bp-24h] BYREF _DWORD v20[2]; // [sp+30h] [bp-14h] BYREF int v21; // [sp+38h] [bp-Ch] int v22; // [sp+3Ch] [bp-8h] int v23; // [sp+40h] [bp-4h] v3 = off_403850; v23 = *off_403850; v18[1] = 16; p_arg10 = ¶ms->arg10; arg10 = params->arg10; v21 = 0; v22 = 0; v16 = 0; a1 = 0; v20[0] = 0; v20[1] = 0; memset(v19, 0, sizeof(v19)); v18[0] = 0; v14 = 0; body = nullptr; gcp_log(level: 0, fmt: aAmdTaGcpGetcon); if ( arg10 < 0x20 ) { v2 = (int)p_arg10 + 4; if ( gcp_lib_52b900d8_88(a1: (int)v19, a2: (int)p_arg10 + 4, a3: 16) != 0 ) { v8 = dword_403878; if ( gcp_sysdrv_rpc_30(a1: dword_403878 - 1879048192, a2: 128, a3: (int)&a1) == 0 ) { v16 = 16; v14 = -1073741824; body = ctx->drm[arg10].body; if ( gcp_sysdrv_rpc_33(a1, a2: (int)&v14, a3: 1) == 0 && gcp_sysdrv_rpc_1(a1: (int)v18, a2: v8, a3: 1, a4: 128) == 0 && gcp_sysdrv_rpc_3(a1: v18[0], a2: a1) == 0 ) { goto LABEL_23; } } v7 = 1051; goto LABEL_9; } v9 = ctx->drm[arg10].body; v10 = *(_DWORD *)&ctx->drm[arg10].body[4]; v11 = *(_DWORD *)&ctx->drm[arg10].body[8]; v12 = *(_DWORD *)&ctx->drm[arg10].body[12]; v20[0] = *(_DWORD *)v9; v20[1] = v10; v21 = v11; v22 = v12; goto LABEL_16; } gcp_log(level: 0, fmt: (const char *)off_4038CC); v7 = 1043; while ( *v3 != v23 ) { ((void (__fastcall *)(int))loc_405CE8)(a1: *v3 - v23); LABEL_23: gcp_sysdrv_rpc_5(a1: v18[0], a2: 0, a3: 0); v7 = gcp_sysdrv_rpc_7(a1: v18[0], a2: v2, a3: 16, a4: (int)v20); if ( v7 != 0 ) v7 = 1052; LABEL_9: if ( v18[0] != 0 ) gcp_crypto_ctx_release(a1: v18[0]); if ( a1 != 0 ) gcp_sysdrv_rpc_31(a1); if ( v7 == 0 ) { LABEL_16: if ( gcp_asd_rpc_202(arg: (unsigned int)v20) != 0 ) { v7 = 1045; gcp_log(level: 0, fmt: aAmdTaGcpTldrma); } else { v7 = 0; } } v21 = 0; v22 = 0; v20[0] = 0; v20[1] = 0; gcp_log(level: 0, fmt: aAmdTaGcpGetcon_0); } return v7; } // ================ cmd 0xfe0f @ 0x403a88 ================ // *** BUG: matches crypto slots WITHOUT checking whether they are live *** // // for (i = 64; i; --i) { if (memcmp(slot+0x18, params+0x10, 8) == 0) break; slot += 88; } // *(params + 6480) = slot->handle; ; hand the slot's session HANDLE to the host // // Two independent defects meet here: // // 1. The slot state word (slot+0x20) is never tested, so free and destroyed slots match. // 2. FE0C NEVER WRITES slot+0x18 at all. It writes slot+0 (handle), slot+8 (from // params+0x10) and slot+0x10 (from params+0x18) -- see 0x402fc8..0x402fee -- and the // 88-byte zero-fill leaves slot+0x18 zeroed. So the field this search compares against // is permanently zero for every session, and an ALL-ZERO query matches the FIRST slot // whatever its state. // // Consequence: any caller obtains a valid handle for a session it did not create, simply by // querying with zeros. // // [HW] full chain on Navi 31, with ASD loaded first so sessions can exist: // FE0C create a LIVE session -> status 0, handle 0x1 // FE0F all-zero query -> status 0, returns 0x1 (the LIVE session's handle) // FE0D destroy with that handle -> status 0 (unauthorised destroy) // FE0F again after the destroy -> status 0, returns 0x1 (stale slot still found) // FE0D again, recovered handle -> status 0 (repeatable, decrements again) // // NOT a use-after-free: the handle is a monotonic counter rebound on slot reuse, so a stale // handle is correctly rejected (0x100) once the slot is re-allocated. The class is missing // authorisation and liveness checks -- cross-client denial of service plus refcount desync. // See fw/FINDING-GCP-FE0F-STALE-SLOT-LOOKUP.md. int __fastcall gcp_cmdFE0F_GetCryptoSessionForDevice(struct gcp_ctx *ctx, struct ta_params *params) { struct gcp_crypto_session *crypto; // r6 int i; // r7 int v6; // r4 gcp_log(level: 0, fmt: aAmdTaGcpGetcry_3); crypto = ctx->crypto; for ( i = 64; ; --i ) { if ( i == 0 ) { v6 = 817; goto LABEL_7; } if ( gcp_lib_52b900d8_88(a1: (int)&crypto->decode_handle, a2: (int)¶ms->arg10, a3: 8) == 0 ) break; ++crypto; } params->out1950 = crypto->handle; v6 = 0; gcp_log(level: 0, fmt: aAmdTaGcpGetCry); LABEL_7: gcp_log(level: 0, fmt: (const char *)off_403B48); return v6; } // ================ cmd 0xfe02 @ 0x403b4c ================ // COPY AUDIT: 3 call(s) with a non-literal length. // sub_404FB0(a1: dword_403CF0 - 1879048192, a2: 128, a3: &v21) len=&v21 // sub_405022(a1: v20, a2: v2, a3: *(_DWORD *)(a2 + 52), a4: v23, a5: &v19) len=&v19 // sub_405084(a1: 0, a2: off_403D34[0], a2) len=a2 // // Guards seen in this function: // if ( v6 >> 30 == 0 ) /*0x403b74*/ // if ( *(_DWORD *)(88 * HIBYTE(v6) + a1 + 2080) != 2 ) /*0x403b8e*/ // if ( (unsigned int)(v8 - 1) > 0xF ) /*0x403b96*/ // if ( sub_4010C4(a1: v22, a2: a2 + 20, a3: v8) == 0 ) /*0x403bc8*/ // if ( sub_404FB0(a1: dword_403CF0 - 1879048192, a2: 128, a3: &v21) != 0 /*0x403c14*/ // while ( *v3 != v26 ) /*0x403c88*/ // // Heuristic lead, not a verdict: a non-literal length may still be bounded elsewhere (HDCP carries bounded ubfx and unbounded ldrb paths in one function). int __fastcall gcp_cmdFE02_GetObfKeyCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { char *v2; // r8 int *v3; // r9 unsigned int arg10; // r1 unsigned int v7; // r5 int v8; // r7 int v9; // r7 unsigned __int8 *rest; // r0 int v11; // r3 int v12; // r5 int v13; // r6 unsigned __int64 *p_arg18; // r4 _DWORD v16[4]; // [sp+8h] [bp-3Ch] BYREF int v17; // [sp+18h] [bp-2Ch] BYREF int a1; // [sp+1Ch] [bp-28h] BYREF _DWORD v19[4]; // [sp+20h] [bp-24h] BYREF _DWORD v20[2]; // [sp+30h] [bp-14h] BYREF int v21; // [sp+38h] [bp-Ch] int v22; // [sp+3Ch] [bp-8h] int v23; // [sp+40h] [bp-4h] v3 = off_403CC0; v23 = *off_403CC0; gcp_log(level: 0, fmt: aAmdTaGcpGetobf); arg10 = params->arg10; if ( arg10 >> 30 == 0 ) { v7 = HIBYTE(arg10); v17 = 0; a1 = 0; if ( ctx->crypto[HIBYTE(arg10)].state != 2 ) { params = (struct ta_params *)801; goto LABEL_17; } v8 = *(_DWORD *)¶ms->data20[20]; if ( (unsigned int)(v8 - 1) > 0xF ) { params = (struct ta_params *)881; goto LABEL_17; } v2 = (char *)¶ms->arg10 + 4; v21 = 0; v22 = 0; v16[3] = 16; v20[0] = 0; v20[1] = 0; memset(v19, 0, sizeof(v19)); if ( gcp_lib_52b900d8_88(a1: (int)v19, a2: (int)¶ms->arg10 + 4, a3: v8) == 0 ) { rest = ctx->crypto[(__int16)v7].rest; v11 = *(_DWORD *)&ctx->crypto[(__int16)v7].rest[4]; v12 = *(_DWORD *)&ctx->crypto[(__int16)v7].rest[8]; v13 = *((_DWORD *)rest + 3); v20[0] = *(_DWORD *)rest; v20[1] = v11; v21 = v12; v22 = v13; goto LABEL_14; } v9 = dword_403CF0; if ( gcp_sysdrv_rpc_30(a1: dword_403CF0 - 1879048192, a2: 128, a3: (int)&a1) != 0 || (v16[2] = 16, v16[0] = -1073741824, v16[1] = ctx->crypto[(__int16)v7].rest, gcp_sysdrv_rpc_33(a1, a2: (int)v16, a3: 1) != 0) || gcp_sysdrv_rpc_1(a1: (int)&v17, a2: v9, a3: 1, a4: 128) != 0 || gcp_sysdrv_rpc_3(a1: v17, a2: a1) != 0 ) { params = (struct ta_params *)882; goto LABEL_17; } goto LABEL_25; } params = (struct ta_params *)256; while ( *v3 != v23 ) { ((void (__fastcall *)(int))loc_405CE8)(a1: *v3 - v23); LABEL_25: gcp_sysdrv_rpc_5(a1: v17, a2: 0, a3: 0); if ( gcp_sysdrv_rpc_7(a1: v17, a2: (int)v2, a3: *(_DWORD *)¶ms->data20[20], a4: (int)v20) != 0 ) { params = (struct ta_params *)883; } else { v8 = *(_DWORD *)¶ms->data20[20]; LABEL_14: LODWORD(params[1].arg18) = v8; p_arg18 = ¶ms[1].arg18; if ( gcp_asd_rpc_202(arg: (unsigned int)v20) != 0 ) { params = (struct ta_params *)880; } else { *(_DWORD *)p_arg18 += 16; params = nullptr; gcp_log(level: 0, fmt: aAmdTaGcpGetObf); } } LABEL_17: gcp_log(level: 0, fmt: (const char *)off_403D34); if ( v17 != 0 ) gcp_crypto_ctx_release(a1: v17); if ( a1 != 0 ) gcp_sysdrv_rpc_31(a1); gcp_log(level: 0, fmt: (const char *)off_403D38); } return (int)params; } // ================ cmd 0xfe08 @ 0x4040e0 ================ int __fastcall gcp_cmdFE08_KeyExchangeAuthenticatedSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int v4; // r0 unsigned __int64 *p_arg10; // r4 unsigned int arg10; // t1 int v7; // r4 char *v8; // r7 _DWORD *v9; // r12 _DWORD *v10; // r1 int v11; // r2 int v12; // r3 int v13; // r7 _DWORD dst[65]; // [sp+8h] [bp-108h] BYREF int v16; // [sp+10Ch] [bp-4h] v16 = dword_405CE4; gcp_log(level: 0, fmt: aAmdTaGcpKeyexc); arg10 = params->arg10; p_arg10 = ¶ms->arg10; v4 = arg10; if ( arg10 >> 28 != 0 ) { v7 = 256; } else { v8 = (char *)ctx + 80 * HIBYTE(v4); if ( *((_DWORD *)v8 + 193) == 1 ) { v7 = 609; } else if ( *((_DWORD *)v8 + 198) == 1 ) { gcp_lib_zero_fill_words(dst, len: 0x100u); v7 = gcp_cmdFE08_KeyExchangeAuthenticatedSession__h1( ctx: (struct gcp_ctx *)((char *)p_arg10 + 4), params: (struct ta_params *)0x100); if ( v7 == 0 ) { v9 = v8 + 792; v10 = v8 + 796; v11 = dst[1]; v12 = dst[2]; v13 = dst[3]; *v10 = dst[0]; v10[1] = v11; v10[2] = v12; v10[3] = v13; *v9 = 2; gcp_log(level: 0, fmt: aAmdTaGcpKeyExc); } } else { v7 = 610; } gcp_log(level: 0, fmt: (const char *)off_4041F0); } if ( dword_405CE4 != v16 ) { ((void (__fastcall *)(int))loc_405CE8)(a1: dword_405CE4 - v16); JUMPOUT(0x404194); } return v7; } // ================ cmd 0xfe0e @ 0x4041f4 ================ int __fastcall gcp_cmdFE0E_KeyExchangeCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int v4; // r0 unsigned __int64 *p_arg10; // r5 unsigned int arg10; // t1 int v7; // r5 unsigned int v8; // r7 char *v9; // r6 _DWORD *v10; // r0 struct gcp_ctx *v11; // r1 unsigned int v12; // r3 int v13; // r4 int v14; // r6 _DWORD dst[65]; // [sp+8h] [bp-108h] BYREF int v17; // [sp+10Ch] [bp-4h] v17 = dword_405CE4; gcp_log(level: 0, fmt: aAmdTaGcpKeyexc_0); arg10 = params->arg10; p_arg10 = ¶ms->arg10; v4 = arg10; if ( arg10 >> 30 != 0 ) { v7 = 256; } else { v8 = HIBYTE(v4); v9 = (char *)ctx + 88 * HIBYTE(v4); if ( *((_DWORD *)v9 + 520) == 1 ) { gcp_lib_zero_fill_words(dst, len: 0x100u); v7 = gcp_cmdFE08_KeyExchangeAuthenticatedSession__h1( ctx: (struct gcp_ctx *)((char *)p_arg10 + 4), params: (struct ta_params *)0x100); if ( v7 == 0 ) { v10 = v9 + 2080; v11 = (struct gcp_ctx *)((char *)ctx + 88 * (__int16)v8); v12 = dst[1]; v13 = dst[2]; v14 = dst[3]; v11 = (struct gcp_ctx *)((char *)v11 + 2084); v11->drm[0].handle = dst[0]; v11->drm[0].state = v12; *(_DWORD *)v11->drm[0].body = v13; *(_DWORD *)&v11->drm[0].body[4] = v14; *v10 = 2; gcp_log(level: 0, fmt: aAmdTaGcpKeyExc_0); } } else { v7 = 801; } gcp_log(level: 0, fmt: aAmdTaGcpKeyexc_1); } if ( dword_405CE4 != v17 ) { ((void (__fastcall *)(int))loc_405CE8)(a1: dword_405CE4 - v17); JUMPOUT(0x4042A4); } return v7; } // ================ cmd 0xfe0a @ 0x404420 ================ // COPY AUDIT: 1 call(s) with a non-literal length. // sub_403678(a1: v11 + 198, a2: a2 + 1620, a3: v7) len=v7 // // Guards seen in this function: // if ( v4 >> 28 == 0 ) /*0x40443c*/ // if ( v6 <= 0xFFFFFFFB ) /*0x404458*/ // if ( v10 + 4 <= v7 ) /*0x4044e8*/ // if ( v11[198] != 3 ) /*0x404470*/ // if ( *((_QWORD *)v11 + 104) >= (unsigned __int64)(unsigned int)v12 ) /*0x404494*/ // if ( (unsigned int)(v11[193] - 1) >= 2 ) /*0x4044cc*/ // // Heuristic lead, not a verdict: a non-literal length may still be bounded elsewhere (HDCP carries bounded ubfx and unbounded ldrb paths in one function). int __fastcall gcp_cmdFE0A_QueryAuthenticatedSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r3 int v5; // r7 unsigned int v6; // r2 unsigned int v7; // r8 int v8; // r12 char v9; // cc int v10; // r11 char *v11; // r4 int v12; // r1 const char *v13; // r10 struct gcp_ctx *v14; // r0 bool v15; // cc int ChannelType; // r0 unsigned int v18; // r0 int AccessibilityAttributes__h1; // r0 unsigned __int8 *v20; // r0 int OMAC; // r0 int v22; // r5 gcp_log(level: 0, fmt: aAmdTaGcpQuerya); arg10 = params->arg10; if ( arg10 >> 28 != 0 ) return 256; v6 = *(_DWORD *)¶ms->data20[2052]; v7 = *(_DWORD *)¶ms->data20[2040]; v8 = *(_DWORD *)¶ms->data20[2048]; v9 = (v6 != -5) & __CFADD__(v6, 5); v10 = *(_DWORD *)¶ms->data20[2056]; if ( v6 <= 0xFFFFFFFB ) v9 = (v10 != -5) & __CFADD__(v10, 5); if ( v9 ) goto LABEL_7; v13 = (const char *)dword_4045F8; v15 = v10 + 4 > v7; v5 = 256; if ( v10 + 4 <= v7 ) v15 = v6 + 4 > *(_DWORD *)¶ms->data20[1012]; if ( !v15 ) { LABEL_7: v11 = (char *)ctx + 80 * HIBYTE(arg10); if ( *((_DWORD *)v11 + 198) == 3 ) { v12 = *((unsigned __int8 *)¶ms->arg10 + v6 + 4) | (*((unsigned __int8 *)¶ms->arg10 + v6 + 5) << 8) | ((*((unsigned __int8 *)¶ms->arg10 + v6 + 6) | (*((unsigned __int8 *)¶ms->arg10 + v6 + 7) << 8)) << 16); if ( *((_QWORD *)v11 + 104) >= (unsigned __int64)(unsigned int)v12 ) { v5 = 256; v13 = aAmdTaGcpErrorI_5; } else { v13 = (const char *)off_404634; *((_DWORD *)v11 + 208) = v12; *((_DWORD *)v11 + 209) = 0; v14 = (struct gcp_ctx *)(v8 - 1); v5 = 577; switch ( v8 ) { case 1: case 14: goto LABEL_34; case 2: ChannelType = gcp_QueryChannelType(ctx, params); goto LABEL_30; case 3: ChannelType = gcp_QueryDeviceHandle(ctx, params); goto LABEL_30; case 4: ChannelType = gcp_QueryCryptoSession(ctx, params); goto LABEL_30; case 5: case 6: if ( (unsigned int)(*((_DWORD *)v11 + 193) - 1) >= 2 ) goto LABEL_36; goto LABEL_34; case 7: v18 = *((_DWORD *)v11 + 193); goto LABEL_33; case 8: ChannelType = gcp_QueryOutputIDCount(ctx: v14, params); goto LABEL_30; case 9: ChannelType = gcp_QueryOutputID(ctx: v14, params); goto LABEL_30; case 10: AccessibilityAttributes__h1 = gcp_cmdFE0A_QueryAccessibilityAttributes__h1(ctx: v14, params); if ( AccessibilityAttributes__h1 != 0 ) { v5 = AccessibilityAttributes__h1; } else { v18 = *((_DWORD *)v11 + 193) & 0xFFFFFFFE; LABEL_33: if ( v18 == 2 ) { LABEL_34: v5 = 0; v20 = ¶ms->data20[v10 + 1016]; *v20 = 0; v20[3] = 0; v20[2] = 0; v20[1] = 0; gcp_lib_db5482d6_100( a1: (int)¶ms->out1950, a2: (int)¶ms->data20[1016], a3: *(_DWORD *)¶ms->data20[2040]); *(_DWORD *)¶ms[1].data20[984] = v7; OMAC = gcp_GenerateOMAC( ctx: (struct gcp_ctx *)(v11 + 792), params: (struct ta_params *)¶ms->out1950); if ( OMAC != 0 ) { v22 = OMAC; gcp_log(level: 0, fmt: (const char *)off_404638); v5 = v22 | 0x30000000; } } else { LABEL_36: v5 = 576; } } break; case 11: case 12: case 13: break; case 15: ChannelType = gcp_QueryCodecTokenChallengeDataBlob(ctx, params); LABEL_30: v5 = ChannelType; if ( ChannelType == 0 ) goto LABEL_34; break; default: gcp_log(level: 0, fmt: (const char *)off_40463C); goto LABEL_36; } } } else { v5 = 610; v13 = off_4045FC; } } gcp_log(level: 0, fmt: v13); return v5; } // ================ cmd 0x2 @ 0x404c54 ================ int __fastcall gcp_cmd02_SendDRMCertificate(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r6 unsigned __int64 *p_arg10; // r7 int v6; // r4 const char *v7; // r6 unsigned int v8; // r0 unsigned int state; // r0 p_arg10 = ¶ms->arg10; arg10 = params->arg10; gcp_log(level: 0, fmt: aAmdTaGcpSenddr); if ( arg10 <= 0x1F ) { v8 = arg10; v7 = off_404D0C; state = ctx->drm[v8].state; v6 = state - 1; if ( state == 1 ) { gcp_lib_thunk_tailjump(a1: (int)p_arg10 + 4); params->out1948 = 6438; } else { v6 = 1046; } } else { v6 = 1043; v7 = aAmdTaGcpSenddr_0; } gcp_log(level: 0, fmt: v7); return v6; } // ================ cmd 0xfe10 @ 0x404e28 ================ int __fastcall gcp_cmdFE10_StartKeyRefreshCryptoSession(struct gcp_ctx *ctx, struct ta_params *params) { unsigned int arg10; // r0 int v5; // r4 unsigned int v6; // r0 unsigned int state; // r1 bool v8; // zf unsigned __int8 *v9; // r5 unsigned int *p_out1950; // r0 unsigned int v11; // r2 unsigned int v12; // r3 unsigned int v13; // r4 gcp_log(level: 0, fmt: aAmdTaGcpStartk); arg10 = params->arg10; if ( arg10 >> 30 != 0 ) return 256; v6 = HIBYTE(arg10); state = ctx->crypto[(__int16)v6].state; v8 = state == 2; if ( state == 2 ) v8 = HIDWORD(params->arg10) == 16; if ( v8 ) { v9 = &ctx->crypto[(__int16)v6].rest[16]; gcp_sysdrv_rpc_35(a1: (int)v9, a2: 16); p_out1950 = ¶ms->out1950; v11 = *((_DWORD *)v9 + 1); v12 = *((_DWORD *)v9 + 2); v13 = *((_DWORD *)v9 + 3); *p_out1950 = *(_DWORD *)v9; p_out1950[1] = v11; p_out1950[2] = v12; p_out1950[3] = v13; v5 = 0; gcp_log(level: 0, fmt: aAmdTaGcpStartC); } else { v5 = 801; } gcp_log(level: 0, fmt: (const char *)off_404F00); return v5; }