Geoff Chappell - Software Analyst
The public symbol file NTKRPAMP.PDB for the original release of Windows 10 tells that the kernel is built with the EX_X.H header at
d:\th\minkernel\ntos\inc
and draws from it the following type definitions:
Line Number | Type |
---|---|
34 | enum _POOL_TYPE |
259 | enum _EX_POOL_PRIORITY |
997 | struct _LOOKASIDE_LIST_EX |
1127 | struct _NPAGED_LOOKASIDE_LIST |
1324 | struct _PAGED_LOOKASIDE_LIST |
2582 | struct _WORK_QUEUE_ITEM |
2946 | struct _OWNER_ENTRY |
2959 | struct _ERESOURCE |
3502 | struct _EX_PUSH_LOCK |
3590 | struct _EX_PUSH_LOCK_AUTO_EXPAND_STATE |
3601 | struct _EX_PUSH_LOCK_AUTO_EXPAND |
3654 | struct _EX_RUNDOWN_REF |
3758 | struct _EXHANDLE |
4020 | struct _EXT_DELETE_PARAMETERS |
The header EX_X.H is not known in any Device Driver Kit (DDK) or Windows Driver Kit (WDK).
That said, all the types that the kernel is known to pick up from EX_X.H are defined in the standard header WDM.H for kernel-mode programming or in one more header, named NTOSP.H, that Microsoft has mostly kept for its own kernel-mode programming but which is available from inspection because of its disclosure in the “minwin” directory of the Windows 10 WDK for the original release and for Version 1511.
Most of the types are extracted to both the widely available WDM.H and the private NTOSP.H, but both miss at least some lines and WDM.H consistently misses more. Some accounting is possible on the assumption that although each of these headers receives only a selection of lines from EX_X.H, their selections are contiguous. Put another way, assume that WDM.H lines 21560 to 23583 and NTOSP.H lines 16296 to 20137 are generated only as extractions from EX_X.H. Then some of the EX_X.H material that goes to NTOSP.H but not to WDM.H goes to NTDDK.H or NTIFS.H instead, but most is otherwise unknown in the WDK headers.
This presumably accidental publication of NTOSP.H allows for high confidence in reconstructing the unseen EX_X.H almost completely. It may be that content is extracted from EX_X.H into the standard headers. It may be that all are extracted from yet some other source. Eithe way, the assumption seems reasonable that each line in the unseen EX_X.H is duplicated in zero or more of the published headers in an order-preserving way, leaving each of the published headers with a contiguous region of lines that can each be inferred as being also in the unseen EX_X.H.
In the table that follows, the line numbers on the left are deduced from type information in a statically linked library named CLFSMGMT.LIB which Microsoft publishes with the Software Development Kit (SDK) as if for user-mode programming. Of all sources of type information that cites EX_X.H as the header from which the compiler obtained the definitions, this is presently the one that is known to have it for the most types. The line numbers on the right are from headers as published in the WDK.
Line Number | Type | WDM.H | NTDDK.H | NTOSP.H | NTOSIFS.H |
---|---|---|---|---|---|
34 | enum _POOL_TYPE | 21560 | 16296 | ||
259 | enum _EX_POOL_PRIORITY | 21773 | 16519 | ||
997 | struct _LOOKASIDE_LIST_EX | 22401 | 17204 | ||
1127 | struct _NPAGED_LOOKASIDE_LIST | 22531 | 17334 | ||
1324 | struct _PAGED_LOOKASIDE_LIST | 22728 | 17531 | ||
2559 | enum _WORK_QUEUE_TYPE | 22966 | 18750 | ||
2582 | struct _WORK_QUEUE_ITEM | 22989 | 18773 | ||
2669 | struct _ZONE_SEGMENT_HEADER | 8244 | 18854 | ||
2674 | struct _ZONE_HEADER | 8249 | 18859 | ||
2946 | struct _OWNER_ENTRY | 23058 | 19129 | ||
2948 | anonymous union in struct _OWNER_ENTRY |
23060 | 19131 | ||
2949 | anonymous struct in anonymous union in struct _OWNER_ENTRY |
23061 | 19132 | ||
2959 | struct _ERESOURCE | 23071 | 19142 | ||
2971 | anonymous union in struct _ERESOURCE |
23083 | 19154 | ||
2973 | anonymous struct in anonymous union in struct _ERESOURCE |
23085 | 19156 | ||
3002 | anonymous union in struct _ERESOURCE |
23114 | 19185 | ||
3020 | struct _RESOURCE_HASH_ENTRY | 23132 | 19203 | ||
3027 | struct _RESOURCE_PERFORMANCE_DATA | 23139 | 19210 | ||
3502 | struct _EX_PUSH_LOCK | 19630 | 100 | ||
3537 | anonymous union in struct _EX_PUSH_LOCK |
19665 | 135 | ||
3538 | anonymous struct in anonymous union in struct _EX_PUSH_LOCK |
19666 | 136 | ||
3559 | struct _EX_PUSH_LOCK_CACHE_AWARE_LEGACY | 19687 | 157 | ||
3567 | struct _EX_PUSH_LOCK_CACHE_AWARE_PADDED | 19695 | 165 | ||
3569 | anonymous union in struct _EX_PUSH_LOCK_CACHE_AWARE_PADDED |
19697 | 167 | ||
3571 | anonymous struct in anonymous union in struct _EX_PUSH_LOCK_CACHE_AWARE_PADDED |
19699 | 169 | ||
3590 | struct _EX_PUSH_LOCK_AUTO_EXPAND_STATE | 19718 | 188 | ||
3591 | anonymous union in struct _EX_PUSH_LOCK_AUTO_EXPAND_STATE |
19719 | 189 | ||
3592 | anonymous struct in anonymous union in struct _EX_PUSH_LOCK_AUTO_EXPAND_STATE |
19720 | 190 | ||
3601 | struct _EX_PUSH_LOCK_AUTO_EXPAND | 19729 | 199 | ||
3654 | struct _EX_RUNDOWN_REF | 23460 | 19780 | ||
3660 | anonymous union in struct _EX_RUNDOWN_REF |
23466 | 19786 | ||
3689 | struct _EX_RUNDOWN_REF_CACHE_AWARE_STATE | 19815 | |||
3690 | anonymous union in struct _EX_RUNDOWN_REF_CACHE_AWARE_STATE |
19816 | |||
3691 | anonymous struct in anonymous union in struct _EX_RUNDOWN_REF_CACHE_AWARE_STATE |
19817 | |||
3712 | struct _EX_RUNDOWN_REF_CACHE_AWARE | 19838 | |||
3758 | struct _EXHANDLE | 19884 | |||
3760 | anonymous union in struct _EXHANDLE |
19886 | |||
3762 | anonymous struct in anonymous union in struct _EXHANDLE |
19888 | |||
4020 | struct _EXT_DELETE_PARAMETERS | 23583 | 20137 | ||
5908 | enum _LICENSING_TAMPER_STATE | 21941 | |||
5974 | anonymous enum | 22007 | |||
6011 | struct _EX_EXTENSION_REGISTRATION_1 | 22041 | 1345 | ||
6080 | struct EX_BOOT_DEVICE_HANDLE_ | ||||
6088 | struct _EX_BOOT_DEVICE_REGISTRATION | 22114 |
For an example of what can be learnt from this skeleton for reconstruction, consider what might be in the 1,235 lines between the opening braces for definitions of _PAGED_LOOKASIDE_LIST and _WORK_QUEUE_TYPE. Only 223 are duplicated in WDM.H. This would be better than nothing, but NTOSP.H has 1,219 lines in this space. It’s all but certain that every one of them is in the corresponding space in EX_X.H. Thus is this part of the unseen EX_X.H reconstructed almost completely from published material.
A small but useful outcome of just this part of the reconstruction is to place the numerous small inline routines that are frequently encountered in any debugging or reverse engineering of Microsoft’s kernel-mode code—not only in the kernel itself—that works with user-mode addresses. Such addresses are well-known to require careful handling—or are not known nearly well enough, depending on perspective. The kernel exports functions named ProbeForRead and ProbeForWrite that help with one aspect of this care, but Microsoft’s own kernel-mode programmers have these as macros and have long benefited from the use of many inline elaborations that are specialised to probing for particular types and small structures. It must make their code more readable.
Debugging their code, including from outside Microsoft, is similarly much easier for recognising the macros and the inlining. Because of this, the inline routines must have attracted attention from very early in any study of kernel-mode Windows that’s worth calling a study. They will have been well-known long before the presumably accidental publication of NTOSP.H. Not only does their inline usage get them named in the kernel’s public symbol files starting with Windows 8, but they are named in older symbol files from having sometimes been instantiated instead of inlined. What was not known (without source-code access) until the NTOSP.H disclosure is which header provides the kernel with these macros and inline routines.
Is it vital to know that the kernel gets them from an unpublished header named EX_X.H? Not for itself, of course not. Does it help with an understanding of how Windows, still by far the world’s most sophisticated operating system in general use, is organised? In its small way, yes.