Kernel Callback Functions

Comprehensive list of documented and undocumented APIs available in the Windows kernel to register callback routines.

© CodeMachine Inc. | codemachine.com | @codemachineinc

Kernel Callback Functions

This technical note provides a comprehensive list all the APIs exported by the Windows Kernel, for driver writes to register callback routines that are invoked by kernel components under various circumstances. Most of these routines are documented in the Windows Driver Kit (WDK) but some of them are for use by in-box drivers. The undocumented functions are described briefly whereas the documented ones are just listed here for reference.

Documented Functions

CmRegisterCallbackEx()
ExAllocateTimer()
ExInitializeWorkItem()
ExRegisterCallback()
FsRtlRegisterFileSystemFilterCallbacks()
IoInitializeThreadedDpcRequest()
IoQueueWorkItem()
IoRegisterBootDriverCallback()
IoRegisterContainerNotification()
IoRegisterFsRegistrationChangeEx()
IoRegisterFsRegistrationChangeMountAware()
IoRegisterPlugPlayNotification()
IoSetCompletionRoutineEx()
IoWMISetNotificationCallback()
KeExpandKernelStackAndCalloutEx()
KeInitializeApc()
KeInitializeDpc()
KeRegisterBugCheckCallback()
KeRegisterBugCheckReasonCallback()
KeRegisterNmiCallback()
KeRegisterProcessorChangeCallback()
KeRegisterProcessorChangeCallback()
ObRegisterCallbacks()
PoRegisterDeviceNotify()
PoRegisterPowerSettingCallback()
PsCreateSystemThread()
PsSetCreateProcessNotifyRoutineEx()
PsSetCreateThreadNotifyRoutine()
PsSetLoadImageNotifyRoutine()
SeRegisterLogonSessionTerminatedRoutine()
TmEnableCallbacks()

Undocumented Functions

DbgSetDebugPrintCallback() installs or removes a caller provided callback function which is invoked whenever DbgPrint(), KdPrint() and their variants are called, giving them access to the formatted debug output buffer. This is useful when the system is not running under the control of a kernel debugger and the user wishes to examine the out of DbgPrint(). The DbgView tool from SysInternals uses this API to capture and display output from kernel components. Multiple callers can install callback, all of which are stored in a doubly linked list whose head is at RtlpDebugPrintCallbackList. The lock at RtlpDebugPrintCallbackLock protects this list. The value in the Boolean RtlpDebugPrintCallbacksActive determines if any driver has installed such a callback. The prototype of this function is available in the WDK and is as follows:

VOID (*PDEBUG_PRINT_CALLBACK) (
    _In_ PSTRING Output,
    _In_ ULONG ComponentId,
    _In_ ULONG Level );

NTSTATUS DbgSetDebugPrintCallback (
    _In_ PDEBUG_PRINT_CALLBACK DebugPrintCallback,
    _In_ BOOLEAN Enable );

IoRegisterPriorityCallback() is used by storage I/O drivers like classpnp.sys to register a callback (e.g. CLASSPNP!ClassIoBoostPriority) which is used to notify the driver about I/O priority changes in IRPs that the driver currently owns. A thread's I/O priority is boosted to mitigate priority inversion issues involving threads with different I/O priorities. The Windows Internals book 6th Edition describes this in Chapter 8. The callbacks are stored in the array IopUpdatePriorityCallbackRoutine[] which can hold a maximum of 8 callbacks. IoBoostThreadIoPriority() is responsible for making the callbacks. The prototype of this function, shown below, is NOT available in the WDK:

VOID (*PIO_PRIORITY_CALLBACK) (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PETHREAD Thread,
    _In_ IO_PRIORITY_HINT PriorityHint );

NTSTATUS IoRegisterPriorityCallback (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PIO_PRIORITY_CALLBACK  Callback  );

VOID IoUnregisterPriorityCallback (
    _In_ PDRIVER_OBJECT DriverObject );

PoRegisterCoalescingCallback() is used by callers like Cache Manager, Configuration Manager and NTFS to register for I/O coalescing notifications. The callback functions are stored in an the array PopIssueCoalescingNotification[] which can hold a maximum of 8 such callbacks. Registered callers are notified to toggle their I/O coalescing state or to flush pending I/O operations during system idle state so that disks can be spun down. The function PopIssueCoalescingNotification() performs the I/O coalescing notifications. The prototype of this function, shown below, is NOT available in the WDK:

VOID (*PPO_COALESCING_CALLBACK) (
    _In_ ULONG Reason,
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PVOID Context );
 
NTSTATUS PoRegisterCoalescingCallback (
    _In_ PPO_COALESCING_CALLBACK Callback,
    _In_ BOOLEAN ClientOrSrever,
    _Out_ PVOID* Handle,
    _In_ PVOID Context );

VOID PoUnregisterCoalescingCallback ( 
    _In_  PVOID Handle);

PsSetLegoNotifyRoutine() is used to register a routine that gets invoked by the kernel during thread termination. The KTHREAD structure contains a pointer sized field called LegoData which can be used by a single kernel component in the system to store per thread data. This is similar in functionality to user mode Thread Local Storage (TLS) but is limited to a single slot per thread. A kernel mode driver can dynamically allocate thread specific context, store it in KTHREAD.LegoData, retrieve it as required and then free it in the lego notify routine. The callback registered by PsSetLegoNotifyRoutine() is stored in the kernel global PspLegoNotifyRoutine. To enable callers to access the KTHREAD.LegoData without having knowledge of the KTHREAD layout, PsSetLegoNotifyRoutine() returns the offset of the KTHREAD.LegoData field that callers can use to access the contents directly. The prototype of this function, shown below, is NOT available in the WDK:

VOID (*PLEGO_NOTIFY_ROUTINE)(
    _In_ PKTHREAD Thread  );

ULONG PsSetLegoNotifyRoutine(
    _In_ PLEGO_NOTIFY_ROUTINE Routine );

SeRegisterImageVerificationCallback() is used by anti-malware drivers like the built-in Windows Defender driver (WdFilter.sys) to register a callback (e.g. WdFilter!MpImageVerificationCallback) which is invoked every time a driver image is loaded into memory. SeValidateImageHeader() queues the work item routine SepImageVerificationCallbackWorker() which is responsible for invoking all the registered image verification callbacks. The image verification callback is invoked in this deferred manner so as not to block the driver load process while images are being verified. In this callback drivers read the information captured in BDCB_IMAGE_INFORMATION and use their own proprietary algorithms to determine if the system state could potentially be compromised by loading the new driver image into memory and subsequently takes appropriate action. SeUnregisterImageVerificationCallback() is used to unregister the callback. The prototype of this function is available in the WDK and is as follows:

VOID (*PSE_IMAGE_VERIFICATION_CALLBACK_FUNCTION) (
    _In_opt_ PVOID CallbackContext,
    _In_ SE_IMAGE_TYPE ImageType,
    _Inout_ PBDCB_IMAGE_INFORMATION ImageInformation );

NTSTATUS SeRegisterImageVerificationCallback (
    _In_ SE_IMAGE_TYPE ImageType,
    _In_ SE_IMAGE_VERIFICATION_CALLBACK_TYPE CallbackType,
    _In_ PSE_IMAGE_VERIFICATION_CALLBACK_FUNCTION CallbackFunction,
    _In_opt_ PVOID CallbackContext,
    _Reserved_ SE_IMAGE_VERIFICATION_CALLBACK_TOKEN Token,
    _Out_ PVOID* CallbackHandle );

VOID SeUnregisterImageVerificationCallback ( 
    _In_ PVOID CallbackHandle );