The kernel’s core has its own set of objects that it works with independently of the Object Manager. Many kernel objects, but not all, begin with a DISPATCHER_HEADER. Some kernel objects, but not all, are the beginning of an object that is managed by the Object Manager. Consider, for example, the process object that is referred to when a handle is obtained to a process: it is an EPROCESS which has the kernel’s KPROCESS at its start. All kernel objects begin with a byte or word that signifies the type of object. These signifiers are formally defined as the KOBJECTS enumeration.

Microsoft does not document the KOBJECTS enumeration, nor even declare it in header files from any Windows Driver Kit (WDK). However, Microsoft’s names for it and its values are known from symbol files for Windows Vista and higher. Since symbol files for earlier versions do not contain type information for KOBJECTS, what’s known for these earlier versions is instead inferred from inspecting different versions of the kernel for corresponding use. Where close correspondence is found 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. Beware, then, that the description below is something of a guess before Windows Vista.

Value Symbol Structure Versions
0x00 EventNotificationObject KEVENT all
0x01 EventSynchronizationObject KEVENT all
0x02 MutantObject KMUTANT all
0x03 MutexObject (proposed) KMUTEX 3.10 only
ProcessObject KPROCESS 3.50 and higher
0x04 QueueObject KQUEUE 3.50 and higher
0x04 (3.10);
SemaphoreObject KSEMAPHORE all
0x05 (3.10);
ThreadObject KTHREAD all
0x07 SpareObject (proposed)   4.0 to 5.2 before Windows Server 2003 SP1
GateObject KGATE 5.2 from Windows Server 2003 SP1, and higher
0x06 (3.10);
0x07 (3.50 to 3.51);
TimerObject (proposed) KTIMER 3.10 to 3.51
TimerNotificationObject KTIMER 4.0 and higher
0x09 TimerSynchronizationObject KTIMER 4.0 and higher
0x0A Spare2Object   4.0 and higher
0x0B Spare3Object   4.0 and higher
0x0C Spare4Object   4.0 and higher
0x0D Spare5Object   4.0 and higher
0x0E Spare6Object   4.0 and higher
0x0F Spare7Object   4.0 and higher
0x10 Spare8Object   4.0 and higher
0x11 Spare9Object   4.0 to 6.1
ProfileCallbackObject KPROFILE (proposed) 6.2 and higher
0x07 (3.10);
0x08 (3.50 to 3.51);
ApcObject KAPC all
0x08 (3.10);
0x09 (3.50 to 3.51);
DpcObject KDPC all
0x09 (3.10);
0x0A (3.50 to 3.51);
DeviceQueueObject KDEVICE_QUEUE all
0x0A (3.10);
0x0B (3.50 to 3.51);
EventPairObject KEVENT_PAIR (proposed) 3.10 to 6.2
PriQueueObject KPRIQUEUE 6.3 and higher
0x0B (3.10);
0x0C (3.50 to 3.51);
InterruptObject KINTERRUPT 3.51 and higher
0x0D (3.10) PowerStatusObject (proposed) KPOWER_STATUS (proposed) 3.10 only
0x0E (3.10) ProcessObject KPROCESS 3.10 only
0x0F (3.10 to 3.51);
ProfileObject KPROFILE (proposed) all
0x18 Timer2NotificationObject KTIMER2 6.3 and higher
0x19 Timer2SynchronizationObject KTIMER2 6.3 and higher
0x18 (5.2 to 6.2);
ThreadedDpcObject KDPC 5.2 and higher
0x10 (3.10 to 3.51);
0x18 (4.0 to 5.1);
0x19 (5.2 to 6.2);

The enumeration is broadly in two distinct sets, each arranged very nearly alphabetically: first the types for kernel objects that begin with a DISPATCHER_HEADER; then those that do not. This separation seems to have been an original intention. Establishing it is perhaps the one and only reason that version 3.50 renumbered ProcessObject. It was not broken until the new timer objects for version 6.3 could not be added to the first set as a pair at a multiple of 8 without large-scale renumbering.

It is not merely coincidental or neat that EventSynchronizationObject, TimerSynchronizationObject and Timer2SynchronizationObject are each one more than a multiple of 8. Kernel objects that begin with a DISPATCHER_HEADER—let’s call them dispatcher objects—are waitable. Their addresses can be passed to the KeWaitForSingleObject and KeWaitForMultipleObjects functions such that the function should not return unless the object or objects (all or any) get signalled. How a dispatcher object gets signalled varies with the object. What’s relevant to the KOBJECTS enumeration is that, starting with version 4.0, if the KOBJECTS for a dispatcher object has 1 for its low 3 bits, then the object is specifically a synchronisation object: when it is signalled and a waiting thread is released from its wait, the object is automatically reset to non-signalled and no more threads are released from waiting until the object is signalled again.

The name TimerObject is proposed for 0x07 in version 3.51 (and 0x06 before that). The corresponding structure is the same KTIMER that later versions support in two types. Before version 4.0, however, the object is specifically a notification timer. The early versions presumably have no notion of a TimerNotificationObject as separate from a TimerSynchronizationObject, just as they have only a KeInitializeTimer function, not the later KeInitializeTimerEx that can initialise either type.

No later use of any kernel object of type 0x07 is known until Windows Server 2003 SP1 introduced the gate object. It seems at least plausible that 0x07 was left as the original SpareObject or Spare1Object as a side-effect of skipping ahead to 0x08 and 0x09 for the new types of timer object so that notification versus synchronisation can be discerned in common for events and timers just from the low 3 bits.

The name KEVENT_PAIR is proposed for the structure that corresponds to EventPairObject. It is just 16 bits for the KOBJECTS value, a 16-bit size, and then two KEVENT structures (specifically, two synchronisation events), but no structure with this layout is known from Microsoft’s symbol files for the kernel in any version. Whatever its name, the kernel’s structure for an event pair is exposed through the Object Manager such that event pairs can be created and worked with through such (undocumented) functions as NtCreateEventPair—or could until Windows 8.1, which has the function fail trivially.

Similarly, Microsoft’s name for the closely compatible structures that are signified by ProfileObject and ProfileCallbackObject is not known, but there would be no surprise if it is KPROFILE. The structure is known as early as version 3.10.

What filled the gap between InterruptObject and ProfileObject before version 4.0 is known only partly. The names PowerStatusObject and KPOWER_STATUS are proposed for whatever object is initialised and worked with through the functions KeInitializePowerStatus, KeInsertQueuePowerStatus and KeRemoveQueuePowerStatus which exist only in version 3.10.