1. Overview

Field Value
Unpatched binary ci_unpatched.dll
Patched binary ci_patched.dll
Overall similarity 0.9627
Matched functions 2499
Changed functions 81
Identical functions 2418
Unmatched (unpatched) 0
Unmatched (patched) 0

Verdict: No delivered security-relevant change. The four changed functions examined resolve as follows.

  • wil_details_GetCurrentFeatureEnabledState (0x1800191900x1800192B8) is Windows Implementation Library feature-staging evaluation, not an Authenticode signature path.
  • SymCryptRsakeyCalculatePrivateFields (0x180002D0C0x18000E1E4) is statically-linked SymCrypt RSA CRT private-field math relocated by the rebuild.
  • CiEvaluatePolicyInfoForCatalog (0x1800E03840x1800E4C58) adds a new package-family-name equality check gated behind the Feature_TrustedLaunchSysAppsOSCatalog staging flag, with the pre-existing KappxValidateCatalogForTrustedLaunch path retained when the flag is off; the accompanying 0xC00/0xC08/0xBF80xC20/0xC28/0xC18 offset moves are a validation-context struct-layout change.
  • SbpParseSignatureDatabase (0x18000BDDC) was refactored so its buffer parsing moved into a new helper SbpParseSignatureDatabaseFromBuffer (0x18001E1DC), which adds a third entry category and RtlULongLongMult checked multiplies; the entry-bounds checks already present in the unpatched parser are unchanged, and the unpatched size arithmetic uses 32-bit counts that cannot overflow a 64-bit total.

2. Vulnerability Summary

Finding #1 — wil_details_GetCurrentFeatureEnabledState (sub_180019190) (WIL feature-flag evaluation change)

  • Severity: Informational
  • Vulnerability class: Not security-relevant (feature-flag gating / build behavior)
  • Affected function: wil_details_GetCurrentFeatureEnabledState (sub_180019190) (Windows Implementation Library feature-enabled-state evaluation)

Root cause. The function at exact address 0x180019190 is wil_details_GetCurrentFeatureEnabledState, part of the Windows Implementation Library feature-staging framework. It queries staging config via wil_RtlStagingConfig_QueryFeatureState (sub_18005448c) and walks a WIL feature dependency list at *(arg1+0x20), re-evaluating each cached feature entry through wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (sub_18001901c). The bytes at 0x1d/0x1e/0x1f are WIL feature-property/state bytes, not parsed certificate fields. This is not an Authenticode signature chain and does not participate in signature verification.

Patch behavior. The patched binary of the same function (relocated to 0x1800192B8, same symbol) adds a (state & 0xc00) != 0xc00 gate and calls wil_details_AreDependenciesEnabled (sub_180018fcc) for the feature-dependency check. This is a feature-flag evaluation change, not a verification fix.

Function correspondence: 1. Unpatched wil_details_GetCurrentFeatureEnabledState @ 0x180019190 2. Patched wil_details_GetCurrentFeatureEnabledState @ 0x1800192B8


Finding #2 — SymCryptRsakeyCalculatePrivateFields (sub_180002D0C) (relocated SymCrypt math)

  • Severity: Informational
  • Vulnerability class: Not security-relevant (statically-linked crypto-library relocation)
  • Affected function: SymCryptRsakeyCalculatePrivateFields (sub_180002D0C) (SymCrypt RSA CRT private-field computation)

Root cause. The function at exact address 0x180002D0C is SymCryptRsakeyCalculatePrivateFields, a statically-linked SymCrypt routine that computes RSA private-key CRT parameters. It begins with call SymCryptFdefSizeofIntFromDigits and operates entirely on SymCrypt big-integer objects. The same function exists in the patched binary at 0x18000E1E4 (same symbol), simply relocated by the recompile; the apparent diff is the address move plus register allocation, not a semantic verification change. There is no arg7 bitmask, no i != 2 signature count, and no dual-signature logic. The helpers cited as "signature comparison" are SymCrypt big-integer primitives: sub_1800256dc is SymCryptFdefIntMulMixedSize, sub_180025418 is SymCryptFdefIntIsEqual, sub_180025934 is SymCryptFdefRawSubUint32, and sub_180028404/sub_180025354 is SymCryptFdefIntCopyMixedSize.

Function correspondence: 1. Unpatched SymCryptRsakeyCalculatePrivateFields @ 0x180002D0C 2. Patched SymCryptRsakeyCalculatePrivateFields @ 0x18000E1E4


Finding #3 — CiEvaluatePolicyInfoForCatalog (0x1800E0384 → 0x1800E4C58) (feature-staged catalog enforcement)

  • Severity: Informational
  • Vulnerability class: Not security-relevant (WIL feature-staging / new catalog-policy enforcement rollout, old path retained)

Root cause / change. The patched CiEvaluatePolicyInfoForCatalog adds a new branch that runs only when both Feature_TrustedLaunchSysAppsOSCatalog__private_IsEnabledDeviceUsageNoInline returns nonzero and *(rsi+0xA98) & 0x40000 is set (0x1800E4D9D0x1800E4DB4). That branch derives a package family name from the catalog full name at rsi+0xC18 via KappxDerivePackageFamilyNameFromFullName, allocates a buffer with ExAllocatePool2, and compares the result against the string at rdi+0x28 with RtlEqualUnicodeString, returning STATUS_INVALID_IMAGE_HASH (0xC0000428) on mismatch or when *(rdi+0x30) is NULL (0x1800E4DBA0x1800E4E4D). When the feature gate is off or the 0x40000 flag is clear, control falls through to the pre-existing KappxValidateCatalogForTrustedLaunch call (0x1800E4E6C0x1800E4E90) — the same validation the unpatched build performs.

Direction. The 0xC00/0xC08/0xBF80xC20/0xC28/0xC18 offset changes appear in that retained old path and reflect a validation-context structure that grew by 0x20 bytes between builds, not a stronger reference. The delivered difference is a staged rollout of a new enforcement path for the TrustedLaunch system-apps OS-catalog scenario with the old path retained; it is not the repair of a demonstrable bypass. The unpatched build simply lacks this new sub-feature, and no attacker-reachable path is shown in which the unpatched build validates a catalog it should reject.

Function correspondence: 1. Unpatched CiEvaluatePolicyInfoForCatalog @ 0x1800E0384 2. Patched CiEvaluatePolicyInfoForCatalog @ 0x1800E4C58


Finding #4 — SbpParseSignatureDatabase (0x18000BDDC) / SbpParseSignatureDatabaseFromBuffer (0x18001E1DC) (parser refactor + defense-in-depth)

  • Severity: Informational
  • Vulnerability class: Not security-relevant (refactor + defense-in-depth arithmetic hardening; no reachable overflow)

Root cause / change. In the unpatched build the signature-database blob is parsed inline by SbpParseSignatureDatabase @ 0x18000BDDC. In the patched build that function (relocated to 0x18000E0FC) is a thin wrapper that calls a new helper SbpParseSignatureDatabaseFromBuffer @ 0x18001E1DC holding the parsing logic. The helper adds a third entry category and computes the allocation size with RtlULongLongMult checked multiplies plus compare-with-addend guards (0x18001E2690x18001E2DD) before SbeAlloc. The 0.1828 similarity is a diff-engine mispairing of this new helper against the unpatched I_MapFileHashes (0x180088580), not a genuine reorganization of one function.

Direction. The unpatched allocation is (count1 + count2) * 0x10 + Size (0x18000BE490x18000BE54), where the counts are held in 32-bit registers (r14d/r15d) and Size is a 32-bit EFI-variable length, so the 64-bit product cannot wrap. The per-entry bounds checks the diff highlights (cmp rax, 0x1c; jb; entry_end = cur + *(cur+0x10); cmp entry_end, cur; jb; cmp entry_end, end; ja; cmp ecx, 0x1c; jb) are present verbatim in BOTH builds (unpatched 0x18000BE930x18000BEAD, patched 0x18001E3680x18001E38E). No undersized-allocation or out-of-bounds-write primitive is reachable in the unpatched parser; the added RtlULongLongMult guards are defense-in-depth that accompany the refactor and the added entry category.


3. Pseudocode Diff

Finding #1 — wil_details_GetCurrentFeatureEnabledState (sub_180019190)

// ===== UNPATCHED (wil_details_GetCurrentFeatureEnabledState @ 0x180019190) =====
// WIL feature-staging evaluation; walks the feature dependency list at *(arg1+0x20)
// and re-evaluates each cached feature entry. Not signature verification.
unsigned long GetCurrentFeatureEnabledState(feature_ctx_t* arg1, int* arg2) {
    wil_RtlStagingConfig_QueryFeatureState(...);            // sub_18005448c
    dep_entry_t* cur = arg1->deps;                          // *(arg1+0x20)
    while (cur) {
        prop_t* e = *cur;
        if (e->byte_1e == 0 && (mask & 1) && e->byte_1d == 0 && e->byte_1f) {
            if (*(unsigned long*)e & 0x2)
                r = *(unsigned long*)e;                      // use cached state
            else
                r = wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState(...); // sub_18001901c
        }
        cur += 1;                                            // +8
    }
    return mask;
}

// ===== PATCHED (same symbol @ 0x1800192B8) =====
unsigned long GetCurrentFeatureEnabledState(feature_ctx_t* arg1, int* arg2) {
    wil_RtlStagingConfig_QueryFeatureState(...);
    if ((state & 0xc00) == 0xc00) r14 = 1;                   // NEW feature-flag gate
    if (arg1->deps)
        wil_details_AreDependenciesEnabled(arg1);            // sub_180018fcc
    return state;
}

Finding #2 — SymCryptRsakeyCalculatePrivateFields (sub_180002D0C)

// ===== UNPATCHED @ 0x180002D0C  /  PATCHED @ 0x18000E1E4 (same symbol) =====
// Statically-linked SymCrypt RSA CRT private-field computation. Pure big-integer
// math; relocated between builds with register-allocation churn. No signature
// verification, no arg7 bitmask, no "i != 2" signature-count check.
int SymCryptRsakeyCalculatePrivateFields(rsakey_t* key, ...) {
    n = SymCryptFdefSizeofIntFromDigits(...);
    // ... SymCrypt big-integer operations:
    //   SymCryptFdefIntMulMixedSize   (sub_1800256dc)
    //   SymCryptFdefIntIsEqual        (sub_180025418)
    //   SymCryptFdefRawSubUint32      (sub_180025934)
    //   SymCryptFdefIntCopyMixedSize  (sub_180028404 / sub_180025354)
}

Finding #4 — SbpParseSignatureDatabase / SbpParseSignatureDatabaseFromBuffer

// ===== UNPATCHED allocation (SbpParseSignatureDatabase @ 0x18000BDDC) =====
// count1/count2 are 32-bit entry counts (r14d/r15d); Size is a 32-bit EFI-variable length.
// (count1 + count2) * 0x10 + Size cannot wrap a 64-bit total.
buf = SbeAlloc((count1 + count2) * 0x10 + Size);

// ===== PATCHED allocation (SbpParseSignatureDatabaseFromBuffer @ 0x18001E1DC) =====
// Same computation, now with a third category and explicit checked multiplies.
RtlULongLongMult(count_a, 0x10, &s0);  if (r < 0) goto error;   // defense-in-depth
RtlULongLongMult(count_b, 0x10, &s1);  if (r < 0) goto error;
s0 += s1;                       if (s0 < s0_prev) goto error;
RtlULongLongMult(count_c, 0x10, &s2);  if (r < 0) goto error;   // NEW third category
total = s0 + s2 + tail;         if (total < s0) goto error;
buf = SbeAlloc(total);

// ===== per-entry loop — IDENTICAL in BOTH builds =====
if ((end - cur) < 0x1c) goto error;         // min remaining size
entry_end = cur + *(cur+0x10); if (entry_end < cur) goto error; // add-overflow guard
if (entry_end > end) goto error;            // bounds
if (*(unsigned int*)(cur+0x10) < 0x1c) goto error; // min entry size

4. Assembly Analysis

Finding #1 — wil_details_GetCurrentFeatureEnabledState (sub_180019190) (unpatched, feature-state path)

wil_details_GetCurrentFeatureEnabledState (unpatched) - feature dependency walk:
mov rdi, qword [rdi+0x20]    ; Load feature dependency list from arg1+0x20
test rdi, rdi
je 0x1800192d8               ; Skip if no dependencies
; --- Per-entry dependency loop ---
cmp byte [rcx+0x1e], 0x0
jne 0x1800192b2              ; WIL feature-property byte 0x1e
test bl, 0x1
je 0x1800192c4               ; If state mask bit 0 not set, skip
cmp byte [rcx+0x1d], 0x0
jne 0x1800192b2              ; WIL feature-property byte 0x1d
xor eax, eax
cmp byte [rcx+0x1f], 0x0
je 0x1800192c4               ; WIL feature-property byte 0x1f
mov r9, qword [rcx]          ; Load dependency entry pointer
mov eax, dword [r9]          ; Load cached-state DWORD
mov dword [rsp+0x58], eax
test al, 0x2                 ; bit 1 of cached-state DWORD
je 0x18001928d               ; If clear, recompute cached state
; If bit 1 set: reuse cached feature state
mov rcx, qword [rdi]
test rcx, rcx
je 0x1800192d8
mov eax, 0x1                 ; enabled-state result for this entry
jmp 0x1800192c6
; --- Recompute path (bit 1 CLEAR) ---
mov rdx, qword [rsp+0x58]
mov r8, rcx
mov rcx, r9
call 0x18001901c             ; wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState
mov rax, qword [rsp+0x58]
jmp 0x18001929d
; --- Patched version (0x1800192B8) adds: ---
; mov edx, 0xc00
; and eax, edx
; cmp eax, edx
; jne 0x180019371               ; (state & 0xc00) != 0xc00 gate
; call sub_180018fcc            ; wil_details_AreDependenciesEnabled

Inline annotations: - mov rdi, qword [rdi+0x20] — loads the WIL feature dependency list head; if NULL, iteration is skipped. - The triple compare at 0x180019272 region (cmp [rcx+0x1e], cmp [rcx+0x1d], cmp [rcx+0x1f]) inspects WIL feature-property/state bytes on the current dependency entry. - test al, 0x2 ... je 0x18001928d — when the cached-state DWORD has bit 1 (0x2) set the cached state is reused; otherwise wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState (sub_18001901c) recomputes it. This is WIL cache logic, not signature validation. - mov eax, 0x1 ; jmp 0x1800192c6 — stores the enabled-state result for the entry.

Finding #2 — SymCryptRsakeyCalculatePrivateFields (sub_180002D0C)

No assembly_diff was provided in the source data. The function at 0x180002D0C is the statically-linked SymCrypt RSA CRT private-field routine; it is present unchanged in semantics at 0x18000E1E4 in the patched binary. The offsets 0x180002D81, 0x180002E3C, 0x180002FB3 are internal branches of that big-integer math, not signature-verification phases.

Finding #4 — SbpParseSignatureDatabase / SbpParseSignatureDatabaseFromBuffer

The patched SbpParseSignatureDatabaseFromBuffer @ 0x18001E1DC uses checked multiplies and compare-with-addend guards before SbeAlloc:

; patched, at 0x18001E29A (repeated at 0x18001E2CA and 0x18001E2DD):
add r10, [rbp+pullResult]   ; sum += addend
cmp r10, [rbp+pullResult]   ; sum < addend?
jb  loc_18001E574           ; wrapped -> error
; each product is computed with RtlULongLongMult (js -> error)

The unpatched allocation instead computes the size directly, but from 32-bit counts, so it cannot wrap a 64-bit total:

; unpatched, at 0x18000BE49:
lea rcx, [r14+rdi]          ; count1 + count2 (both 32-bit)
shl rcx, 4                  ; * 0x10
add rcx, r15               ; + Size (32-bit)
call SbeAlloc

The per-entry bounds checks are the same in both builds (unpatched 0x18000BE930x18000BEAD, patched 0x18001E3680x18001E38E). Only the explicit RtlULongLongMult checked-multiply/compare-with-addend guards are new, alongside the added third entry category.


5. Trigger Conditions

Finding #1 — wil_details_GetCurrentFeatureEnabledState (sub_180019190)

No attacker trigger applies: 0x180019190 is WIL feature-enabled-state evaluation, not a signature path. The 0x1d/0x1e/0x1f bytes are WIL feature-property/state bytes on an internal feature dependency list and are not attacker-supplied certificate fields. The patched change is a (state & 0xc00) != 0xc00 feature-flag gate plus a wil_details_AreDependenciesEnabled (sub_180018fcc) call.

Finding #2 — SymCryptRsakeyCalculatePrivateFields (sub_180002D0C)

No attacker trigger applies: 0x180002D0C is a statically-linked SymCrypt RSA CRT private-field routine. The same function exists in the patched binary at 0x18000E1E4; the apparent diff is a build relocation with register-allocation churn, not a signature-count or cross-validation change.

Finding #4 — SbpParseSignatureDatabase / SbpParseSignatureDatabaseFromBuffer

No attacker trigger applies. The entry counts in the unpatched parser are held in 32-bit registers and the length comes from a 32-bit EFI-variable size, so (count1 + count2) * 0x10 + Size cannot wrap a 64-bit total; there is no undersized allocation to reach. The per-entry bounds checks that constrain the parse loop are present in both builds. The patched change is a refactor into a new helper plus defense-in-depth RtlULongLongMult guards and support for a third entry category.


6. Exploit Primitive & Development Notes

Primitives provided

Finding Primitive
#1 (wil_details_GetCurrentFeatureEnabledState) None — WIL feature-staging evaluation, not a signature path.
#2 (SymCryptRsakeyCalculatePrivateFields) None — relocated SymCrypt RSA math, not a signature path.
#3 (CiEvaluatePolicyInfoForCatalog) None — feature-gated new catalog enforcement with the old validation path retained; no demonstrable bypass in the unpatched build.
#4 (SbpParseSignatureDatabase / SbpParseSignatureDatabaseFromBuffer) None — unpatched size arithmetic uses 32-bit counts and cannot wrap; per-entry bounds checks present in both builds.

Assessment

No finding yields an attacker-reachable exploit primitive. Findings #1 and #2 are not signature paths; Finding #3 is a staged rollout of a new enforcement path with the pre-existing path retained; Finding #4 is a parser refactor plus defense-in-depth arithmetic guards with no reachable overflow. There is no undersized allocation, out-of-bounds write, or policy bypass to develop, so no exploitation notes or mitigation-bypass analysis apply.


7. Debugger PoC Playbook

This section is oriented toward an analyst with WinDbg/KD attached to the unpatched ci.dll. Symbols are assumed to be loaded (ci!sub_*).

Finding #1 — wil_details_GetCurrentFeatureEnabledState (sub_180019190) feature-state walk

Breakpoints (ready to paste):

bp ci!wil_details_GetCurrentFeatureEnabledState        ; function entry; RCX=arg1, RDX=arg2 (result out)
bp ci!wil_details_GetCurrentFeatureEnabledState+0x8e   ; start of dependency loop; RDI=current entry
bp ci!wil_details_FeatureStateCache_ReevaluateCachedFeatureEnabledState  ; sub_18001901c recompute path

What to inspect at each breakpoint:

  • At entry: the feature dependency list head is at [rcx+0x20].
  • At +0x8e (loop head): r rdi → current dependency entry pointer. db @rdi+0x1d l3 reads the WIL feature-property/state bytes; dd @rdi l1 reads the cached-state DWORD (bit 1 selects reuse vs recompute).
  • bl (low byte of rbx) tracks the running state mask.

Key offsets (unpatched):

Offset Instruction Note
0x18001925e loop start RDI = current dependency entry
0x180019272 cmp [rcx+0x1e]/[rcx+0x1d]/[rcx+0x1f] WIL feature-property byte checks
0x180019286 mov eax, 1; jmp reuse cached feature state
0x18001928d call sub_18001901c recompute cached feature state
0x1800192c9 state mask update accumulate step

Note: This is WIL feature-staging evaluation; there is no attacker-facing trigger and no Authenticode input. The patched function at 0x1800192B8 adds a (state & 0xc00) != 0xc00 gate and a wil_details_AreDependenciesEnabled (sub_180018fcc) call.

Expected observation: - The two versions differ by the 0xc00 feature-flag gate and the wil_details_AreDependenciesEnabled (sub_180018fcc) call; no signature-verification verdict is produced here.

Struct/offset notes:

Offset Field
arg1+0x20 WIL feature dependency list head — pointer to list of 8-byte pointers
entry+0x00 cached-state DWORD; bit 1 (0x2) selects reuse vs recompute
entry+0x1d WIL feature-property/state byte
entry+0x1e WIL feature-property/state byte
entry+0x1f WIL feature-property/state byte

Finding #2 — SymCryptRsakeyCalculatePrivateFields (sub_180002D0C)

Breakpoints:

bp ci!SymCryptRsakeyCalculatePrivateFields    ; function entry (0x180002D0C)

What to inspect:

  • This is SymCrypt RSA CRT private-field computation. The offsets 0x180002D81, 0x180002E3C, 0x180002FB3, 0x180002FC5 are internal branches of that big-integer math. The same function exists at 0x18000E1E4 in the patched binary; the diff is a build relocation, not a verification change.

Expected observation: - No signature-verification behavior; identical semantics across both builds.


Finding #4 — SbpParseSignatureDatabase / SbpParseSignatureDatabaseFromBuffer

Breakpoints (to confirm the identity/refactor, not an exploit):

bp ci!SbpParseSignatureDatabase             ; unpatched parser (0x18000BDDC), inline parse
bp ci!SbpParseSignatureDatabaseFromBuffer   ; patched helper (0x18001E1DC), RCX=buffer

In the unpatched parser, the allocation at 0x18000BE49 computes (count1 + count2) * 0x10 + Size from 32-bit counts, and the per-entry bounds checks at 0x18000BE930x18000BEAD bound the loop. In the patched helper, the same bounds checks appear at 0x18001E3680x18001E38E, preceded by the added RtlULongLongMult checked multiplies and compare-with-addend guards (0x18001E29A, 0x18001E2CA, 0x18001E2DD). No wrap is reachable with realistic inputs in either build; there is no PoC.


8. Changed Functions — Full Triage

Function Sim. Change type Note
wil_details_GetCurrentFeatureEnabledState (sub_180019190) 0.5265 Behavioral WIL feature-staging evaluation (patched at 0x1800192B8); new 0xc00 feature-flag gate and wil_details_AreDependenciesEnabled (sub_180018fcc) call. Not signature verification.
SymCryptRsakeyCalculatePrivateFields (sub_180002D0C) 0.7010 Cosmetic Statically-linked SymCrypt RSA CRT math; same function relocated to 0x18000E1E4. Build relocation / register-allocation churn, not a verification change.
CiEvaluatePolicyInfoForCatalog (0x1800E0384 → 0x1800E4C58) 0.8057 Behavioral Feature-staged: new package-family-name RtlEqualUnicodeString check gated behind Feature_TrustedLaunchSysAppsOSCatalog__private_IsEnabledDeviceUsageNoInline + 0x40000 flag; old KappxValidateCatalogForTrustedLaunch path retained. 0xC00/0xC08/0xBF8 → 0xC20/0xC28/0xC18 is a struct-layout move. Not a delivered fix.
SbpParseSignatureDatabase (0x18000BDDC)SbpParseSignatureDatabaseFromBuffer (0x18001E1DC) 0.1828 Behavioral Parse logic refactored into a new helper (mispaired by the diff engine with I_MapFileHashes (0x180088580)); adds a third entry category and RtlULongLongMult defense-in-depth guards. Per-entry bounds checks unchanged; unpatched 32-bit-count arithmetic cannot overflow. Not a vuln fix.
CipFindFileHash (sub_1800D9CDC) 0.9142 Behavioral Threads sub_1800196ec() (feature/capability check) into dispatch loop; adds handling for +0xc20/+0xc28 fields. Extends framework, not a direct vuln fix.
CipMinCryptToAuthRoot2 (sub_180054C04) 0.7953 Behavioral Adds arg5 output for cert verification status; new EKU validation gating via sub_180018df8(); new cert policy validator sub_1800d2b90 replacing sub_1800cf570.
CipValidateEndpointSecPolicyFile (sub_180058470) 0.8031 Behavioral PE signature file read path gated by sub_180018df8(); new sub_1800599a0 alternate path; new parsing helpers.
CipShouldRevalidateForPolicyChange (sub_1800675E4) 0.5609 Cosmetic Address-range helper swaps global range-boundary variable (data_180043370 → data_180045368) and simplifies conditionals — build-time symbol move only.

Cosmetic/register-allocation differences (typical across all functions): reordering of mov/xor prologues, setcc/cmovcc rewrites, stack slot relocation of locals (var_58, var_b8, var_c8, etc.), and conversion of while(true)+break into do/while. None of these affect control flow at a security boundary beyond what's noted above.


9. Unmatched Functions

The diff reports 0 added and 0 removed functions because it force-paired every function by nearest match. In reality the patched build introduces the new helper SbpParseSignatureDatabaseFromBuffer (0x18001E1DC), which the diff engine paired against the unrelated unpatched I_MapFileHashes (0x180088580) (similarity 0.18); this is a refactor of the signature-database parse path rather than a deleted sanitizer or a new mitigation-check helper.


10. Confidence & Caveats

Confidence: High that this patch carries no delivered security-relevant change. For Finding #3, the new package-family-name check in the patched CiEvaluatePolicyInfoForCatalog @ 0x1800E4C58 runs only under the Feature_TrustedLaunchSysAppsOSCatalog__private_IsEnabledDeviceUsageNoInline staging gate plus the *(rsi+0xA98) & 0x40000 flag, and the pre-existing KappxValidateCatalogForTrustedLaunch path is retained; the offset moves are a struct-layout change. For Finding #4, the unpatched counterpart is exactly SbpParseSignatureDatabase @ 0x18000BDDC; its allocation uses 32-bit counts ((count1 + count2) * 0x10 + Size) that cannot wrap a 64-bit total, and the per-entry bounds checks are identical in both builds, so the added RtlULongLongMult guards are defense-in-depth. Findings #1 (wil_details_GetCurrentFeatureEnabledState) and #2 (SymCryptRsakeyCalculatePrivateFields) resolve by exact header to WIL feature-staging machinery and statically-linked SymCrypt RSA math respectively, and are not signature-verification paths.

Assumptions made:

  • The 0x1d/0x1e/0x1f bytes and +0x20 list at 0x180019190 are WIL feature-property bytes and a WIL feature dependency list, per the exact symbol wil_details_GetCurrentFeatureEnabledState.
  • 0x180002D0C and its patched twin 0x18000E1E4 are the same SymCrypt routine; the diff is a build relocation.

What to verify manually:

  1. Confirm the default state of the Feature_TrustedLaunchSysAppsOSCatalog staging flag; the new CiEvaluatePolicyInfoForCatalog branch is inert unless that gate and the 0x40000 context flag are both set, and the old validation path is retained regardless.
  2. Confirm that 0x180019190 / 0x180002D0C play no role in the signature-verification verdict by tracing their callers.