Windows系统内核提供了一系列的事件通知(Notify)机制以及回调(Callback)机制。
事件通知机制:用于监控系统内某一事件的操作。
回调机制:用来反映系统内某一个部件的状态,也可以被用来实现多个内核模块之间的通信。
Windows的回调机制
回调机制为驱动程序提供了一种通用的方法来发送和接收某类通告,这些通告可以是系统某个部件的状态发生变化而产生的通告,也可以是开发者自定义的某个条件因为条件成立而产生的通告。
Windows操作系统内置了一部分回调,如电源状态变化回调(PowerState Callback)、系统时间变化回调(SetSystemTime Callback)等,用户也可以自定义回调。
回调对象(Callback Object)是回调机制中最重要的数据结构,类型为CALLBACK_OBJECT
,用来唯一地描述一个回调。它属于一种命名内核对象。
当系统或开发者为驱动程序发送某一类通告时,需要创建一个回调对象A,如果驱动程序接受这类通告,则需要打开回调对象A,成功打开后,驱动程序还需要把一个回调例程注册到回调对象A上。这样,回调对象A和回调例程就被关联起来了,当系统或开发者发送通告时,就可以 通告回调对象A发起通告,最后回调例程就会被调用。
例如:系统再启动时创建了一个回调对象用于描述电源状态的变化,这个回调对象名称叫做\Callback\PowerState
,驱动程序需要接收电源状态变化通告时,首先可以调用ExCreateCallback
函数,打开\Callback\PowerState
对象,得到PCALLBACK_OBJECT
类型的指针,然后驱动程序调用ExRegisterCallback
函数,把一个回调例程注册到这个回调对象上。当系统电源状态发生变化时,系统可以调用ExNotifyCallback
函数来发送通告,驱动程序所注册的回调例程会被调用。
创建/打开回调对象
NTSTATUS
NTAPI
ExCreateCallback(
OUT PCALLBACK_OBJECT *CallbackObject, // 回调对象的指针,用完之后要通告ObDereferenceObject函数释放
IN POBJECT_ATTRIBUTES ObjectAttributes, // 属性,可以通过其指定回调对象名称
IN BOOLEAN Create, // 为TRUE表示若打开失败后会创建该回调对象,为FALSE表示若打开失败(即不存在),函数会返回失败
IN BOOLEAN AllowMultipleCallbacks); // 为TRUE表示允许多个回调例程注册到该回调对象上
注册回调对象,即把一个回调例程绑定到回调对象上,当发送通告时,系统会一次调用与该回调对象绑定的回调例程。
注册成功时返回一个注册句柄,注册失败返回NULL。
PVOID
NTAPI
ExRegisterCallback(
IN PCALLBACK_OBJECT CallbackObject, // 回调对象指针
IN PCALLBACK_FUNCTION CallbackFunction, // 回调例程
IN PVOID CallbackContext); // 用户自定义数据,传给回调例程的参数,可以为NULL
移除注册
VOID
NTAPI
ExUnregisterCallback(IN PVOID CallbackRegistrationHandle);
回调例程,注意其IRQL <= APC_LEVEL
VOID
NTAPI
CallbackFunction(
IN PVOID CallbackContext, // ExRegisterCallback的第三参数
IN PVOID Argument1, // 根据具体的回调对象决定
IN PVOID Argument2); // 根据具体的回调对象决定
发送通告,它内部会对CallbackObject进行分析,查找注册到其上的回调例程,按照注册顺序依次调用。
VOID
NTAPI
ExNotifyCallback(
IN PCALLBACK_OBJECT CallbackObject,
IN PVOID Argument1,
IN PVOID Argument2);
测试代码
PCALLBACK_OBJECT CallbackObject = { 0 };
VOID
CallbackFunction(
IN PVOID CallbackContext,
IN PVOID Argument1,
IN PVOID Argument2)
{
PAGED_CODE();
DbgPrint("CallbackFunction() is called");
}
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
ExUnregisterCallback(CallbackFunction);
ObDereferenceObject(CallbackObject);
}
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
//解引用
UNREFERENCED_PARAMETER(RegisterPath);
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
UNICODE_STRING ObjectName = { 0 };
do
{
RtlInitUnicodeString(&ObjectName, L"\\Callback\\KeyBoard");
InitializeObjectAttributes(&ObjectAttributes, &ObjectName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ExCreateCallback(&CallbackObject, &ObjectAttributes, TRUE, TRUE);
if (!NT_SUCCESS(Status))
{
break;
}
if (ExRegisterCallback(CallbackObject, CallbackFunction, NULL) == NULL)
{
break;
}
ExNotifyCallback(CallbackObject, NULL, NULL);
} while (FALSE);
//设置驱动卸载例程
DriverObject->DriverUnload = DriverUnload;
return Status;
}