Geoff Chappell, Software Analyst
CURRENT WORK ITEM - PREVIEW ONLY
The KTIMER is the kernel’s representation of a timer object. Like all dispatcher objects, timers can be waited on until they get signalled. As an elaboration that is not supported for all dispatcher objects, a timer’s usefulness comes not just from being waitable. It can also be configured so that signalling causes a Deferred Procedure Call (DPC) to be scheduled. For timers, signalling is done by the kernel on noticing that a specified time has been reached. This specified time can be absolute or relative, the former being a system time, the latter being a difference from the current interrupt time.
Kernel-mode code allocates space for a KTIMER and gets it ready for use by calling either KeInitializeTimer or the newer, more capable, KeInitializeTimerEx. Thereafter, conditions for the timer’s expiration can be specified through the progressively more newer and more capable functions KeSetTimer, KeSetTimerEx and KeSetCoalescableTimer. These functions each reset the timer to be non-signalled.
The kernel itself exposes timer objects to the Object Manager. Code in both kernel mode and user mode can call NtCreateTimer or ZwCreateTimer, as appropriate, to get the kernel to create a KTIMER and make it accessible through a handle. A timer that is created this way can have a name, such that another handle can be obtained, most usefully in another process, by calling NtOpenTimer or ZwOpenTimer. Of course, well-behaved user-mode code doesn’t call these native API functions directly but instead goes through such higher-level functions as CreateWaitableTimerEx and OpenWaitableTimer which are exported from KERNEL32.
In all versions, the KTIMER is 0x28 and 0x40 bytes in 32-bit and 64-bit Windows respectively.
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x00 | 0x00 |
DISPATCHER_HEADER Header; |
3.51 and higher | |
0x10 | 0x18 |
ULARGE_INTEGER DueTime; |
3.51 and higher | |
0x18 | 0x20 |
LIST_ENTRY TimerListEntry; |
3.51 and higher | |
0x20 | 0x30 |
KDPC *Dpc; |
3.51 and higher | |
0x38 |
ULONG Processor; |
6.1 and higher | ||
0x24 | 0x38 (late 5.2 to 6.0); 0x3C |
BOOLEAN Inserted; |
3.51 only | moves to Header |
LONG Period; |
4.0 to 6.0 | |||
ULONG Period; |
6.1 and higher |
The DueTime is the interrupt time at which the timer is set to expire.
Much of the new functionality in successive versions has been accommodated by finding space inside the Header. The DISPATCH_HEADER is a complex structure that begins all kernel objects that can be waited on. The following tables simplify by disregarding the nested unions, extracting only the branches that apply to timers.
Offset | Definition | Versions | Remarks |
---|---|---|---|
0x00 |
UCHAR Type; |
3.51 to 6.3 | |
UCHAR TimerType; |
10.0 and higher | ||
0x01 |
UCHAR Spare; |
3.51 only | |
UCHAR Absolute; |
4.0 to 6.0 | becomes bit in TimerControlFlags | |
union { UCHAR TimerControlFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher | ||
0x02 |
USHORT Size; |
3.51 only | |
UCHAR Size; |
4.0 to early 5.2 | ||
UCHAR Hand; |
late 5.2 and higher | ||
0x03 |
UCHAR Inserted; |
4.0 to 6.0 | becomes bit in TimerMiscFlags |
union { UCHAR TimerMiscFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher | ||
0x04 |
LONG SignalState; |
3.51 and higher | |
0x08 |
LIST_ENTRY WaitListHead; |
3.51 and higher |
As for all dispatchable objects, the low 7 bits of the Type—or all 8 bits in version 3.51—are from the KOBJECTS enumeration. For the KTIMER specifically, these bits are 0x08 (TimerNotificationObject) or 0x09 (TimerSynchronizationObject) according to whether NotificationTimer or SynchronizationTimer is given as the Type argument when initialising the timer through the KeInitializeTimerEx function.
Of several expansions of functionality that Windows NT 4.0 brought to timers, automatically restarting the timer at repeating intervals required more storage in the KTIMER. Three bytes had been left undefined as a side-effect of alignment after the Inserted member. To find four bytes for saving the whole Period that could be given to the new KeSetTimerEx function, the Inserted member was moved into the DISPATCHER_HEADER, taking space from the Size.
Mask | Definition | Versions |
---|---|---|
0x01 |
UCHAR Absolute : 1; |
6.1 and higher |
0x02 |
UCHAR Coalescable : 1; |
6.1 only |
UCHAR Wake : 1; |
6.2 and higher | |
0x04 (6.1) |
UCHAR KeepShifting : 1; |
6.1 only |
0xF8 (6.1); 0xFC |
UCHAR EncodedTolerableDelay : 5; |
6.1 only |
UCHAR EncodedTolerableDelay : 6; |
6.2 and higher |
Mask (x86) | Mask (x64) | Definition | Versions |
---|---|---|---|
0x01 |
UCHAR Index: 1; |
6.1 and higher | |
0x3F |
UCHAR Index: 6; |
6.1 and higher | |
0x3E |
UCHAR Processor : 5; |
6.1 and higher | |
0x40 |
UCHAR Inserted : 1; |
6.1 and higher | |
0x80 |
UCHAR volatile Expired : 1; |
6.1 and higher |