KPROCESS

The KPROCESS structure is the Kernel Core’s portion of the EPROCESS structure. The latter is the process object as exposed through the Object Manager. The KPROCESS is the core of it.

Variability

The KPROCESS structure is plainly internal to the kernel and its layout changes greatly between Windows versions and even between builds. In the following table of sizes, different builds of the same version are distinguished as early and late because they are known to vary the structure even if they don’t change the size. These descriptions, as early and late, are then used throughout the article as a shorthand.

Version Size (x86) Size (x64)
3.10 0x70  
3.50 to 4.0 0x68  
5.0
early 5.1 (before Windows XP SP2)
late 5.1 (Windows XP SP2 and higher)
early 5.2 (before Windows Server 2003 SP1)
0x6C  
late 5.2 (Windows Server 2003 SP1 and higher) 0x78 0xB8
early 6.0 (before Windows Vista SP1)
late 6.0 (Windows Vista SP1 and higher)
0x80 0xC0
6.1 0x98 0x0160
6.2 to 6.3 0xA0 0x02C8
10.0 0xA8 0x02D8

These sizes, and the offsets, types and names in the tables that follow, are from Microsoft’s symbol files for the kernel starting with Windows 2000 SP3. Since symbol files for earlier versions do not contain type information for the KPROCESS, what’s known for them is instead inferred from what use the kernel is seen to make of the KPROCESS. Sizes are relatively straightforward, even without symbol files, but Microsoft’s names and types are something of a guess. Where use of a member corresponds closely with that of a version for which Microsoft’s symbols are available, it seems reasonable to suppose continuity. Some use, however, has no correspondence, the code having changed too much. Even where the use hasn’t changed, tracking it down exhaustively would be difficult, if not impossible, even with source code.

Layout

For the detailed layout in the tables that follow, it helps with some processor-dependent definitions if a macro, say MAX_PROC_GROUPS, is presumed for the maximum number of processor groups:

Note that the intention here is not to say that these limits on processor groups are permanent, let alone that they should be depended on when programming, just that they’re what are built in to various members of the KPROCESS in the presently observed Windows versions.

Original

It is well known that the KPROCESS is a kernel object that can be waited on until it gets signalled, as happens when the process ends its execution. In the DISPATCHER_HEADER at the beginning of a KPROCESS, the Type is ProcessObject (3) in the KOBJECTS enumeration.

Well, the Type is 3 in all versions except 3.10, which has it as 0x0E. This different numbering is in sequence—alphabetically, even—with other kernel objects that are not waitable and which begin with a Type and Size but not with a DISPATCHER_HEADER. And indeed, it cannot be that the KPROCESS in version 3.10 has a DISPATCHER_HEADER. What would be the latter’s WaitListHead is plainly used differently in version 3.10, as the list head for process-specific KPROFILE objects. Can it really be that in version 3.10 processes are not waitable objects (or cannot be waited on by arbitrary threads)? There is non-trivial pre-history to sort out here!

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 0x00
DISPATCHER_HEADER Header;
3.50 and higher  
0x00 (3.10)  
USHORT Type;
3.10 only  
0x02 (3.10)  
USHORT Size;
3.10 only  
0x04 (3.10)   unaccounted four bytes 3.10 only  
0x08 (3.10);
0x10
0x18
LIST_ENTRY ProfileListHead;
all  
0x10 (3.10);
0x18 (3.50)
 
LIST_ENTRY ReadyListHead;
3.10 to 3.50 next at 0x40
0x18 (3.10);
0x20 (3.50)
 
LIST_ENTRY SwapListEntry;
3.10 to 3.50 next at 0x48
0x20 (3.10);
0x28 (3.50)
 
LIST_ENTRY ThreadListHead;
3.10 to 3.50 next at 0x50
0x28 (3.10);
0x30 (3.50)
 
LARGE_INTEGER KernelTime;
3.10 only  
ULONG KernelTime;
3.50 only next at 0x38
0x30 (3.10);
0x34 (3.50)
 
LARGE_INTEGER UserTime;
3.10 only  
ULONG UserTime;
3.50 only next at 0x3C
0x38 (3.10 to 3.50);
0x18
0x28
ULONG_PTR DirectoryTableBase [2];
3.51 to 5.2  
ULONG_PTR DirectoryTableBase;
6.0 and higher  
0x1C (6.0) 0x30 (6.0)
ULONG_PTR Unused0;
6.0 only  
0x40 (3.10)   unknown KSPIN_LOCK 3.10 only  
0x44 (3.10)  
ULONG Iopl;
3.10 only next as UCHAR at 0x5E
0x48 (3.10);
0x3C (3.50)
  unknown eight bytes 3.10 to 3.50  
0x50 (3.10)  
LONG ThreadQuantum;
3.10 only next as CHAR at 0x65
0x54 (3.10);
0x44 (3.50)
 
KAFFINITY ActiveProcessors;
3.10 to 3.50 next at 0x34
0x58 (3.10);
0x48 (3.50)
 
KAFFINITY Affinity;
3.10 to 3.50 next at 0x5C
0x5C (3.10);
0x4C (3.50);
0x20 (3.51 to 6.0);
0x1C
 
KGDTENTRY LdtDescriptor;
all  
0x54 (3.50);
0x28 (3.51 to 6.0);
0x24
 
KIDTENTRY Int21Descriptor;
3.50 and higher  
0x64 (3.10);
0x5C (3.50);
0x30 (3.51 to 6.0)
0x38 (late 5.2 to 6.0)
USHORT IopmOffset;
3.10 to 6.0 next at 0x6E (x86)
0x5E (3.50);
0x32 (3.51 to 6.0)
 
UCHAR Iopl;
3.50 to early 6.0 previously ULONG at 0x44
UCHAR Unused1;
late 6.0 only  
0x66 (3.10);
0x5F (3.50);
0x33 (3.51 to 6.0)
 
UCHAR VdmFlag;
3.10 to 5.0  
UCHAR Unused;
5.1 to early 6.0  
UCHAR Unused2;
late 6.0 only  
0x34 (3.51 to 6.0) 0x40 (late 5.2 to 6.0)
KAFFINITY ActiveProcessors;
3.51 to 5.1 previously 0x44
KAFFINITY volatile ActiveProcessors;
5.2 to 6.0 next as KAFFINITY_EX volatile at 0x50 and 0x88
0x38 (3.51 to 6.0) 0x48 (5.0 to 6.0)
ULONG KernelTime;
3.51 to 6.0 previously 0x30;
next at 0x88 and 0xF8
0x3C (3.51 to 6.0) 0x4C (5.0 to 6.0)
ULONG UserTime;
3.51 to 6.0 previously 0x34;
next at 0x8C and 0xFC
0x2C 0x30
LIST_ENTRY ThreadListHead;
6.1 and higher previously 0x50 and 0x70
0x34 0x40
ULONG_PTR ProcessLock;
6.1 only previously 0x58 and 0x80
ULONG ProcessLock;
6.2 and higher  
  0x44
ULONG Spare0;
6.2 and higher  
0x38 0x48
ULONGLONG DeepFreezeStartTime;
10.0 and higher  
0x38 (6.1 to 6.3);
0x40
0x48 (6.1 to 6.3);
0x50
KAFFINITY_EX Affinity;
6.1 and higher previously KAFFINITY at 0x5C and 0x88
0x40 (3.51 to 6.0);
0x44 (6.1 to 6.3);
0x4C
0x50 (late 5.2 to 6.0);
0x70 (6.1);
0xF0 (6.2 to 6.3);
0xF8
LIST_ENTRY ReadyListHead;
3.51 and higher previously 0x18
0x48 (3.51 to 6.0);
0x4C (6.1 to 6.3);
0x54
 
LIST_ENTRY SwapListEntry;
3.51 to 5.0 previously 0x20
0x60 (late 5.2 to 6.0);
0x80 (6.1);
0x0100 (6.2 to 6.3);
0x0108
SINGLE_LIST_ENTRY SwapListEntry;
5.1 and higher  
0x50 (6.1 to 6.3);
0x58
0x88 (6.1);
0x0108 (6.2 to 6.3);
0x0110
KAFFINITY_EX volatile ActiveProcessors;
6.1 and higher previously KAFFINITY volatile at 0x34 and 0x40
0x4C (5.1 to 6.0)  
PVOID VdmTrapcHandler;
5.1 to 6.0 next at 0x90
  0x68 (late 5.2 to 6.0)
PVOID Reserved1;
late 5.2 only  
PVOID InstrumentationCallback;
6.0 only next at 0x0100
0x50 (3.51 to 6.0) 0x70 (late 5.2 to 6.0)
LIST_ENTRY ThreadListHead;
3.51 to 6.0 previously 0x28;
next at 0x2C and 0x30
0x58 (3.51 to 6.0) 0x80 (late 5.2 to 6.0)
KSPIN_LOCK ProcessLock;
3.51 to 6.0 next at 0x34 and 0x40
0x5C (3.51 to 6.0) 0x88 (late 5.2 to 6.0)
KAFFINITY Affinity;
3.51 to 6.0 previously at 0x48;
next as KAFFINITY_EX at 0x40 and 0x50
0x60 (3.50 to early 5.2)  
USHORT StackCount;
3.50 to early 5.2 next as ULONG_PTR at 0x6C and 0xA0
0x60 (late 5.2 to 6.0);
0x5C (6.1 to 6.3);
0x64
0x90 (late 5.2 to 6.0);
0xB0 (6.1);
0x01B0 (6.2 to 6.3);
0x01B8
union {
    struct {
        /*  bit fields, follow link  */
    };
    LONG ProcessFlags;
};
late 5.2 only  
union {
    struct {
        /*  bit fields, follow link  */
    };
    LONG volatile ProcessFlags;
};
6.0 and higher  
0x67 (3.10);
0x62 (3.50)
 
BOOLEAN AutoAlignment;
3.10 to 3.50 next at 0x64
0x68 (3.10);
0x63 (3.50);
0x62 (3.51 to early 5.2);
0x64 (late 5.2 to 6.0);
0x60 (6.1 to 6.3);
0x68
0x94 (late 5.2 to 6.0);
0xB4 (6.1);
0x01B4 (6.2 to 6.3);
0x01BC
CHAR BasePriority;
all  
0x69 (3.10);
0x64 (3.50)
 
UCHAR State;
3.10 to 3.50 next at 0x65;
last member in 3.10
0x65 (3.50);
0x63 (3.51 to early 5.2);
0x65 (late 5.2 to 6.0);
0x61 (6.1 to 6.3);
0x69
0x95 (late 5.2 to 6.0);
0xB5 (6.1);
0x01B5 (6.2 to 6.3);
0x01BD
CHAR ThreadQuantum;
3.50 to early 5.2 previously LONG at 0x50;
last member in 3.50
CHAR QuantumReset;
late 5.2 and higher  
0x64 (3.51 to early 5.2)  
BOOLEAN AutoAlignment;
3.51 to early 5.2 previously 0x62;
next in ProcessFlags
0x65 (3.51 to early 5.2);
0x66 (late 5.2 to 6.0)
0x96 (late 5.2 to 6.0)
UCHAR State;
3.51 to 6.0 previously 0x64;
next in StackCount;
last member in 3.51

Whatever may have motivated the reordering of byte-size members at the end in versions 3.10, 3.50 and 3.51, all these versions end the structure with unused space because of alignment: six in 3.10 because of the structure’s 8-byte alignment (from using LARGE_INTEGER for KernelTime and UserTime); two in the next versions.

Appended for Windows NT 4.0

Version 4.0 adds just two bytes which anyway fit into the earlier versions’ alignment padding. Neither stays put.

Offset (x86) Offset (x64) Definition Versions Remarks
0x66 (4.0 to early 5.2);
0x67 (late 5.2 to 6.0)
0x97 (late 5.2 to 6.0)
UCHAR ThreadSeed;
4.0 to 6.0 next as ULONG array at 0x64 and 0xB8
0x67 (4.0 to early 5.2)  
BOOLEAN DisableBoost;
4.0 to early 5.2 next in ProcessFlags;
last member in 4.0

The ThreadSeed helps to spread the process’s threads over the available processors. It is initialised pseudo-randomly (from the low byte of KeTickCount) when the process is created. As each thread is initialised, the process’s ThreadSeeed (modulo the number of processors) becomes that thread’s IdealProcessor and is then incremented in anticipation of the next thread. When Windows 7 greatly increased the potential number of processors, the single-byte processor number widened to the four-byte processor index and the ThreadSeed was reworked elsewhere in the structure.

Appended for Windows 2000

Offset (x86) Offset (x64) Definition Versions Remarks
0x68 (5.0 to 6.0) 0x98 (late 5.2 to 6.0)
UCHAR PowerState;
5.0 to 6.0  
0x69 (5.0 to early 5.2)  
BOOLEAN DisableQuantum;
5.0 to early 5.2 next in ProcessFlags
0x6A (5.0)
 
UCHAR Spare [2];
5.0 only last member in 5.0

Appended for Windows XP

Additions for version 5.1 begin with the bytes that version 5.0 left explicitly Spare at its end.

Offset (x86) Offset (x64) Definition Versions Remarks
0x6A (5.1 to early 5.2);
0x69 (late 5.2 to 6.0)
0x99 (late 5.2 to 6.0)
UCHAR IdealNode;
5.1 to 6.0 next as USHORT array at 0x68 and 0xC8
0x6A (late 5.2 to 6.0);
0x62 (6.1 to 6.3);
0x6A
0x9A (late 5.2 to 6.0);
0xB6 (6.1);
0x01B6 (6.2 to 6.3);
0x01BE
BOOLEAN Visited;
late 5.2 and higher  
0x6B (5.1 to 6.0);
0x63 (6.1 to 6.3);
0x6B
 
UCHAR Spare;
early 5.1;
early 5.2
last member in early 5.1;
last member in early 5.2
0x9B (late 5.2 to 6.0);
0xB7 (6.1);
0x01B7 (6.2 to 6.3);
0x01BF
union {
    KEXECUTE_OPTIONS Flags;
    UCHAR ExecuteOptions;
};
late 5.1;
late 5.2 to 6.0
next without union at 0x6C and 0xD2;
last member in late 5.1
UCHAR Spare3;
6.1 only  
KEXECUTE_OPTIONS Flags;
6.2 and higher previously at 0x6C and 0xD2

Insertions for Windows 7

Offset (x86) Offset (x64) Definition Versions Remarks
0x64 (6.1 to 6.3);
0x6C
0xB8 (6.1);
0x01B8 (6.2 to 6.3);
0x01C0
ULONG ThreadSeed [MAX_PROC_GROUPS];
6.1 and higher previously UCHAR at 0x67 and 0x97
0x68 (6.1 to 6.3);
0x70
0xC8 (6.1);
0x0208 (6.2 to 6.3);
0x0210
USHORT IdealNode [MAX_PROC_GROUPS];
6.1 and higher previously UCHAR at 0x69 and 0x99
0x6A (6.1 to 6.3);
0x72
0xD0 (6.1);
0x0230 (6.2 to 6.3);
0x0238
USHORT IdealGlobalNode;
6.1 and higher  
0x6C (6.1 to 6.3);
0x74
0xD2 (6.1);
0x0232 (6.2 to 6.3);
0x023A
KEXECUTE_OPTIONS Flags;
6.1 only previously in union at 0x6B and 0x9B;
next at 0x63 and 0x01B7
USHORT Spare1;
6.2 and higher  
0x6D (6.1) 0xD3 (6.1)
UCHAR Unused1;
6.1 only  
0x6E (6.1 to 6.3);
0x76
 
USHORT IopmOffset;
6.1 and higher previously at 0x30
  0xD4 (6.1)
ULONG Unused2;
6.1 only  
0x70 (6.1 to 6.3);
0x78
0xD8 (6.1)
ULONG Unused4;
6.1 only  
 
KSCHEDULING_GROUP *SchedulingGroup;
6.2 and higher x64 at 0x0258

Appended for Windows Server 2003 SP1

While the first version with x64 builds found a few opportunities to save space by converting the AutoAlignment, DisableBoost and DisableQuantum booleans to bit fields in what were then the new ProcessFlags, it widened the StackCount and moved it to the end. Version 6.1 merged the ancient State into the StackCount as a bit field.

Offset (x86) Offset (x64) Definition Versions Remarks
0x6C (late 5.2 to 6.0);
0x74 (6.1 to 6.3);
0x7C
0xA0 (late 5.2 to 6.0);
0xDC (6.1);
0x0234 (6.2 to 6.3);
0x023C
ULONG_PTR StackCount;
late 5.2 to 6.0 previously USHORT at 0x60
KSTACK_COUNT StackCount;
6.1 only  
KSTACK_COUNT volatile StackCount;
6.2 and higher  
0x70 (late 5.2 to 6.0);
0x78 (6.1 to 6.3);
0x80
0xA8 (late 5.2 to 6.0);
0xE0 (6.1);
0x0238 (6.2 to 6.3);
0x0240
LIST_ENTRY ProcessListEntry;
late 5.2 and higher last member in late 5.2

The ProcessListEntry may be worth studying. On x64 builds, it links processes into a global list whose head is an internal kernel variable that symbol files name KiProcessListHead. Yet I don’t see where they ever get removed from the list and I don’t know any use at all on x64 builds.

Appended For Windows Vista

Offset (x86) Offset (x64) Definition Versions Remarks
0x78 (6.0);
0x80 (6.1 to 6.3);
0x88
0xB8 (late 5.2 to 6.0);
0xF0 (6.1);
0x0248 (6.2 to 6.3);
0x0250
ULONGLONG volatile CycleTime;
6.0 to 6.1 last member in 6.0
ULONGLONG CycleTime;
6.2 and higher  

Inserted For Windows 8

Offset (x86) Offset (x64) Definition Versions Remarks
0x88 (6.2 to 6.3);
0x90
0x0250 (6.2 to 6.3);
0x0258
ULONGLONG ContextSwitches;
6.2 and higher  
  0x0258 (6.2 to 6.3);
0x0260
KSCHEDULING_GROUP *SchedulingGroup;
6.2 and higher x86 at 0x70
0x90 (6.2 to 6.3);
0x98
0x0260 (6.2 to 6.3);
0x0268
ULONG FreezeCount;
6.2 and higher  

Appended For Windows 7

Version 6.1 did not so much append to the structure as move some old, even ancient, members to the end.

Offset (x86) Offset (x64) Definition Versions Remarks
0x88 (6.1);
0x94 (6.2 to 6.3);
0x9C
0xF8 (6.1);
0x0264 (6.2 to 6.3);
0x026C
ULONG KernelTime;
6.1 and higher previously 0x38 and 0x48
0x8C (6.1);
0x98 (6.2 to 6.3);
0xA0
0xFC (6.1);
0x0268 (6.2 to 6.3);
0x0270
ULONG UserTime;
6.1 and higher previously 0x3C and 0x4C
0x90 (6.1);
0x9C (6.2 to 6.3);
0xA4
 
PVOID VdmTrapcHandler;
6.1 and higher previously 0x4C;
last member in 6.1 and higher (x86)

Appended for 64-Bit Windows

The only truly new members that Windows 7 added or that any later version has yet appended to the KPROCESS are for 64-bit Windows only. Even these relatively few additions are not pretty, since shifts in preceding members allowed version 6.2 to get tighter packing just from rearranging what version 6.1 added.

Offset (x64) Definition Versions Remarks
0x0100 (6.1)
PVOID InstrumentationCallback;
6.1 only previously 0x68;
next at 0x02C0
0x0108 (6.1)
KDGENTRY64 LdtSystemDescriptor;
6.1 only next at 0x0270
0x0118 (6.1)
PVOID LdtBaseAddress;
6.1 only next at 0x0280
0x0120 (6.1)
KGUARDED_MUTEX LdtProcessLock;
6.1 only next as FAST_MUTEX at 0x0288
0x0158 (6.1);
0x026C (6.2 to 6.3);
0x0274
USHORT LdtFreeSelectorHint;
6.1 and higher  
0x015A (6.1);
0x026E (6.2 to 6.3);
0x0276
USHORT LdtTableLength;
6.1 and higher last member in 6.1 (x64)
0x0270 (6.2 to 6.3);
0x0278
KGDTENTRY64 LdtSystemDescriptor;
6.2 and higher previously 0x0108
0x0280 (6.2 to 6.3);
0x0288
PVOID LdtBaseAddress;
6.2 and higher previously 0x0118
0x0288 (6.2 to 6.3);
0x0290
FAST_MUTEX LdtProcessLock;
6.2 and higher previously KGUARDED_MUTEX at 0x0120
0x02C0 (6.2 to 6.3);
0x02C8
PVOID InstrumentationCallback;
6.2 and higher previously 0x0100;
last member in 6.2 to 6.3 (x64)
0x02D0
ULONGLONG SecurePid;
10.0 and higher last member in 10.0 (x64)