【我的】Windows驱动开发——读取注册表
作者:zcr214 时间:2016/5/5
注册表对于驱动来说是很重要的小伙伴,注册表可以很好的扮演用户到内核的桥梁角色,很多时候用户可以通过修改注册表的内容来达到控制驱动的目的。那么驱动要做的首先当然是读取到注册表啦,WDK提供了标准的接口函数,所以直接使用就可以了。
1. ZwOpenKey()打开注册表
WDK提供了打开注册表的接口函数ZwOpenKey,其原型如下:
NTSTATUS
NTAPI
ZwOpenKey(
_Out_PHANDLE KeyHandle,
_In_ACCESS_MASK DesiredAccess,
_In_POBJECT_ATTRIBUTES ObjectAttributes
);
这个函数将得到一个打开注册表的操作句柄指针,保存在KeyHandle,并返回状态值。
需要ACCESS_MASK来指定打开该注册表的权限,读权限对应KEY_READ,写权限对应KEY_WRITE,如果需要全部权限则是KEY_ALL_ACCESS。
从第三个参数可以看出,它不是接受一个字符串来表示一个注册表项,而是要求输入一个OBJECT_ATTRIBUTES的指针,这需要我们提前初始化一个OBJECT_ATTRIBUTES。下面举个初始化的例子:
OBJECT_ATTRIBUTESobj_attr={0};
InitializeObjectAttributes(
&obj_attr,
Reg_Key_Path,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
其中obj_attr是初始化的OBJECT_ATTRIBUTES,Reg_Key_Path是注册表项的路径,它是一个UNICODE_STRING字符串,如UNICODE_STRINGReg_key_path=RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\ControlSet001\\services\\SwapBuffers\\");其余三个参数表示大小写敏感和安全描述符,一般情况下就按照这个例子这样填写即可。
初始化完毕后就可以打开注册表了。
status=ZwOpenKey(&Reg_Handle,KEY_READ,&obj_attr);
2. ZwQueryValueKey()读取键值
读取注册表子键的值,使用ZwQueryValueKey(),其原型如下:
NTSTATUS
NTAPI
ZwQueryValueKey(
_In_HANDLE KeyHandle,
_In_PUNICODE_STRING ValueName,
_In_KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
_Out_writes_bytes_opt_(Length)PVOIDKeyValueInformation,
_In_ULONG Length,
_Out_PULONG ResultLength
);
KeyHandle是之前使用ZwOpenKey打开的注册表操作句柄。
ValueName是要读取的子键的名字。
KeyValueInformationClass是获取的信息类型,包括Basic,Full,Partial三种,Basic信息包含子键名和类型,Full包含子键名,类型和值,Partial包含类型和值,一般的我们读取注册表显然已经知道了名字,是为了得到数据类型和值,因此获取Partial信息最常用。即KeyValuePartialInformation。
KeyValueInformation指针所指的内存,用于保存函数返回来的注册表键值信息,是一个_KEY_VALUE_PARTIAL_INFORMATION结构,其原型如下:
typedefstruct_KEY_VALUE_PARTIAL_INFORMATION{
ULONG TitleIndex;
ULONG Type;
ULONG DataLength;
_Field_size_bytes_(DataLength)UCHARData[1];//Variable size
}KEY_VALUE_PARTIAL_INFORMATION,*PKEY_VALUE_PARTIAL_INFORMATION;
Length是用户指定的输出空间KeyValueInformation的长度。
ResultLength是返回回来的实际需要的长度。
通过分析这个函数可以知道,主要问题存在于读取长度这里,如果为了方便总是定义一个足够大的空间,这样势必造成内存的浪费,所以实际应用中,应该耐心的获取合适的长度,不足时再动态分配内存,所以应如下读取键值。
//用来试探大小的Reg_try_info
KEY_VALUE_PARTIAL_INFORMATIONReg_try_info;
//实际获取的Reg_info
PKEY_VALUE_PARTIAL_INFORMATIONReg_info;
ULONGReal_length;
UNICODE_STRINGReg_key_Name=RTL_CONSTANT_STRING(L"ChosenVolume");
//下面开始试图读取值
status=ZwQueryValueKey(
Reg_Handle,
Reg_Key_Name,
KeyValuePartialInformation,
&Reg_try_info,
sizeof(KEY_VALUE_PARTIAL_INFORMATION),
&Real_length);
if(!NT_SUCCESS(status)
&&status!=STATUS_BUFFER_OVERFLOW
&&status!=STATUS_BUFFER_TOO_SMALL)
{
//错误处理
DbgPrint("getRegistryValue:打开注册表键值失败");
ZwClose(Reg_Handle);
returnNULL;
}
//如果读取成功,则分配足够的空间再次读取
Reg_info=(PKEY_VALUE_PARTIAL_INFORMATION)
ExAllocatePoolWithTag(NonPagedPool,Real_length,NAME_TAG);
if(Reg_info==NULL)
{
//错误处理
DbgPrint("getRegistryValue:打开注册表键值对指针为空");
ZwClose(Reg_Handle);
returnNULL;
}
status=ZwQueryValueKey(
Reg_Handle,
Reg_Key_Name,
KeyValuePartialInformation,
Reg_info,
Real_length,
&Real_length);
ZwClose(Reg_Handle);
所有的操作完毕,如果此时status为成功,那么注册表信息已经保存在Reg_info->Data中了,它是UCHAR类型的可变长度数组,下面只需要再转化成需要的字符串类型PCHAR,UNICODE_STRING等即可。
3. 注意的问题
传入函数的所有类型的参数都要严格的初始化定义,最有可能出问题的地方就是UNICODE_STRING字符串。
例如在初始化OBJECT_ATTRIBUTE或ZwQueryValueKey时,如果Reg_Key_Path或Reg_Key_Name并不是一个静态定义好的UNICODE_STRING,而是作为函数参数传递过来的,它的Buffer实际长度和Length指定的长度一定要吻合,否则将会出现错误,导致注册表打开失败。