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

一 注册表编程

二 注册表简介

2.1 ring3注册表

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

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

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

2.2 重启删除原理

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

如下:
内核:
RegistryMachineSYSTEMCurrentControlSetControlSession ManagerpendingFileRenameOperations

对应Ring3
计算机HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerpendingFileRenameOperations
这个key里面有个值是 REG_MULTI_SZ类型,这个类型存储的是多个 结尾的路径。

使用 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_MACHINESYSTEMCurrentControlSetServicesLanmanServerShares你共享文件夹的名字
找到文件共享名字.寻找值取出路径进行拼接.

对应内核:

registrymachineSYSTEMCurrentControlSetServicesLanmanServerShares你的共享文件夹名

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

代码演示.

原文地址:https://www.cnblogs.com/iBinary/p/10990686.html