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

一 注册表编程

二 注册表简介

2.1 ring3注册表

在内核中咱们的注册表只有两个 keyapi

内核 对应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结尾的路径。code

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

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

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

三丶注册表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;
}

代码演示.

相关文章
相关标签/搜索