您的位置:首页 > 其它

【Windows内核驱动开发】——读取注册表

2016-11-28 16:53 435 查看
【我的】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指定的长度一定要吻合,否则将会出现错误,导致注册表打开失败。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  驱动开发 注册表