The previous article, Denial Of Service In Windows 11 22H2, described an infinite recursion bug triggered by a crafted SetParent WINAPI call in the Windows kernel. This bug was not triggered in the Windows Insider Canary build due to code updates. This article will present a comparative analysis of the updated code against the old buggy code. This will be done via a patch diff, typically downloading a target patch.
Table of Contents
ToggleA patch is typically a collection of files which have been updated to correct various mistakes or bugs from old versions of files. Once the patch is downloaded, the target file(s) are identified to compare the code against that from old files. In this case, the target file was the win32kfull.sys driver. The updated new driver’s version is 10.0.25951.1000, while the old version is 10.0.22621.2283. We loaded the old and new drivers in IDA Pro instead of using Bin Diff as a different approach to diff’ing code. The symbols for both drivers were also available at the Microsoft Symbol Server.
Static comparative analysis
As per the debugger stack trace we shared in the previous blog, the bug was stack exhaustion due to the potentially infinite recursion of the zzzSetWindowCompositionCloak function. The buggy and updated versions of win32kfull.sys were loaded with corresponding symbols in IDA PRO. We can see changes to the code of the zzzSetWindowCompositionCloak function, as illustrated below. For the sake of simplicity, in every illustration, we have included the buggy/old code on the left side and the not-buggy code on the right side, unless otherwise mentioned.

The changes include moving the primary code of ‘zzzSetWindowCompositionCloak’ to the ‘zzzSetWindowCompositionCloakWorker’ function and addition of calls to ‘SDGGetUserSessionState’, ‘AtomicExecutionCheck::AtomicExecutionCheck()’ and ‘AtomicExecutionCheck::Disarm’. Also, note the changes in function prototypes, that is, the number and data type of parameters. The function in the old driver takes three parameters, while that in the updated driver takes two parameters.

Checking the code of the ‘zzzSetWindowCompositionCloakWorker’ function in an updated driver, we find that recursion is still present, albeit it is for ‘zzzSetWindowCompositionCloakWorker’. We assume the additional calls mentioned above may not directly relate to the bug not getting triggered.
Comparing the old code for ‘zzzSetWindowCompositionCloak’ with the new code for ‘zzzSetWindowCompositionCloakWorker’, there are a few changes made, like the addition of some checking code and removal of blocks of code.
Presence of checks
The following checking code is not present in the old buggy driver but is added to the newly updated driver.

Removal of blocks of code
Apart from the checks introduced, we also see that blocks of code have been removed in the new code, as shown below.





NextOwnedWindow code
There is a call to ‘NextOwnedWindow()’, the return value of which can break out of the loop and, hence, possibly out of the recursion. Checking the code for ‘NextOwnedWindow()’, it is found to have no changes in the new driver. Therefore, the call to ‘NextOwnedWindow()’ in the new driver also possibly returns the same value as that in the old driver.

Comprehensively, we can make the following assumptions at this stage –
- The code in ‘NextOwnedWindow’ may not be directly responsible for the bug, as this code is the same in both drivers.
- The changes in the ‘zzzSetWindowCompositionCloakWorker’ function in the new driver may be responsible for the bug getting ‘fixed’.
Dynamic analysis
To confirm which changes were responsible for fixing the bug, we analysed the dynamic code by checking the code execution flow at run time. During this dynamic analysis, we found that the checking code in the updated driver makes the execution exit without even hitting ‘NextOwnedWindow’ or the recursive call to ‘zzzSetWindowComppsitionCloakWorker’.


tagWND structure
As per the prototype of function ‘zzzSetWindowCompositionCloakWorker’, the first parameter, in ‘rcx’, is a pointer to tagWND structure. To get a sense of values from this tagWND structure, we will use the definition reversed by Palo Alto Networks’ researchers from the article [1]. This is because Microsoft no longer maintains symbols and documentation for this structure, which has changed. The definition used is from Windows 10 21H1, as the article mentions.
For reader’s reference, we’ve included below the structure definitions from the article [1].


The updated code begins by taking a pointer from offset 0x28 of the input tagWND structure. This is the pointer ‘pWND’, as illustrated in figure 7.a., which points to another tagWND structure containing user-mode tagWND data, as described in figure 7.b.

The check is performed on byte at offset 0xE9 of this user-mode tagWND data. As shown in Figure 7.b, this byte should be the first from the ‘gap’ array after ‘dwExtraFlag’. As it is used to decide return/not return from ‘zzzSetWindowCompositionCloakWorker’, we can assume that this byte is a flag of some sort. For now, let us call it the ‘windowCompositionCloakFlag’. The ‘windowCompositionCloakFlag’ byte is zero in our analysis, as seen from run time disassembly in Figure 6 – instruction at offset 0x19 of the function zzzSetWindowCompositionCloakWorker.
Pseudo-algorithm of the fix
As per the disassembly, the following algorithm is carried out in the updated driver. In this algorithm, ‘CloakState_parameter’ is the second parameter passed to ‘zzzSetWindowCompositionCloakWorker’ and ‘value’ is calculated from the ‘windowCompositionCloakFlag’ flag. The ‘value’ is checked against ‘CloakState_parameter’, which fails. Hence, the bug is not triggered.
- Set value = most significant nibble from ‘windowCompositionCloakFlag’
- If (a penultimate bit of ‘windowCompositionCloakFlag’ is zero) OR (‘windowCompositionCloakFlag’ is signed), then value = value BITWISE_OR 1
- if (value == ‘CloakState_parameter’) then check failed
return from ‘zzzSetWindowCompositionCloakWorker.’