内核模式的注册表操作(Windows 驱动开发详解)

首先说一下 应用层的注册表操作 主要也就是 遍历 增删改查 之类 的

然后 RegOpenKeyEx  函数  这个可以  打开 其中 RegCreateKeyEx 可以创造 也可以打开 

然后 RegSetVableEx 函数 在注册表下设置指定值的数据和类型    其中如果想实现 某个程序开机自启动的话 其实也很简单 

 只不过在windows10 或者有些win7 要权限的 

BOOL Reg_CurrentUser(char *lpszFileName, char *lpszValueName)
{
	// 默认权限
	HKEY hKey;
	// 打开注册表键
	if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey))
	{
		ShowError("RegOpenKeyEx");
		return FALSE;
	}
	// 修改注册表值,实现开机自启
	if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName))))
	{
		::RegCloseKey(hKey);
		ShowError("RegSetValueEx");
		return FALSE;
	}
	// 关闭注册表键
	::RegCloseKey(hKey);

	return TRUE;
}

上面的代码 出自于 Windows 黑客编程

然后我们看一下  内核里面的函数都有那些   关于注册表的把 

然后 先说创建函数把 

在上面可以看得出来  注册表也需要得出来 一个注册表句柄 然后 根据这个句柄进行操作    可以通过 ZWCreateKey 打开句柄

第一个参数 就是 获得的 注册表句柄 

第二个参数 就是访问权限  一般设置成KEY_ALL_ACCESS

第三个参数 就是 OBJECT_ATTRIBUTES数据结构

第四个参数 很少用的 一般置位0

第五个参数  一般置位NULL

第六个参数 创建的选项 一般置位REG_OPTION_NON_VOLATLE

第七个参数 返回时 创建成功 还是打开成功  如果时创建成功 REG_CREATED_NEW_KEY  打开成功 REG_OPENED_EXISTING_KEY  

那么 直接上代码 吧。

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	ULONG ulresult;
	NTSTATUS nt = ZwCreateKey(&hRegister,
		KEY_ALL_ACCESS,
		&object,
		0,
		NULL,
		REG_OPTION_NON_VOLATILE,
		&ulresult);
	if (NT_SUCCESS(nt))
	{
		if (ulresult == REG_CREATED_NEW_KEY)
		{
			KdPrint(("register创建成功!\n"));
		}
		else if (ulresult == REG_OPENED_EXISTING_KEY)
		{
			KdPrint(("register 本来就存在 现在已经打开\n"));
		}
		else
		{
			KdPrint(("好像出问题了-----------\n"));
		}
	}
	UNICODE_STRING subregstring;
	HANDLE hsubreg;
	RtlInitUnicodeString(&subregstring, L"subitem");
	OBJECT_ATTRIBUTES subobject;
	InitializeObjectAttributes(&subobject,
		&subregstring,
		OBJ_CASE_INSENSITIVE,
		hRegister,
		NULL);
	nt = ZwCreateKey(&hsubreg,
		KEY_ALL_ACCESS,
		&subobject,
		0,
		NULL,
		REG_OPTION_NON_VOLATILE,
		&ulresult);
	if (NT_SUCCESS(nt))
	{
		if (ulresult == REG_CREATED_NEW_KEY)
		{
			KdPrint(("register 创建成功!\n"));
		}
		else if (ulresult == REG_OPENED_EXISTING_KEY)
		{
			KdPrint(("register 本来就存在 现在已经打开\n"));
		}
	}
	ZwClose(hRegister);
	ZwClose(hsubreg);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

然后 下面的是 打开注册表  从上面看  其实 ZwCreatKey  也是可以打开的  但是  也有 更为简便的 ZwOpenKey 

下面演示用 ZwOpen Key打开注册表项目 

这里面 三个参数 被返回的句柄 一个打开权限  一个是 objectattributes的数据结构

下面就是 添加 修改 注册表键值

其中 ZwSetValueKey  就是来完成这个任务的

第一个参数 是注册表句柄  第二个参数是 要新建或者修改的键名  第三个 很少用 一般设置成0  Type 选择键值类型 

第四个是 记录键值数据的大小   

如果 键值不存在直接创建  存在打开 

下面是添加 修改 注册表键值 

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	NTSTATUS nt = ZwOpenKey(&hRegister,
		KEY_ALL_ACCESS,
		&object);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("打开成功!\n"));
	}
	else
	{
		KdPrint(("打开失败 可惜!\n"));
		driver->DriverUnload = DriverUnload;
		return STATUS_SUCCESS;
	}
	UNICODE_STRING ValueName;
	RtlInitUnicodeString(&ValueName, L"REG_DWORD value");
	ULONG ulvalue = 1000;
	ZwSetValueKey(hRegister,
		&ValueName,
		0,
		REG_DWORD,
		&ulvalue,
		sizeof(ulvalue));
	RtlInitUnicodeString(&ValueName, L"REG_SZ value");
	WCHAR* strValue = L"hello world";
	ZwSetValueKey(hRegister,
		&ValueName,
		0,
		REG_SZ,
		strValue,
		wcslen(strValue)*2+2);
	RtlInitUnicodeString(&ValueName, L"REG_BINARY value");
	UCHAR buffer[10];
	RtlFillMemory(buffer, sizeof(buffer), 0xff);
	ZwSetValueKey(hRegister,
		&ValueName,
		0,
		REG_BINARY,
		buffer,
		sizeof(buffer));
	ZwClose(hRegister);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

下面是 查询注册表 

这里微软 也封装好了一个函数  ZwQueryValueKry 

其中 第一个参数是 打开的句柄 第二个是 要查询的键名  第三个是 根据不同选择 选择不同的查询类别  

有 keyvaluebasicinformation  keyvaluefullinformation keyvaluepartialInformation

第四个是 查询数据的长度  第五个是 实际查询数据的长度  

一般都用KeyValuePartiaInformation   

这个有数据  数据类型 数据长度 数据指针 

由于这个数据结构 大小不确定 所以要先确定这个长度  一般分为4步

第一  用ZwQuertValueKey 来获取这二个数据结构的长度

第二  分配如此长度的内存 用来查询

第三  再次调用 ZwQuertValueKey 来获取键值

第四 回收内存 

然后下面就是代码 

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	NTSTATUS nt = ZwOpenKey(&hRegister,
		KEY_ALL_ACCESS,
		&object);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("打开成功!\n"));
	}
	else
	{
		KdPrint(("打开失败 可惜!\n"));
		driver->DriverUnload = DriverUnload;
		return STATUS_SUCCESS;
	}
	UNICODE_STRING ValueName;
	RtlInitUnicodeString(&ValueName, L"REG_DWORD value");
	ULONG size = 1000;
	nt = ZwQueryValueKey(hRegister,
		&ValueName,
		KeyValuePartialInformation,
		NULL,
		0,
		&size);
	if (nt == STATUS_OBJECT_NAME_NOT_FOUND || size == 0)
	{
		ZwClose(hRegister);
		KdPrint(("这个项目不存在!\n"));
		return STATUS_SUCCESS;
	}
	PKEY_VALUE_PARTIAL_INFORMATION pvpi =
		(PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, size);
	nt = ZwQueryValueKey(hRegister,
		&ValueName,
		KeyValuePartialInformation,
		pvpi,
		size,
		&size);
	if (!NT_SUCCESS(nt))
	{
		ZwClose(hRegister);
		KdPrint(("查询失败!\n"));
		return STATUS_SUCCESS;
	}
	if (pvpi -> Type == REG_DWORD&&pvpi->DataLength == sizeof(ULONG))
	{
		PULONG pulValue = (PULONG)pvpi->Data;
		KdPrint(("注册表的值是%d\n", *pulValue));
	}
	ExFreePool(pvpi);
	RtlInitUnicodeString(&ValueName, L"REG_SZ value");
	nt = ZwQueryValueKey(hRegister,
		&ValueName,
		KeyValuePartialInformation,
		NULL,
		0,
		&size);
	if (nt == STATUS_OBJECT_NAME_NOT_FOUND || size == 0)
	{
		ZwClose(hRegister);
		KdPrint(("查询失败!\n"));
		return STATUS_SUCCESS;
	}
	pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool, size);
	nt = ZwQueryValueKey(hRegister,
		&ValueName,
		KeyValuePartialInformation,
		pvpi,
		size,
		&size);
	if (!NT_SUCCESS(nt))
	{
		ZwClose(hRegister);
		KdPrint(("查询失败!\n"));
		return STATUS_SUCCESS;
	}
	if (pvpi->Type == REG_SZ)
	{
		KdPrint(("键值是 %s\n",pvpi->Data));
	}
	ZwClose(hRegister);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

然后下面的是 枚举子项 

DDK提供了 枚举子项的 函数  有ZwQueryKey  还有 ZwEnimerateKey      

ZwQueryKey   的参数 有  五个 

第一个 是 注册表的句柄

第二个 查询的类别  一般是  KeyInformationClass 

第三个是 数据指针   

第四个是 数据长度 

第五个是 返回的数据长度 

而   ZwEnimerateKey

第一个是 注册表句柄 

第二个 很少用 一般为0

第三个 是该子项的信息 

第四个是  子项信息的长度

第五个 是返回子项信息的长度

这个两个函数的差异 应该是很好看得出来的  第一个呢是获取注册表中究竟有几个子项  第二个是 第几项获取第几项的信息 

在 使用第一个的时候  可以将KeyInformationClass   KEYFullInformattion  然后 数据里SubKey 就有了 多少子项

然后   第二个是 第四个参数 大小可变  要调用两次 第一次  获取 长度 第二次 获取数据 

下面是代码 

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	NTSTATUS nt = ZwOpenKey(&hRegister,
		KEY_ALL_ACCESS,
		&object);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("打开成功!\n"));
	}
	else
	{
		KdPrint(("打开失败 可惜!\n"));
		driver->DriverUnload = DriverUnload;
		return STATUS_SUCCESS;
	}
	ULONG size;
	ZwQueryKey(hRegister,
		KeyFullInformation,
		NULL,
		0,
		&size);
	PKEY_FULL_INFORMATION pfi =
		(PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, size);
	ZwQueryKey(hRegister,
		KeyFullInformation,
		pfi,
		size,
		&size);
	for (ULONG i = 0; i < pfi->SubKeys; i++)
	{
		ZwEnumerateKey(hRegister,
			i,
			KeyBasicInformation,
			NULL,
			0,
			&size);
		PKEY_BASIC_INFORMATION pbi =
			(PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, size);
		ZwEnumerateKey(hRegister,
			i,
			KeyBasicInformation,
			pbi,
			size,
			&size);
		UNICODE_STRING unikeyName;
		unikeyName.Length =
			unikeyName.MaximumLength =
			(USHORT)pbi->NameLength;
		unikeyName.Buffer = pbi->Name;
		KdPrint(("第 %d 的名字是 %wZ\n", i, &unikeyName));
		ExFreePool(pbi);
	}
	ExFreePool(pfi);
	ZwClose(hRegister);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

然后枚举子键也是两个函数配合   用的是 ZwQueryKey  还有ZwEnumerateValueKey  两个函数配合的  下面直接 给代码 

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	NTSTATUS nt = ZwOpenKey(&hRegister,
		KEY_ALL_ACCESS,
		&object);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("打开成功!\n"));
	}
	else
	{
		KdPrint(("打开失败 可惜!\n"));
		driver->DriverUnload = DriverUnload;
		return STATUS_SUCCESS;
	}
	ULONG size;
	ZwQueryKey(hRegister,
		KeyFullInformation,
		NULL,
		0,
		&size);
	PKEY_FULL_INFORMATION pfi =
		(PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, size);
	ZwQueryKey(hRegister,
		KeyFullInformation,
		pfi,
		size,
		&size);
	for (ULONG i = 0; i < pfi->Values; i++)
	{
		ZwEnumerateValueKey(hRegister,
			i,
			KeyValueBasicInformation,
			NULL,
			0,
			&size);
		PKEY_VALUE_BASIC_INFORMATION pvbi =
			(PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(PagedPool, size);
		ZwEnumerateValueKey(hRegister,
			i,
			KeyValueBasicInformation,
			pvbi,
			size,
			&size);
		UNICODE_STRING keyname;
		keyname.Length =
			keyname.MaximumLength =
			(USHORT)pvbi->NameLength;
		keyname.Buffer = pvbi->Name;
		KdPrint(("这个 %d 键值是 %wZ\n",i,&keyname));
		if (pvbi->Type == REG_SZ)
		{
			KdPrint(("这个键值的类型:REG_SZ\n"));
		}
		else if (pvbi->Type == REG_MULTI_SZ)
		{
			KdPrint(("这个键值的类型: REG_MULTI_SZ\n"));
		}
		else if (pvbi->Type == REG_DWORD)
		{
			KdPrint(("这个键值的类型:REG_DWORD \n"));
		}
		else if (pvbi->Type == REG_BINARY)
		{
			KdPrint(("这个键值的类型: REG_BINARY\n"));
		}
		ExFreePool(pvbi);
	}
	ExFreePool(pfi);
	ZwClose(hRegister);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

下面就是删除 子项了

删除子项就比较简单了   ZwDeleteKey  就一个参数  就是  打开的句柄

这个需要指出的是   这个函数只能删除 没有子项的项目  如果项中还有 子项 那么就不能删除  这个时候只能删除 全部的子项   再删除 该项   代码如下 

#include<ntddk.h>
#define  MY_REG_SOFTWARE_KEY_NAME  L"\\Registry\\Machine\\Software\\Mzf"
#define  MY_REG_SOFTWARE_KEY_NAME1 L"\\Registry\\Machine\\Software\\Zhangfan\\SubItem"
#define BUFFER_SIZE 1024
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	KdPrint(("驱动开始卸载!")); 

}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	UNICODE_STRING Register;
	HANDLE hRegister;
	RtlInitUnicodeString(&Register, MY_REG_SOFTWARE_KEY_NAME1);
	OBJECT_ATTRIBUTES object;
	InitializeObjectAttributes(&object,
		&Register,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);
	NTSTATUS nt = ZwOpenKey(&hRegister,
		KEY_ALL_ACCESS,
		&object);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("打开成功!\n"));
	}
	else
	{
		KdPrint(("打开失败 可惜!\n"));
		driver->DriverUnload = DriverUnload;
		return STATUS_SUCCESS;
	}
	nt = ZwDeleteKey(hRegister);
	if (NT_SUCCESS(nt))
	{
		KdPrint(("成功删除!\n"));
	}
	else if (nt == STATUS_ACCESS_DENIED)
	{
		KdPrint(("STATUS_ACCESS_DENIED!\n"));
	}
	else if (nt == STATUS_INVALID_HANDLE)
	{
		KdPrint(("STATUS_INVALID_HANDLE!\n"));
	}
	else
	{
		KdPrint(("也许已经被删除了!\n"));
	}
	/*
	ULONG size;
	ZwQueryKey(hRegister,
		KeyFullInformation,
		NULL,
		0,
		&size);
	PKEY_FULL_INFORMATION pfi =
		(PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, size);
	ZwQueryKey(hRegister,
		KeyFullInformation,
		pfi,
		size,
		&size);
	for (ULONG i = 0; i < pfi->Values; i++)
	{
		ZwEnumerateValueKey(hRegister,
			i,
			KeyValueBasicInformation,
			NULL,
			0,
			&size);
		PKEY_VALUE_BASIC_INFORMATION pvbi =
			(PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(PagedPool, size);
		ZwEnumerateValueKey(hRegister,
			i,
			KeyValueBasicInformation,
			pvbi,
			size,
			&size);
		UNICODE_STRING keyname;
		keyname.Length =
			keyname.MaximumLength =
			(USHORT)pvbi->NameLength;
		keyname.Buffer = pvbi->Name;
		KdPrint(("这个 %d 键值是 %wZ\n",i,&keyname));
		if (pvbi->Type == REG_SZ)
		{
			KdPrint(("这个键值的类型:REG_SZ\n"));
		}
		else if (pvbi->Type == REG_MULTI_SZ)
		{
			KdPrint(("这个键值的类型: REG_MULTI_SZ\n"));
		}
		else if (pvbi->Type == REG_DWORD)
		{
			KdPrint(("这个键值的类型:REG_DWORD \n"));
		}
		else if (pvbi->Type == REG_BINARY)
		{
			KdPrint(("这个键值的类型: REG_BINARY\n"));
		}
		ExFreePool(pvbi);
	}
	ExFreePool(pfi);*/
	ZwClose(hRegister);
	driver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

然后下面的就是 Rtl 封装的函数了    往往一条就能实现 若干函数的功能  我就看的写了

猜你喜欢

转载自blog.csdn.net/qq_41071646/article/details/86169880