i8042prt.sys — version-churn diff; keyboard-hook IOCTL handler identical in both builds
KB5073723
1. Overview
- Unpatched Binary:
i8042prt_unpatched.sys(file version 10.0.17763.1) - Patched Binary:
i8042prt_patched.sys(file version 10.0.28000.1896) - Overall Similarity Score: 0.5167
- Diff Statistics:
- Matched Functions: 183
- Changed Functions: 157
- Identical Functions: 26
- Unmatched Functions: 0 (in both directions)
- Verdict: No security-relevant change. The two binaries are separated by an approximately eight-year version gap (a 2018 build versus a 2026 build), not an adjacent security update, and no CVE is attributed to this component. The large diff is explained entirely by non-security churn: image rebase (0x1C0000000 to 0x140000000), WPP trace-enable gating,
ExAllocatePoolWithTagtoExAllocatePool2modernization, and compiler register-allocation and instruction-scheduling differences. The keyboard-hook IOCTL handler and interrupt service routine that a race-condition claim would rest on are byte-for-byte equivalent across the two builds.
2. Vulnerability Summary
Finding: Keyboard-hook IOCTL callback update — no change between builds
- Severity: None (no security-relevant change)
- Vulnerability Class: N/A
- Affected Function:
I8xInternalDeviceControl(unpatched at0x1C0002B30; content-matched handler in the patched build begins the equivalent basic block at0x1400076E0).
Analysis:
I8xInternalDeviceControl handles internal IOCTLs for the PS/2 keyboard/mouse port driver. One branch handles IOCTL_INTERNAL_I8042_HOOK_KEYBOARD (0xB3FC3), which lets an upper keyboard-filter driver install an ISR hook. After checking the input buffer is at least 0x30 bytes, the handler copies three caller-supplied pointer-sized values from the Type3InputBuffer into the device extension: a context value at +0x3A8, and two routine pointers at +0x398 and +0x3A0 (each stored only if non-zero). The keyboard ISR later invokes the pointer at +0x3A0, passing the context at +0x3A8.
The store sequence in the IOCTL handler is not wrapped in KeSynchronizeExecution, and the ISR-side invocation is guarded by Control Flow Guard (__guard_dispatch_icall_fptr). Both of these facts are true of both builds. The patched build performs the identical three stores at 0x1400076E7 / 0x1400076F7 / 0x140007707 and the identical CFG-guarded ISR call at 0x140002195. Nothing was synchronized, hardened, or otherwise altered in this path. There is therefore no direction of change to attribute a fix to.
Additional context that argues against attacker reachability even if the path is considered in isolation: IOCTL_INTERNAL_I8042_HOOK_KEYBOARD is a METHOD_NEITHER, FILE_ANY_ACCESS internal device-control code. It is issued by a kernel-mode upper-filter driver during device setup, not by user-mode callers, and the hook pointer it installs is a kernel function address supplied by that trusted driver. KeSynchronizeExecution appears 20 times in each build (unchanged), so the driver's use of interrupt synchronization is not what differs between the two versions.
3. Pseudocode Diff
The keyboard-hook handler is functionally identical in both builds. The stores below appear the same way in each.
// IOCTL_INTERNAL_I8042_HOOK_KEYBOARD (0xB3FC3) — SAME in unpatched and patched
if (CurrentStackLocation->InputBufferLength < 0x30)
return STATUS_INVALID_PARAMETER; // C000000D on the short-buffer path
hook = CurrentStackLocation->Type3InputBuffer;
DeviceExt[+0x3A8] = hook[0]; // context (always stored)
if (hook[1] != 0) DeviceExt[+0x398] = hook[1]; // routine pointer (stored if non-zero)
if (hook[2] != 0) DeviceExt[+0x3A0] = hook[2]; // routine pointer (stored if non-zero)
// Keyboard ISR — SAME in both builds
fp = DeviceExt[+0x3A0];
if (fp != 0)
__guard_dispatch_icall_fptr(/* rcx = */ DeviceExt[+0x3A8], ... ); // CFG-guarded
4. Assembly Analysis
The IOCTL handler store sequence and the ISR invocation are shown from both binaries. They match instruction-for-instruction (only the image base and register allocation differ).
I8xInternalDeviceControl — hook handler, UNPATCHED (0x1C0002B30)
00000001C0002C16 cmp dword ptr [rdi+10h], 30h ; InputBufferLength >= 0x30
00000001C0002C1A jnb short 0x1C0002C25
00000001C0002C25 mov rcx, [rdi+20h] ; Type3InputBuffer
00000001C0002C29 mov rax, [rcx]
00000001C0002C2C mov [rbx+3A8h], rax ; store context
00000001C0002C33 mov rax, [rcx+8]
00000001C0002C37 test rax, rax
00000001C0002C3A jz short 0x1C0002C43
00000001C0002C3C mov [rbx+398h], rax ; store routine ptr (if non-zero)
00000001C0002C43 mov rax, [rcx+10h]
00000001C0002C47 test rax, rax
00000001C0002C4A jz 0x1C0002DFC
00000001C0002C50 mov [rbx+3A0h], rax ; store routine ptr (if non-zero)
I8xInternalDeviceControl — hook handler, PATCHED (equivalent block at 0x1400076C2)
00000001400076C2 cmp dword ptr [rsi+10h], 30h ; InputBufferLength >= 0x30
00000001400076C6 jnb short 0x1400076E0
00000001400076E0 mov rcx, [rsi+20h] ; Type3InputBuffer
00000001400076E4 mov rax, [rcx]
00000001400076E7 mov [rdi+3A8h], rax ; store context
00000001400076EE mov rax, [rcx+8]
00000001400076F2 test rax, rax
00000001400076F5 jz short 0x1400076FE
00000001400076F7 mov [rdi+398h], rax ; store routine ptr (if non-zero)
00000001400076FE mov rax, [rcx+10h]
0000000140007702 test rax, rax
0000000140007705 jz short 0x14000770E
0000000140007707 mov [rdi+3A0h], rax ; store routine ptr (if non-zero)
Keyboard ISR invocation — UNPATCHED (I8042KeyboardInterruptService, 0x1C0006050)
00000001C0006201 mov rax, [rbx+3A0h] ; hook routine pointer
00000001C0006208 test rax, rax
00000001C000620B jz short 0x1C0006267 ; skip if none installed
00000001C000624C mov rcx, [rbx+3A8h] ; context -> first argument
00000001C0006253 call cs:__guard_dispatch_icall_fptr ; CFG-guarded indirect call
Keyboard ISR invocation — PATCHED (content-matched ISR at 0x140001DD0)
0000000140001E78 mov rax, [rdi+3A0h] ; hook routine pointer
0000000140001E7F test rax, rax
0000000140001E82 jnz 0x14000214E ; invoke if installed
000000014000218E mov rcx, [rdi+3A8h] ; context -> first argument
0000000140002195 call _guard_dispatch_icall ; CFG-guarded indirect call
The store sequence is unsynchronized in both builds, and the ISR call is Control-Flow-Guard-protected in both builds. There is no KeSynchronizeExecution in this path in either build.
5. Trigger Conditions
Not applicable. There is no security-relevant change to trigger. For completeness, the handler in question serves IOCTL_INTERNAL_I8042_HOOK_KEYBOARD (0xB3FC3), a METHOD_NEITHER internal control code issued by a kernel-mode upper keyboard-filter driver during device setup. It is not part of the user-mode device-control surface, and the value it installs is a kernel routine pointer chosen by that trusted driver.
6. Exploit Primitive & Development Notes
Not applicable. No control-flow-hijack, arbitrary-write, or information-disclosure primitive is demonstrable, because the code path is identical in both builds and the ISR-side call is CFG-guarded. No exploit chain is claimed.
7. Debugger PoC Playbook
Not applicable. There is no vulnerability to reproduce. The addresses of interest, if an analyst wishes to confirm the equivalence described above, are the IOCTL-handler store block (unpatched 0x1C0002C25, patched 0x1400076E0) and the CFG-guarded ISR call (unpatched 0x1C0006253, patched 0x140002195).
8. Changed Functions — Full Triage
Every function the diff flagged as changed was reviewed; none carries a security-relevant behavioral change. The differences are the non-security churn categories noted in the Overview.
I8xInternalDeviceControl(0.1121 sim): Keyboard/mouse IOCTL dispatcher. The hook-handler store sequence and buffer-size checks are identical (see Section 4). The low similarity comes from WPP trace-enable gating (off_140012008compares replacing inline recorder calls) and theExAllocatePoolWithTagtoExAllocatePool2change in the connect path. No synchronization or validation change.I8042KeyboardInterruptService(0.2454 sim): Reads the hook pointer at+0x3A0and invokes it via CFG-guarded indirect call, passing context at+0x3A8. Identical logic in both builds; the diff is WPP gating and register allocation.I8042MouseInterruptService(0.0759 sim): Mouse packet state machine. Reviewed; queue writes and packet handling match. Low similarity is dominated by WPP gating and scheduling differences, not a synchronization or bounds change.I8xKeyboardServiceParameters(0.1689 sim): Registry parameter parsing. Defaults, theKeyboardFailedReset > 2clamp, the zero-queue-size fallback, and the12 * countallocation math are identical in both builds. Registry values here are HKLM/administrator-controlled, not an attacker boundary. No validation added or removed.- WPP / compiler churn (grouped):
I8xWriteDataToKeyboardQueue(0.4127 sim): 12-byte entry,head==tail && count!=0full check,0xFFoverflow marker,+12advance, wraparound — all identical; only WPP gating differs.I8xWriteDataToMouseQueue(0.3248 sim): 24-byte entry, matching full check,+24advance, matching wraparound — all identical; only WPP gating differs.I8xMouseServiceParameters(0.3826 sim): mouse registry configuration; churn only.I8xQueueCurrentMouseInput(0.3284 sim): queues mouse data and DPC; churn only.I8xProcessCrashDump(0.0116 sim): crash-dump scan-code path; not attacker-reachable; churn only.I8xInitializeKeyboard(0.4548 sim): hardware initialization via polled I/O; not IOCTL-reachable; churn only.
9. Unmatched Functions
There were 0 unmatched functions in either direction. No sanitizer, bounds check, or synchronization routine was added or removed as a standalone function.
10. Confidence & Caveats
- Confidence: High. The keyboard-hook store sequence and the ISR invocation were compared directly in both binaries and match instruction-for-instruction (Section 4).
KeSynchronizeExecutionappears 20 times in each build. No CVE is attributed to this component, and the two builds are an approximately eight-year version pair rather than an adjacent update. - Basis: Device-extension offsets (
+0x398,+0x3A0,+0x3A8) are taken directly from the disassembly of both builds and correspond between the IOCTL handler (writer) and the keyboard ISR (reader). - Conclusion: The previously hypothesized unsynchronized-callback race condition is not a real change: the pattern exists identically in both builds and the ISR-side call is CFG-guarded in both. This diff contains no security-relevant fix.