内核监控Register注册表回调在安全软件、系统监控和调试工具等领域有着广泛的应用。开发者可以利用这个机制来监视和拦截系统中的注册表操作,以保护系统安全。
默认情况下CmRegisterCallback需传入三个参数,参数一回调函数地址,参数二空余,参数三回调句柄,微软定义如下。
1 | // 参数1:回调函数地址 |
自定义注册表回调函数MyLySharkCallback需要保留三个参数,CallbackContext回调上下文,Argument1是操作类型,Argument2定义详细结构体指针。
1 | NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2) |
在自定义回调函数内Argument1则可获取到操作类型,类型是一个REG_NOTIFY_CLASS枚举结构,微软对其的具体定义如下所示。
1 | typedef enum _REG_NOTIFY_CLASS { |
其中对于注册表最常用的监控项为以下几种类型,当然为了实现监控则我们必须要使用之前,如果使用之后则只能起到监视而无法做到监控的目的。
如果需要实现监视则,首先CmRegisterCallback注册一个自定义回调,当有消息时则触发MyLySharkCallback其内部获取到lOperateType操作类型,并通过switch选择不同的处理例程,每个处理例程都通过GetFullPath得到注册表完整路径,并打印出来,这段代码实现如下。
1 |
|
运行驱动程序,则会输出当前系统中所有针对注册表的操作,如下图所示。
如上的代码只能实现注册表项的监视,而如果需要保护则需要在回调函数MyLySharkCallback判断,如果指定注册表项是需要保护的则直接返回status = STATUS_ACCESS_DENIED;从而达到保护注册表的目的,核心代码如下所示。
1 | // 反注册表删除回调 |
运行驱动程序,然后我们尝试删除\\LyShark\HKEY_LOCAL_MACHINE\SOFTWARE\lyshark.com里面的子项,则会提示如下信息。
当然这里的RegNtPreDeleteValueKey是指的删除操作,如果将其替换成RegNtPreSetValueKey,那么只有当注册表被创建才会拦截,此时就会变成拦截创建。
1 | // 拦截创建操作 |
加载驱动保护,然后我们尝试在\\LyShark\HKEY_LOCAL_MACHINE\SOFTWARE\lyshark.com里面创建一个子项,则会提示创建失败。
注册表回调则有点不同,所有回调函数都保存在一个叫 nt!CallbackListHead的链表
首先我们需要找到一个使用 nt!CallbackListHead 的函数,理想情况下还要有一个导出函数,用于字节搜索。
首先,让我们计算 nt 的偏移量!从 nt 基底的 CallbackListHead。
1 | lkd> ? nt!CallbackListHead - nt |
然后在 IDA 中,我们通过跳跃=> 跳转到地址 ,并使用 00ef6d90 来到达该地址。
现在我们可以把鼠标悬停在 CallbackListHead => 上,点击它=>,然后按 X 进行交叉比对,这样我们就能知道到底是谁在使用这个列表。
幸运的是,第一个函数 CmUnRegisterCallback 也是导出函数,因此我们可以用以下函数作为搜索的起点和结尾。
nt!CallbackListHead 是一个链表,其中偏移量 0x28 处的每个条目是被调用的函数。
加载上我们编译好的驱动。看效果。
1 | Head (49048590) |
需要改两处:
1 | ; 1. Head.Flink 跳过 71ce0d90,直接指向 6b3f4140 |
验证:
1 | dq fffff807`49048590 L2 |
期望结果:
1 | Head: ffffa60d`6b3f4140 ffffa60d`72a2e510 |
这样 71ce0d90 就从链表中孤立了,g 之后不会崩溃。
此时的链表。
重新弄了一个图