驱动注册表回调(监控注册表)

回调监控注册表

说明:技术仅供学习使用,如恶意利用,与作者无关!

在 WIN32 平台上,监控注册表的手段通常是 SSDT HOOK。不过用 SSDT HOOK的方式监控注册表实在是太麻烦了,要 HOOK 一大堆函数,还要处理一些 NT6 系统有而 NT5 系统没有的函数。下面我就来介绍一种完胜 SSDT HOOK 监控注册表的方法,效果跟 SSDT HOOK 一样好。这个方法就是使用微软推荐的注册表监控函数:CmRegisterCallbak。此函数其实在 XP 系统上就有了,不过那时功能不完善,只能简单的禁止或允许,无法获得完整的注册表修改信息(即做不到监控);在 VISTA 以及之后的系统里,微软对此函数做了相当大的改进,使之能获得完整的注册表修改信息。本文最后实现的效果就是:把所有操作全部阻止,如需仅拦截regedit.exe,请修改代码中被注释部分。

函数原型:

NTSTATUS CmRegisterCallback

(

_In_ PEX_CALLBACK_FUNCTION Function,

_In_opt_ PVOID Context,

_Out_ PLARGE_INTEGER Cookie

);

这三个参数分别为:回调函数的地址,随便设置的值(直接传入 NULL 即可),回调的句柄。相反还有个函数用于销毁回调,它是 CmUnRegisterCallback,原型如下

NTSTATUS CmUnRegisterCallback( _In_ LARGE_INTEGER Cookie);

CmUnRegisterCallback 函数唯一的的参数就是 cookie,也就是我所说的“回调的句柄”。创建和销毁回调的代码如下:

LARGE_INTEGER CmHandle;

NTSTATUS CmSt;

CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle);

if(NT_SUCCESS(CmSt))

    DbgPrint("CmRegisterCallback SUCCESS!");

else

    DbgPrint("CmRegisterCallback Failed!");

CmUnRegisterCallback(CmHandle);

接下来看看回调函数的原型:

NTSTATUS RegistryCallback

(

_In_ PVOID CallbackContext,

_In_opt_ PVOID Argument1, //操作类型(只是操作编号,不是指针)

_In_opt_ PVOID Argument2 //操作详细信息的结构体指针

)

```CallbackContext`` 基本可以忽略,重要的就是下面的两个参数 Argument1和 Argument2。1 Argument1 记录的是操作类型(这个参数不是指针,只是操作类型的编号而已),2 Argument2 记录的是有关操作信息的结构体指针。接下来举个例子。比如我们已经注册了一个注册表回调,当有删除注册表项的操作发生时,我们注册的回调就会被调用,Argument1 的信息是 RegNtPreDeleteKey(pre 是“操作前”的意思),Argument2 的信息是一个指向

REG_DELETE_KEY_INFORMATION 结构体的指针。当操作完成后,我们的注册表回调又会被调用一次,此时 Argument1 的信息是 RegNtPostDeleteKey(post 是“操作后”的意思),Argument2 的信息是一个指向REG_POST_OPERATION_INFORMATION 结构体的指针。在所有的结构体里,有一项是肯定有的,就是 Object,它是你操作了那个项或者根项的对象指针(相对于新建项而言,就是根项的对象指针;相对于新建/设置/删除/重命名键值和删除项而言,就是项的对象指针)。

需要注意的是,此函数如果返回 STATUS_SUCCESS,注册表操作就会继续执行,如果返回 STATUS_ACCESS_DENIED,注册表操作就不会执行执行了。这样子就达到了“监控”的效果。最终效果如下:

使用说明

//注册回调:
CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle);
//注销回调:
if(NT_SUCCESS(CmSt))
CmUnRegisterCallback(CmHandle);

代码区

#include <ntddk.h>

#define REGISTRY_POOL_TAG 'pRE'

NTKERNELAPI NTSTATUS ObQueryNameString
(
    IN  PVOID Object,
    OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
    IN  ULONG Length,
    OUT PULONG ReturnLength
);

NTKERNELAPI NTSTATUS RtlUnicodeStringCopy
(
    __out  PUNICODE_STRING DestinationString,
    __in   PUNICODE_STRING SourceString
);

NTKERNELAPI UCHAR* PsGetProcessImageFileName(PEPROCESS Process);

LARGE_INTEGER CmHandle;

BOOLEAN IsProcessName(char* string, PEPROCESS eprocess)
{
    char xx[260] = { 0 };
    strcpy(xx, (CONST CHAR*)PsGetProcessImageFileName(eprocess));
    if (!_stricmp(xx, string))
        return TRUE;
    else
        return FALSE;
}

BOOLEAN GetRegistryObjectCompleteName(PUNICODE_STRING pRegistryPath, PUNICODE_STRING pPartialRegistryPath, PVOID pRegistryObject)
{
    BOOLEAN foundCompleteName = FALSE;
    BOOLEAN partial = FALSE;
    if ((!MmIsAddressValid(pRegistryObject)) || (pRegistryObject == NULL))
        return FALSE;
    /* Check to see if the partial name is really the complete name */
    if (pPartialRegistryPath != NULL)
    {
        if ((((pPartialRegistryPath->Buffer[0] == '\\') || (pPartialRegistryPath->Buffer[0] == '%')) ||
            ((pPartialRegistryPath->Buffer[0] == 'T') && (pPartialRegistryPath->Buffer[1] == 'R') &&
            (pPartialRegistryPath->Buffer[2] == 'Y') && (pPartialRegistryPath->Buffer[3] == '\\'))))
        {
            RtlCopyUnicodeString(pRegistryPath, pPartialRegistryPath);
            partial = TRUE;
            foundCompleteName = TRUE;
        }
    }
    if (!foundCompleteName)
    {
        /* Query the object manager in the kernel for the complete name */
        NTSTATUS status;
        ULONG returnedLength;
        PUNICODE_STRING pObjectName = NULL;
        status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, 0, &returnedLength);
        if (status == STATUS_INFO_LENGTH_MISMATCH)
        {
            pObjectName = ExAllocatePoolWithTag(NonPagedPool, returnedLength, REGISTRY_POOL_TAG);
            status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, returnedLength, &returnedLength);
            if (NT_SUCCESS(status))
            {
                RtlCopyUnicodeString(pRegistryPath, pObjectName);
                foundCompleteName = TRUE;
            }
            if (pObjectName)
                ExFreePoolWithTag(pObjectName, REGISTRY_POOL_TAG);
        }
    }
    return foundCompleteName;
}

NTSTATUS RegistryCallback
(
    IN PVOID CallbackContext,
    IN PVOID Argument1,
    IN PVOID Argument2
)
{
    long type;
    NTSTATUS CallbackStatus = STATUS_SUCCESS;
    UNICODE_STRING registryPath;
    registryPath.Length = 0;
    registryPath.MaximumLength = 2048 * sizeof(WCHAR);
    registryPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, registryPath.MaximumLength, REGISTRY_POOL_TAG);
    if (registryPath.Buffer == NULL)
        return STATUS_SUCCESS;
    type = (REG_NOTIFY_CLASS)Argument1;
    switch (type)
    {
    case RegNtPreCreateKeyEx:    //出现两次是因为一次是OpenKey,一次是createKey
    {
        //if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
        {
            GetRegistryObjectCompleteName(&registryPath, NULL, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);
            KdPrint(("[RegNtPreCreateKeyEx]KeyPath: %wZ", registryPath));    //新键的路径
            KdPrint(("[RegNtPreCreateKeyEx]KeyName: %wZ",
                ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName));//新键的名称
            CallbackStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    case RegNtPreDeleteKey:
    {
        //if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
        {
            GetRegistryObjectCompleteName(&registryPath, NULL, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);
            KdPrint(("[RegNtPreDeleteKey]%wZ", registryPath));    //新键的路径
            CallbackStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    case RegNtPreSetValueKey:
    {
        //if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
        {
            GetRegistryObjectCompleteName(&registryPath, NULL, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);
            KdPrint(("[RegNtPreSetValueKey]KeyPath: %wZ", registryPath));
            KdPrint(("[RegNtPreSetValueKey]ValName: %wZ", ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName));
            KdPrint((""));
            CallbackStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    case RegNtPreDeleteValueKey:
    {
        //if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
        {
            GetRegistryObjectCompleteName(&registryPath, NULL, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);
            KdPrint(("[RegNtPreDeleteValueKey]KeyPath: %wZ", registryPath));
            KdPrint(("[RegNtPreDeleteValueKey]ValName: %wZ", ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName));
            KdPrint((""));
            CallbackStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    case RegNtPreRenameKey:
    {
        //if (IsProcessName("regedit.exe", PsGetCurrentProcess()))
        {
            GetRegistryObjectCompleteName(&registryPath, NULL, ((PREG_RENAME_KEY_INFORMATION)Argument2)->Object);
            KdPrint(("[RegNtPreRenameKey]KeyPath: %wZ", registryPath));
            KdPrint(("[RegNtPreRenameKey]NewName: %wZ", ((PREG_RENAME_KEY_INFORMATION)Argument2)->NewName));
            KdPrint((""));
            CallbackStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    //『注册表编辑器』里的“重命名键值”是没有直接函数的,是先SetValueKey再DeleteValueKey
    default:
        break;
    }
    if (registryPath.Buffer != NULL)
        ExFreePoolWithTag(registryPath.Buffer, REGISTRY_POOL_TAG);
    //return CallbackStatus;
    return STATUS_ACCESS_DENIED;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDO, PUNICODE_STRING pRP)
{
    NTSTATUS CmSt;
    CmSt = CmRegisterCallback(RegistryCallback, NULL, &CmHandle);
    //     if (NT_SUCCESS(CmSt))
    //         CmUnRegisterCallback(CmHandle);
    return CmSt;
}

结尾

代码已经贴出,没有很多,就不放下载链接了qwq
如遇任何问题,可以加我QQ:2513881812
喜欢的话不妨打赏一下[滑稽]


   转载规则

请联系作者付费转载。
 上一篇
驱动级监控进程 驱动级监控进程
技术讲解说明:技术仅供学习使用,如恶意利用,与作者无关! 有时候我们希望能够动态监视系统中任意进程/线程的创建与销毁。为了达到此目的我翻阅了 DDK 手册,发现其提供的 PsSetCreateProcessNotifyRoutine(),P
2020-03-11
下一篇 
网站建成了! 网站建成了!
首先,感谢屏幕前的你来到我的博客,想要写点什么可是脑子里没东西...
2020-03-09
  目录