RtlQueryRegistryValues 函数 (wdm.h)

RtlQueryRegistryValues 例程允许调用方通过单个调用从注册表子树查询多个值。

语法

NTSYSAPI NTSTATUS RtlQueryRegistryValues(
  [in]           ULONG                     RelativeTo,
  [in]           PCWSTR                    Path,
  [in, out]      PRTL_QUERY_REGISTRY_TABLE QueryTable,
  [in, optional] PVOID                     Context,
  [in, optional] PVOID                     Environment
);

参数

[in] RelativeTo

指定 路径 是绝对注册表路径还是相对于预定义路径,如下所示。

价值 意义
RTL_REGISTRY_ABSOLUTE 路径是绝对注册表路径。
RTL_REGISTRY_CONTROL 路径相对于 \Registry\Machine\System\CurrentControlSet\Control
RTL_REGISTRY_DEVICEMAP 路径相对于 \Registry\Machine\Hardware\DeviceMap
RTL_REGISTRY_SERVICES 路径相对于 \Registry\Machine\System\CurrentControlSet\Services
RTL_REGISTRY_USER 路径相对于 \Registry\User\CurrentUser。 (对于系统进程,这是 \User\。默认.)
RTL_REGISTRY_WINDOWS_NT 路径相对于 \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion

RelativeTo 值可以通过按位 ORing 来修改它,并使用以下标志之一。

意义
RTL_REGISTRY_OPTIONAL 指定此参数引用的键和 Path 参数是可选的。
RTL_REGISTRY_HANDLE 指定 Path 参数实际上是要使用的注册表句柄。

[in] Path

指向绝对注册表路径或相对于 RelativeTo 参数指定的已知位置的路径的指针。 请注意,此类路径中的键名称必须已知给调用方,包括路径中的最后一个键。 如果指定了RTL_REGISTRY_HANDLE标志,则此参数是一个注册表句柄,用于直接查询已打开的密钥。

[in, out] QueryTable

指向调用方感兴趣的一个或多个值名称和子项名称的表的指针。 每个表项都包含调用方提供的 QueryRoutine 函数的地址,该函数将针对注册表中存在的每个值名称调用。 该表必须以 NULL 表项终止,该表是具有 NULLQueryRoutine 成员的表项,以及 NULLName 成员。 查询表条目的结构定义如下:

typedef struct _RTL_QUERY_REGISTRY_TABLE {
    PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
    ULONG Flags;
    PWSTR Name;
    PVOID EntryContext;
    ULONG DefaultType;
    PVOID DefaultData;
    ULONG DefaultLength;
} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;

如果调用方为 QueryTable 参数指向的查询表分配存储,则调用方负责在 RtlQueryRegistryValues 调用返回后释放此存储。

QueryRoutine

使用注册表值的名称、类型、数据和数据长度调用的 QueryRoutine 函数的地址。 如果此成员和 名称 成员都 NULL,则它将标记表的末尾。

QueryRoutine 函数声明如下:

NTSTATUS
QueryRoutine (
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    );

有关详细信息,请参阅 QueryRoutine

标志

用于控制如何解释 RTL_QUERY_REGISTRY_TABLE 结构的剩余成员的标志。 为此成员定义了以下标志位。

价值 意义
RTL_QUERY_REGISTRY_SUBKEY 此表项的 名称 是注册表项的另一个路径,下表条目都适用于该键,而不是由 Path 参数指定的键。 焦点更改将持续到表的末尾或看到另一个RTL_REGISTRY_SUBKEY或RTL_QUERY_REGISTRY_TOPKEY条目。 每个此类条目必须指定一个相对于调用 RtlQueryRegistryValues中指定的 Path 的路径。
RTL_QUERY_REGISTRY_TOPKEY 将当前注册表项句柄重置为由 RelativeToPath 参数指定的原始注册表项句柄。 这在使用 RTL_QUERY_REGISTRY_SUBKEY 标志降到子项后返回到原始节点非常有用。
RTL_QUERY_REGISTRY_REQUIRED 指定如果 defaultType = REG_NONE,则必须存在此注册表值;否则,如果未找到,RtlQueryRegistryValues 立即退出状态代码STATUS_OBJECT_NAME_NOT_FOUND。 如果 名称 成员 NULL 且当前键没有子项,或者如果 Name 指定不存在的子项,则会出现此退出。 (如果未指定此标志,则如果未为非NULLName找到匹配项,则例程将使用 DefaultValue 成员作为值。当 名称NULL 且当前键没有子项时,例程只会跳过该表项。
RTL_QUERY_REGISTRY_NOVALUE 指定即使此表项没有 名称,所有调用方都希望是回调:也就是说,调用方不想枚举当前键下的所有值。 QueryRoutine 使用 ValueDataNULL 进行调用,ValueTypeREG_NONE,ValueLength为零。
RTL_QUERY_REGISTRY_NOEXPAND 对于REG_EXPAND_SZ或REG_MULTI_SZ类型的注册表值,此标志将替代默认行为,即在调用 QueryRoutine 例程之前预处理注册表值。 默认情况下,RtlQueryRegistryValues 扩展REG_EXPAND_SZ值中的环境变量引用,并在单独的 QueryRoutine 调用中枚举REG_MULTI_SZ值中的每个以 null 结尾的字符串,以便字符串显示为具有相同 ValueName的REG_SZ值。 如果设置了此标志,QueryRoutine 从注册表接收原始REG_EXPAND_SZ或REG_MULTI_SZ值。 有关这些值的数据格式的详细信息,请参阅 KEY_VALUE_BASIC_INFORMATION
RTL_QUERY_REGISTRY_DIRECT 不使用 QueryRoutine 成员(并且必须 NULL),EntryContext 指向缓冲区来存储值。 如果调用方设置此标志,调用方应额外设置RTL_QUERY_REGISTRY_TYPECHECK标志,以防止缓冲区溢出。 有关详细信息,请参阅“备注”部分。
RTL_QUERY_REGISTRY_TYPECHECK 将此标志与RTL_QUERY_REGISTRY_DIRECT标志一起使用,验证存储注册表值的 REG_XXX 类型是否与调用方预期的类型匹配。 如果类型不匹配,调用将失败。 有关详细信息,请参阅“备注”部分。
RTL_QUERY_REGISTRY_DELETE 此标志用于在查询值键后将其删除。

从 Windows 2000 开始,为上表中的所有标志位提供收件箱支持,但RTL_QUERY_REGISTRY_TYPECHECK除外。 从 Windows 8 开始,对RTL_QUERY_REGISTRY_TYPECHECK的收件箱支持可用。 对于早期版本的 Windows,通过 Windows 更新提供对RTL_QUERY_REGISTRY_TYPECHECK的支持。 有关详细信息,请参阅“备注”。

名字

这是调用方查询的值的名称。 如果 名称NULL,则为此表项指定的 QueryRoutine 函数将针对与当前注册表项关联的所有值调用。 如果设置了RTL_QUERY_REGISTRY_DIRECT标志,则必须提供 名称 的非NULL 值。

EntryContext

如果设置了RTL_QUERY_REGISTRY_DIRECT标志,则这是指向缓冲区的指针,用于存储此键的查询操作的结果。 否则,此值作为 QueryRoutineEntryContext 参数传递。

DefaultType

如果找不到匹配键且未指定RTL_QUERY_REGISTRY_REQUIRED标志,则此成员的最小有效字节指定要返回的数据REG_XXX 类型。 为无默认类型指定REG_NONE。 如果设置了RTL_QUERY_REGISTRY_TYPECHECK标志,则此成员最重要的字节指定调用方期望的存储注册表值的 REG_XXX 类型。 此成员的位 8 到 23 是保留的,应为零。

DefaultData

如果未找到匹配键且未指定RTL_QUERY_REGISTRY_REQUIRED标志,则指向要返回的默认值的指针。 如果 defaultType = REG_NONE,则忽略此成员。 否则,DefaultData 指向的数据类型应符合由 DefaultType 成员指定的注册表值类型。 有关注册表值类型的详细信息,请参阅 KEY_VALUE_BASIC_INFORMATION类型 参数的定义。

DefaultLength

指定 defaultData 成员 长度(以字节为单位)。 如果 defaultType 为 REG_SZ、REG_EXPAND_SZ 或 REG_MULTI_SZ,则调用方可以选择指定零来指示 RtlQueryRegistryValues 应根据默认数据值计算长度。 如果 DefaultType = REG_NONE,则忽略此成员。

[in, optional] Context

指定每次调用查询Routine 时作为 QueryRoutine 函数的 Context 参数传递的值。

[in, optional] Environment

指向在REG_EXPAND_SZ注册表值中展开变量值时使用的环境的指针,或 NULL 指针(可选)。

返回值

RtlQueryRegistryValues 返回 NTSTATUS 代码。 可能的返回值包括:

返回代码 描述
STATUS_SUCCESS 已成功处理整个查询表。
STATUS_INVALID_PARAMETER 处理以无效表条目结尾的查询表。 如果指定的标志要求 QueryRoutineName 成员为非NULL,但提供了 NULL 值,则表项可能无效。
STATUS_OBJECT_NAME_NOT_FOUND Path 参数与有效键不匹配,或者查询表的处理以具有RTL_QUERY_REGISTRY_REQUIRED标志集且找不到匹配键的条目终止。 如果 Name 成员 NULL 且当前键没有子项,或者如果 Name 指定不存在的子项,则会出现这种情况。
STATUS_BUFFER_TOO_SMALL 设置RTL_QUERY_REGISTRY_DIRECT标志,EntryContext 指定的缓冲区太小,无法保存键值数据。
STATUS_OBJECT_TYPE_MISMATCH 设置RTL_QUERY_REGISTRY_TYPECHECK标志,并且存储的注册表值的类型与调用方预期的类型不匹配。

RtlQueryRegistryValues 如果表项的 QueryRoutine 函数返回 NTSTATUS 错误代码,并返回该错误代码作为其结果,则也会终止对表的处理。 (有一个例外:如果 QueryRoutine 返回STATUS_BUFFER_TOO_SMALL,则忽略错误代码。

言论

调用方指定初始键路径和表。 该表包含一个或多个条目,用于描述调用方感兴趣的键值和子项名称。 该表由 NULLQueryRoutine 成员和 NULLName 成员的条目终止。 表必须从非分页池分配。

内核模式驱动程序必须指定RTL_QUERY_REGISTRY_NOEXPAND标志,以防止调用环境变量例程。 这些例程不安全,因此内核模式驱动程序不应使用它们。

谨慎

如果使用RTL_QUERY_REGISTRY_DIRECT标志,则不受信任的用户模式应用程序可能会导致缓冲区溢出。 如果驱动程序使用此标志读取分配了错误的类型的注册表值,则可能会出现缓冲区溢出。 在所有情况下,使用RTL_QUERY_REGISTRY_DIRECT标志的驱动程序应使用RTL_QUERY_REGISTRY_TYPECHECK标志来防止此类溢出。

如果在表项中设置了RTL_QUERY_REGISTRY_TYPECHECK标志,则调用方必须在表项的 32 位 DefaultType 成员的 8 位中指定预期的 REG_XXX 类型。 如以下代码示例所示,定义为 24 的 RTL_QUERY_REGISTRY_TYPECHECK_SHIFT 常量可用作将预期的 REG_XXX 类型放置在 DefaultType 成员的 8 个 MSB 中所需的移位计数。

RTL_QUERY_REGISTRY_TABLE QueryRegTable[2];    
...
QueryRegTable[0].DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
...
QueryRegTable[1].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
...

从 Windows 8 开始,如果 RtlQueryRegistryValues 调用访问不受信任的 hive,并且调用方为此调用设置RTL_QUERY_REGISTRY_DIRECT标志,则调用方必须额外设置RTL_QUERY_REGISTRY_TYPECHECK标志。 来自用户模式的调用违反此规则会导致异常。 来自内核模式的调用违反此规则会导致0x139 bug 检查(KERNEL_SECURITY_CHECK_FAILURE)。

仅信任系统配置单元。 访问系统配置单元的 RtlQueryRegistryValues 调用不会导致异常或 bug 检查是否已设置RTL_QUERY_REGISTRY_DIRECT标志,并且未设置RTL_QUERY_REGISTRY_TYPECHECK标志。 但是,最佳做法是,如果设置了RTL_QUERY_REGISTRY_DIRECT标志,应始终设置RTL_QUERY_REGISTRY_TYPECHECK标志。

同样,在 Windows 8 之前的 Windows 版本中,最佳做法是,RtlQueryRegistryValues 调用来设置RTL_QUERY_REGISTRY_DIRECT标志应额外设置RTL_QUERY_REGISTRY_TYPECHECK标志。 但是,如果不遵循此建议,则不会导致异常或 bug 检查。

下面是系统配置单元的列表:

  • \REGISTRY\MACHINE\HARDWARE

  • \REGISTRY\MACHINE\SOFTWARE

  • \REGISTRY\MACHINE\SYSTEM

  • \REGISTRY\MACHINE\SECURITY

  • \REGISTRY\MACHINE\SAM

可通过适用于 Windows 7、Windows Vista、Windows Server 2003 和 Windows XP 的 Windows 更新获取对RTL_QUERY_REGISTRY_TYPECHECK标志的支持。 有关此更新的详细信息,请参阅 Windows 内核中的 漏洞可能允许提升权限(2393802)。 在没有此更新的这些操作系统版本中,调用方可以使用RTL_QUERY_REGISTRY_TYPECHECK标志。 但是,RtlQueryRegistryValues 例程忽略此标志。

从 Windows 驱动程序工具包 (WDK) 8 开始,RTL_QUERY_REGISTRY_TYPECHECK标志在 Wdm.h 头文件中定义,如下所示:

#define RTL_QUERY_REGISTRY_TYPECHECK 0x00000100

如果条目未指定RTL_QUERY_REGISTRY_DIRECT标志,RtlQueryRegistryValues 使用指定的 QueryRoutine 函数向调用方报告值名称、类型、数据和数据长度(以字节为单位)。 如果条目的 名称 成员 NULLRtlQueryRegistryValues 报告键的每个直接子项。 如果键类型为REG_MULTI_SZ且未指定RTL_QUERY_REGISTRY_NOEXPAND标志,则例程调用 QueryRoutine 针对每个字符串单独调用;否则,例程将其报告为单个值。 如果条目指定RTL_QUERY_REGISTRY_DIRECT标志,RtlQueryRegistryValues 将键的值存储在条目 的 entryContext 成员指向的缓冲区中。 返回的数据的格式如下所示。

键数据类型 如何返回数据
以 null 结尾的 Unicode 字符串(例如REG_SZ、REG_EXPAND_SZ)。 EntryContext 必须指向初始化的 UNICODE_STRING 结构。 如果 UNICODE_STRINGBuffer 成员 NULL,则例程将为字符串数据分配存储。 否则,它将字符串数据存储在 Buffer 指向的缓冲区中。
REG_MULTI_SZ 必须为此键数据类型指定RTL_QUERY_REGISTRY_NOEXPAND标志。 EntryContext 指向初始化的 UNICODE_STRING 结构。 例程将键值存储为单个字符串值。 字符串中的每个单个组件都以零结尾。 如果 UNICODE_STRINGBuffer 成员 NULL,则例程将为字符串数据分配存储。 否则,它将字符串数据存储在 Buffer 指向的缓冲区中。
大小(以字节为单位)的非字符串数据 <= sizeof(ULONG) 该值存储在 EntryContext指定的内存位置。
具有大小的非字符串数据(以字节为单位)>sizeof(ULONG) EntryContext 指向的缓冲区必须以带符号 LONG 值开头。 值的大小必须指定缓冲区的大小(以字节为单位)。 如果值的符号为负值,RtlQueryRegistryValues 将仅存储键值的数据。 否则,它将使用缓冲区中的第一个 ULONG 来记录值长度,以字节为单位,第二个 ULONG 来记录值类型,其余的缓冲区用于存储值数据。

如果在查询表的任何处理阶段发生错误,RtlQueryRegistryValues 停止处理表并返回错误状态。

有关可能的 REG_XXX 值的说明,请参阅 ZwSetValueKey

要求

要求 价值
目标平台 普遍
标头 wdm.h (包括 Wdm.h、Ntddk.h、Ntifs.h)
Ntoskrnl.lib
DLL Ntoskrnl.exe
IRQL PASSIVE_LEVEL

另请参阅

QueryRoutine

RtlZeroMemory

UNICODE_STRING

ZwEnumerateKey

ZwEnumerateValueKey

ZwSetValueKey