内核的注册表操作

Home / C++ MrLee 2017-3-9 3732

在驱动程序的开发中,经常会对注册表进行操作。注册表的主要组成如图3-1所示,注册表的五个主要组成部分如下。

20170309200700

图3-1注册表的主要组成 注册表项:注册表中的一个项目,类似于目录。每个项中存储着多个二元结构,即键名-键值。每个项中,可以有若干个子项。 注册表子项:类似于目录中的子目录。 键名:类似于目录下的文件名,可用于索引,通过键名可以寻找到相应的键值。 键值类别:每个键值存储的时候会有不同类别,即键值类别,比如整型、字符串等类型。 键值:键名下对应存储的数据值。 WDK提供了一套对注册表进行操作的函数集,下面对这些函数进行简要介绍。 1. 初始化一个OBJECT_ATTRIBUTES结构体 为了能对注册表进行操作,必须先调用InitializeObjectAttributes函数初始化一个OBJECT_ATTRIBUTES结构体,其原型如下:
VOID InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES InitializedAttributes,
    IN PUNICODE_STRING ObjectName,
    IN ULONG Attributes,
    IN HANDLE RootDirectory,
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
);

上述函数中,参数InitializedAttributes指向需要初始化的OBJECT_ATTRIBUTES结构体;参数ObjectName 描述需要打开的注册表对象的名称,用Unicode字符串表示;参数Attributes为标识,若将其设置为OBJ_CASE_INSENSITIVE,则在用ObjectName与已存在的对象进行比较时就不区分大小写了;参数RootDirectory用于描述ObjectName参数的根目录,如果ObjectName是一个完全的名称,则设为NULL;参数SecurityDescriptor为一个安全描述,如果把它设为NULL,驱动程序将采用默认的安全设置。 2. 打开注册表 WDK提供了内核函数ZwOpenKey,用来打开一个已存在的注册表项。如果ZwOpenKey指定的项不存在,则其不会创建这个项,而是返回一个错误状态。该函数的原型如下:
NTSTATUS ZwOpenKey(OUT PHANDLE KeyHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes
);

上述函数中,参数KeyHandle返回被打开的句柄;参数DesiredAccess为打开的权限,一般设为KEY_ALL_ACCESS;参数ObjectAttributes是OBJECT_ATTRIBUTES数据结构,用于指示打开的状态。 如果打开成功,函数ZwOpenKey返回STATUS_SUCCESS,否则返回一个错误代码,错误代码可能为STATUS_INVALID_HANDLE或STATUS_ACCESS_DENIED。 3. 关闭注册表 已打开的注册表,如果不再使用,需要采用ZwClose函数关闭它,其原型如下: NTSTATUS ZwClose( IN HANDLE Handle ); 上述函数中,参数Handle为所要关闭的注册表句柄。 4. 查询注册表 驱动程序中有时需要对注册表的项进行查询,从而获取注册表的键值。WDK提供的ZwQueryValueKey函数可以完成这个任务,其原型如下:
NTSTATUS ZwQueryValueKey(
    IN HANDLE KeyHandle,
    IN PUNICODE_STRING ValueName,
    IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    OUT PVOID KeyValueInformation,
    IN ULONG Length,
    OUT PULONG ResultLength
);

上述函数中,参数KeyHandle为打开的注册表句柄;参数ValueName为要查询的键名;参数KeyValueInformationClass决定不同的查询类别;参数KeyValueInformation用于选择一种查询类别,可选择KeyValueBasicInformation、KeyValueFullInformation或KeyValuePartialInformation类型;参数Length为要查询数据的长度;参数ResultLength为实际查询返回的数据长度。 如果打开成功,函数返回STATUS_SUCCESS,否则返回一个错误代码。 使用ZwQueryValueKey函数查询注册表单时,需要用KeyValueInformationClass参数来选择一种查询方式,可以是KeyValueBasicInformation、KeyValueFullInformation或者KeyValuePartialInformation中的一种。它们分别代表查询基本信息、查询全部信息和查询部分信息,并且每种查询类型会用一种对应的数据结构来获得查询结果。 一般情况下,选择KeyValuePartialInformation参数就可以查询键值的数据了,它对应的查询数据结构是KEY_VALUE_PARTIAL_INFORMATION,具体定义如下:
Typedef struct _KEY_VALUE_PARTIAL_INFORMATION{
    ULONG TitleIndex;
    ULONG Type;
    ULONG DataLength;
    UCHAR Data[1]; //
}KEY_VALUE_PARTIAL_INFORMATION,
*PKEY_VALUE_PARTIAL_INFORMATION;

注意KEY_VALUE_PARTIAL_INFORMATION数据结构的长度并未固定,所以首先要确定这个长度。 使用ZwQueryValueKey函数时,一般会按下列四个步骤进行操作: 用ZwQueryValueKey函数获取这个数据结构的长度。 分配如此长度的内存,用来查询。 再次调用ZwQueryValueKey函数,获取键值。 回收内存。 如果选择KeyValueFullInformation参数,它对应的是KEY_VALUE_FULL_INFORMATION查询数据结构体,具体定义如下:
Typedef struct _KEY_VALUE_FULL_INFORMATION{
    ULONG TitleIndex;
    ULONG Type;
    ULONG DataOffset;
    ULONG DataLength;
    ULONG NameLength;
    WCHAR Name[1]; //
}KEY_VALUE_FULL_INFORMATION,*PKEY_VALUE_FULL_INFORMATION;

5. 枚举子项 在注册表中,经常使用另外两种操作,分别是枚举子项和枚举子键。枚举子项就是事先不知道该项中有多少个子项目,用某个函数将子项一一列举出来。而枚举子键是指事先不知道该项中有多少个子键,用某个函数将子键一一列举出来。 WDK提供了列举子项的ZwQueryKey函数和ZwEnumerateKey函数。ZwQueryKey函数的作用主要是获得某注册表项究竟有多少个子项,而ZwEnumerateKey函数的作用主要是针对某个子项获取其具体信息。接下来看一看这两个函数的原型。 ZwQueryKey函数的原型如下:
NTSTATUS ZwQueryKey(
    IN HANDLE KeyHandle,
    IN KEY_INFORMATION_CLASS  KeyInformationClass,
    OUT PVOID  KeyInformation,
    IN ULONG  Length,
    OUT PULONG  ResultLength
);

上述函数中,参数KeyHandle为注册表项的句柄;参数KeyInformationClass为查询的类别,一般选择KeyFullInformation;参数KeyInformation为查询的数据指针,如果KeyInformationClass是KeyFullInformation,则该指针指向一个KEY_FULL_INFORMATION的数据结构;参数Length为数据长度;参数ResultLength为实际返回数据的长度。 如果函数执行成功,返回STATUS_SUCCESS,否则返回一个错误代码。 ZwEnumerateKey函数的原型如下:
NTSTATUS ZwEnumerateKey(
    IN HANDLE  KeyHandle,
    IN ULONG  Index,
    IN KEY_INFORMATION_CLASS  KeyInformationClass,
    OUT PVOID  KeyInformation,
    IN ULONG  Length,
    OUT PULONG  ResultLength
);

上述函数中,参数KeyHandle为注册表项句柄;参数Index为从0开始的索引;参数KeyInformationClass为子项的信息类型;参数Length为子项信息的长度;参数ResultLength为返回子键信息的长度。 如果函数执行成功,返回STATUS_SUCCESS,否则返回一个错误代码。 在使用ZwQueryKey函数时,可以将参数KeyInformationClass指定为KeyFullInformation。这样,参数KeyInformation就可对应一个KEY_FULL_INFORMATION的数据结构了,该数据结构中的SubKeys指明了该项中有多少个子项。KEY_FULL_INFORMATION数据结构的长度可变,所以要调用两次ZwQueryKey,第一次获取KEY_FULL_INFORMATION结构的长度,第二次才是真正获取KEY_FULL_INFORMATION结构的数据。 在使用ZwEnumerateKey函数时,需要将参数KeyInformationClass设置为KeyBasicInformation,这样,参数KeyInformation就能对应KEY_BASIC_INFORMATION的数据结构了。KEY_BASIC_INFORMATION数据结构长度也是可变的,故同样需要调用ZwEnumerateKey两次,第一次获取KEY_BASIC_INFORMATION结构的长度,第二次获取KEY_BASIC_INFORMATION结构的数据。 6. 枚举子键 和枚举子项类似,通过ZwQueryKey和ZwEnumerateValueKey这两个函数的配合可以完成对子键的枚举。ZwEnumerateValueKey函数的使用方法和ZwEnumerateKey函数类似,其原型如下:
NTSTATUS ZwEnumerateValueKey(
    IN HANDLE KeyHandle,
    IN ULONG Index,
    IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    OUT PVOID KeyValueInformation,
    IN ULONG Length,
    OUT PULONG ResultLength
);

本文链接:https://www.it72.com/11889.htm

推荐阅读
最新回复 (0)
返回