Kernel Dispatcher objects and Keyed Events are the last two synchronization methods I’m going to explain, which run within Kernel Mode. Since I’ve already introduced Kernel Dispatcher objects, and explained how they work slightly, then I’ll begin this post about these objects. Keyed Events will be explained in my next blog post since this post got a little too big.
Kernel Dispatcher Objects
We should all understand from my previous posts, that a dispatcher object is either in Signaled or Non-Signaled state. A thread synchronizes with that dispatcher object by waiting to acquire the object’s handle, which also is either Signaled or Non-Signaled. Any threads waiting for a dispatcher object has added to a wait queue. You will usually notice that a thread has been waiting for a dispatcher object with KeWaitForSingleObject and KeWaitForMultipleObjects.
When a dispatcher object, has been set to the Signaled state, then the KiWaitTest routine is called to check if any other threads are waiting for the object, and not waiting for any other objects. Some threads may be released from their wait state, if they are waiting for other objects, to prevent any problems discussed in my other blog posts.
You may or may not be surprised to know, that a process object can also be signaled or non-signaled, once set to signaled the process object releases all it’s threads. This procedure occurs when the process is terminated.
Let’s look at some data structures:
All dispatcher objects have a _DISPATCHER_HEADER data structure associated with them. You should take into consideration, that this is a prototype of the data structure, if I used the address of a dispatcher object, then some the fields may be removed since they are specific to a certain object.
Let’s apply that data structure to a Event Synchronization object.
Let’s examine the most important fields within this data structure. The Type field indicates the type of dispatcher object, these are stored in enumeration called _KOBJECTS. The SignaledState field shows wherever the dispatcher object is signaled or non-signaled (the field may be 0 or 1), and most likely uses a boolean true or false type. The Lock field shows the spinlock which is used to protect the SignalState and WaitListHead field. The WaitListHead field contains a linked list of _KWAIT_BLOCK data structures. The Size field indicates the size of the object divided by 4, I believe this may mean the size of the pool allocation.
The specific object fields are as follows:
Abandoned (Mutex): Thread with mutex was terminated.
Absolute (Timers): Expiration time is absolute, not relative.
NpxIrql (Threads): IRQL in which FPU registers were saved.
Signalling (Gates): Signaled state of the gate.
Hand (Timers): Index into the timer handle table.
Inserted (Timers): Timer was inserted into the timer handle table (if set).
DebugActive (Process): Process is being debugged.
DPCActive (Mutex): Mutex was acquired during a DPC.
Referring back to the ListWaitHead field, if the two pointers are the same, then only one thread is waiting on the object and therefore there is only one _KWAIT_BLOCK data structure.
We can gain the same information, by either using !thread or using !process and then looking at the threads associated with that process.
Before I conclude on this blog post, please be aware there two types of timer objects and event objects.
Synchronization Event objects release one thread and then resets, whereas, a Notification Event objects release all threads. The same rule applies to timer objects.
With queues and gates, one thread is released when the object is signaled.
We can gain a list of timer objects with the !timer extension.