The public symbol files NTKRPAMP.PDB and NTKRNLMP.PDB for the original releases of 32-bit and 64-bit Windows 10 tell that the kernel is built with the WHEAI.H header at


and draws from it the type definitions that are tabulated below. The header WHEAI.H is not known in any Device Driver Kit (DDK) or Windows Driver Kit (WDK). The WHEA in the name is of course for the Windows Hardware Error Architecture. The I perhaps stands for Internal. All the types that show in the symbol files begin as WHEAP, the P perhaps standing for Private.

Line Number Type
163 struct _WHEAP_WORK_QUEUE
186 struct _WHEAP_INFO_BLOCK

In the downloadable packages of public symbols for the original Windows 10, only those for the kernel have type information from this header. No object file or statically linked library has yet been found that has type information from this header. It is therefore not known if this header defines yet more types.

Reverse-Engineering Forensics

Because the known types are manageably few, a study may be useful of how these particular types that are defined in WHEAI.H are the ones that get into the public symbols. That a public symbol file for the kernel has any type information at all is from compiling a separate source file such that type information generated as its private symbols gets merged into the public symbols that were already created from building the kernel. This source file, which for the kernel is named ntsym.c, evidently has a #include of WHEAI.H or of some other header that picks up WHEAI.H as a nested inclusion. If a programmer wants that a particular type shows in the public symbols, then if the type is not used (as the compiler sees it) in the header, an explicit use of it must be written into NTSYM.C.

What seems to be the usual reason for wanting a particular type to show in the kernel’s public symbols is that the type information is looked up for a debugger extension’s command that’s supposed to work even for non-Microsoft programmers who don’t have the kernel’s private symbols. The only known command that looks up any of the types from WHEAI.H is !whea as implemented by KDEXTS.DLL. It assumes access to type information for the following three of the types listed above:

The programmer could ensure this availability by using the first two explicitly in NTSYM.C. The third is used implicitly by the first. It, in turn, has an implied use of _WHEAP_ERROR_RECORD_WRAPPER_FLAGS. This leaves _WHEAP_INFO_BLOCK or _WHEAP_WORK_QUEUE. They could, of course, be used explicitly in NTSYM.C, but an easier explanation fits a close reading of the type information: _WHEAP_INFO_BLOCK is almost certainly picked up just from including the header, and _WHEAP_ERROR_SOURCE likely is too, and the two together account for all the others.

Several mechanisms select a type as used such that type information is generated for the public symbols. The most common, most natural, and thus most expected, is implicit: the type is on some chain of references from the definition of a type that is explicitly selected. The easiest and plausibly the intended mechanism of explicit selection is simply to declare a variable of this type in NTSYM.C. Less often, but arguably more notable, the explicit selection is overlooked by the programmer: the type is used by code in the header, as for an inlined routine.

This last mechanism is capable of surprise, both of Microsoft’s programmers and of reverse engineers. It is, for instance, how type information for such kernel-mode structures as the KPROCESS turns up in public symbols for user-mode modules that ought never to have even read-only access—and not just in public symbol files for the lowest-level modules such as NTDLL.DLL but even for higher-level modules as far out as URLMON.DLL from Internet Explorer. Almost certainly this is the mechanism that gets _WHEAP_INFO_BLOCK into the kernel’s public symbols.

For these types that the public symbols show as defined in WHEAI.H, the sequence of records that hold the type information is organised in two sets. First, _WHEAP_INFO_BLOCK brings in:

These are each brought in through pointers, which turn out to be the types of the _WHEAP_INFO_BLOCK members named ErrorSourceTable and WorkQueue. Second, from _WHEAP_ERROR_SOURCE:

The first of these comes in as a pointer but the second is nested whole, as the types of the _WHEAP_ERROR_SOURCE members Records and Descriptor. The last is brought in by its use for the _WHEAP_ERROR_RECORD_WRAPPER member named Flags. The _WHEAP_ERROR_RECORD_WRAPPER also has a member, named ErrorSource, that points (back) to a _WHEAP_ERROR_SOURCE.

There ends the tedium of accounting for references that bring these types into the public symbols implicitly. For both _WHEAP_INFO_BLOCK and _WHEAP_ERROR_SOURCE, the LF_STRUCTURE record is followed immediately by an LF_POINTER record. Each is in the public symbols for being first used as a pointer, but not from being pointed to from another type in the public symbols. Although the pointer record for _WHEAP_ERROR_SOURCE is referenced (from the LF_FIELDLIST record for _WHEAP_ERROR_RECORD_WRAPPER), the pointer record for _WHEAP_INFO_BLOCK is completely unreferenced. Concentrate on this last observation for it allows two inferences as almost certain for the compilation of NTSYM.C when generating type information for the public symbols. One is that the first, and perhaps only, use of _WHEAP_INFO_BLOCK is for a routine that either returns the pointer type or has it as an argument. The other inference is indirect: this routine is not called from another, else the called routine’s type (and name, if compiling with Visual Studio 2012 or higher) would be recorded as type information and the pointer record for the _WHEAP_INFO_BLOCK would then be referenced.

In practice, the routine would be defined in the header and will need to be an inlined routine. Good motivation for an inlined routine that returns a pointer to a _WHEAP_INFO_BLOCK follows from knowing first that the kernel creates a _WHEAP_INFO_BLOCK for each processor and keeps the address as the WheaInfo member of the processor’s KPRCB, and second that WheaInfo is defined as a pointer to void and therefore needs a type cast whenever the pointer is read for interpretation. The intention is presumably that the _WHEA_INFO_BLOCK is opaque even for most of Microsoft’s programmers who work on the kernel: only the programmers of the kernel’s WHEA functionality need to interpret what’s pointed to. Still, these programmers will not want to keep writing the cast. If they need to read the pointer more than once, as has been so since the functionality’s substantial elaboration for Windows Vista SP1, then it will be wholly unsurprising if they wrap this simple but tedious work into a macro or inlined routine.

Search a disassembly of the kernel and a repeating sequence that does exactly this work and looks like an inlining is easily found. For reasons that are not yet understood, it finds the current KPRCB via the current KPCR rather than use the long-established KeGetCurrentPrcb. Rendering in C is complicated by differences in how the x86 KPCR and x64 KPCR each label their embedded KPRCB and their pointer to it. What follows (with an invented name) captures what’s done by the binary code but one might hope it’s not what the WHEA programmers work with:

    #if defined (_X86_) 
    return (PWHEAP_INFO_BLOCK) KeGetPcr () -> Prcb -> WheaInfo;
    #elif defined (_AMD64_)
    return (PWHEAP_INFO_BLOCK) KeGetPcr () -> Prcb.WheaInfo;
    /* of no concern to this study */

Assume that this or something like it is defined in WHEAI.H, perhaps immediately after the structure’s definition. Then a #include of this header, even as a nested inclusion, in NTSYM.C for merging type information into the public symbols will count as a use of _WHEA_INFO_BLOCK and will get its type information into the public symbols.

That _WHEA_ERROR_SOURCE also is in the public symbols only because of its use for an inline routine is more than merely plausible, but a candidate routine is not yet known with high confidence. It need be nothing more than a convenience that’s so slight it barely shows in the binary. For instance, the following (with invented names and arguments) for getting the first in the linked list of these structures, and then for moving along the list, would suffice:

WheapGetFirstErrorSource (
        ErrorSourceTable -> ListHead.Flink, 
WheapGetNextErrorSource (
        ErrorSource -> ListEntry.Flink, 

Incidentally, these particular suppositions are not without support. The internal routine (WheapGetErrorSource) that searches the list from the ListHead in the WHEAP_ERROR_SOURCE_TABLE for a WHEAP_ERROR_SOURCE whose Descriptor matches a given ErrorSourceId is conspicuous for ending the search by counting entries, not by checking for a link back to the head.