1. Overview

Field Value
Unpatched Binary netio_unpatched.sys
Patched Binary netio_patched.sys
Overall Similarity 0.9914
Matched Functions 1901
Changed Functions 1 (NsipInitializeBootFirmwareTableData)
Removed Functions 2 (WIL feature helpers for Feature_TCPIP_2025_Wave2_NsiIbftFix)
Added Functions 0

Verdict: The only real code change is in NsipInitializeBootFirmwareTableData (iBFT lazy initialization, 0x140049B20). The patch removes a WIL feature-staging gate for Feature_TCPIP_2025_Wave2_NsiIbftFix and reverts the function to its single unconditional (feature-off) path. The two generated WIL helper functions for that feature are dropped from the build. The iBFT query, parse, count, allocate, and read-out logic (in NsipGetBootFirmwareTableFromSystemInformation, NsipInitializeBootNicAndTargets, and the caller NsipReadBootFirmwareTableData) is byte-identical between the two builds. No memory-safety behavior is added or removed in any delivered code path. This is staged-feature-gate servicing churn, not a security fix.


2. Change Summary

Finding 1: iBFT initialization — WIL feature-gate removal (no delivered security change)

Field Detail
Severity None (informational)
Class Feature-staging / servicing churn
Affected Function NsipInitializeBootFirmwareTableData (0x140049B20)
Feature Feature_TCPIP_2025_Wave2_NsiIbftFix

What the code actually does

NsipInitializeBootFirmwareTableData performs lazy initialization of the iBFT (iSCSI Boot Firmware Table) NIC/target arrays. In the unpatched build it contains two behaviors selected by a WIL feature gate:

  • It loads Feature_TCPIP_2025_Wave2_NsiIbftFix__private_featureState (global at 0x140097370). If the cached-state bit (0x10) is set it uses the cached enabled bit (& 1); otherwise it calls the generated helper Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledDeviceUsageNoInline (0x140064BD4). This is standard WIL feature-staging inline code, not a validation routine.
  • Feature enabled path: after the iBFT query returns success, it additionally requires both global counters dword_140096CF8 and dword_140096CFC to be non-zero before proceeding to allocation; if either is zero it sets STATUS_NOT_FOUND, clears the init flag byte_140096CD8, and cleans up.
  • Feature disabled path: on query success it proceeds directly to allocation with no counter precondition.

The patched build removes the feature-state load, the helper call, and the entire feature-enabled branch. What remains is exactly the feature-disabled path: query success proceeds directly to allocation. The allocation block itself (two ExAllocatePool2 calls sized counter << 3 with tag 0x7249534E, two calls to the parser, and the cleanup that frees and nulls the three arrays) is functionally identical in both builds.

Why there is no delivered security change

  • The allocation, parse (NsipInitializeBootNicAndTargets), counter-write, and read-out (NsipReadBootFirmwareTableData) logic is byte-identical between the two builds. Any counter-accumulation or array-population behavior present in one build is present identically in the other. The patch does not add a counter reset, a bounds check, or a zeroing step, and it does not change the sizes, the tag, or the pool flags.
  • The only behavioral delta is the presence/absence of the feature-enabled precondition guard and its extra byte_140096CD8 = 0. That code sits behind a default-off staged feature gate and, if anything, is the branch that could clear the init flag when the counters are zero. Removing it does not introduce or remove a reachable memory-safety condition.
  • Feature_*__private_IsEnabled* WIL gating with the pre-existing path retained in the else branch is a staged rollout marker, not a security fix.

There is no evidence in either build of a reachable information-disclosure or denial-of-service primitive attributable to this diff.

Reachability context

NsipInitializeBootFirmwareTableData is called by NsipReadBootFirmwareTableData (0x1400498A0), which reads the parsed iBFT arrays. iBFT retrieval is exposed through NSI parameter queries reachable from user-mode network enumeration. This reachability is identical in both builds and is not altered by the patch; it is noted only for context, not as an exploit chain.


3. Pseudocode Diff

Unpatched

// NsipInitializeBootFirmwareTableData — UNPATCHED (0x140049B20)
int32_t NsipInitializeBootFirmwareTableData() {
    if (byte_140096CD8 == 0)                 // init flag
        return STATUS_NOT_FOUND;

    int32_t status = NsipGetBootFirmwareTableFromSystemInformation();

    // WIL feature-staging gate for Feature_TCPIP_2025_Wave2_NsiIbftFix
    int32_t fs = Feature_..._NsiIbftFix__private_featureState;
    int32_t enabled = (fs & 0x10) ? (fs & 1)
                                  : Feature_..._NsiIbftFix__private_IsEnabledDeviceUsageNoInline();

    if (enabled) {                           // feature-on path
        if (status < 0) goto error;
        if (dword_140096CF8 != 0 && dword_140096CFC != 0)
            goto allocate;                   // both counters non-zero
        status = STATUS_NOT_FOUND;
        byte_140096CD8 = 0;                  // clear init flag
        goto cleanup;
    }

    // feature-off path
    if (status >= 0) goto allocate;
error:
    if (status != STATUS_NOT_FOUND) goto cleanup;
    byte_140096CD8 = 0;                      // clear init flag
    goto cleanup;

allocate:
    NsipInitializeBootNicAndTargets();       // counting pass
    qword_140096CE8 = ExAllocatePool2(0x40, dword_140096CF8 << 3, 'NSIr');
    if (!qword_140096CE8) goto cleanup;
    qword_140096CF0 = ExAllocatePool2(0x40, dword_140096CFC << 3, 'NSIr');
    if (!qword_140096CF0) goto cleanup;
    NsipInitializeBootNicAndTargets();       // population pass
    return status;

cleanup:
    // frees SystemInformation, qword_140096CE8, qword_140096CF0; nulls them
    return status;
}

Patched

// NsipInitializeBootFirmwareTableData — PATCHED (0x140049B20)
int32_t NsipInitializeBootFirmwareTableData() {
    if (byte_140096CD8 == 0)
        return STATUS_NOT_FOUND;

    int32_t status = NsipGetBootFirmwareTableFromSystemInformation();
    if (status < 0) {
        if (status == STATUS_NOT_FOUND)
            byte_140096CD8 = 0;              // clear init flag
        goto cleanup;
    }

    // proceeds directly to allocation (identical to the unpatched feature-off path)
    NsipInitializeBootNicAndTargets();
    qword_140096CE8 = ExAllocatePool2(0x40, dword_140096CF8 << 3, 'NSIr');
    if (!qword_140096CE8) goto cleanup;
    qword_140096CF0 = ExAllocatePool2(0x40, dword_140096CFC << 3, 'NSIr');
    if (!qword_140096CF0) goto cleanup;
    NsipInitializeBootNicAndTargets();
    return status;

cleanup:
    // frees SystemInformation, qword_140096CE8, qword_140096CF0; nulls them
    return status;
}

Key Differences

  • The Feature_TCPIP_2025_Wave2_NsiIbftFix feature-state load and the call to Feature_..._NsiIbftFix__private_IsEnabledDeviceUsageNoInline are removed.
  • The feature-enabled branch (the counter precondition guard and its extra byte_140096CD8 = 0) is removed.
  • The remaining control flow equals the unpatched feature-off path. The allocation, parse, and cleanup blocks are functionally unchanged.

4. Assembly Analysis

Unpatched NsipInitializeBootFirmwareTableData (0x140049B20)

0000000140049B20  mov     [rsp+arg_8], rbx
0000000140049B25  push    rdi
0000000140049B26  sub     rsp, 20h
0000000140049B2A  cmp     cs:byte_140096CD8, 0                 ; init flag
0000000140049B31  jnz     short loc_140049B3D
0000000140049B33  mov     eax, 0C0000225h                      ; STATUS_NOT_FOUND
0000000140049B38  jmp     loc_140049C60
0000000140049B3D  call    NsipGetBootFirmwareTableFromSystemInformation
0000000140049B42  mov     ebx, eax
0000000140049B44  mov     edi, eax
; --- WIL feature gate for Feature_TCPIP_2025_Wave2_NsiIbftFix (removed in patch) ---
0000000140049B46  mov     eax, cs:Feature_TCPIP_2025_Wave2_NsiIbftFix__private_featureState
0000000140049B4C  test    al, 10h                              ; cached-state bit
0000000140049B4E  jz      short loc_140049B55
0000000140049B50  and     eax, 1                               ; cached enabled bit
0000000140049B53  jmp     short loc_140049B5A
0000000140049B55  call    Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledDeviceUsageNoInline
0000000140049B5A  test    eax, eax
0000000140049B5C  jz      short loc_140049B7B                  ; feature off -> old path
; --- feature-on path ---
0000000140049B5E  test    ebx, ebx
0000000140049B60  js      short loc_140049B7F
0000000140049B62  cmp     cs:dword_140096CF8, 0                ; counter precondition
0000000140049B69  jz      short loc_140049B74
0000000140049B6B  cmp     cs:dword_140096CFC, 0
0000000140049B72  jnz     short loc_140049B90                  ; both non-zero -> allocate
0000000140049B74  mov     ebx, 0C0000225h
0000000140049B79  jmp     short loc_140049B87
; --- feature-off path ---
0000000140049B7B  test    edi, edi
0000000140049B7D  jns     short loc_140049B90                  ; status >= 0 -> allocate
0000000140049B7F  cmp     ebx, 0C0000225h
0000000140049B85  jnz     short loc_140049BF1
0000000140049B87  mov     cs:byte_140096CD8, 0                 ; clear init flag
0000000140049B8E  jmp     short loc_140049BF1
; --- allocation / parse ---
0000000140049B90  call    NsipInitializeBootNicAndTargets      ; counting pass
0000000140049B95  mov     edx, cs:dword_140096CF8
0000000140049B9B  mov     edi, 7249534Eh                       ; 'NSIr'
0000000140049BA0  shl     edx, 3
0000000140049BA3  mov     r8d, edi
0000000140049BA6  mov     ecx, 40h
0000000140049BAB  call    cs:__imp_ExAllocatePool2
0000000140049BB7  mov     cs:qword_140096CE8, rax
0000000140049BBE  test    rax, rax
0000000140049BC1  jz      short loc_140049BEC
0000000140049BC3  mov     edx, cs:dword_140096CFC
0000000140049BC9  mov     r8d, edi
0000000140049BCC  shl     edx, 3
0000000140049BCF  mov     ecx, 40h
0000000140049BD4  call    cs:__imp_ExAllocatePool2
0000000140049BE0  mov     cs:qword_140096CF0, rax
0000000140049BE7  test    rax, rax
0000000140049BEA  jnz     short loc_140049C59
0000000140049BEC  mov     ebx, 0C000009Ah                      ; STATUS_INSUFFICIENT_RESOURCES
; --- cleanup (frees arrays, nulls pointers) ---
0000000140049BF1  mov     rcx, cs:SystemInformation
0000000140049BF8  test    rcx, rcx
0000000140049BFB  jz      short loc_140049C5E
0000000140049BFD  xor     edx, edx
0000000140049BFF  call    cs:__imp_ExFreePoolWithTag
0000000140049C0B  mov     rcx, cs:qword_140096CE8
0000000140049C12  and     cs:SystemInformation, 0
0000000140049C1A  test    rcx, rcx
0000000140049C1D  jz      short loc_140049C35
0000000140049C1F  xor     edx, edx
0000000140049C21  call    cs:__imp_ExFreePoolWithTag
0000000140049C2D  and     cs:qword_140096CE8, 0
0000000140049C35  mov     rcx, cs:qword_140096CF0
0000000140049C3C  test    rcx, rcx
0000000140049C3F  jz      short loc_140049C5E
0000000140049C41  xor     edx, edx
0000000140049C43  call    cs:__imp_ExFreePoolWithTag
0000000140049C4F  and     cs:qword_140096CF0, 0
0000000140049C57  jmp     short loc_140049C5E
0000000140049C59  call    NsipInitializeBootNicAndTargets      ; population pass
0000000140049C5E  mov     eax, ebx
0000000140049C60  mov     rbx, [rsp+28h+arg_8]
0000000140049C65  add     rsp, 20h
0000000140049C69  pop     rdi
0000000140049C6A  retn

Patched NsipInitializeBootFirmwareTableData (0x140049B20)

0000000140049B20  push    rbx
0000000140049B22  sub     rsp, 20h
0000000140049B26  cmp     cs:byte_140096CD8, 0
0000000140049B2D  jnz     short loc_140049B39
0000000140049B2F  mov     eax, 0C0000225h
0000000140049B34  jmp     loc_140049C25
0000000140049B39  call    NsipGetBootFirmwareTableFromSystemInformation
0000000140049B3E  mov     ebx, eax
0000000140049B40  test    eax, eax
0000000140049B42  jns     short loc_140049B54                  ; status >= 0 -> allocate
0000000140049B44  cmp     eax, 0C0000225h
0000000140049B49  jnz     short loc_140049BB6
0000000140049B4B  mov     cs:byte_140096CD8, 0                 ; clear init flag (NOT_FOUND only)
0000000140049B52  jmp     short loc_140049BB6
0000000140049B54  call    NsipInitializeBootNicAndTargets
0000000140049B59  mov     edx, cs:dword_140096CF8
0000000140049B5F  mov     ecx, 40h
0000000140049B64  shl     edx, 3
0000000140049B67  mov     r8d, 7249534Eh
0000000140049B6D  call    cs:__imp_ExAllocatePool2
0000000140049B79  mov     cs:qword_140096CE8, rax
0000000140049B80  test    rax, rax
0000000140049B83  jz      short loc_140049BB1
0000000140049B85  mov     edx, cs:dword_140096CFC
0000000140049B8B  mov     ecx, 40h
0000000140049B90  shl     edx, 3
0000000140049B93  mov     r8d, 7249534Eh
0000000140049B99  call    cs:__imp_ExAllocatePool2
0000000140049BA5  mov     cs:qword_140096CF0, rax
0000000140049BAC  test    rax, rax
0000000140049BAF  jnz     short loc_140049C1E
0000000140049BB1  mov     ebx, 0C000009Ah
0000000140049BB6  mov     rcx, cs:SystemInformation
;   ... cleanup identical in structure to the unpatched build ...
0000000140049C1E  call    NsipInitializeBootNicAndTargets
0000000140049C23  mov     eax, ebx
0000000140049C25  add     rsp, 20h
0000000140049C29  pop     rbx
0000000140049C2A  retn

What changed: the block from 0x140049B46 through 0x140049B8E (feature-state load, WIL helper call, feature-enabled counter guard, and its extra init-flag clear) is deleted. Control flow collapses to a single test eax, eax / jns on the query status. The allocation and cleanup blocks are unchanged in behavior (only register scheduling of the pool tag differs: unpatched caches 'NSIr' in edi, patched uses the immediate directly).


5. Direction and Reachability

  • Direction: the patched build retains the unpatched feature-off path. The removed code is the staged Feature_TCPIP_2025_Wave2_NsiIbftFix branch. Neither path adds a bounds check, a counter reset, or memory zeroing relative to the other.
  • Query function: NsipGetBootFirmwareTableFromSystemInformation is the same function in both builds, relocated from 0x140064C28 (unpatched) to 0x140064B94 (patched) by the removal of the two WIL helpers ahead of it. It is not a renamed or different routine.
  • Parser: NsipInitializeBootNicAndTargets is the same function in both builds, relocated from 0x140064D54 to 0x140064CC0. Its counter-write logic (dword_140096CF8, dword_140096CFC) is byte-identical between builds.
  • Reader/caller: NsipReadBootFirmwareTableData (0x1400498A0) is byte-identical between builds; its read loop over dword_140096CFC entries is unchanged.

6. Global / Symbol Reference

Address Type Symbol Role
0x140096CD8 byte init flag (byte_140096CD8) Gates iBFT initialization; cleared on STATUS_NOT_FOUND in both builds
0x140096CE0 qword SystemInformation Raw iBFT buffer from the system-information query
0x140096CE8 qword qword_140096CE8 Primary array pointer (size dword_140096CF8 << 3, tag 'NSIr')
0x140096CF0 qword qword_140096CF0 Secondary array pointer (size dword_140096CFC << 3, tag 'NSIr')
0x140096CF8 dword dword_140096CF8 Counter written by the parser; identical logic in both builds
0x140096CFC dword dword_140096CFC Counter incremented by the parser; identical logic in both builds
0x140097370 dword Feature_TCPIP_2025_Wave2_NsiIbftFix__private_featureState WIL feature-state cache (gate removed in patch)

7. Changed / Removed Functions — Full Triage

NsipInitializeBootFirmwareTableData (0x140049B20) — real logic change

  • Similarity: 0.8083
  • Change type: feature-gate removal (staging churn), not security-relevant.
  • What changed: removal of the Feature_TCPIP_2025_Wave2_NsiIbftFix WIL gate and its feature-enabled counter-precondition branch (0x140049B46–0x140049B8E), reverting to the unconditional feature-off path. The rdi second status copy is no longer needed.
  • Impact: none on delivered memory-safety behavior; the allocation/parse/cleanup blocks are functionally unchanged.

Removed functions (unpatched only)

  • Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledDeviceUsageNoInline (0x140064BD4) — generated WIL feature-check helper.
  • Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledFallback (0x140064C0C) — generated WIL fallback helper.

Both are dropped because the feature was removed from the staging table.

Cosmetic-only deltas (not code changes)

An independent content diff of both builds (matching by function content across relocation) flags a set of functions — including NsiGetParameterEx, NsiEnumerateObjectsAllParametersEx, the NsipValidate*Request family, several Stream*/Wfp*/Kfd* routines, and WIL staging infrastructure (wil_InitializeFeatureStaging, wil_details_UnregisterFeatureStagingChangeNotification). Direct inspection shows these differ only in disassembler labeling of the same WPP_MAIN_CB structure fields (e.g. +148h vs .DeviceObjectExtension, .DeviceLock.Header vs .ActiveThreadCount) and in jump-table / data addresses shifted by the small data-section move caused by removing the two helpers. Instruction bytes and offsets are equivalent; none are code changes.


8. Unmatched Functions

Direction Count Details
Removed (unpatched only) 2 Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledDeviceUsageNoInline (0x140064BD4), Feature_TCPIP_2025_Wave2_NsiIbftFix__private_IsEnabledFallback (0x140064C0C)
Added (patched only) 0 None

The removals are the WIL helper functions generated for the retired feature. No sanitizer, bounds-check, or mitigation function was added.


9. Confidence & Caveats

Confidence: High

The diff is clean and the single real change is fully visible in the disassembly of both builds. The surrounding iBFT query, parse, count, allocate, and read-out functions are byte-identical (only relocated), so the change is isolated to the removal of one staged WIL feature gate.

Notes

  • Feature_TCPIP_2025_Wave2_NsiIbftFix is a WIL feature-staging gate. In the unpatched build both the feature-on and feature-off paths are present; the patched build removes the gate and retains the feature-off path. This is a staged-rollout / servicing pattern.
  • No reachable information-disclosure or denial-of-service primitive is attributable to this diff: the code that performs allocation and read-out is unchanged between builds, and no counter reset, bounds check, or zeroing was added or removed.