EX_CALLBACK_FUNCTION回调函数 (wdm.h)
筛选器驱动程序的 RegistryCallback 例程可以监视、阻止或修改注册表操作。
语法
EX_CALLBACK_FUNCTION ExCallbackFunction;
NTSTATUS ExCallbackFunction(
[in] PVOID CallbackContext,
[in, optional] PVOID Argument1,
[in, optional] PVOID Argument2
)
{...}
参数
[in] CallbackContext
驱动程序在注册此 RegistryCallback 例程时作为 Context 参数传递给 CmRegisterCallback 或 CmRegisterCallbackEx 的值。
[in, optional] Argument1
一个REG_NOTIFY_CLASS类型的值,该值标识正在执行的注册表操作的类型,以及在执行注册表操作之前还是之后调用 RegistryCallback 例程。
[in, optional] Argument2
指向结构的指针,该结构包含特定于注册表操作类型的信息。 结构类型取决于 Argument1的REG_NOTIFY_CLASS类型值,如下表所示。 有关哪些REG_NOTIFY_CLASS类型值可用于哪些操作系统版本的信息,请参阅 REG_NOTIFY_CLASS。
从 Windows 7 开始,当通知类为 RegNtPreCreateKeyEx 或 RegNtPreOpenKeyEx 时传入的实际数据结构是此结构的 V1 版本, REG_CREATE_KEY_INFORMATION_V1 或 REG_OPEN_KEY_INFORMATION_V1。 检查“保留”成员以确定结构的版本。
版本号 | 结构名称 |
---|---|
0 | REG_CREATE_KEY_INFORMATION和REG_OPEN_KEY_INFORMATION |
1 | REG_CREATE_KEY_INFORMATION_V1和REG_OPEN_KEY_INFORMATION_V1 |
返回值
有关 RegistryCallback 例程何时应返回这些状态值的详细信息,请参阅 筛选注册表调用。
注解
若要收到注册表操作通知,内核模式组件 ((如防病毒软件包) 的驱动程序组件)可以调用 CmRegisterCallback 或 CmRegisterCallbackEx 来注册 RegistryCallback 例程。
RegistryCallback 例程可以检查为注册表操作提供的输入和输出缓冲区的内容。 注册表操作可由调用用户模式注册表例程 ((例如 RegCreateKeyEx 或 RegOpenKeyEx) )的用户模式应用程序启动,也可以由调用内核模式注册表例程 (的驱动程序启动,例如 ZwCreateKey 或 ZwOpenKey) 。 输入缓冲区是由发起程序提供的内存缓冲区,注册表从中读取操作的输入数据。 输出缓冲区是由发起程序提供的缓冲区,注册表会将发起程序请求的输出数据写入其中。
在调用 RegistryCallback 例程之前,内核探测 (验证参数 2 结构的所有成员) 的对齐和可访问性,这些成员指向用户模式内存中的输出缓冲区,但不捕获系统内存中的用户模式输出缓冲区。 回调例程必须将输出缓冲区的任何访问包含在尝试/中,但块除外。 如果回调例程需要将输出缓冲区指针传递到系统例程 (例如 ZwOpenKey) ,并且缓冲区位于用户模式内存中,则回调例程必须首先捕获缓冲区。
输入缓冲区的处理取决于 Windows 版本。 从Windows 8开始,内核在调用 RegistryCallback 例程之前捕获系统内存中 Argument2 结构的成员指向的所有输入缓冲区。 在 Windows 8 之前的 Windows 版本中,内核探测指向用户模式内存中的输入缓冲区的 Argument2 结构的所有成员,但仅捕获系统内存中的部分缓冲区。 在这些早期版本的 Windows 中,回调例程必须将输入缓冲区的任何访问包含在尝试/中,但块除外。 此外,如果回调例程需要将输入缓冲区指针传递到系统例程 (例如 ZwOpenKey) ,并且缓冲区位于用户模式内存中,则回调例程必须首先捕获缓冲区。
下表汇总了 RegistryCallback 例程访问缓冲区的要求。
缓冲区类型 | Windows 版本 | 传递给回调例程的缓冲区指针 | 回调例程可以直接访问是安全的? | 安全地传递到系统例程 (,例如 ZwOpenKey) ? |
---|---|---|---|---|
用户模式输入 | Windows 8 及更高版本 | 指向捕获的数据。 | 是 | 是 |
用户模式输入 | Windows 7 和更早版本 | 指向捕获的数据或原始用户模式缓冲区。 | 错误。 必须在 try/except 下读取。 | 错误。 必须分配内核内存,在 try/except 下从原始缓冲区复制数据,并将复制的数据传递到系统例程。 |
用户模式输出 | 全部 | 指向原始用户模式缓冲区。 | 错误。 必须在 try/except 下写入。 | 错误。 必须分配内核内存,将内核内存传递给系统例程,并将结果复制回 try/except 下的原始缓冲区。 |
内核模式输入和输出 | 全部 | 指向原始内核模式缓冲区。 | 是 | 是 |
有关 RegistryCallback 例程和注册表筛选器驱动程序的详细信息,请参阅 筛选注册表调用。
RegistryCallback 在 IRQL = PASSIVE_LEVEL 执行注册表操作的线程上下文中执行。
示例
若要定义 RegistryCallback 回调例程,必须先提供一个函数声明,用于标识要定义的回调例程的类型。 Windows 为驱动程序提供了一组回调函数类型。 使用回调函数类型声明函数可帮助 驱动程序的代码分析、 静态驱动程序验证程序 (SDV) 和其他验证工具查找错误,这是为 Windows 操作系统编写驱动程序的要求。
例如,若要定义名为 MyRegistryCallback
的 RegistryCallback 回调例程,请使用 EX_CALLBACK_FUNCTION 类型,如以下代码示例所示:
EX_CALLBACK_FUNCTION MyRegistryCallback;
然后,按如下所示实现回调例程:
_Use_decl_annotations_
NTSTATUS
MyRegistryCallback(
PVOID CallbackContext,
PVOID Argument1,
PVOID Argument2
)
{
// Function body
}
EX_CALLBACK_FUNCTION函数类型在 Wdm.h 头文件中定义。 若要在运行代码分析工具时更准确地识别错误,请务必将 Use_decl_annotations 注释添加到函数定义。 Use_decl_annotations批注可确保使用应用于头文件中EX_CALLBACK_FUNCTION函数类型的注释。 有关函数声明要求的详细信息,请参阅 使用 WDM 驱动程序的函数角色类型声明函数。 有关 Use_decl_annotations的信息,请参阅 批注函数行为。
要求
要求 | 值 |
---|---|
最低受支持的客户端 | 支持从 Windows XP 开始 (请参阅返回值部分) 。 |
目标平台 | 桌面 |
标头 | wdm.h(包括 Wdm.h、Ntddk.h、Ntifs.h) |
IRQL | PASSIVE_LEVEL (请参阅备注部分) 调用。 |