| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
ta_entry_points read the trampoline's +0xA0 (Create) and +0xB0 (Invoke) slots and threw away the
three between them. Those are TA_DestroyEntryPoint, TA_OpenSessionEntryPoint and
TA_CloseSessionEntryPoint. That is the whole reason TMZ sat at 50% named: it has six functions and
three of the unnamed ones were entry stubs the header identifies exactly.
The slot order is read off GCP rather than assumed from the GP spec. GCP has all five named
independently and its table reads Create, nullsub(destroy), OpenSession, CloseSession, Invoke, with
WSPE and HDCP agreeing and the types agreeing too -- slots 0, 2, 4 return a status, slots 1 and 3 are
void. Running the new derivation against GCP reproduces GCP's own names exactly, which is the check
that makes this evidence rather than convention.
Applied to every module, only where porting left the address unnamed. Per-module: tmz 50 -> 100,
rap 76 -> 98, csu 70 -> 92, oc 90 -> 97, dtm 75 -> 89. Corpus 19,038 -> 19,126 meaningful names
(65.8% -> 66.1%), field share 75.5% -> 75.8%.
RAS disagrees and is deliberately left alone. Its libdump names TA_CreateEntryPoint and
TA_DestroyEntryPoint at addresses the header assigns to OpenSession and CloseSession, a systematic
two-slot shift across all 18 builds. My first run wrote the header names anyway, which left RAS
asserting two different Create entry points -- a contradiction I introduced and then caught by
checking for duplicated roles. A guard now refuses to add a second holder of a role, so the conflict
is reported on every build and nothing is written. No build has a duplicated entry-point role.
Which source is right for RAS stays open: the obvious tiebreaker, argument count, is not decisive on
functions IDA never prototyped.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two GCP handlers were mislabelled in the libdump symbol set and the mislabels had been
propagating into every artifact. The duplicate name was the tell: FE0B and FE0D were both called
DestroyAuthenticatedSession.
The code settles it without needing the documents. gcp_cmdFE0C iterates ctx->crypto, zero-fills a
0x58 gcp_crypto_session and sets crypto->state and crypto->key, so it creates a CRYPTO session.
gcp_cmdFE0D computes "(char *)ctx + 88 * HIBYTE(arg10)" -- stride 88, the crypto array at
ctx+0x800 -- while gcp_cmdFE0B uses stride 80, the auth array at ctx+0x300. AUDIT-NEW-TAS-RE.md
states exactly that split, and the hardware findings agree: FINDING-GCP-ACQUIRE-GATE-BYPASS
records 0xFE0C as CreateCryptoSession.
Worth distinguishing from the FE07 case: that one claimed the wrong COMMAND and the dispatcher
cross-check caught it automatically. These two claim the right command and the wrong SEMANTICS,
so no command-id check can see them -- only reading what the code touches. The corrections are
applied by name through a RENAME_FIX table so they hold for every build.
While verifying them, the dispatcher kept printing the old callee name in four builds after the
rename had demonstrably succeeded. Hex-Rays caches pseudocode per function and
remote_ops.rename refreshes only the function it renamed, never its callers, so a stale cache was
reading as a failed rename. Every function's cached text is now invalidated before the artifacts
are emitted.
That turned out to matter far beyond the two names. Applied TYPES were also being hidden behind
stale text: GCP's field share goes 59% to 83.4%, its typed field accesses 1716 to 3081 and its raw
dereferences 1193 to 612, with no new structure added -- the types had been applied all along and
the emitted artifacts were showing pre-typing output. WSPE lands at 91%, HDCP 70%, XGMI 69%,
POWERPLAY 67%. Corpus field share 65.9% -> 66.4%, held down by ASD, which dominates the corpus and
barely moved.
This is the sixth consecutive pass whose main finding was that something already done was not
visible in the output. The remaining low scores are now informative rather than suspect: merit at
0% and opm at 19% are low because their structures are mostly opaque body arrays, not because
typing failed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
functions named
The artifacts were full of ANON blocks that did not need to be. This pipeline treated a function
as named only if ITS OWN porting had assigned a name, and threw away the name the decompiler
states in the signature. For ASD that was most of the module: 11,737 of its unnamed functions are
4-byte thunks, and Hex-Rays resolves thunks and prints 'j_<target>' in the signature even where
the stored symbol is still sub_*, so 7,812 perfectly good derived names were being written out as
"ANON size=4".
The fix takes the name from the decompiled signature whenever porting produced none and the name
is not a bare sub_*. This is IDA's own thunk resolution rather than an inference, but it is
recorded as a distinct tier -- THUNK for the j_ names, IDA for the rest -- so it never passes for
a ported identity.
Named functions across all 99 builds: 10,259 of 28,945 (35.4%) -> 18,204 of 28,945 (62.9%). ASD
alone goes from 29% to 62%. Tiers: 7508 EXACT, 7812 THUNK, 2269 MNEM, 303 DISPATCH, 179 FUZZY,
133 IDA.
Two measurements on the way that corrected my earlier reasoning about where the naming gap was:
* the ASD reference has only 46 ambiguous bodies involving 109 functions, not the 677-908 the
per-build logs report. The per-build figure counts ambiguous TARGETS, and ASD's are
overwhelmingly tiny identical thunks -- which is also why disambiguating them by address
order was never worth its 12% error rate. They are now named from their jump targets instead,
which needs no tiebreak at all.
* 70% of ASD's reference names embed an address (asd_hdcp1_sub_1144, asd_svc_sub_273E), so they
carry subsystem information but claim an offset that is wrong in any other build. Worth
knowing before relying on a ported ASD name.
Log-string naming was also measured as an alternative and is small by comparison: only 98 unnamed
functions across the corpus reference a string literal at all (86 in GCP), and ASD logs numeric
codes rather than names, so the firmware's own log lines cannot name it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Rather than pick the next target by hand, the artifacts were measured for where raw dereferences
through function ARGUMENTS concentrate. That surfaced something better than another per-module
struct: the same offset set (0x08, 0x18, 0x1C, 0x20) appears through a1 in gcp, powerplay, wspe
and xgmi, in functions whose names are content-hash clusters -- lib_e53fbaaa_32, lib_7fca7c43_82,
lib_8680f995_112, lib_366a1ceb_178.
RAS is what identifies it. There the same cluster carries real names -- vsnprintf_fmt_int,
vsnprintf_fmt_ll, vsnprintf_pad, vsnprintf_prefix -- and adds 0x10, 0x14 and the tail 0x24 byte,
0x25 byte, 0x26 word, closing the layout at 0x28. It is the runtime's string-formatting state.
Declaring ONE shared type here is sound where the powerplay ack-word guess was not, and the
difference is worth stating: those "_<hash8>_" names come from ta_libmatch.py, which accepts a
cluster only when the function bytes match EXACTLY across modules. The functions are literally
the same code, so they necessarily share the structure. The earlier ack-word generalisation
rested on a resemblance and was wrong.
Applied via the first-parameter rewrite, so each function keeps the rest of its recorded
signature: 24 functions in RAS, 6 each in wspe and xgmi, 4 each in gcp and powerplay.
HDCP was examined and deliberately NOT given a shared struct. Its three heaviest functions --
hdcp2_rx_AKE_Send_Pairing_Info, hdcp2_rx_RepeaterAuth_ReceiverID_List and hdcp2_rx_AKE_Send_Cert,
590 dereferences between them -- share only two offsets, 0x18 and 0x24. They are distinct HDCP2
message layouts, not one structure, and forcing a common type would have misdescribed all three.
Honest limit on the reach of this change: the cluster is only NAMED in five modules. In hdcp, opm,
merit, auc, fp, rap, csu, oc, dtm and asd the same helpers exist but are still sub_*, so the
fragment-keyed typing cannot attach to them. Running the libnames clustering across those modules
first would let the same single declaration cover them too -- the clearest next step.
Measured across fw/decomp: typed field accesses 5246 -> 7101, raw pointer-arithmetic dereferences
12701 -> 11849, field share 29.2% -> 37.5%, struct-typed signatures 4.8% -> 5.7%.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Seven more request structures derived the same way as the earlier four, which takes the count
from 8 to 15 of 16. The sixteenth is TMZ, and it needs nothing: its invoke entry is literally
'return 0', which confirms from the entry itself the "TMZ is a stub" note in AUDIT-NEW-TAS.md.
Four of the seven close against a size the firmware itself enforces, which is the standard the
earlier xgmi/oc/wspe/hdcp structures set:
csu floor 0x24 cmd at +0, ack at +4, status at +8, body to 0x24
dtm floor 0x50 args at +0x10, 0x10 + 0x40 = 0x50 exactly
merit floor 0x2024 handlers receive the whole buffer
opm floor 0x3D54 independently reproduces the "params_size >= 0x3D54" already recorded
in AUDIT-NEW-TAS-RE.md, from the entry rather than from that note
Three do not enforce a size and say so in their declarations, as asd_req and powerplay_req
already do: auc (ctx bounded exactly at +0x10..+0x298, params extent only observed), fp (the
_DWORD * interior pointers scale, so v4 + 3 is +0x0C and v4 + 68 is +0x110), and rap -- which
is nonetheless the best bounded of the three because its entry zero-fills exactly 24 bytes at
+0x18 and then stores at +0x30, closing the layout at 0x34 without guesswork.
RAP also REFINES the negative recorded against powerplay_req. That commit said POWERPLAY is the
only entry writing a 'command | 0x80000000' ack word; the test looked for it at +4. RAP writes
the same ack at +0x08 and CSU at +0x04. So the ack convention is NOT unique to POWERPLAY -- it
simply sits at a component-specific offset, which remains a reason not to hoist one shared
header, but not the reason first given.
Two more cases of the inherited-prototype defect, both recorded rather than silently applied:
CSU's handlers are called as csu_cmdNN(ctx: *a4, params: v5) where v5 is a4[1], the declared
SIZE and an integer, so the 'struct ta_params *' prototype from fw/pseudo misdescribes it --
the same defect already noted for OC.
Metric correction worth stating: the field-access measure now also counts struct-by-value
accesses, because XGMI applies its struct to a stack buffer and reads v12.cmd_id rather than
v12->cmd_id, which the arrow-only count had been scoring as zero. On the comparable named-only
basis: typed field accesses 0 at session start -> 3742, against 8254 raw pointer-arithmetic
dereferences, a 31.2% field share. Struct-typed signatures 309/10070 (3.1%) -> 487/10088 (4.8%).
Only MERIT and the TMZ stub still show no typed field accesses, MERIT because IDA keeps an
explicit cast at its one dispatch read even though the pointer is typed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The decompiled artifacts were missing almost all type information. Three things were
available in the tree and only one was being used:
* declare_type was never called, so no type definitions were parsed at all;
* set_type ran only for the four handover types, because fw/libdumps records just
{ea,hash,name,size} and the twelve Windows TA types therefore had no signatures to port;
* the recorded comments were read into a variable and discarded without ever being applied.
All three are now applied, in dependency order, and counted per build so the result is
measurable instead of something you have to notice by reading the output. Across 99 builds:
7727 prototypes (was 1232, all of them ASD), 6068 comments (was zero), 309 struct-typed
signatures (was zero).
The reason prototypes were failing was NOT the missing types, which is what I assumed when I
reported "202 of 1434 rejected" without diagnosing it. Declaring the types first changed the
rejection count by nothing. 205 of the 239 failures were prototypes recorded in the bare
shape 'int()', with no __fastcall for the name-insertion to latch onto, so they were emitted
as the unparsable 'int();'. The name now goes before the first parenthesis whatever the
shape, and prototypes land 100%: 1434/1434 on the ASD reference, 99/99 HDCP, 9/9 DTM.
Worth recording so nobody repeats the detour: the 43 'local_types' in each handover symbol
set are ARM NEON and SVE vector typedefs plus va_list and BOOL -- compiler boilerplate with
no domain structs in it. Declaring them is harmless and buys nothing. The only real PSP
structs in this tree are the seven hand-written ones in fw/ta_ida.py (gcp_ctx, ta_params,
gcp_drm_session, gcp_auth_session, gcp_crypto_session, merit_slot, merit_table), which were
sitting unused and are now applied: GCP command handlers finally read as
'(struct gcp_ctx *ctx, struct ta_params *params)' rather than '(int, int)'.
So the remaining type gap is a data gap, not a pipeline one. Thirteen of the sixteen
components have no struct definitions anywhere in the repo to apply, and ASD's 6800
prototypes are almost entirely primitive-typed, which is why it shows only 30 struct-typed
signatures against 1439 named functions. Closing that needs structs to be defined by hand,
component by component.
|
|
|
The twelve Windows TA types take their symbols from fw/libdumps; ASD, HDCP, RAS and DTM
have theirs in psp_out/handover, in a different shape that also carries prototypes and
comments. This adds that reference path, so every component the corpus holds is covered by
one pipeline rather than twelve of fifteen.
ASD needed its own ELFs. It is driver-class, not a TA: base 0xD00100, and no TA entry
trampoline, so dispatcher-anchored naming does not apply to it at all -- its RPC dispatcher
hangs off trampoline+0x1C instead. The base is not guessed; it matches
psp_out/handover/elf/asd2.elf, whose LOAD segment contains 1562/1562 of the recorded ASD
symbols. The frame discriminator used for the TAs is inapplicable here for the same reason
the trampoline is: there is none, and it reports "neither" for all 17 builds.
Prototypes are applied where recorded. Without them Hex-Rays guesses argument counts and
types, which is most of the distance between readable C and a wall of _DWORD; 1232 of 1434
land on the ASD reference and the rest are rejected by IDA, reported rather than hidden.
One more false-positive class in the dispatcher cross-check, found by running it on HDCP:
the agreement test assumed a ported name encodes its command number, which is GCP's
convention and not HDCP's, so every one of HDCP's 16 commands was reported as conflicting.
A name can only contradict the dispatcher if it claims a command itself, so the check now
requires the ported name to match <ta>_cmd<hex> before judging it. HDCP drops to zero
conflicts while GCP's single real one -- the 0xFE07 handler carrying the inherited name
gcp_cmdFE03_CreateAuthenticatedSession -- is still reported.
Coverage is honest rather than flattering. ASD lands 162-425 named of ~1100-1400 functions
per build, and the dominant limiter is ambiguity, not similarity: 677-908 functions per
build share a byte signature with a differently-named reference function and are skipped
instead of being assigned arbitrarily. ASD is full of identical small stubs, so this is
expected, but it means the ASD figures are a floor and a smarter tie-break would raise
them. RAS is weaker still at 13-20 named per build.
|