Geoff Chappell - Software Analyst
The KTHREAD structure is the Kernel Core’s portion of the ETHREAD structure. The latter is the thread object as exposed through the Object Manager. The KTHREAD is the core of it.
The KTHREAD structure is plainly internal to the kernel and its layout varies greatly between Windows versions and even between builds. Indeed, it is the most highly variable of all significant kernel-mode structures. It has been treated multiple times to large-scale rearrangements in which members are brought together from opposite ends of the structure only to have the next build scatter them again. Tracking the structure’s history on one page looks to be impossible and is therefore spread over several:
Be aware, please, that notes about any particular member are on the page for the version that introduced the member. Yes, this means that the page about version 3.10 is not merely for historians!
Even were attention narrowed just to recent Windows versions, description of the KTHREAD would be unusually complicated. Specially notable is the packing of small members into spare fields in other members. Some such reused fields are explicitly spare, as with several members of the KAPC structure. WDM.H even defines macros to ease the reference to these fields by their offsets. Other reuse is available only because of alignment padding, as with the last byte of the KAPC_STATE for 32-bit Windows. As if this weren’t messy enough by itself, the greater opportunity for this reuse in the 64-bit builds, whose wider pointers tend to create more alignment padding as a side-effect, allows that more than a few members are placed very differently in the 32-bit and 64-bit builds.
Some quick sense of this structure’s unusual variability can be gained just from the changing size. In the following table, different builds of the same version are distinguished as early, late and even very late because they are known to vary the structure even if they don’t change the size. These descriptions are then used as a shorthand throughout this article and its companions.
Version | Size (x86) | Size (x64) |
---|---|---|
3.10 | 0x01D8 | |
3.50 to 5.0 | 0x01B0 | |
5.1 | 0x01C0 | |
early 5.2 (before SP1) | 0x01C8 | |
late 5.2 (SP1) | 0x01B8 | 0x0320 |
very late 5.2 (SP2) | 0x01B8 | 0x0308 |
early 6.0 (before SP1); late 6.0 (SP1 and higher) |
0x01E0 | 0x0330 |
6.1 | 0x0200 | 0x0360 |
6.2 | 0x01E8 | 0x0348 |
6.3 | 0x0338 | 0x05D0 |
10.0 to 1511 | 0x0348 | 0x05D8 |
1607 | 0x0348 | 0x05E0 |
1703 | 0x0350 | 0x05E8 |
1709 to 1809 | 0x0350 | 0x05F0 |
1903 | 0x0358 | 0x0600 |
2004 | 0x0280 | 0x0430 |
It is well known that the KTHREAD is a kernel object that can be waited on until it gets signalled, which happens when the thread ends its execution. This is possible because the KTHREAD is specifically a dispatcher object: in all versions, it begins with a DISPATCHER_HEADER. Indeed, this Header is the only KTHREAD member that has not been moved forwards, backwards and round about the structure from version to version. This is here taken as reason for special treatment, independently of the version-specific pages.
Though the Header always begins the KTHREAD, it has changed internally. Significant new functionality has sometimes been accommodated by finding space inside the Header. In one case, old functionality was moved into the Header and then developed. The following table simplifies by disregarding the nested unions and other such scaffolding that provide for the DISPATCHER_HEADER to begin all types of waitable kernel object. The table instead extracts only the construction that applies to threads. The intention is convenience: after all, if you’re looking at a thread in the debugger (or other disassembly), then what you want at each offset is the interpretation that applies to threads.
Offset | Definition | Versions |
---|---|---|
0x00 |
SHORT Type; |
3.10 to 3.50 |
UCHAR Type; |
3.51 to 6.3 | |
UCHAR ThreadType; |
10.0 and higher | |
0x01 |
UCHAR Spare; |
3.51 only |
defined, but meaningful only for other objects | 4.0 to 6.3 | |
UCHAR ThreadReserved; |
10.0 to 1709 | |
union { UCHAR ThreadSpecControl; struct { UCHAR SpecControlIbrs : 1; UCHAR SpecControlStibp : 1; UCHAR SpecControlReserved : 6; }; }; |
1803 only | |
UCHAR ThreadReserved; |
1809 and higher | |
0x02 |
SHORT Size; |
3.10 to 3.50 |
USHORT Size; |
3.51 only | |
UCHAR Size; |
4.0 to 6.0 | |
union { UCHAR ThreadControlFlags; struct { /* bit fields, follow link */ }; }; |
6.1 and higher | |
0x03 | defined, but meaningful only for other objects | 4.0 to 5.1 |
BOOLEAN DebugActive; |
5.2 to 6.0 | |
union { UCHAR DebugActive; struct { /* bit fields, follow link */ }; }; |
6.1 only (x86); 6.1 and higher (x64) |
|
BOOLEAN DebugActive; |
6.2 and higher (x86) | |
0x04 |
LONG SignalState; |
all |
0x08 |
LIST_ENTRY WaitListHead; |
all |
As for all dispatcher objects, the low 7 bits of the Type—or all 8 bits in version 3.51 and all 16 bits before then—are from the KOBJECTS enumeration. In a KTHREAD, the Type is specifically ThreadObject. This is 6 in all versions except for being 5 in version 3.10 only.
At offset 0x01, the ThreadSpecControl, including the union and its strucure of bit fields, is from public symbol files for the kernel. The C-language definition in WDM.H from the WDK for the 1803 release of Windows 10 persists with ThreadReserved.
The Size at offset 0x02 is originally that of the KTHREAD in bytes. With the narrowing to 8 bits for version 4.0, it measures in dwords. Starting with version 6.1, the KTHREAD joins the KTIMER in not having a Size in its Header. Where other objects have their Size, the KTHREAD instead has the ThreadControlFlags (in union with a structure of bit fields).
The byte at offset 0x03, freed by the narrowing of the Size, was at first used only for timer objects. Its first use for thread objects was simply to bring in the ancient DebugActive boolean. It was originally very nearly at the end of the KTHREAD (at offset 0x01C5 in version 3.10 when the whole structure was 0x01D8 bytes). Version 3.51 brought it near to the front and version 5.2 brought it into the Header. This was concurrent with defining the first four bytes as a Lock. The intention seems at least plausible of working with DebugActive using interlocked operations, but no instance of this is known until version 6.0.
Interpretation of DebugActive as bit flags is a subject for research. C-language definitions in WDM.H and even the type information in public symbol files plainly do not capture anything like the 32-bit kernel’s use of the byte. It can safely be said on one hand that the byte was still treated as a BOOLEAN in the original Windows Server 2003 and just as safely on the other that it hasn’t been in any later version of 32-bit Windows, no matter that Microsoft defines it as bit fields only for Windows 7. Indeed, the definition with bit fields for 32-bit Windows 7 appears to be an oversight which the C-language definition in the WDK for Windows 8 corrects with a conditional compilation block.
For 64-bit Windows, the formal definition leaves DebugActive as a simple BOOLEAN until Windows 7 but interpretation as bit flags starts even in the 64-bit Windows Server 2003 (SP1), This has code that sets and clears DebugActive as a BOOLEAN but also has code that tests for the boolean’s TRUE as the bit that Microsoft later defines as the ActiveDR7.