The W32THREADNONPAGED is a small structure that Windows 10 introduced to sit between the KTHREAD and W32THREAD structures (the latter being a reduced THREADINFO). Where the Win32Thread member of the KTHREAD used to point directly to the W32THREAD, it now points to the W32THREADNONPAGED. The W32THREAD itself is one pointer away, its address being held in the pW32Thread member. Note that the Win32ThreadInfo member of the TEB, which is not just readable but writable in user mode, continues to hold the (kernel-mode) address of the W32THREAD.

As its name suggests, the W32THREADNONPAGED is allocated from non-paged (no-execute) pool. Less obviously, and in contrast to the W32THREAD, the allocation is global, i.e., not per-session.

Offset Definition Versions
W32THREAD *pW32Thread;
10.0 and higher
LONGLONG llQPCUserCritAcquire;
10.0 and higher
ULONGLONG ullUserCritAcquireToken;
10.0 and higher

If only for now, what’s gained from the extra distance between the KTHREAD and the W32THREAD is that WIN32K gets two new items of per-thread data that can be touched without fear of causing paging. These two support performance monitoring of WIN32K’s use of critical sections. For instance, asking to enter a critical section, as by the WIN32KBASE export EnterCrit, loads llQPCUserCritAcquire with the current time (in units of the high-frequency performance counter) so that when the called function is about to return, having acquired the critical section, the time spent acquiring it can be traced. When the thread eventually asks to leave this critical section, the time spent holding it can be traced. The ullUserCritAcquireToken records from a globally maintained sequence number of acquisitions.