1.1.8 内核的注册表操作
在驱动程序的开发中,经常会对注册表进行操作。
DDK
提供一套对注册表操作的函数。
v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}
图3-1 注册表的在组成
首先对注册表的五个主要组成部分说明一下,如图
3-1
所示。
注册表项:注册表中的一个项目,类似目录的概念。每个项中存储多个二元结构,键名一键值。每个项中,可以有若干个子项。
注册表子项:类似于目录中的子目录。
键名:通过键名可以寻找到相应的键值。
键值类别:每个键值存储的时候有不同的类别,可以是整型、字符串等数据。
键值:键名下对应存储的数据。
1.1.8.1 初始化一个 OBJECT_ATTRIBUTES 结构体
为了能对注册执行操作,必须首先调用
InitializeObjectAttributes
函数初始化一个
OBJECT_ATTRIBUTES
结构体,函数原形如下:
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
,采用默认的安全。
1.1.8.2 打开注册表
DDK
提供了内核函数
ZwOpenKey
打开一个已存在的注册表项。如果
ZwOpenKey
指定的项不存在,就不会创建这个项,而是返回一个错误状态。该函数的声明如下:
ZwOpenKey(
OUT PHANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
参数
KeyHandle
为返回被打开的句柄。参数
DesiredAccess
为打开的权限,一般设为
KEY_ALL_ACCESS
。参数
ObjectAttri110utes
是
OBJECT_ATTRIBUTES
数据结构,指示打开的状态。
如果打开成功函数ZwOpenKey返回STATUS_SUCCESS,否则返回一个错误代码,可能为STATUS_INVALID_HANDLE或STATUS_ACCESS_DENIED。
1.1.8.3 关闭注册表
打开的注册表,如果不在使用需要采用
ZwClose函数
关闭它,函数原型如下。
ZwClose(
IN HANDLE Handle
参数
Handle
为所要关闭的注册表句柄。
1.1.8.4 查询注册表
驱动程序中有时需要对注册表的项进行查询,从而获取注册表的键值
aDDK
提供的
ZwQueryValueKey
函数可以完成这个任务,其声明如下:
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
为要查询的键名。参数
KeyValuelnformationClass
决定不同的查询类别。参数
KeyValuelnformation
选择一种查询类别。选择
KeyValueBasiclnformation
、
KeyValueFulllnformation
或者
KeyValuePartiallnformation
。参数
Length
为要查数据的长度。参数
ResultLength
为实际查询返回的数据长度。
如果打开成功函数返回
STATUS_SUCCESS
,否则返回一个错误代码。
使用
ZwQueryValueKey
函数查询注册表单时,需要用
KeyValuelnformationClass
选择一种查询方式。这可以是
KeyVajueBasiclnformation
、
KeyValueFulllnformation
或者
KeyValuePartiallnformation
中的一种。这分别代表查询基本信息,查询全部信息和查询部分信息,每种查询类型会有对应的一种数据结构获得查询结果。
一般情况下,选择
KeyValuePartiallnformation
就可以查询键值的数据了,它对应的查询数据结构是
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
分为
4
个步骤操作。
<!--[if !supportLists]-->
Ø <!--[endif]-->
用
ZwQueryValueKey
获取这个数据结构的长度。
<!--[if !supportLists]-->
Ø <!--[endif]-->
分配如此长度的内存,用来查询。
<!--[if !supportLists]-->
Ø <!--[endif]-->
再次调用
ZwQueryValueKey
,获取键值。
<!--[if !supportLists]-->
Ø <!--[endif]-->
回收内存。
如果选择
KeyValueFulllnformation,它对应的查询数据结构是
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;
1.1.8.5 枚举子项
在注册表的操作中,还经常有另外两种操作,分别是枚举子项和枚举子键。枚举子项就是事先不知道该项中有多少个子项目,用某个函数将子项一一列举出来。而枚举子键是事先不知道该项中有多少个子键,用某个函数一一将子键列举出来。
DDK
提供了列举子项的
ZwQueryKey
函数和
ZwEnumerateKey
。先看一下这两个函数的声明。
ZwQueryKey(
IN HANDLE KeyHandle,
IN KEY_INFORMATION_CLASS KeyInformationClass,
OUT PVOID KeyInformation,
IN ULONG Length,
OUT PULONG ResultLength
参数
KeyHandle
为注册表项的句柄。参数
KeylnformationClass
为查询的类别,一般选择
KeyFulllnformation
。参数
Keylnformation
为查询的数据指针。如果
KeylnformationClass
是
KeyFulllnformation
,则该指针指向一个
KEY_FULL_INFORMATION
的数据结构。参数
Length
为数据长度。参数
ResultLength
为实际返回数据的长度。
如果函数成功则返回
STATUS_SUCCESS
,否则返回一个错误代码。
ZwEnumerateKey(
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
OUT PVOID KeyInformation,
IN ULONG Length,
OUT PULONG ResultLength
参数
KeyHandle
为注册表项句柄。参数
Index:
为由
0
开始的索引。参数
KeylnformationClass
为子项的信息类型。参数
Length
为子项信息的长度。参数
ResultLength
为返回子键信息的长度。
如果函数成功则返回
STATUS_SUCCESS
,否则返回一个错误代码。
ZwQueryKey
的作用主要是获得某注册表项究竟有多少个子项,而
ZwEnumerateKey
的作用主要是针对第几个子项获取该子项的具体信息。
在使用
ZwQueryKey
时,可以将参数
KeylnformationClass
指定为
KeyFulllnformation
。这样参数
Keylnformation
就对应一个
KEY_FULL_INFORMATION
的数据结构,该数据结构中的
SubKeys
指明了项中有多少个子项。
KEY_FULL_INFORMATION
数据结构的大小是变长的,所以要调用两次
ZwQueryKey
。第一次获取
KEY_FULL_INFORMATION
数据的长度,第二次真正获取
KEY_FULL_INFORMATION
数据。
在使用
ZwEnumerateKey
时,需要将参数
KeylnformationClass
设置为
KeyBasicInformation
,这样其参数
Keylnformation
兢能对应
KEY_B ASIC_INFORMATION
的数据结构。
同理
KEY_BASIC_INFORMATION
也是变长的数据结构,需要两次调用
ZwEnumerateKey
。第一次获取
KEY_BASIC_INFORMATION
的长度,第二次获取
KEY_BASIC_INFORMATION
数据。
1.1.8.6 枚举子键
和枚举子项类似,枚举子键是通过
ZwQueryKey
和
ZwEnumerateValueKey
两个函数的配合完成的。
ZwEnumerateValueKey
函数的使用和
ZwEnumerateKey
函数的使用类似。
ZwEnumerateValueKey(
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
OUT PVOID KeyValueInformation,
IN ULONG Length,
OUT PULONG ResultLength
1.1.8.7 WinPcap中注册表操作实例
下列代码是
WinPcap
中对注册操作的实际代码。
PWCHAR getAdaptersList(void)
{
…
/*
*设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了后续调用
*其中NDIS_STRING AdapterListKey =
* NDIS_STRING_CONST("\\Registry\\Machine\\System\\CurrentControlSet
* \\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}");
*/
InitializeObjectAttributes(&objAttrs, &AdapterListKey,
OBJ_CASE_INSENSITIVE, NULL, NULL);
/*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/
status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);
if (!NT_SUCCESS(status)) {
//打开失败
}
else { //打开成功
ULONG resultLength;
KEY_VALUE_PARTIAL_INFORMATION valueInfo;
CHAR AdapInfo[1024];
UINT i=0;
/*遍历设备链表,获取一个已打开注册表项子项的信息*/ while((status=ZwEnumerateKey(keyHandle,i,KeyBasicInformation,
AdapInfo,sizeof(AdapInfo),&resultLength))==STATUS_SUCCESS)
{
…
/*设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了后续调用*/
InitializeObjectAttributes(&objAttrs, &AdapterKeyName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
/*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/
status=ZwOpenKey(&ExportKeyHandle,KEY_READ,&objAttrs);
/*查找“Export”键名的键值信息*/
status = ZwQueryValueKey(ExportKeyHandle, &FinalExportKey,
KeyValuePartialInformation, &valueInfo,
sizeof(valueInfo), &resultLength);
if
(!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW)) {
//查询失败
}
else { //查询成功
/*计算所需的内存大小*/
…
/*分配内存,用于查询*/
…
if (valueInfoP != NULL) {//分配内存成功
status = ZwQueryValueKey(ExportKeyHandle,
&FinalExportKey,
KeyValuePartialInformation,
valueInfoP,
valueInfoLength, &resultLength);
if (!NT_SUCCESS(status)) {
//查询失败
}
else{//查询成功
…
}
ExFreePool(valueInfoP);
}
else {
//分配用于查询的内存失败
}
}//一次查找“Export”键名的键值信息结束
…
//关闭注册表子项
ZwClose (ExportKeyHandle);
i++;
}//结束while语句
/*关闭注册表项*/
ZwClose (keyHandle);
}
…