64位内核开发第九讲,注册表编程.

一 注册表编程

二 注册表简介

2.1 ring3注册表

在内核中我们的注册表只有两个 key

内核

对应ring3

\\Registry\\Machine\\software

HKEY_LOCAL_MACHINE

\\Registry\\User\\

HKEY_USERS

其它的三个是从这些内核中映射出来的。

2.2 重启删除原理

重启删除,其实信息是放在注册表中的。

如下: 内核: \Registry\Machine\SYSTEM\CurrentControlSet\Control\Session Manager\pendingFileRenameOperations

对应Ring3 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\pendingFileRenameOperations 这个key里面有个值是 REG_MULTI_SZ类型,这个类型存储的是多个 \0结尾的路径。

使用 MoveFileEx(路径,NULL,MOVEFILE_DELAY_UNTIL_REBOOT)这个函数进行重启删除。 参数2不为空,就是替换,为NULL就是删除。 就是移动某个文件到某个目录下,如果某个目录存在就替换。

参数3: 参数3是很重要的。如果你替换的时候文件在使用则替换不了。给了这个参数。 那么在重启之后。会给你进行替换。也就是重启删除了。

这次重启删除则会放到上面那个注册表中。

三丶注册表API操作

3.1 Reg操作API

操作Key的函数

API

作用

ZwCreateKey

创建或者打开Key

ZwEnumerateKey

枚举key

ZwQueryKey

查询Key

ZwDeleteKey

删除Key

操作Valuekey的函数.也就是key下面的值.

API

作用

ZwEnumerateValueKey

枚举Valuekey 值

ZwQueryValueKey

查询valuekey值

ZwSetValueKey

设置ValueKey的值

ZwDeleteValueKey

删除Valuekey的值

四丶注册表操作例子

4.1 ZwCreateKey创建key

创建key需要注意参数. 分别为创建临时key跟创建永久key

读取共享文件夹下的路径. 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Shares\你共享文件夹的名字 找到文件共享名字.寻找值取出路径进行拼接.

对应内核:

registry\machine\SYSTEM\CurrentControlSet\Services\LanmanServer\Shares\你的共享文件夹名

UNC路径 = \\共享网络名字\共享文件夹的名字\xxx文件

代码如下

#include <ntddk.h>
#include <ntstrsafe.h>




DRIVER_UNLOAD DriverUnLoad;




//************************************
// Method:    ntIBinaryCreateKey
// FullName:  ntIBinaryCreateKey
// Access:    public 
// Returns:   NTSTATUS
// Qualifier: 创建注册表键值
// Parameter: UNICODE_STRING uPathKeyName
//************************************


NTSTATUS ntIBinaryCreateKey(UNICODE_STRING uPathKeyName);
NTSTATUS ntIBinaryInit();


void DriverUnLoad (PDRIVER_OBJECT pDeviceObject)
{
    KdPrint(("驱动已卸载"));
}

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  pDriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status = STATUS_SUCCESS;


    KdPrint(("驱动加载成功"));
    pDriverObject->DriverUnload = DriverUnLoad;
    
    return ntIBinaryInit();
}


NTSTATUS ntIBinaryInit()
{
    NTSTATUS status = STATUS_ERROR_PROCESS_NOT_IN_JOB;
    UNICODE_STRING uKeyPath;

    RtlUnicodeStringInit(&uKeyPath,L"\\registry\\machine\\SoftWare\\IBinary");
    status = ntIBinaryCreateKey(uKeyPath);
    if (!NT_SUCCESS(status))
    {
        
        KdPrint(("创建Key失败"));
        return status;
    }

    return status;
}



NTSTATUS ntIBinaryCreateKey(UNICODE_STRING uPathKeyName)
{
    
    NTSTATUS status = STATUS_ERROR_PROCESS_NOT_IN_JOB;
    OBJECT_ATTRIBUTES objAttri;
    HANDLE hKeyHandle;

    UNICODE_STRING uSubKey;
    HANDLE hSubKey;
    OBJECT_ATTRIBUTES objSubAttri;
    ULONG isRegStatus;  //注册表的状态,传出.
    InitializeObjectAttributes(
        &objAttri,
        &uPathKeyName,
        OBJ_CASE_INSENSITIVE, //句柄只能内核访问,而且只能一个打开.
        NULL, NULL);

    status = ZwCreateKey(&hKeyHandle,
        KEY_ALL_ACCESS,
        &objAttri,
        0,
        NULL,
        REG_OPTION_BACKUP_RESTORE,
        (PULONG)(&isRegStatus)
    );
    if (!NT_SUCCESS(status))
    {
        ZwClose(hKeyHandle);
        return status;
    }

    //创建子Key
    RtlUnicodeStringInit(&uSubKey, L"MyReg");
    
/*
    InitializeObjectAttributes(p, n, a, r, s) {
        \
            (p)->Length = sizeof(OBJECT_ATTRIBUTES);          \
            (p)->RootDirectory = r;                             \
            (p)->Attributes = a;                                \
            (p)->ObjectName = n;                                \
            (p)->SecurityDescriptor = s;                        \
            (p)->SecurityQualityOfService = NULL;               \
    }
    */
    //InitializeObjectAttributes(&objAttri, &uSubKey, OBJ_CASE_INSENSITIVE, hKeyHandle, NULL);
    //不使用宏,手工进行赋值.
    objSubAttri.Length = sizeof(OBJECT_ATTRIBUTES);
    objSubAttri.Attributes = OBJ_CASE_INSENSITIVE;
    objSubAttri.ObjectName = &uSubKey;
    objSubAttri.SecurityDescriptor = NULL;
    objSubAttri.SecurityQualityOfService = NULL;
    objSubAttri.RootDirectory = hKeyHandle;  //注意这里.父目录设置为我们上面创建的key


    status = ZwCreateKey(&hSubKey,  //传出创建的Key
        KEY_ALL_ACCESS,             //权限
        &objSubAttri,               //路径
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,   //创建的Key重启是否存在还是临时的
        &isRegStatus);             //保存key的状态,创建成功还是打开

    if (!NT_SUCCESS(status))
    {
        ZwClose(hSubKey);
        ZwClose(hKeyHandle);
        return status;
    }
    ZwClose(hSubKey);
    ZwClose(hKeyHandle);
    KdPrint(("创建Key成功"));
    return status;
}

ZwCreateKey 来创建Key. 创建子Key也是用这个函数.只不过你需要在初始化子类的路径的时候.传入父类的Key即可.

2.删除Key

删除Key很简单了.使用 ZwOpenKey打开key ZwDeleteKey删除key

NTSTATUS ntIBinaryDeleteKey(UNICODE_STRING uPathKeyName)
{
    NTSTATUS ntStatus;
    HANDLE hKey;
    OBJECT_ATTRIBUTES ObjAttr;
    ULONG isRegStatus;

    ObjAttr.Length = sizeof(OBJECT_ATTRIBUTES);
    ObjAttr.Attributes = OBJ_CASE_INSENSITIVE;
    ObjAttr.ObjectName = &uPathKeyName;
    ObjAttr.RootDirectory = NULL;
    ObjAttr.SecurityDescriptor = NULL;
    ObjAttr.SecurityQualityOfService = NULL;
    __try
    {

        
        ntStatus = ZwOpenKey(&hKey, KEY_ALL_ACCESS, &ObjAttr);//打开Key在进行删除

        if (!NT_SUCCESS(ntStatus))
        {
            ZwClose(hKey);
            return ntStatus;
        }
        ntStatus = ZwDeleteKey(hKey);

        if (!NT_SUCCESS(ntStatus))
        {
            ZwClose(hKey);
            return ntStatus;
        }
        KdPrint(("删除Key成功"));
    }
    __except (GetExceptionCode())
    {
        KdPrint(("删除Key出现异常"));
    }
    return ntStatus;
}

3.查询遍历Key

查询遍历Key也很简单. 1.使用函数 ZwOpenKey打开你想遍历的Key 2.两次调用 ZwQueryKey* ,第一次获取你想遍历Key的缓冲区大小.第二次.获得缓冲区大小了.为这个结构体申请内存.传入这个结构体.继续遍历.关于结构体可以查看MSDN介绍.

3.通过结构体成员.拿到子key数量.建立for循环遍历子key 4.遍历过程中.调用两次 ZwEnumerateKey 第一次调用. 拿到你遍历当前key的基本信息结构体的大小.然后为结构体申请内存. 第二次调用传入结构体.得到当前key的基本信息.这个基本信息是放在这个结构体中.

最后初始化UNICODE_STRING字符串.进行打印即可.

代码:

NTSTATUS ntIBinaryQueryKey(UNICODE_STRING uPathKeyName) //查询Key
{
    NTSTATUS ntStatus;
    HANDLE hKey;
    OBJECT_ATTRIBUTES objAttri = { 0 };
    PKEY_FULL_INFORMATION pkfinfo = NULL;
    ULONG uSize = 0;
    ULONG iteratorValue = 0; //遍历的变量
    PKEY_BASIC_INFORMATION pBaseinfo = NULL;
    UNICODE_STRING uDbgValue = { 0 };//遍历出来的信息保存到UNICODE_STRING结构体中
    //首先打开Key,然后遍历Key

    
    __try
    {

        InitializeObjectAttributes(
            &objAttri,
            &uPathKeyName,
            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
            NULL,
            NULL);

        ntStatus = ZwOpenKey(&hKey, KEY_ALL_ACCESS, &objAttri);
        if (!NT_SUCCESS(ntStatus))
        {

            return ntStatus;
        }


        //遍历Key.需要两次调用.第一次调用得出数据大小.第二次调用则是填充数据
        ntStatus = ZwQueryKey(hKey, KeyFullInformation, NULL, 0, &uSize);
        //得出KEY_FUN_INFOMATION 结构的大小.进行内存申请即可.
        //查询MSDN得出,ZwQuery当数据不足会返回两个状态.所以判断一下即可.

        //STATUS_BUFFER_OVERFLOW或STATUS_BUFFER_TOO_SMALL
        if (ntStatus != STATUS_BUFFER_OVERFLOW && ntStatus != STATUS_BUFFER_TOO_SMALL)
        {
            ZwClose(hKey);
            return ntStatus;
        }


        pkfinfo = (PKEY_FULL_INFORMATION)ExAllocatePoolWithTag(PagedPool, uSize, 'niBI');
        if (NULL == pkfinfo)
        {
            ZwClose(hKey);
            return ntStatus;
        }


        //申请了KEY_FULL_INFOMATION结构数组大小.然后进行获取大小

        ntStatus = ZwQueryKey(hKey, KeyFullInformation, pkfinfo, uSize, &uSize);
        if (!NT_SUCCESS(ntStatus))
        {
            ExFreePoolWithTag(pkfinfo, 'niBI');
            ZwClose(hKey);
            return ntStatus;
        }


        for (iteratorValue = 0; iteratorValue < pkfinfo->SubKeys; iteratorValue++)
        {
            //遍历出Key就要进行枚举出Key的详细信息.使用ZwEnumerateKey即可.也是枚举一个结构.
            ntStatus = ZwEnumerateKey(hKey,
                0,
                KeyBasicInformation,
                NULL,
                0,
                &uSize);

            if (ntStatus != STATUS_BUFFER_OVERFLOW && ntStatus != STATUS_BUFFER_TOO_SMALL)
            {
                ZwClose(hKey);
                return ntStatus;
            }


            pBaseinfo = (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(PagedPool, uSize, 'niBI');
            if (NULL == pkfinfo)
            {
                ZwClose(hKey);
                return ntStatus;
            }

            //继续申请一次得出需要的
            ntStatus = ZwEnumerateKey(hKey,
                0,
                KeyBasicInformation,
                pBaseinfo,
                uSize,
                &uSize);

            if (!NT_SUCCESS(ntStatus))
            {
                if (NULL != pBaseinfo)
                    ExFreePoolWithTag(pBaseinfo, 'niBI');
                if (NULL != pkfinfo)
                    ExFreePoolWithTag(pkfinfo, 'niBI');
                ZwClose(hKey);
                return ntStatus;
            }

            //得出信息则可以进行进一步操作了.

            //初始化UNICODE结构.进行打印输出即可.

            uDbgValue.Length = (USHORT)pBaseinfo->NameLength;
            uDbgValue.MaximumLength = (USHORT)pBaseinfo->NameLength;
            uDbgValue.Buffer = pBaseinfo->Name;

            KdPrint(("得出的key 名字 = %wZ", &uDbgValue));

            ExFreePool(pBaseinfo); //同上释放内存
        }

        //释放资源
        if (NULL != pkfinfo)
            ExFreePool(pkfinfo);
        ZwClose(hKey);
    }
    __except (GetExceptionCode())
    {
        KdPrint(("出现异常,异常代码为: %ld", GetExceptionCode()));
    }
    return ntStatus;
}

结果

4.创建并且设置Value的值.

上面说的只是创建key.下面则是怎么设置对应的Value

代码也很简单. 原理如下下: 1.打开Key 2.使用函数 ZwSetValueKey创建并且设置Value即可.

代码如下

NTSTATUS ntIBinarySetKeyValue(UNICODE_STRING uPathKeyName)
{
    NTSTATUS ntStatus;
    OBJECT_ATTRIBUTES objAttri;
    HANDLE hKey;
    UNICODE_STRING uSetValueKeyName;
    ULONG Value = 10;

    InitializeObjectAttributes(&objAttri,
        &uPathKeyName, 
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL, 
        NULL);

    ntStatus = ZwOpenKey(&hKey, KEY_ALL_ACCESS, &objAttri);
    if (!NT_SUCCESS(ntStatus))
    {
        return ntStatus;
    }
        

    //设置KEY value的值
    RtlUnicodeStringInit(&uSetValueKeyName, L"IBinaryFrist");
    ntStatus = ZwSetValueKey(hKey,
        &uSetValueKeyName,
        0,
        REG_DWORD,
        &Value,
        sizeof(ULONG));
    if (!NT_SUCCESS(ntStatus))
    {
        ZwClose(hKey);
        return ntStatus;
    }

    KdPrint(("设置Key成功"));
    ZwClose(hKey);

    return ntStatus;
}

代码演示.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券