8、Windows驱动开发技术详解笔记(4) 基本语法回顾

 4、注册表操作

和文件操作类似,在操作注册表之前需要首先打开注册表,获得一个句柄,这可以通过函数ZwCreateKey 完成。与ZwCreateFile函数类似,它通过一个OBJECT_ATTRIBUTES 获得需要创建或打开的路径信息,但在内核中这个路径与用户模式下不相同,实际上,因为用户模式下的应用程序总是由某个当前用户打开的,因此在用户模式下可以直接访问HKEY_CLASSES_ROOT HKEY_CURRENT_USER,但工作在内核模式下的驱动程序不属于任何一个用户,因此不能直接访问这两个根键。

如果ZwCreateKey 指定的项不存在,则会直接创建该项,同时由函数的Disposition参数返回REG_CREATED_NEW_KEY;如果指定项已经存在了,则 Disposition返回值REG_OPENED_EXISTING_KEYDDK同样提供了一个ZwOpenKey函数用以简化打开注册表的操作。同时DDK还提供一系列以Rtl 开头的运行时函数,它们可以是对Zw系列函数的封装,可以有效地简化对注册表的操作过程。

wps_clip_image-6573

wps_clip_image-25020

Zw系统注册相关函数

http://msdn.microsoft.com/en-us/library/ff566425%28VS.85%29.aspx

wps_clip_image-10646

Rtl系统注册相关函数

http://msdn.microsoft.com/en-us/library/ff561822%28VS.85%29.aspx

1)读写注册表

注册表是以二元形式存储的,即键名键值,通过键名来设置键值,其中键值分为多种情况。

wps_clip_image-11238

表 注册键类型

通过ZwSetValueKe y 函数添加或修改注册表键值,通过ZwQueryValueKe y

数查询相关键值。

2)枚举

枚举注册表通常分两种情况:枚举一个注册表项的所有子项和枚举一个注册表项的所有子键。

枚举子项使用ZwQueryKey(注意不是ZwQueryValueKey)和ZwEnumerateKe y配合完成,枚举子键使用ZwQueryKeyZwEnumerateValueKey配合完成。

我们以枚举子项来说明思路,首先利用ZwQueryKey获得某项究竟有多少个子项,然后利用ZwEnumerateKe y来获取指定子项的详细信息,这个过程是通过一个子项索引(index)来完成的。在使用ZwQueryKey时,可以将参数KeyInformationClass指定为KeyFullInformation,它对应KEY_FULL_INFORMATION结构中的SubKeys指明了该项中有多少子项。

3)一个例子如下

代码
1 /************************************************************************
2
3 * 函数名称:RegEnumTest
4
5 * 功能描述:测试读取注册表
6
7 * 参数列表:
8
9 * 返回 值:返回状态
10
11 *************************************************************************/
12
13 NTSTATUS
14
15 RegEnumTest()
16
17 {
18
19 UNICODE_STRING ustrRegString;
20
21 UNICODE_STRING ustrKeyName;
22
23 HANDLE hRegister;
24
25 ULONG ulSize, i = 0;
26
27 OBJECT_ATTRIBUTES obj_attrib;
28
29 NTSTATUS status;
30
31 PKEY_FULL_INFORMATION pfi;
32
33 PKEY_BASIC_INFORMATION pbi;
34
35  // 初始化
36  
37 RtlInitUnicodeString(&ustrRegString,L"\\Registry\\Machine\\SOFTWARE\\ODBC\\ODBC.INI");
38
39 InitializeObjectAttributes(&obj_attrib,
40
41  &ustrRegString,
42
43 OBJ_CASE_INSENSITIVE,
44
45 NULL,
46
47 NULL);
48
49  // 打开注册表
50
51 status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &obj_attrib);
52
53 if (NT_SUCCESS(status))
54
55 {
56
57 KdPrint(("[Test] ZwOpenKey %wZ Success!", ustrRegString));
58
59 }
60
61 // 第一次调用是为了获取需要的长度
62
63 ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize);
64
65 pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);
66
67 // 第二次调用是为了获取数据
68
69 ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize);
70
71 for (i = 0; i < pfi->SubKeys; i++)
72
73 {
74
75 // 获取第i 个子项的长度
76
77 ZwEnumerateKey(hRegister, i, KeyBasicInformation, NULL, 0, &ulSize);
78
79 pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);
80
81 // 获取第i 个子项的数据
82
83 ZwEnumerateKey(hRegister, i, KeyBasicInformation, pbi, ulSize, &ulSize);
84
85 ustrKeyName.Length = (USHORT)pbi->NameLength;
86
87 ustrKeyName.Buffer = pbi->Name;
88
89 KdPrint(("[Test] The %d SubItem Name : %wZ.\n", i, &ustrKeyName));
90
91 // 释放内存
92
93 ExFreePool(pbi);
94
95 }
96
97 ExFreePool(pfi);
98
99 ZwClose(hRegister);
100
101 return STATUS_SUCCESS;
102
103 }

4)读与写

一般使用ZwQueryValueKey来读取注册表中键的值。要注意的是注册表中的值可能有多种数据类型。而且长度也是没有定数的。为此,在读取过程中,就可能要面对很多种可能的情况。

http://msdn.microsoft.com/en-us/library/ff567069%28VS.85%29.aspx

其参数中:

KeyValueInformationClass:本次查询所需要查询的信息类型。这有如下的三种可能。

KeyValueBasicInformation:获得基础信息,包含值名和类型。

KeyValueFullInformation:获得完整信息。包含值名、类型和值的数据。

KeyValuePartialInformation:获得局部信息。包含类型和值数据。

使用KeyValuePartialInformation最常见。

KeyValueInformation:当KeyValueInformationClass被设置为KeyValuePartialInformation时,KEY_VALUE_PARTIAL_INFORMATION结构将被返回到这个指针所指内存中。

示例代码:

代码
1 // 要读取的值的名字
2
3 UNICODE_STRING my_key_name =
4
5 RTL_CONSTANT_STRING(L”SystemRoot”);
6
7 // 用来试探大小的key_infor
8
9 KEY_VALUE_PARTIAL_INFORMATION key_infor;
10
11 // 最后实际用到的key_infor指针。内存分配在堆中
12
13 PKEY_VALUE_PARTIAL_INFORMATION ac_key_infor;
14
15 ULONG ac_length;
16
17 ……
18
19 // 前面已经打开了句柄my_key,下面如此来读取值:
20
21 status = ZwQueryValueKey(
22
23 my_key,
24
25 &my_key_name,
26
27 KeyValuePartialInformation,
28
29 &key_infor,
30
31 sizeof(KEY_VALUE_PARTIAL_INFORMATION),
32
33 &ac_length);
34
35 if(!NT_SUCCESS(status) &&
36
37 status != STATUS_BUFFER_OVERFLOW &&
38
39 status != STATUS_BUFFER_TOO_SMALL)
40
41 {
42
43 // 错误处理
44
45
46
47 }
48
49 // 如果没失败,那么分配足够的空间,再次读取
50
51 ac_key_infor = (PKEY_VALUE_PARTIAL_INFORMATION)
52
53 ExAllocatePoolWithTag(NonpagedPool,ac_length ,MEM_TAG);
54
55 if(ac_key_infor == NULL)
56
57 {
58
59 stauts = STATUS_INSUFFICIENT_RESOURCES;
60
61 // 错误处理
62
63
64
65 }
66
67 status = ZwQueryValueKey(
68
69 my_key,
70
71 &my_key_name,
72
73 KeyValuePartialInformation,
74
75 ac_key_infor,
76
77 ac_length,
78
79 &ac_length);
80
81 // 到此为止,如果status为STATUS_SUCCESS,则要读取的数据已经
82
83 // 在ac_key_infor->Data中。请转换为UNICODE_STRING
84
85 ……

写:

一般使用函数ZwSetValueKey

http://msdn.microsoft.com/en-us/library/ff567109%28VS.85%29.aspx

其中的TileIndex参数请始终填入0

Data是要写入的数据的开始地址,而DataSize是要写入的数据的长度。由于Data是一个空指针,因此,Data可以指向任何数据。 ZwSetValueKey执行时:如果Value已经存在,则写入覆盖,否则新建一个。下面的例子写入一个名字为“Test”,而且值为“My Test Value”的字符串值。假设my_key是一个已经打开的子键的句柄。

UNICODE_STRING name = RTL_CONSTANT_STRING(LTest);

PWCHAR value = { LMy Test Value};

// 数据长度要将字符串长度加上1,把最后一个空结束符

status = ZwSetValueKey(my_key,

&name,0,REG_SZ,value,(wcslen(value)+1)*sizeof(WCHAR));

if(!NT_SUCCESS(status))

{

// 错误处理

……

}

posted @ 2010-10-18 22:02  浪里飞  阅读(3946)  评论(0编辑  收藏  举报