// OPM TA command surface, decompiled with restored names, prototypes and types. // Source: psp_out/versions_win/ta_25-0-4-48_ea9026f1.bin // Handlers resolved from fw/cmdmaps/opm.json; loc_* labels mapped to their // containing function. Generated by fw/ta_surface.py. // ================ cmd 0x9 @ 0x401768 ================ int __fastcall opm_cmd09(void *ctx, struct ta_params *params) { unsigned int v2; // r6 int v3; // r5 int *v4; // r8 int *v5; // r4 int v6; // r0 _DWORD *v7; // r1 int v8; // r2 v2 = *((_DWORD *)ctx + 4); if ( v2 > 0x3F ) return 259; v4 = (int *)off_4023E4; v5 = &(*off_4023E4)[22 * v2]; v6 = *v5; if ( *v5 == 3 ) { if ( v5[18] != 1 ) { LABEL_9: v3 = 0; goto LABEL_10; } v7 = off_4017D8; v8 = *(_DWORD *)off_4017D8; if ( *(_DWORD *)off_4017D8 == 0 ) { v3 = 265; goto LABEL_10; } v3 = 0; v5[18] = 0; *v7 = v8 - 1; if ( v8 != 1 ) { LABEL_10: opm_lib_zero_fill_words(dst: v5, len: 0x58u); v4[22 * v2 + 13] = -1; return v3; } opm_asd_rpc_305(arg: 4u); v6 = *v5; } if ( v6 != 0 ) goto LABEL_9; return 259; } // ================ cmd 0x3 @ 0x4017dc ================ // COPY AUDIT: 1 call(s) with a non-literal length. // sub_401058(a1: v9, a2: v13, a3: v12) len=v12 // // Guards seen in this function: // if ( v2 > 1 ) /*0x4017e2*/ // if ( v4 != nullptr ) /*0x401820*/ // if ( v5 != nullptr ) /*0x401824*/ // if ( sub_402E7C(a1: &v14) != 0 ) /*0x40183a*/ // if ( (unsigned int)(v15 - 1) < 2 ) /*0x401848*/ // if ( v15 != 7 ) /*0x40184c*/ // // 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 opm_cmd03(void *ctx, struct ta_params *params) { unsigned int v3; // r0 int result; // r0 char *v5; // r1 unsigned int *v6; // r2 unsigned int v7; // r3 unsigned int v8; // r7 unsigned int *v9; // r4 int v10; // r5 unsigned int v11; // r8 bool v12; // zf unsigned int v13; // r7 int v14; // [sp+0h] [bp-40h] BYREF int v15; // [sp+4h] [bp-3Ch] int v16; // [sp+8h] [bp-38h] v3 = *((_DWORD *)ctx + 5); if ( v3 > 1 ) return 561; v5 = (char *)ctx + 9244; v6 = (unsigned int *)((char *)ctx + 15696); v7 = *((_DWORD *)ctx + 4); v8 = v3; result = 264; if ( v5 != nullptr ) { v9 = v6; if ( v6 != nullptr ) { v10 = (int)v5; v11 = v7; opm_lib_zero_fill_words(dst: &v14, len: 0x28u); if ( opm_asd_rpc_200(arg: (unsigned int)&v14) != 0 ) { return 0x20000000; } else { if ( (unsigned int)(v15 - 1) < 2 ) goto LABEL_13; v12 = v15 == 7; if ( v15 != 7 ) v12 = v15 == 4; if ( v12 && v16 == 1 ) { LABEL_13: if ( v8 != 0 ) v13 = 6431; else v13 = 3972; } else if ( v8 != 0 ) { v13 = 6340; } else { v13 = 3972; } if ( v13 <= v11 ) { opm_lib_thunk_tailjump(a1: v10); *v9 = v13; return 0; } else { return 257; } } } } return result; } // ================ cmd 0x4 @ 0x4022b4 ================ int __fastcall opm_cmd04(void *ctx, struct ta_params *params) { unsigned int v3; // r0 int *v5; // r6 _DWORD *v6; // r0 int v7; // r2 int v8; // r3 int v9; // r4 v3 = *((_DWORD *)ctx + 4); if ( v3 > 0x3F ) return 259; if ( (*off_4023E4)[22 * v3] != 1 ) return 266; v5 = &(*off_4023E4)[22 * v3]; opm_sysdrv_rpc_35(a1: (int)(v5 + 2), a2: 16); v6 = (char *)ctx + 9244; v7 = v5[3]; v8 = v5[4]; v9 = v5[5]; *v6 = v5[2]; v6[1] = v7; v6[2] = v8; v6[3] = v9; *v5 = 2; return 0; } // ================ cmd 0x5 @ 0x4029f0 ================ // OPM command 5 -- the host-side entry of the path that reaches ASD 0x301. // // a1 is the host params buffer: // v5 = *(a1+16) host-supplied index, bounds-checked <= 0x3F // off_402ADC[22*v5] global slot table; requires slot state == 2 // sub_4023EC(v7, a1+20, 256, v11, &v10) <- a2 = host_params+20, length 256, // destination v11 = local 304-byte buffer // // sub_4023EC is mutually reachable with opm_asd301_path (sub_4012C0), which issues // ASD opcode 0x301 at 0x401544. // // So host-supplied bytes (256 of them, from host_params+20) enter the call chain that // reaches the 0x301 emitter, and the copy at this level is bounded (256 into 304). // NOT established: that those bytes reach the 0x301 request's content or length -- the // emitter passes R7+0x101C and a stack value, and R7 has not been traced to the host // page. That is the remaining question for this path. int __fastcall opm_cmd05(void *ctx, struct ta_params *params) { int *v2; // r5 int *v3; // r8 int *v4; // r6 unsigned int v6; // r7 int result; // r0 int v8; // r3 unsigned int v9; // r4 _BYTE dst[16]; // [sp+8h] [bp-134h] BYREF int v11; // [sp+18h] [bp-124h] int v12; // [sp+1Ch] [bp-120h] unsigned int v13; // [sp+20h] [bp-11Ch] unsigned int v14; // [sp+24h] [bp-118h] __int64 v15; // [sp+28h] [bp-114h] int v16; // [sp+138h] [bp-4h] v4 = off_402AA8[0]; v16 = *off_402AA8[0]; v6 = *((_DWORD *)ctx + 4); if ( v6 <= 0x3F ) { v2 = (int *)off_402ADC; if ( (*off_402ADC)[22 * v6] == 2 ) { v3 = &(*off_402ADC)[22 * v6]; opm_lib_zero_fill_words(dst, len: 0x130u); if ( sub_4023EC(a1: v3[1], a2: (int)ctx + 20, a3: 256, a4: (int)dst) == 0 ) goto LABEL_9; result = 271; } else { result = 266; } } else { result = 259; } while ( *v4 != v16 ) { ((void (__fastcall *)(int, int))loc_402F50)(a1: result, a2: *v4 - v16); LABEL_9: v2 += 22 * v6; if ( opm_lib_52b900d8_88(a1: (int)(v2 + 2), a2: (int)dst, a3: 16) != 0 ) { result = 264; } else { v8 = v12; v6 = v14; v9 = v13; v2[6] = v11; v2[7] = v8; *((_QWORD *)v2 + 4) = __PAIR64__(v6, v9); *off_402AAC = 65; *v3 = 3; *((_QWORD *)v2 + 5) = v15; result = 0; } } return result; } // --------------------------------------------------------------- // COMMANDS ENTERED AT AN INTERNAL LABEL, NOT AT A FUNCTION ENTRY. // These dispatch into the middle of a larger function, which is what a // switch-style dispatcher produces. IDA refuses to split them out, and // undefining the owner to force a split was not worth the risk, so the // containing function is reproduced below IN FULL. Read the command's // code at the stated offset -- the function as a whole is NOT the handler. // --------------------------------------------------------------- // cmd 0x0 -> 0x401704 (enters sub_401702+0x2) // cmd 0x1 -> 0x401704 (enters sub_401702+0x2) // cmd 0x2 -> 0x4017fc (enters opm_cmd03+0x20) // cmd 0x8 -> 0x401320 (enters opm_asd301_path+0x60) // cmd 0xe -> 0x401704 (enters sub_401702+0x2) // ---- containing function sub_401702 @ 0x401702 (entered mid-body by the commands above) ---- // Residue function, documented rather than named (naming would be invention). // // size: 96 bytes // class: no named caller; not referenced as data -> indirect or dead // callers: none found // callees: none // // Basis: IDA function boundaries + capstone decode of this function only; // see fw/AUDIT-NEW-TAS-RE.md for how the residue was classified. int __fastcall sub_401702(int a1) { unsigned int v1; // r1 int v2; // r1 int v3; // r3 int *v4; // r4 int v5; // r2 int *v6; // r3 int v7; // r4 v1 = *(_DWORD *)(a1 + 28); if ( v1 > 1 ) return 529; if ( v1 == 0 && *(_DWORD *)(a1 + 24) != 0 ) return 268; v2 = dword_401764; v3 = 0; v4 = (int *)off_4017D4; v5 = 0; while ( v3 != 1408 ) { if ( (*off_4017D4)[v3] == 0 ) { (*off_4017D4)[v3] = 1; v6 = &v4[v3]; v6[1] = *(_DWORD *)(a1 + 28) != 0; v6[13] = *(_DWORD *)(a1 + 20); v6[12] = *(_DWORD *)(a1 + 16); v2 = 0; v7 = *(_DWORD *)(a1 + 24); v6[18] = 0; v6[14] = v7; *(_DWORD *)(a1 + 9244) = v5; return v2; } v3 += 22; ++v5; } return v2; } // ---- containing function opm_cmd03 @ 0x4017dc (entered mid-body by the commands above) ---- // COPY AUDIT: 1 call(s) with a non-literal length. // sub_401058(a1: v9, a2: v13, a3: v12) len=v12 // // Guards seen in this function: // if ( v2 > 1 ) /*0x4017e2*/ // if ( v4 != nullptr ) /*0x401820*/ // if ( v5 != nullptr ) /*0x401824*/ // if ( sub_402E7C(a1: &v14) != 0 ) /*0x40183a*/ // if ( (unsigned int)(v15 - 1) < 2 ) /*0x401848*/ // if ( v15 != 7 ) /*0x40184c*/ // // 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 opm_cmd03(void *ctx, struct ta_params *params) { unsigned int v3; // r0 int result; // r0 char *v5; // r1 unsigned int *v6; // r2 unsigned int v7; // r3 unsigned int v8; // r7 unsigned int *v9; // r4 int v10; // r5 unsigned int v11; // r8 bool v12; // zf unsigned int v13; // r7 int v14; // [sp+0h] [bp-40h] BYREF int v15; // [sp+4h] [bp-3Ch] int v16; // [sp+8h] [bp-38h] v3 = *((_DWORD *)ctx + 5); if ( v3 > 1 ) return 561; v5 = (char *)ctx + 9244; v6 = (unsigned int *)((char *)ctx + 15696); v7 = *((_DWORD *)ctx + 4); v8 = v3; result = 264; if ( v5 != nullptr ) { v9 = v6; if ( v6 != nullptr ) { v10 = (int)v5; v11 = v7; opm_lib_zero_fill_words(dst: &v14, len: 0x28u); if ( opm_asd_rpc_200(arg: (unsigned int)&v14) != 0 ) { return 0x20000000; } else { if ( (unsigned int)(v15 - 1) < 2 ) goto LABEL_13; v12 = v15 == 7; if ( v15 != 7 ) v12 = v15 == 4; if ( v12 && v16 == 1 ) { LABEL_13: if ( v8 != 0 ) v13 = 6431; else v13 = 3972; } else if ( v8 != 0 ) { v13 = 6340; } else { v13 = 3972; } if ( v13 <= v11 ) { opm_lib_thunk_tailjump(a1: v10); *v9 = v13; return 0; } else { return 257; } } } } return result; } // ---- containing function opm_asd301_path @ 0x4012c0 (entered mid-body by the commands above) ---- // CAUTION: IDA MERGED TWO FUNCTIONS HERE. 0x401320 is a real prologue -- // PUSH.W {R4-R8,SB,SL,FP,LR} ; SUB.W SP,#0x1260 ; SUB SP,#4 (frame ~0x1264) // which is why references inside quote a #0x1390 frame while this function's own // prologue is SUB SP,#0x108. Attempts to split it (define undef/thumb/func) did not // take; treat 0x401320.. as its own function when reading. // // That inner function is the dispatcher's **cmd 8** handler (case 8 -> loc_401320, // called with a1 = the host params buffer). // // R7 = arg0 = HOST PARAMS BUFFER. Proof: at 0x401332 MOV R7,R0, then // 0x401340 LDR R4,[R7,#0x10] ; 0x401342 CMP R4,#0x3F ; BLS // which is the same host-index-bounds-check idiom opm_cmd05 uses on *(a1+16) <= 0x3F. // // At the ASD 0x301 call (0x401544): // 0x401536 LDR R1,[R7,#0x1018] ; CMP R1,#0 ; BEQ skip <- guard reads HOST data // 0x40153c R2 = R7 + 0x101C <- arg is HOST params+0x101C // The invoke entry requires params size >= 0x3D54, so +0x101C is inside the host page. // // => host-controlled data reaches ASD 0x301's arguments. Still unproven: that the // WRITE LENGTH is host-controlled, and any hardware demonstration (ASD-side writes // have no host-visible artefact on this rig). int __fastcall opm_asd301_path(int a1, int a2, int a3) { unsigned int v3; // r4 char *v4; // r5 int v5; // r6 _DWORD *v6; // r9 int v7; // r10 int v8; // r11 unsigned int v10; // r1 char v11; // t1 int result; // r0 int v13; // r0 int v14; // lr int v15; // r7 int *v16; // r11 int *v17; // r9 int v18; // t1 __int64 v19; // r0 int v20; // r7 unsigned int v21; // r7 unsigned int v22; // r8 bool v23; // zf int *v24; // r0 _DWORD *v25; // r0 int v26; // t1 _DWORD *v27; // r2 int v28; // r1 int v29; // r0 int v30; // r1 int *v31; // r0 _DWORD *v32; // r0 int v33; // t1 _DWORD *v34; // r1 int v35; // r2 _DWORD *v36; // r0 int v37; // r1 int *v38; // r11 int v39; // r0 int *v40; // r11 int v41; // t1 int v42; // r1 bool v43; // zf int v44; // r1 bool v45; // zf _DWORD *v46; // r0 int v47; // r1 bool v48; // zf _BYTE v49[16]; // [sp+10h] [bp-1380h] BYREF _DWORD v50[5]; // [sp+20h] [bp-1370h] BYREF int v51; // [sp+34h] [bp-135Ch] unsigned int arg[1010]; // [sp+38h] [bp-1358h] BYREF _BYTE v53[304]; // [sp+1010h] [bp-380h] BYREF _DWORD v54[71]; // [sp+1140h] [bp-250h] BYREF int v55; // [sp+125Ch] [bp-134h] unsigned int v56; // [sp+1264h] [bp-12Ch] char *v57; // [sp+1268h] [bp-128h] int v58; // [sp+126Ch] [bp-124h] int v59; // [sp+1270h] [bp-120h] int *v60; // [sp+1274h] [bp-11Ch] _DWORD *v61; // [sp+1278h] [bp-118h] int v62; // [sp+127Ch] [bp-114h] int v63; // [sp+1280h] [bp-110h] int v64; // [sp+1284h] [bp-10Ch] _BYTE v65[260]; // [sp+1288h] [bp-108h] BYREF int v66; // [sp+138Ch] [bp-4h] v66 = dword_402F4C; if ( a1 != 0 ) { v5 = a2; if ( a2 != 0 ) { v3 = a3; if ( (unsigned int)a3 <= 0x100 ) { v4 = v65; v10 = 256 - a3; if ( (unsigned int)a3 > 0xFF ) v10 = 0; opm_lib_zero_fill_bytes(dst: &v65[a3], len: v10); opm_lib_thunk_tailjump(a1: (int)v65); while ( v3 != 0 ) { v11 = *v4++; *(_BYTE *)(v5 - 1 + v3--) = v11; } } } } result = dword_402F4C - v66; if ( dword_402F4C != v66 ) { v13 = ((int (__fastcall *)(int))loc_402F50)(a1: result); v56 = v3; v57 = v4; v58 = v5; v59 = a1; v60 = &dword_402F4C; v61 = v6; v62 = v7; v63 = v8; v64 = v14; v15 = v13; v55 = dword_402F4C; if ( v13 == 0 ) goto LABEL_88; v3 = *(_DWORD *)(v13 + 16); if ( v3 > 0x3F ) goto LABEL_88; v16 = (int *)off_4022FC; if ( (*off_4022FC)[22 * v3] != 3 ) goto LABEL_88; opm_lib_db5482d6_100(a1: (int)v49, a2: v13 + 24, a3: 4096); v17 = &v16[22 * v3]; v18 = v17[11]; v6 = v17 + 11; if ( v18 != v50[4] || sub_402300(a1: v3, a2: (int)v50, a3: 4080, a4: (int)v49) == 0 ) goto LABEL_88; if ( opm_lib_52b900d8_88(a1: off_4016C8, a2: (int)v50, a3: 16) != 0 && opm_lib_52b900d8_88(a1: off_4016CC, a2: (int)v50, a3: 16) != 0 && opm_lib_52b900d8_88(a1: off_4016D0, a2: (int)v50, a3: 16) != 0 ) { if ( opm_lib_52b900d8_88(a1: off_4016D4, a2: (int)v50, a3: 16) == 0 ) goto LABEL_95; LODWORD(v19) = opm_lib_52b900d8_88(a1: off_4016D8, a2: (int)v50, a3: 16); if ( (_DWORD)v19 != 0 ) { if ( opm_lib_52b900d8_88(a1: off_4016DC, a2: (int)v50, a3: 16) != 0 ) { if ( opm_lib_52b900d8_88(a1: off_4016E0, a2: (int)v50, a3: 16) != 0 && opm_lib_52b900d8_88(a1: off_4016E4, a2: (int)v50, a3: 16) != 0 ) { v20 = opm_lib_52b900d8_88(a1: off_4016E8, a2: (int)v50, a3: 16); if ( v20 != 0 && opm_lib_52b900d8_88(a1: off_4016EC, a2: (int)v50, a3: 16) != 0 ) { if ( opm_lib_52b900d8_88(a1: off_4016F0, a2: (int)v50, a3: 16) != 0 && opm_lib_52b900d8_88(a1: off_4016F4, a2: (int)v50, a3: 16) != 0 ) { if ( opm_lib_52b900d8_88(a1: off_4016F8, a2: (int)v50, a3: 16) == 0 || opm_lib_52b900d8_88(a1: off_4016FC, a2: (int)v50, a3: 16) == 0 ) { goto LABEL_91; } } else { v42 = *(_DWORD *)(*(_DWORD *)byte_401700 + 8); v43 = v42 == 1; if ( v42 == 1 ) v43 = **(_DWORD **)byte_401700 == v3; if ( !v43 || v51 == 8 ) goto LABEL_95; } goto LABEL_88; } v38 = &v16[22 * v3]; v41 = v38[1]; v40 = v38 + 1; v39 = v41; if ( v41 == 1 ) { if ( v20 != 0 ) goto LABEL_88; } else if ( v39 != 0 || opm_lib_52b900d8_88(a1: off_4016EC, a2: (int)v50, a3: 16) != 0 ) { goto LABEL_88; } if ( v51 == 280 ) { v44 = *(_DWORD *)(*(_DWORD *)byte_401700 + 8); v45 = v44 == 1; if ( v44 == 1 ) v45 = **(_DWORD **)byte_401700 == v3; if ( v45 ) { opm_lib_db5482d6_100(a1: (int)v54, a2: (int)arg, a3: 280); if ( v54[68] == 16 ) { v3 = (unsigned int)v54; opm_asd301_path__h1(); opm_lib_zero_fill_words(dst: v53, len: 0x130u); if ( sub_4023EC(a1: *v40, a2: (int)v54, a3: 256, a4: (int)v53) == 0 ) { opm_asd301_path__h1(); goto LABEL_95; } } else if ( v54[68] == 0 ) { goto LABEL_95; } } } } else { v36 = *(_DWORD **)byte_401700; if ( *(_DWORD *)(*(_DWORD *)byte_401700 + 8) != 1 ) { **(_DWORD **)byte_401700 = v3; v36[2] = 1; if ( v36[1] == 1 ) v37 = 0; else v37 = v16[22 * v3 + 15]; v36[3] = v37; goto LABEL_95; } if ( **(_DWORD **)byte_401700 == v3 ) goto LABEL_95; } } } else { HIDWORD(v19) = v16[22 * v3 + 1]; if ( v19 == 0x100000000LL && v51 == 4 ) { v30 = *(_DWORD *)(v15 + 4120); if ( v30 != 0 && opm_asd_rpc_301(a1: arg[0], a2: v30, a3: v15 + 4124) == 0 ) goto LABEL_95; } } } else { if ( v51 != 16 ) goto LABEL_88; v21 = arg[0]; v22 = arg[1]; v23 = arg[0] == 32; if ( arg[0] != 32 ) v23 = arg[0] == 8; if ( v23 ) { if ( v16[22 * v3 + 1] != 1 ) goto LABEL_88; } else if ( arg[0] != 1 || v16[22 * v3 + 1] != 0 ) { goto LABEL_88; } if ( arg[1] >= 3 && arg[1] != 0x40000000 || opm_lib_52b900d8_88(a1: off_4016D0, a2: (int)v50, a3: 16) == 0 ) goto LABEL_88; v24 = &v16[22 * v3]; v24[19] = v21; v24[20] = v22; if ( v22 - 1 >= 2 ) { if ( v22 != 0 ) goto LABEL_88; v31 = &v16[22 * v3]; v33 = v31[18]; v32 = v31 + 18; if ( v33 != 1 ) goto LABEL_95; v34 = off_4017D8; v35 = *(_DWORD *)off_4017D8; if ( *(_DWORD *)off_4017D8 == 0 ) goto LABEL_88; *v32 = 0; *v34 = v35 - 1; if ( v35 != 1 ) goto LABEL_95; v29 = 4; } else { v26 = v24[18]; v25 = v24 + 18; if ( v26 != 0 ) goto LABEL_95; v27 = off_4017D8; v28 = *(_DWORD *)off_4017D8; if ( *(_DWORD *)off_4017D8 > 0x3Fu ) goto LABEL_88; *v25 = 1; *v27 = v28 + 1; if ( v28 != 0 ) goto LABEL_95; v29 = 3; } if ( opm_asd_rpc_305(a1: v29) == 0 ) goto LABEL_95; } while ( 1 ) { LABEL_88: if ( dword_402F4C == v55 ) __asm { POPEQ.W {R4-R11,PC} } ((void (__fastcall *)(int))loc_402F50)(a1: dword_402F4C - v55); LABEL_91: v46 = *(_DWORD **)byte_401700; v47 = *(_DWORD *)(*(_DWORD *)byte_401700 + 8); v48 = v47 == 1; if ( v47 == 1 ) v48 = **(_DWORD **)byte_401700 == v3; if ( v48 ) { *(_DWORD *)(*(_DWORD *)byte_401700 + 8) = 0; v46[3] = 0; *v46 = 65; } LABEL_95: ++*v6; } } return result; }