六、
在驱动中操作注册表
注册表是Windows的核心,日常的许多操作其实最终都是转化成了对注册表的操作,我们经常需要利用注册表达到一些特殊的效果,例如实现自启动等。
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
19
6.1 创建、打开注册表
和文件操作类似,在操作注册表之前需要首先打开注册表,获得一个句柄,这可以通过函数ZwCreateKey完成。
与ZwCreateFile函数类似,它通过一个OBJECT_ATTRIBUTES获得需要创建或打开的路径信息,但在内核中这个路径与用户模式下不相同,如下表所示:
表6-1 注册表中路径的写法
应用程序中对应的子键
驱动编程中的路径写法
HEKY_LOCAL_MACHINE
\Registry\Machine
HEKY_USER
\Registry\User
HEKY_CLASSES_ROOT
没有对应的路径
HEKY_CURRENT_USER
没有简单的对应路径,但是可以求得
实际上,因为用户模式下的应用程序总是由某个“当前用户”打开的,因此在用户模式下可以直接访问HKEY_CLASSES_ROOT和HKEY_CURRENT_USER,但工作在内核模式下的驱动程序不属于任何一个用户,因此不能直接访问这两个根键。
如果ZwCreateKey指定的项不存在,则会直接创建该项,同时由函数的Disposition参数返回REG_CREATED_NEW_KEY;如果指定项已经存在了,则Disposition返回值REG_OPENED_EXISTING_KEY。
DDK同样提供了一个ZwOpenKey函数用以简化打开注册表的操作。同时DDK还提供一系列以Rtl开头的运行时函数,它们可以是对Zw系列函数的封装,可以有效地简化对注册表的操作过程。如下面两张表所示:
表6-2 注册表相关Zw系列函数
函数
功能
ZwCreateKey
创建打开指定的注册表项
ZwOpenKey
打开注册表项(ZwCreateKey的简化)
ZwSetValueKey
添加或修改指定的键值
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
20
ZeQueryKey
查询指定的项
ZwQueryValueKey
查询指定的键值
ZwEnumerateValueKey
枚举子项
ZwEnumerateValueKey
枚举子键
ZwDeletekey
删除指定的项
图6-3 注册表相关Rtl系列函数
函数
功能
RtlCreaterRegistryKey
创建注册表项
RtlCheckRegistrykey
检查指定的注册表项是否存在
RtlWriteRegistryValue
写注册表
RtlQueryRegistryValues
读注册表
RtlDeleteRegistryValue
删除指定的键值
6.2 读写注册表
注册表是以二元形式存储的,即“键名”和“键值”,通过键名来设置键值,其中键值分为多种情况,如表6-4所示:
图6-4 键值的分类
分类
描述
REG_BINARY
键值用二进制存储
REG_SZ
键值用宽字符串存储,字符串以\0隔开
REG_EXPAND_SZ
同上,该字符串可扩展
REG_MULTI_SZ
键存储多个字符串,每个字符串以\0隔开
REG_DWORD
键使用4字节存储
REG_QWORD
键使用8字节存储
我们可以通过ZwSetValueKey函数添加或修改注册表键值,通过ZwQueryValueKey函数查询相关键值。这两个函数的使用与ring3没有多大差异,通过DDK的帮助文档可以很容易知道它的用法,因此这里不再赘述。
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
21
6.3 枚举注册表
枚举注册表是一个非常实用的功能,它通常分两种情况:枚举一个注册表项的所有子项和枚举一个注册表项的所有子键。
枚举子项使用ZwQueryKey(注意不是ZwQueryValueKey)和ZwEnumerateKey配合完成,枚举子键使用ZwQueryKey和ZwEnumerateValueKey配合完成。
我们以枚举子项来说明思路,首先利用ZwQueryKey获得某项究竟有多少个子项,然后利用ZwEnumerateKey来获取指定子项的详细信息,这个过程是通过一个子项索引(index)来完成的。
在使用ZwQueryKey时,可以将参数KeyInformationClass指定为KeyFullInformation,它对应KEY_FULL_INFORMATION结构中的SubKeys指明了该项中有多少子项。