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

一 丶注册表编程

二 注册表简介

2.1 ring3注册表

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

内核 对应ring3
\Registry\Machine\software HKEY_LOCAL_MACHINE
\Registry\User\ HKEY_USERS

其它的三个是从这些内核中映射出来的。
HKEY_CLASSES_ROOT HKEY_CURRENT_CONFIG HKEY_CURRENT_USER,在内核中是映射出来的.
HKEY_CLASSES_ROOT是被HKEY_LOCAL_MACHINE\SOFTWARE\Classes 映射的.所以我们在Classes下面就能看到HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG是被HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current映射的
映射表如下:

内核 对应ring3
\Registry\Machine\software\Classes HKEY_CLASSES_ROOT
\Registry\Machine\system\currentControlSet\Hardware Profiles\Current HKEY_CURRENT_CONFIG
\Registry\User\SID HKEY_CURRENT_USER

用户态的程序是由系统当前登录用户运行,而内核态的程序是由system用户运行,HKEY_CURRENT_USER是保存系统当前登录用户的相关信息,所以在内核中,无法直接访问HKEY_CURRENT_USER.
在内核中想要访问需要先获取用户的SID字符串 也就是说 HKEY_CURRENT_USER是链接到HKEY_USERS[当前登录用户的SID]的.
在注册表中如下可以获取用户的SID字符串:

计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\ SID(长的那一串)

SID也称为安全标识符. 可以查看微软文档介绍 sid介绍
我们可以使用(内核)注册表操作API来进行遍历SID来得到SID.
其中这个注册表项下面还有记录我们的profile路径如下:

另外几种获取SID的方法如下:

  • ring3获取传给ring0
  • Attach到当前登录用户运行的进程,然后再调用RtlFormatCurrentUserKeyPath
    参考资料: 注册表

我这里实现了内核中获取SID的方法.
获取到SID之后你只需要拼接为如下:

\\Registry\\User\\SID  ==> HKEY_CURRENT_USER
如果想要访问 HKEY_CURRENT_USER下的任意注册表 那么就写为
\\Registry\\User\\SID\\xxx\xxx\xxx\xxx

代码:

//use dynameic Pool
NTSTATUS GetSidByDynameicPool(PUNICODE_STRING pSidReg, BOOLEAN isAllocate)
{

    // //\\registry\\machine\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
    //\Registry\Machine
    //\Registry\user
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrSaveSidReg = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList");
    HANDLE hRegHandle = NULL;
    OBJECT_ATTRIBUTES objSid;
    PKEY_FULL_INFORMATION pkfiQuery = NULL;
    PKEY_BASIC_INFORMATION pbiEnumKey = NULL;
    ULONG uRetLength = 0;
    InitializeObjectAttributes(&objSid, &ustrSaveSidReg, OBJ_KERNEL_HANDLE, 0, 0);
    ULONG uIndex = 0;
    status = ZwOpenKey(&hRegHandle, KEY_READ, &objSid);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    //ZwQueryKey ZwEnumKey Get Sid
    status = ZwQueryKey(hRegHandle, KeyFullInformation, NULL, NULL, &uRetLength);
    if (status != STATUS_BUFFER_TOO_SMALL)
    {
        goto END;
    }
    pkfiQuery = (PKEY_FULL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, uRetLength, 'abcd');
    if (pkfiQuery == NULL)
    {
        goto END;
    }
    RtlZeroMemory(pkfiQuery, uRetLength);
    status = ZwQueryKey(hRegHandle, KeyFullInformation, pkfiQuery, uRetLength, &uRetLength);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    for (uIndex = 0; uIndex < pkfiQuery->SubKeys; uIndex++)
    {
        status = ZwEnumerateKey(hRegHandle, uIndex, KeyBasicInformation, NULL, NULL, &uRetLength);
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            goto END;
        }
        pbiEnumKey = (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, uRetLength, 'abcd');
        if (pbiEnumKey == NULL)
        {
            goto END;
        }
        RtlZeroMemory(pbiEnumKey, uRetLength);
        status = ZwEnumerateKey(hRegHandle, uIndex, KeyBasicInformation, pbiEnumKey, uRetLength, &uRetLength);
        if (!NT_SUCCESS(status))
        {
            goto END;
        }

        //pbi
        if (pbiEnumKey->NameLength > 0x20)
        {

            if (isAllocate)
            {
                pSidReg->Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, pbiEnumKey->NameLength, 'abcd');
                if (pSidReg->Buffer == NULL)
                {
                    status = STATUS_UNSUCCESSFUL;
                    goto END;
                }
                RtlZeroMemory(pSidReg->Buffer, pbiEnumKey->NameLength);
                pSidReg->Length = pbiEnumKey->NameLength;
                pSidReg->MaximumLength = pbiEnumKey->NameLength;
                RtlUnicodeStringCbCopyStringN(pSidReg, pbiEnumKey->Name, pbiEnumKey->NameLength);
                KdPrint(("The Sid is %S", pbiEnumKey->Name));
            }
            else if (pSidReg->Buffer != NULL && pSidReg->Length >= pbiEnumKey->NameLength)
            {
                RtlZeroMemory(pSidReg->Buffer, pSidReg->Length);
                RtlUnicodeStringCbCopyStringN(pSidReg, pbiEnumKey->Name, pbiEnumKey->NameLength);
                KdPrint(("The Sid is %S", pbiEnumKey->Name));
            }
            else
            {
                status = STATUS_BUFFER_TOO_SMALL;
                if (pbiEnumKey != NULL)
                {
                    ExFreePoolWithTag(pbiEnumKey, 'abcd');
                    pbiEnumKey = NULL;
                }
                goto END;
            }
        }
        if (pbiEnumKey != NULL)
        {
            ExFreePoolWithTag(pbiEnumKey, 'abcd');
            pbiEnumKey = NULL;
        }
    }
END:
    if (pbiEnumKey != NULL)
    {
        ExFreePoolWithTag(pbiEnumKey, 'abcd');
        pbiEnumKey = NULL;
    }
    if (pkfiQuery != NULL)
    {
        ExFreePoolWithTag(pkfiQuery, 'abcd');
        pkfiQuery = NULL;
    }
    if (hRegHandle != NULL)
    {
        ZwClose(hRegHandle);
        hRegHandle = NULL;
    }
    return status;
}

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;
}

代码演示.

5.查询键值

查询键值主要是使用ZwQueryValueKey 他也是通过Class信息来查询的.
可以查询 基础信息 全部信息 以及键值. 对于使用这个API来说 查询基础信息以及全部信息
都是多此一举.所以只需要查询键值.

NTSYSAPI NTSTATUS ZwQueryValueKey(
  [in]            HANDLE                      KeyHandle,
  [in]            PUNICODE_STRING             ValueName,
  [in]            KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  [out, optional] PVOID                       KeyValueInformation,
  [in]            ULONG                       Length,
  [out]           PULONG                      ResultLength
);

参数也很简单. 查询下MSDN使用即可.

核心代码如下,使用两次查找方法查找键值. 第一次查找是用于申请空间.第二次才是真正查询.
使用的类型为: PKEY_VALUE_PARTIAL_INFORMATION 查询出来的值在他的data下存储着.是一个宽字符类型内存表示. 长度由DateLength表示. Data并不是0结尾.需要配合DataLength

PKEY_VALUE_PARTIAL_INFORMATION pPartial = NULL;
//... ZwOpenKey or ZwCreateKey ...
// Two ZwQueryValueKey first GetBufferLength Two GetKeyValueData
status = ZwQueryValueKey(hKey, &ustrReadKeyValue, KeyValuePartialInformation, NULL, 0, &uRetValue);
if (status != STATUS_BUFFER_TOO_SMALL)
{
    goto END;
}
pPartial = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, uRetValue);
if (pPartial == NULL)
{
    goto END;
}
status = ZwQueryValueKey(hKey, &ustrReadKeyValue, KeyValuePartialInformation, pPartial, uRetValue, &uRetValue);
if (!NT_SUCCESS(status))
{
    goto END;
}

完整代码. 遍历 HKEY_CURRENT_USER下的7-zip的路径.
我的注册表如下:

我要遍历其Path信息下的路径.
HKEY_CURRENT_USER需要内核获取SID并且拼接. 上面有说.
这里直接贴代码.

#include <ntifs.h>
#include <ntstrsafe.h>
VOID DriverUnLoad(
    PDRIVER_OBJECT DriverObject)
{

    KdPrint(("Exit"));
}

// use dynameic Pool
NTSTATUS GetSidByDynameicPool(PUNICODE_STRING pSidReg, BOOLEAN isAllocate)
{

    // //\\registry\\machine\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
    //\Registry\Machine
    //\Registry\user
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrSaveSidReg = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList");
    HANDLE hRegHandle = NULL;
    OBJECT_ATTRIBUTES objSid;
    PKEY_FULL_INFORMATION pkfiQuery = NULL;
    PKEY_BASIC_INFORMATION pbiEnumKey = NULL;
    ULONG uRetLength = 0;
    InitializeObjectAttributes(&objSid, &ustrSaveSidReg, OBJ_KERNEL_HANDLE, 0, 0);
    ULONG uIndex = 0;
    status = ZwOpenKey(&hRegHandle, KEY_READ, &objSid);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    // ZwQueryKey ZwEnumKey Get Sid
    status = ZwQueryKey(hRegHandle, KeyFullInformation, NULL, NULL, &uRetLength);
    if (status != STATUS_BUFFER_TOO_SMALL)
    {
        goto END;
    }
    pkfiQuery = (PKEY_FULL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, uRetLength, 'abcd');
    if (pkfiQuery == NULL)
    {
        goto END;
    }
    RtlZeroMemory(pkfiQuery, uRetLength);
    status = ZwQueryKey(hRegHandle, KeyFullInformation, pkfiQuery, uRetLength, &uRetLength);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    for (uIndex = 0; uIndex < pkfiQuery->SubKeys; uIndex++)
    {
        status = ZwEnumerateKey(hRegHandle, uIndex, KeyBasicInformation, NULL, NULL, &uRetLength);
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            goto END;
        }
        pbiEnumKey = (PKEY_BASIC_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, uRetLength, 'abcd');
        if (pbiEnumKey == NULL)
        {
            goto END;
        }
        RtlZeroMemory(pbiEnumKey, uRetLength);
        status = ZwEnumerateKey(hRegHandle, uIndex, KeyBasicInformation, pbiEnumKey, uRetLength, &uRetLength);
        if (!NT_SUCCESS(status))
        {
            goto END;
        }

        // pbi
        if (pbiEnumKey->NameLength > 0x20)
        {

            if (isAllocate)
            {
                pSidReg->Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, pbiEnumKey->NameLength, 'abcd');
                if (pSidReg->Buffer == NULL)
                {
                    status = STATUS_UNSUCCESSFUL;
                    goto END;
                }
                RtlZeroMemory(pSidReg->Buffer, pbiEnumKey->NameLength);
                pSidReg->Length = pbiEnumKey->NameLength;
                pSidReg->MaximumLength = pbiEnumKey->NameLength;
                RtlUnicodeStringCbCopyStringN(pSidReg, pbiEnumKey->Name, pbiEnumKey->NameLength);
#ifdef DBG
                PWCHAR pPrint = (PWCHAR)ExAllocatePool(NonPagedPool, pbiEnumKey->NameLength * 2);
                if (pPrint != NULL)
                {
                    RtlZeroMemory(pPrint, pbiEnumKey->NameLength * 2);
                    RtlCopyMemory(pPrint, pbiEnumKey->Name, pbiEnumKey->NameLength);
                    KdPrint(("The Sid is %S", pPrint));
                    ExFreePool(pPrint);
                    pPrint = NULL;
                }
#endif
            }
            else if (pSidReg->Buffer != NULL && pSidReg->Length >= pbiEnumKey->NameLength)
            {
                RtlZeroMemory(pSidReg->Buffer, pSidReg->Length);
                RtlUnicodeStringCbCopyStringN(pSidReg, pbiEnumKey->Name, pbiEnumKey->NameLength);
#ifdef DBG
                PWCHAR pPrint = (PWCHAR)ExAllocatePool(NonPagedPool, pbiEnumKey->NameLength * 2);
                if (pPrint != NULL)
                {
                    RtlZeroMemory(pPrint, pbiEnumKey->NameLength * 2);
                    RtlCopyMemory(pPrint, pbiEnumKey->Name, pbiEnumKey->NameLength);
                    KdPrint(("The Sid is %S", pPrint));
                    ExFreePool(pPrint);
                    pPrint = NULL;
                }
#endif
            }
            else
            {
                status = STATUS_BUFFER_TOO_SMALL;
                if (pbiEnumKey != NULL)
                {
                    ExFreePoolWithTag(pbiEnumKey, 'abcd');
                    pbiEnumKey = NULL;
                }
                goto END;
            }
        }
        if (pbiEnumKey != NULL)
        {
            ExFreePoolWithTag(pbiEnumKey, 'abcd');
            pbiEnumKey = NULL;
        }
    }
END:
    if (pbiEnumKey != NULL)
    {
        ExFreePoolWithTag(pbiEnumKey, 'abcd');
        pbiEnumKey = NULL;
    }
    if (pkfiQuery != NULL)
    {
        ExFreePoolWithTag(pkfiQuery, 'abcd');
        pkfiQuery = NULL;
    }
    if (hRegHandle != NULL)
    {
        ZwClose(hRegHandle);
        hRegHandle = NULL;
    }
    return status;
}

NTSTATUS GetCurrentUserKeyByRegName(PUNICODE_STRING pOutRegName, PWCHAR regName, ULONG ulength)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING ustrSid;

    status = GetSidByDynameicPool(&ustrSid, TRUE);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    status = RtlUnicodeStringCbCopyStringN(pOutRegName, L"\\Registry\\User\\", wcslen(L"\\Registry\\User\\") * 2);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    status = RtlUnicodeStringCchCatN(pOutRegName, &ustrSid, ustrSid.Length);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    status = RtlUnicodeStringCchCatStringN(pOutRegName, regName, ulength);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

END:
    if (ustrSid.Buffer != NULL)
    {
        RtlFreeUnicodeString(&ustrSid);
    }
    return status;
}

NTSTATUS QueryKeyValue(PWCHAR pQueryName, PWCHAR outRegValue, ULONG uOutRegValueLength)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING uRegValue = {0};
    uRegValue.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, sizeof(WCHAR) * 0x200);
    uRegValue.Length = sizeof(WCHAR) * 0x200;
    uRegValue.MaximumLength = sizeof(WCHAR) * 0x200;
    UNICODE_STRING ustrReadKeyValue = {0};
    HANDLE hKey = NULL;
    OBJECT_ATTRIBUTES objQueryKey;
    ULONG uRetValue;
    PKEY_VALUE_PARTIAL_INFORMATION pPartial = NULL;
    RtlInitUnicodeString(&ustrReadKeyValue, pQueryName);
    status = GetCurrentUserKeyByRegName(&uRegValue, L"\\Software\\7-Zip", wcslen(L"\\Software\\7-Zip") * 2);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    InitializeObjectAttributes(&objQueryKey, &uRegValue, OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenKey(&hKey, KEY_ALL_ACCESS, &objQueryKey);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    // Two ZwQueryValueKey first GetBufferLength Two GetKeyValueData
    status = ZwQueryValueKey(hKey, &ustrReadKeyValue, KeyValuePartialInformation, NULL, 0, &uRetValue);
    if (status != STATUS_BUFFER_TOO_SMALL)
    {
        goto END;
    }
    pPartial = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, uRetValue);
    if (pPartial == NULL)
    {
        goto END;
    }
    status = ZwQueryValueKey(hKey, &ustrReadKeyValue, KeyValuePartialInformation, pPartial, uRetValue, &uRetValue);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    //

    if (uOutRegValueLength <= 0 || uOutRegValueLength < pPartial->DataLength)
    {
        status = STATUS_BUFFER_TOO_SMALL;
        goto END;
    }
    RtlCopyMemory(outRegValue, pPartial->Data, pPartial->DataLength);

END:
    if (pPartial != NULL)
    {
        ExFreePool(pPartial);
        pPartial = NULL;
    }
    if (hKey != NULL)
    {
        ZwClose(hKey);
        hKey = NULL;
    }
    if (uRegValue.Buffer != NULL)
    {
        RtlFreeUnicodeString(&uRegValue);
    }
    return status;
}
extern "C" NTSTATUS DriverEntry(
    PDRIVER_OBJECT pDriverObj,
    PUNICODE_STRING pReg)
{
    UNREFERENCED_PARAMETER(pReg);
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    pDriverObj->DriverUnload = DriverUnLoad;

    PWCHAR pwzBuffer = NULL;
    pwzBuffer = (PWCHAR)ExAllocatePool(NonPagedPool, 0x100);
    if (pwzBuffer == NULL)
    {
        goto END;
    }
    RtlZeroMemory(pwzBuffer, 0x100);
    status = QueryKeyValue(L"Path", pwzBuffer, 0X100);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    KdPrint(("The Path is %S \n", pwzBuffer));
END:
    if (pwzBuffer != NULL)
    {
        ExFreePool(pwzBuffer);
        pwzBuffer = NULL;
    }
    return STATUS_SUCCESS;
}

结果:

6.遍历键值

遍历键值使用的是 ZwQueryKey 来查询有多少Values(注意不是查询多少key了。) 然后使用 ZwEnumerateValueKey查询Value的类型。
代码如下:

NTSTATUS ExampleEnumberValueKey()
{
    HANDLE hKey = NULL;
    UNICODE_STRING ustrKeyName = {0};
    OBJECT_ATTRIBUTES objKeyName = {0};
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    RtlUnicodeStringInit(&ustrKeyName, L"\\Registry\\machine\\HARDWARE\\DESCRIPTION\\System\\BIOS");
    InitializeObjectAttributes(&objKeyName, &ustrKeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    ULONG uRetLength = 0;
    PKEY_FULL_INFORMATION pfi = NULL;
    PKEY_VALUE_BASIC_INFORMATION pbi = NULL;
    ULONG uIndex = 0;
    PVOID pPrintBuffer = NULL;
    status = ZwOpenKey(&hKey, KEY_ALL_ACCESS, &objKeyName);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }
    // ZwQueryKey And ZwEnumerateValueKey()
    status = ZwQueryKey(hKey, KeyFullInformation, NULL, NULL, &uRetLength);
    if (status != STATUS_BUFFER_TOO_SMALL)
    {
        goto END;
    }
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(NonPagedPool, uRetLength);
    if (pfi == NULL)
    {
        goto END;
    }
    RtlZeroMemory(pfi, uRetLength);
    status = ZwQueryKey(hKey, KeyFullInformation, pfi, uRetLength, &uRetLength);
    if (!NT_SUCCESS(status))
    {
        goto END;
    }

    for (uIndex = 0; uIndex < pfi->Values; uIndex++)
    {
        status = ZwEnumerateValueKey(hKey, uIndex, KeyValueBasicInformation, NULL, NULL, &uRetLength);
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            goto END;
        }
        pbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, uRetLength);
        if (pbi == NULL)
        {
            goto END;
        }
        RtlZeroMemory(pbi, uRetLength);

        status = ZwEnumerateValueKey(hKey, uIndex, KeyValueBasicInformation, pbi, uRetLength, &uRetLength);
        if (NT_ERROR(status))
        {
            goto END;
        }
        // print pbi info msg
#ifdef DBG
        pPrintBuffer = ExAllocatePool(NonPagedPool, pbi->NameLength * 2);
        if (pPrintBuffer == NULL)
        {
            goto END;
        }
        RtlZeroMemory(pPrintBuffer, pbi->NameLength * 2);
        RtlCopyMemory(pPrintBuffer, pbi->Name, pbi->NameLength);
        switch (pbi->Type)
        {
        case REG_SZ:
            KdPrint(("Type is REG_SZ Name is %S \n", (PWCHAR)pPrintBuffer));
            break;
        case REG_DWORD:
            KdPrint(("Type is REG_SZ Name is %S \n", (PWCHAR)pPrintBuffer));
            break;
        }
        if (pbi != NULL)
        {
            ExFreePool(pbi);
            pbi = NULL;
        }

        if (pPrintBuffer != NULL)
        {
            ExFreePool(pPrintBuffer);
            pPrintBuffer = NULL;
        }
#endif // DBG
    }
END:
#ifdef DBG
    if (pPrintBuffer != NULL)
    {
        ExFreePool(pPrintBuffer);
        pPrintBuffer = NULL;
    }
#endif // DBG
    if (pbi != NULL)
    {
        ExFreePool(pbi);
        pfi = NULL;
    }
    if (pfi != NULL)
    {
        ExFreePool(pfi);
        pfi = NULL;
    }
    if (hKey != NULL)
    {
        ZwClose(hKey);
        hKey = NULL;
    }

    return status;
}

结果:

五丶RtlxxxxApi操作

windows内核还为我们提供了RtlXXX注册表的操作。很方便的就可以操作注册表

API如下:

API 操作
RtlCreateRegistryKey 创建一个key到注册表,参数2是一个相对路径。
RtlDeleteRegistryValue 删除一个keyValue
RtlQueryRegistryValues 查询一个key
RtlWriteRegistryValue 写一个keyValue(写一个注册表值)
RtlCheckRegistryKey 检查key是否存在

参数含义:

windows内核给我封装的Rtl注册表操作API 其参数1(RelativeTo) 是有含义的,含义如下:

参数值 含义
RTL_REGISTRY_ABSOLUTE 代表路径是绝对的注册表路径(也就是参数2)
RTL_REGISTRY_SERVICES 代表主路径为:\Registry\Machine\System\CurrentControlSet\Services 你参数中的给的path都是位于这个路径下的。path就是一个相对路径了
RTL_REGISTRY_CONTROL 同上: Registry\Machine\System\CurrentControlSet\Control
RTL_REGISTRY_WINDOWS_NT 同上:\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion.
RTL_REGISTRY_DEVICEMAP 同上:\Registry\Machine\Hardware\DeviceMap
RTL_REGISTRY_USER 用户路径为: \Registry\User\CurrentUser. 如果是系统路径则是\users.Default
RTL_REGISTRY_OPTIONAL 参数1(RelativeTo)和参数2(path)是可选的如果指定了这个参数
RTL_REGISTRY_HANDLE 代表我们的path参数是使用的注册表句柄

其实这个参数就是固定写死key的路径,你的相对路径就是相对于写死key的路径的下面。

距离:

如果参数1写为:RTL_REGISTRY_SERVICES

参数2写为: L""\\123"

那么全路径就为:

代表主路径为:\Registry\Machine\System\CurrentControlSet\Services\123

请注意 RTL_REGISTRY_ABSOLUTE 这个参数,如果给定这个参数那么你给的path就必须是全路径了。不再是相对路径了。

5.1 RtlCreateRegistryKey的使用

NTSTATUS RtlCreateKeyOpt()
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    status = RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\machine\\SOFTWARE\\testkey");
    //注意相对路径的路径名前边不用加\\
    status = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES, L"testkey");
    return status;
}

分别在 HKEY_LOCAL_MACHINE 下以及 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\ 创建了testkey

使用的注意事项

在创建的时候如果你给定的路径中有丢失的键那么这个函数是不会帮你创建的。举例:

\Registry\machine\123\abc 我想在123下面创建abc 但是我并没有123这个键,所以我必须先创建123之后才能创建abc。否则此函数失败(或者你循环创建来保证创建成功**

5.2 RtlCheckRegistryKey

检查Key是否存在用法如下:

status = RtlCheckRegistryKey(
        RTL_REGISTRY_ABSOLUTE,
        L"\\Registry\\machine\\SOFTWARE\\testkey");

5.3 RtlDeleteRegistryValue

删除一个key关联的值。也就是删除 value

 status = RtlDeleteRegistryValue(
        RTL_REGISTRY_ABSOLUTE, 
        L"\\Registry\\machine\\SOFTWARE\\testkey", 
        L"");
    status = RtlDeleteRegistryValue(
        RTL_REGISTRY_ABSOLUTE,
         L"\\Registry\\machine\\SOFTWARE\\testkey", 
         L"ABC");

第三个参数如果给 空字符串则是删除 注册表值的默认值。 如果不是空就是删除的指定值。

5.4 RtlWriteRegistryValue

写一个keyValue到key中。

NTSTATUS RtlCreateKeyOpt()
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    status = RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\machine\\SOFTWARE\\testkey");

    status = RtlWriteRegistryValue(
        RTL_REGISTRY_ABSOLUTE,
        L"\\Registry\\machine\\SOFTWARE\\testkey",
        L"abc",
        REG_SZ,
        L"HelloWorld",
        wcslen(L"HelloWorld") * 2);

    return status;
}

结果

5.5 RtlQueryRegistryValues 查询数据

查询也很简单,就是你可以给一个数组,也可以单独定义一个值。 在这个值里面我们指定我们要查询的值的类型 指定查询后数据放在那里。 如果查不到默认要给什么值。 指定buffer的长度
代码如下:

NTSTATUS RtlCreateKeyOpt()
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    PCWSTR pKey = L"\\Registry\\machine\\HARDWARE\\DESCRIPTION\\System\\BIOS";
    RTL_QUERY_REGISTRY_TABLE queryTable[2];
    RtlZeroMemory(queryTable, sizeof(queryTable));

    PULONG pBuffer = NULL;
    pBuffer = (PULONG)(ExAllocatePool(NonPagedPool, 100));
    if (pBuffer == NULL)
    {
        goto END;
    }
    RtlZeroMemory(pBuffer, 100);
    queryTable[0].EntryContext = pBuffer; // 指定查询的数据在哪
    queryTable[0].DefaultType = REG_SZ;   //告诉他查询类型是什么
    queryTable[0].DefaultLength = 100;    //数据缓冲的大小
    queryTable[0].DefaultData = REG_NONE; //查询不好默认给什么值
    queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;//固定
    queryTable[0].Name = L"BaseBoardProduct"; //要查询的值的名称
    //查询的时候我直接查询的queryTable 这个参数是传入传出参数。根据你传入的值来将查询结果传出。 一定注意EntryContext是一个缓冲地址。
    // 请注意 queryTable必须是个数组,且最后一项都为0.这样RtlQueryxxx才知道何时停止。
    status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, pKey, queryTable, NULL, NULL); 

END:
    if (pBuffer != NULL)
    {
        ExFreePool(pBuffer);
        pBuffer = NULL;
    }
    return status;
}

PS:EntryContext字段我们是给了一个Buffer 但是如果查询出来之后那么此缓冲区将会被格式化为 UNICODE_STRING结构。所以我们如果调试的时候去查看这块缓冲区的时候请一定转换为 UNICODE_STRING 去查看。

posted @ 2019-06-08 14:48  iBinary  阅读(1845)  评论(0)    收藏  举报