KTHREAD

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.

Variability

The KTHREAD structure is plainly internal to the kernel and its layout varies greatly between Windows versions and even between builds. Some sense of the variability can be gained just from the structure’s 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

Unfortunately for the editorial matter of tracking changes within the structure, these changes of size don’t begin to convey the changes inside the structure. Among undocumented kernel-mode structures, and certainly the most important ones, the KTHREAD stands out for having 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. These notes track the earlier versions separately:

The rest of this article is specific to versions 6.2 and higher. Be aware, please, that notes about individual members are typically in the article for the version in which the member first appears.

Even with attention narrowed just to recent Windows versions, description of the KTHREAD is 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.

Layout

It is well known that the KTHREAD is a kernel object that can be waited on until it gets signalled, as happens when the thread ends its execution. In the DISPATCHER_HEADER at the beginning of a KTHREAD, the Type is ThreadObject (6) in the KOBJECTS enumeration. Ever since version 5.2, some other members of this DISPATCHER_HEADER are specific to the KTHREAD.

Offset (x86) Offset (x64) Definition Versions History
0x00 0x00
DISPATCHER_HEADER Header;
6.2 and higher previously at 0x00
0x10 0x18
PVOID SListFaultAddress;
6.2 and higher previously at 0x01F0 and 0x0318
0x18 0x20
ULONGLONG QuantumTarget;
6.2 and higher previously at 0x20 (both)
0x20 0x28
PVOID InitialStack;
6.2 and higher previously at 0x28 (both)
0x24 0x30
PVOID volatile StackLimit;
6.2 and higher previously at 0x2C and 0x30
0x28 0x38
PVOID StackBase;
6.2 and higher previously at 0x0190 and 0x0278
0x2C 0x40
KSPIN_LOCK ThreadLock;
6.2 and higher previously at 0x34 and 0x40
0x30 0x48
ULONGLONG volatile CycleTime;
6.2 and higher previously at 0x10 and 0x18
0x38  
ULONG volatile HighCycleTime;
6.2 and higher previously at 0x18
0x3C  
PVOID ServiceTable;
6.2 and higher previously at 0xBC
0x40 0x50
ULONG CurrentRunTime;
6.2 and higher  
0x44 0x54
ULONG ExpectedRunTime;
6.2 and higher  
0x48 0x58
PVOID KernelStack;
6.2 and higher previously at 0x30 and x038
0x4C 0x60
XSAVE_FORMAT *StateSaveArea;
6.2 and higher  
0x50 0x68
KSCHEDULING_GROUP *SchedulingGroup;
6.2 and higher  
0x54 0x70
KWAIT_STATUS_REGISTER WaitRegister;
6.2 and higher previously at 0x38 and 0x48
0x55 0x71
BOOLEAN volatile Running;
6.2 and higher previously at 0x39 and 0x49
0x56 0x72
BOOLEAN Alerted [2];
6.2 and higher previously at 0x3A and 0x4A
0x58 0x74
union {
    struct {
        /*  bit fields, follow link  */
    };
    LONG MiscFlags;
};
6.2 and higher previously at 0x3C and 0x4C
0x5C 0x78
union {
    struct {
        /*  bit fields, follow link  */
    };
    LONG volatile ThreadFlags;
};
6.2 and higher previously at 0xB8 and 0x0100
0x60 0x7C
UCHAR volatile Tag;
10.0 and higher  
0x61 0x7D
UCHAR SystemHeteroCpuPolicy;
10.0 and higher  
0x62 0x7E
UCHAR UserHeteroCpuPolicy : 7;
UCHAR ExplicitSystemHeteroCpuPolicy : 1;
10.0 and higher  
0x60 (6.2 to 6.3);
0x63
0x7C (6.2 to 6.3);
0x7F
UCHAR Spare0;
6.2 and higher (x86);
6.2 to 1803 (x64)
 
union {
    struct {
        UCHAR RunningNonRetpolineCode : 1;
        UCHAR SpecCtrlSpare : 7;
    };
    UCHAR SpecCtrl;
};
1809 and higher (x64)  
0x64 0x80
ULONG SystemCallNumber;
6.2 and higher previously at 0x013C and 0x01F8
  0x84
ULONG Spare1;
6.2 to 1607  
ULONG ReadyTime;
1703 and higher  
0x68 0x88
PVOID FirstArgument;
6.2 and higher previously at 0x012C and 0x01E0
0x6C 0x90
KTRAP_FRAME *TrapFrame;
6.2 and higher previously at 0x0128 and 0x01D8
0x70 0x98
union {
    KAPC_STATE ApcState;
    /*  overlay, see below  */
};
6.2 and higher previously at 0x40 and 0x50

Overlaying the ApcState is first some padding to get past the defined members of the KAPC_STATE to the space that’s left for alignment:

struct {
    UCHAR ApcStateFill [KAPC_STATE_ACTUAL_LENGTH];
    /*  5 bytes, see below  */
};

With this construction, the 64-bit builds squeeze five bytes into the end of the 0x30-byte ApcState. In 32-bit builds, only the first fits the 0x18-byte ApcState: the remaining four just follow as if they had been declared outside the union. Disregard the construction, and the members that are packed with the ApcState are:

Offset (x86) Offset (x64) Definition Versions History
0x87 0xC3
CHAR Priority;
6.2 and higher previously at 0x57 and 0x7B
0x88 0xC4
ULONG UserIdealProcessor;
6.2 and higher previously at 0x0164 and 0x022C

The structures used for larger members of the KTHREAD tend to have more spare space in the 64-bit builds, whose wider pointers have a wider alignment requirement. There follow a few members that the 32-bit builds don’t treat to this packing game. The 64-bit builds pack them with the WaitBlock member (further into the structure).

Offset (x86) Offset (x64) Definition Versions Remarks History
0x8C  
ULONG ContextSwitches;
6.2 and higher x64 at 0x0154 previously at 0x64
0x90  
UCHAR volatile State;
6.2 and higher x64 at 0x0184 previously at 0x68
0x91  
CHAR NpxState;
6.2 to 6.3 x64 at 0x0185;
next as LONGLONG at 0x0340
previously as UCHAR at 0x69
CHAR Spare12;
10.0 and higher    
0x92  
KIRQL WaitIrql;
6.2 and higher x64 at 0x0186 previously at 0x6A
0x93  
KPROCESSOR_MODE WaitMode;
6.2 and higher x64 at 0x0187 previously at 0x6B
0x94 0xC8
LONG_PTR volatile WaitStatus;
6.2 and higher   previously at 0x6C and 0x90
0x98 0xD0
KWAIT_BLOCK *WaitBlockList;
6.2 and higher   previously at 0x70 and 0x98
0x9C 0xD8
union {
    LIST_ENTRY WaitListEntry;
    SINGLE_LIST_ENTRY SwapListEntry;
};
6.2 and higher   previously at 0x74 and 0xA0
0xA4 0xE8
KQUEUE *Queue;
6.2 and higher   previously at 0x7C and 0xB0
0xA8 0xF0
PVOID Teb;
6.2 and higher   previously at 0x88 and 0xB8
0xB0 0xF8
ULONGLONG RelativeTimerBias;
6.2 and higher    
0xB8 0x0100
KTIMER Timer;
6.2 and higher   previously as union at 0x90 and 0xC0
0xE0 0x0140
union {
    KWAIT_BLOCK WaitBlock [4];
    /*  overlay, see below  */
};
6.2 and higher   previously without union at 0xC0 (x86);
previously as changed union at 0x0108 (x64)

Overlaying the long-standing WaitBlock array are a succession of structures that each pad from the start of the array to spare or resuable members:

#ifdef _AMD64_                      // SpareLong exists only for x64
struct {
    UCHAR WaitBlockFill4 [FIELD_OFFSET (KWAIT_BLOCK, SpareLong)];
    /*  reclaimed 4 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill5 [sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, SpareLong)];
    /*  reclaimed 4 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill6 [2 * sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, SpareLong)];
    /*  reclaimed 4 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill7 [3 * sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, SpareLong)];
    /*  reclaimed 4 bytes, see below  */
};
#endif
struct {
    UCHAR WaitBlockFill8 [FIELD_OFFSET (KWAIT_BLOCK, SparePtr)];
    /*  reclaimed 4 or 8 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill9 [sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, SparePtr)];
    /*  reclaimed 4 or 8 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill10 [2 * sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, SparePtr)];
    /*  reclaimed 4 or 8 bytes, see below  */
};
struct {
    UCHAR WaitBlockFill11 [3 * sizeof (KWAIT_BLOCK) + FIELD_OFFSET (KWAIT_BLOCK, Object)];
    /*  reclaimed 8 or 16 bytes, see below  */
;

The KWAIT_BLOCK structure changed for version 6.2. The x86 builds had an explicitly spare byte and the x64 builds had both this and an explicitly spare long. In version 6.2 and higher, both have an explicitly spare pointer and the x64 builds still have the spare long too. With the KTHREAD having four of these structures, packing members into these spares is certainly worthwhile, even if it’s beyond messy. Moroever, for the particular use that the KTHREAD makes of the last KWAIT_BLOCK in the array, the Object member is known to be irrelevant and is therefore also available for reuse.

Disregard all this scaffolding and here are the members that get squeezed in with the WaitBlock array:

Offset (x86) Offset (x64) Definition Versions Remarks History
  0x0154
ULONG ContextSwitches;
6.2 and higher x86 at 0x8C previously at 0x0134
0xF4 0x0168
KTHREAD_COUNTERS *ThreadCounters;
6.2 and higher   previously at 0x01F4 and 0x0350
  0x0184
UCHAR volatile State;
6.2 and higher x86 at 0x90 previously at 0x0164
  0x0185
CHAR NpxState;
6.2 to 6.3 x86 at 0x91;
next as LONGLONG at 0x0250
previously as UCHAR at 0x0165
CHAR Spare13;
10.0 and higher    
  0x0186
KIRQL WaitIrql;
6.2 and higher x86 at 0x92 previously at 0x0166
  0x0187
KPROCESSOR_MODE WaitMode;
6.2 and higher x86 at 0x93 previously at 0x0167
0x010C 0x0198
XSTATE_SAVE *XStateSave;
6.2 and higher   previously at 0x01F8 and 0x0358
  0x01B4
ULONG WaitTime;
6.2 and higher x86 at 0x0138 previously at 0x0194
0x0124 0x01C8
PVOID volatile Win32Thread;
6.2 and higher   previously at 0x018C and 0x0270
  0x01E4
union {
    struct {
        SHORT KernelApcDisable;
        SHORT SpecialApcDisable;
    };
    ULONG CombinedApcDisable;
};
6.2 and higher x86 at 0x013C previously at 0x01C4
0x0138  
ULONG WaitTime;
6.2 and higher x64 at 0x01B4 previously at 0x7C
  0x01F0
UMS_CONTROL_BLOCK *Ucb;
6.2 and higher   previously at 0x01B8
  0x01F8
KUMS_CONTEXT_HEADER *Uch;
6.2 and higher    

Note that WaitTime is listed twice above. Because it is packed with the spare Object in 32-bit builds but the SpareLong in 64-bit builds, it is ordered differently with respect to Win32Thread. The union of members that count the depth of requests to disable APC delivery is another example that is packed into spare space in the x64 builds (above) but not in the x86 builds (immediately below):

Offset (x86) Offset (x64) Definition Versions Remarks History
0x013C  
union {
    struct {
        SHORT KernelApcDisable;
        SHORT SpecialApcDisable;
    };
    ULONG CombinedApcDisable;
};
6.2 and higher x64 at 0x01E4 previously at 0x84
  0x0200
union {
    LONG volatile ThreadFlags2;
    struct {
        /*  bit fields, follow link  */
    };
};
2004 and higher    
  0x0200 (6.2 to 1903);
0x0204
PVOID TebMappedLowVa;
6.2 to 1607   previously at 0x01B0
PVOID Spare21;
1703 to 1903    
ULONG Spare21;
2004 and higher    
0x0140 0x0208
LIST_ENTRY QueueListEntry;
6.2 and higher   previously at 0x0120 and 0x01C8
0x0148 0x0218
ULONG volatile NextProcessor;
6.2 only   previously at 0x58 and 0x7C
union {
    ULONG volatile NextProcessor;
    struct {
        ULONG NextProcessorNumber : 31;
        ULONG SharedReadyQueue : 1;
    };
};
6.3 and higher    
0x014C 0x021C
ULONG volatile DeferredProcessor;
6.2 only   previously at 0x5C and 0x80
LONG QueuePriority;
6.3 and higher    
0x0150 0x0220
KPROCESS *Process;
6.2 and higher   previously at 0x0150 and 0x0210
0x0154 0x0228
union {
    GROUP_AFFINITY volatile UserAffinity;
    /*  overlay, see below  */
};
6.2 and higher   previously without union at 0x0144 and 0x0200

Version 6.1, with its support for potentially many more processors, changed UserAffinity and Affinity from KAFFINITY to GROUP_AFFINITY. The latter has six bytes explicitly labelled as Reserved. Version 6.1 doesn’t pack anything into them, but version 6.2 goes all the way. Overlaying UserAffinity is:

struct {
    UCHAR UserAffinityFill [FIELD_OFFSET (GROUP_AFFINITY, Reserved)];
    /*  reclaimed 6 bytes, see below  */
};

Reclaimed from UserAffinity:

Offset (x86) Offset (x64) Definition Versions History
0x015A 0x0232
KPROCESSOR_MODE PreviousMode;
6.2 and higher previously at 0x013A and 0x01F6
0x015B 0x0233
CHAR BasePriority;
6.2 and higher previously at 0x0135 and 0x01F1
0x015C 0x0234
union {
    CHAR PriorityDecrement;
    struct {
        UCHAR ForegroundBoost : 4;
        UCHAR UnusualBoost : 4;
    };
};
6.2 and higher previously at 0x0136 and 0x01F2
0x015D 0x0235
BOOLEAN Preempted;
6.2 and higher previously at 0x0137 and 0x01F3
0x015E 0x0236
UCHAR AdjustReason;
6.2 and higher previously at 0x0138 and 0x01F4
0x015F 0x0237
CHAR AdjustIncrement;
6.2 and higher previously at 0x0139 and 0x01F5

Insertion of the AffinityVersion for Windows 10 produces the first change of offset for any KTHREAD member since Windows 8.

Offset (x86) Offset (x64) Definition Versions History
0x0160 0x0238
ULONG_PTR AffinityVersion;
10.0 and higher  
0x0160 (6.2 to 6.3);
0x0164
0x0238 (6.2 to 6.3);
0x0240
union {
    GROUP_AFFINITY volatile Affinity;
    /*  overlay, see below  */
};
6.2 and higher previously at 0x0154 and 0x0218

Overlaying Affinity:

struct {
    UCHAR AffinityFill [FIELD_OFFSET (GROUP_AFFINITY, Reserved)];
    /*  reclaimed 6 bytes, see below  */
};

Reclaimed from Affinity:

Offset (x86) Offset (x64) Definition Versions History
0x0166 (6.2 to 6.3);
0x016A
0x0242 (6.2 to 6.3);
0x024A
UCHAR ApcStateIndex;
6.2 and higher previously at 0x0134 and 0x01F0
0x0167 (6.2 to 6.3);
0x016B
0x0243 (6.2 to 6.3);
0x024B
UCHAR WaitBlockCount;
6.2 and higher  
0x0168 (6.2 to 6.3);
0x016C
0x0244 (6.2 to 6.3);
0x024C
ULONG IdealProcessor;
6.2 and higher previously at 0x0160 and 0x0228

The KTHREAD keeps two KAPC_STATE structures, the second to support the KeStackAttachProcess function. From as far back as version 3.51, the KTHREAD has initialised its ApcStatePointer array with the addresses of these two structures and then left them there, unchanging, with the (8-bit) ApcStateIndex to select which pointer to use. This always was an extravagance for an implementation that’s supposedly so concerned about space that it fits small members into otherwise unused space in larger members. Windows 10 does away with it.

Offset (x86) Offset (x64) Definition Versions Remarks History
0x016C (6.2 to 6.3) 0x0248 (6.2 to 6.3)
KAPC_STATE *ApcStatePointer [2];
6.2 to 6.3   previously at 0x0168 and 0x0230
  0x0250
ULONGLONG NpxState;
10.0 and higher x86 at 0x0340;
previously as CHAR at 0x0185
 
0x0170  
ULONG Spare15 [1];
10.0 and higher    

See that Windows 10 inserts members with the effect, if not the intention, of restoring continuity of offsets for the second KAPC_STATE structure and beyond.

Offset (x86) Offset (x64) Definition Versions History
0x0174 0x0258
union {
    KAPC_STATE SavedApcState;
    /*  overlay, see below  */
};
6.2 and higher previously at 0x0170 and 0x0240

Overlaying the SavedApcState:

struct {
    UCHAR SavedApcStateFill [KAPC_STATE_ACTUAL_LENGTH];
    /*  reclaimed, see below  */
};

Reclaimed from the SavedApcState:

Offset (x86) Offset (x64) Definition Versions History
0x018B 0x0283
UCHAR WaitReason;
6.2 and higher previously at 0x0187 and 0x026B
0x018C 0x0284
CHAR SuspendCount;
6.2 and higher previously at 0x0188 and 0x026C
0x018D 0x0285
CHAR Saturation;
6.2 and higher previously at 0x013B and 0x01F7
0x018E 0x0286
USHORT SListFaultCount;
6.2 and higher previously as ULONG at 0x01DC and 0x02D4

What had been the SuspendApc (previously at offsets 0x0194 and 0x0280) is renamed to SchedulerApc.

Offset (x86) Offset (x64) Definition Versions
0x0190 0x0288
union {
    KAPC SchedulerApc;
    /*  overlay, see below  */
};
6.2 and higher

Overlaying the SchedulerApc:

 struct {
    UCHAR SchedulerApcFill0 [KAPC_OFFSET_TO_SPARE_BYTE0];
    /*  reclaimed byte, see below  */
};
struct {
    UCHAR SchedulerApcFill1 [KAPC_OFFSET_TO_SPARE_BYTE1];
    /*  reclaimed byte, see below  */
};
struct {
    UCHAR SchedulerApcFill2 [KAPC_OFFSET_TO_SPARE_LONG];
    /*  reclaimed four bytes, see below  */
};
struct {
    UCHAR SchedulerApcFill3 [KAPC_OFFSET_TO_SYSTEMARGUMENT1];
    /*  reclaimed 4 or 8 bytes, see below  */
};
struct {
    UCHAR SchedulerApcFill4 [KAPC_OFFSET_TO_SYSTEMARGUMENT2];
    /*  reclaimed 4 or 8 bytes, see below  */
};
struct {
    UCHAR SchedulerApcFill5 [KAPC_ACTUAL_LENGTH];
    /*  reclaimed 1 or 5 bytes, see below)  */
};

Whatever its name, its sprinkling of other members in spare space barely changes:

Offset (x86) Offset (x64) Definition Versions History
0x0191 0x0289
UCHAR ResourceIndex;
6.2 and higher previously at 0x0195 and 0x0281
0x0193 0x028B
UCHAR QuantumReset;
6.2 and higher previously at 0x0197 and 0x0283
0x0194 0x028C
ULONG KernelTime;
6.2 and higher previously at 0x0198 and 0x0284
0x01B4 0x02C8
KPRCB * volatile WaitPrcb;
6.2 and higher previously at 0x01B8 and 0x02C0
0x01B8 0x02D0
PVOID LegoData;
6.2 and higher previously at 0x01BC and 0x02C8
0x01BF 0x02DB
UCHAR CallbackNestingLevel;
6.2 and higher  
0x01C0 0x02DC
ULONG UserTime;
6.2 and higher previously at 0x01C4 and 0x02DC

That’s the end of the contrivances over packing members into space that’s left unused in other members.

Offset (x86) Offset (x64) Definition Versions Remarks History
0x01C4 0x02E0
KEVENT SuspendEvent;
6.2 and higher    
0x01D4 0x02F8
LIST_ENTRY ThreadListEntry;
6.2 and higher   previously at 0x01E0 and 0x02F8
0x01DC 0x0308
LIST_ENTRY MutantListHead;
6.2 and higher last member in 6.2 (x86) previously at 0x01E8 and 0x0308
0x01E4 (6.3) 0x0318 (6.3)
SINGLE_LIST_ENTRY LockEntriesFreeList;
6.3 only    
0x01E4 0x0318
UCHAR AbEntrySummary;
10.0 and higher    
0x01E5 0x0319
UCHAR AbWaitEntryCount;
10.0 and higher previously at 0x032B and 0x058B  
0x01E6 0x031A
UCHAR AbAllocationRegionCount;
1703 and higher    
0x01E6 (10.0 to 1607);
0x01E7
0x031A (10.0 to 1607);
0x031B
USHORT Spare20;
10.0 to 1607    
UCHAR Spare20;
1703 to 1709    
CHAR SystemPriority;
1803 and higher    
  0x031C
ULONG SecureThreadCookie;
10.0 and higher    
0x01E8 0x0320
KLOCK_ENTRY LockEntries [6];
6.3 to 1903    
KLOCK_ENTRY *LockEntries;
2004 and higher  
0x0308 (6.3 to 1903);
0x01EC
0x0560 (6.3 to 1903);
0x0328
SINGLE_LIST_ENTRY PropagateBoostsEntry;
6.3 and higher    
0x030C (6.3 to 1903);
0x01F0
0x0568 (6.3 to 1903);
0x0330
SINGLE_LIST_ENTRY IoSelfBoostsEntry;
6.3 and higher    
0x0310 (6.3 to 1903);
0x01F4
0x0570 (6.3 to 1903);
0x0338
UCHAR PriorityFloorCounts [0x10];
6.3 and higher    
0x0204 0x0348
UCHAR PriorityFloorCountsReserved [0x10];
2004 and higher    
0x0320 (6.3 to 1903);
0x0214
0x0580 (6.3 to 1903);
0x0358
ULONG PriorityFloorSummary;
6.3 and higher    
0x0324 (6.3 to 1903);
0x0218
0x0584 (6.3 to 1903);
0x035C
LONG volatile AbCompletedIoBoostCount;
6.3 and higher    
0x0328 (1607 to 1903);
0x021C
0x0588 (6.3 to 1903);
0x0360
LONG volatile AbCompletedIoQosBoostCount;
1607 and higher    
0x0328 (6.3 to 1511);
0x032C (1607 to 1903);
0x0220
0x0588 (6.3 to 1511);
0x058C (1607 to 1903);
0x0364
SHORT volatile AbReferenceCount;
6.3 only    
SHORT volatile KeReferenceCount;
10.0 and higher    
0x032A (6.3 to 1511);
0x032E (1607 to 1903);
0x0222
0x058A (6.3 to 1511);
0x058E (1607 to 1903);
0x0366
UCHAR AbFreeEntryCount;
6.3 only    
UCHAR AbOrphanedEntrySummary;
10.0 and higher    
0x032B (6.3 to 1511);
0x032F (1607 to 1903);
0x0223
0x058B (6.3 to 1511);
0x058F (1607 to 1903);
0x0367
UCHAR AbWaitEntryCount;
6.3 only next at 0x01E5 and 0x0319  
UCHAR AbOwnedEntryCount;
10.0 and higher    
0x032C (6.3 to 1511);
0x0330 (1607 to 1903);
0x0224
0x058C (6.3 to 1511);
0x0590 (1607 to 1903);
0x0368
ULONG ForegroundLossTime;
6.3 and higher    
0x0330 (6.3 to 1511);
0x0334 (1607 to 1903);
0x0228
0x0590 (6.3 to 1511);
0x0598 (1607 to 1903);
0x0370 
union {
    LIST_ENTRY GlobalForegroundListEntry;
    struct {
        SINGLE_LIST_ENTRY ForegroundDpcStackListEntry;
        ULONG InGlobalForegroundList;
    };
};
6.3 and higher last member in 6.3 (x86)  
  0x0318 (6.2);
0x05A0 (6.3 to 1511);
0x05A8 (1607 to 1903);
0x0380
LONGLONG ReadOperationCount;
6.2 and higher   previously at 0x0320
  0x0320 (6.2);
0x05A8 (6.3 to 1511);
0x05B0 (1607 to 1903);
0x0388
LONGLONG WriteOperationCount;
6.2 and higher   previously at 0x0328
  0x0328 (6.2);
0x05B0 (6.3 to 1511);
0x05B8 (1607 to 1903);
0x0390
LONGLONG OtherOperationCount;
6.2 and higher   previously at 0x0330
  0x0330 (6.2);
0x05B8 (6.3 to 1511);
0x05C0 (1607 to 1903);
0x0398
LONGLONG ReadTransferCount;
6.2 and higher   previously at 0x0338
  0x0338 (6.2);
0x05C0 (6.3 to 1511);
0x05C8 (1607 to 1903);
0x03A0
LONGLONG WriteTransferCount;
6.2 and higher   previously at 0x0340
  0x0340 (6.2);
0x05C8 (6.3 to 1511);
0x05D0 (1607 to 1903);
0x03A8
LONGLONG OtherTransferCount;
6.2 and higher last member in 6.2 to 6.3 (x64) previously at 0x0348

Appended for Windows 10

Offset (x86) Offset (x64) Definition Versions Remarks
0x0338 (10.0 to 1511);
0x033C (1607 to 1903);
0x0230
0x05D0 (10.0 to 1511);
0x05D8 (1607 to 1903);
0x03B0
KSCB *QueuedScb;
10.0 and higher last member in 10.0 to 1607 (x64)
0x0340 (10.0 to 1903);
0x0238
 
ULONGLONG NpxState;
10.0 and higher x64 at 0x0250;
previously as CHAR at 0x91;
last member in 10.0 to 1607 (x86)
0x0348 (1703 to 1903);
0x0240
0x05E0 (1703 to 1903);
0x03B8
ULONG volatile ThreadTimerDelay;
1703 and higher last member in 1703 (x86)
  0x05E4 (1703)
LONG Spare22;
1703 only last member in 1703 (x64)
0x034C (1709 to 1903);
0x0244
0x05E4 (1709 to 1903);
0x03BC
union {
    LONG volatile ThreadFlags2;
    struct {
        /*  bit fields, follow link  */
    };
};
1709 to 1903 last member in 1709 to 1809 (x86)
union {
    LONG volatile ThreadFlags3;
    struct {
        /*  bit fields, follow link  */
    };
};
2004 and higher  
  0x05E8 (1903);
0x03C0
ULONGLONG TracingPrivate [1];
1903 and higher  
  0x05E8 (1709 to 1809);
0x05F0 (1903);
0x03C8
PVOID SchedulerAssist;
1709 and higher last member in 1709 to 1809 (x64)
0x0350 (1903);
0x0248
0x05F8 (1903);
0x03D0
PVOID volatile AbWaitObject;
1903 and higher last member in 1903
0x024C 0x03D8
ULONG ReservedPreviouslyReadyTimeValue;
2004 and higher  
0x0250 0x03E0
ULONGLONG KernelWaitTime;
2004 and higher  
0x0258 0x03E8
ULONGLONG UserWaitTime;
2004 and higher  
  0x03F0
union {
    LIST_ENTRY GlobalUpdateVpThreadPriorityListEntry;
    struct {
        SINGLE_LIST_ENTRY UpdateVpThreadPriorityDpcStackListEntry;
        ULONGLONG InGlobalUpdateVpThreadPriorityList;
    };
};
2004 and higher  
  0x0400
LONG SchedulerAssistPriorityFloor;
2004 and higher  
  0x0404
ULONG Spare28;
2004 and higher  
0x0260  
ULONG Spare29 [3];
2004 and higher  
0x026C 0x0408
ULONG_PTR EndPadding [5];
2004 and higher (x86) last member in 2004