回调函数的注册与卸载
在驱动中进行注册表保护,可以通过 注册表回调
函数来实现。
在VISTA以下的系统中,使用 CmRegisterCallback
函数来注册回调,
在VISTA及以上的系统中,微软提供了新的 CmRegisterCallbackEx
函数来进行替代,
并引入了 Altitude
的概念,数值越大越先调用,比如 423000 -> 422000 -> 421000
,
对于旧的 CmRegisterCallback
函数,系统会使用 nt!CmLegacyAltitude
的全局变量,
内容为 L"425000"
的 UNICODE_STRING
,注意:其他 Altitude
值无法注册两个相同的
1 2 3 4 5
| NTSTATUS CmRegisterCallback( _In_ PEX_CALLBACK_FUNCTION Function, _In_opt_ PVOID Context, _Out_ PLARGE_INTEGER Cookie );
|
1 2 3 4 5 6 7 8
| NTSTATUS CmRegisterCallbackEx( _In_ PEX_CALLBACK_FUNCTION Function, _In_ PCUNICODE_STRING Altitude, _In_ PVOID Driver, _In_opt_ PVOID Context, _Out_ PLARGE_INTEGER Cookie, _Reserved_ PVOID Reserved );
|
两个函数注册的回调函数格式相同,如下所示。参数 CallbackContext
对应注册时 Context
,
参数 Argument1
为本次操作类型,参数 Argument2
为本次操作类型对应的结构体信息。
1 2 3 4 5
| NTSTATUS RegistryCallback( _In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2 );
|
卸载回调函数时也是使用同一个卸载函数,如果驱动自身提供卸载功能,
需要在 DriverUnload
中卸载注册时生成的 Cookie
信息,通常情况下都不会提供卸载。
1 2 3
| NTSTATUS CmUnRegisterCallback( _In_ LARGE_INTEGER Cookie );
|
操作注册表时触发的动作
需要处理的注册表操作共有以下几类:
(1)创建注册表键或值 (2)修改注册表键或值 (3)删除注册表键或值
在回调函数中,操作类型分为 动作之前
和 动作之后
两种:
做拦截和保护时,需要在 动作之前
进行处理。做行为监控时,可以在 动作之后
进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| NTSTATUS RegCallback(IN PVOID Context, IN PVOID Argument1, IN PVOID Argument2) { UNREFERENCED_PARAMETER(Context); PsGetCurrentProcessId(); switch ((ULONG_PTR)Argument1) { case RegNtPreDeleteKey: ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object; break;
case RegNtPreSetValueKey: ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object; ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName; break; case RegNtPreDeleteValueKey: ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object; ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName; break; case RegNtPreRenameKey: ((PREG_RENAME_KEY_INFORMATION)Argument2)->Object; ((PREG_RENAME_KEY_INFORMATION)Argument2)->NewName; break; case RegNtPreCreateKey: ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName; break; case RegNtPreCreateKeyEx: ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject; ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName; break; } return STATUS_SUCCESS; }
|
重要的注意事项
在WIN7及以上系统中(未测VISTA),在当前注册表回调中,再次操作注册表比如打开,
会触发回调链表中,当前回调的下一个回调,并不会从初始位置调用,所以就不会导致重入,
比如 A -> B -> C 三层,如果在B层中,执行打开,就只会触发C层,而在XP系统是从A开始