第17章:注册表
注册表概述
Windows 注册表(Registry)是一个分层数据库,用于存储系统配置和用户相关信息。从数据持久性角度,注册表数据分为两类:
- 易失性数据(Volatile Data):运行时生成,系统关机后即删除,仅存在于内存中。
- 非易失性数据(Non-volatile Data):持久保存在磁盘文件中,重启后依然存在。
操作注册表的主要工具有 Regedit.exe(GUI 图形界面)、reg.exe(命令行工具)和 PowerShell。微软官方建议:"建议不要使用注册表来存储应用程序或与用户相关的数据",推荐使用 INI、XML、JSON 或 YAML 等文件格式。注册表应主要由 Windows 自身使用。
注册表项(The Hives)
注册表被划分为多个 hive(项,或称配置单元)。从逻辑结构上看,注册表呈现出树状层次结构,但实际上只有两个"真正的"根项(Root Hive):HKEY_USERS 和 HKEY_LOCAL_MACHINE,其余根键均由这两个组合或映射而成。
HKEY_LOCAL_MACHINE (HKLM)
HKEY_LOCAL_MACHINE(简称 HKLM)存储与计算机相关的信息,与具体登录用户无关。其中许多数据对系统启动至关重要,默认情况下仅管理员(Administrator)可修改。关键子键包括:
SOFTWARE:应用程序存储与用户无关的配置信息,常见的组织模式为 SOFTWARE\[公司名称]\[产品名称]\[版本号]。
SYSTEM:系统核心参数,包含以下重要子键:
SYSTEM\CurrentControlSet\Services:服务(Service)与设备驱动程序(Device Driver)的信息。SYSTEM\CurrentControlSet\Enum:硬件设备驱动程序的父键。SYSTEM\CurrentControlSet\Control:内核(Kernel)、Smss.exe、csrss.exe、Services.exe 等系统关键进程读取的设置。SYSTEM\BCD00000000:启动配置数据(BCD,Boot Configuration Data)。SYSTEM\SECURITY:本地安全策略(Local Security Policy),默认仅 SYSTEM 账户可访问。SYSTEM\SAM:本地用户和组信息,与 SECURITY 具有相同的访问限制。
SYSTEM\CurrentControlSet实际是指向ControlSet001的符号链接(Symbolic Link),这与 Windows 的"最后一次正确配置"(Last Known Good Configuration)功能有关。SYSTEM\Select键中保存了哪个控制集(Control Set)是"当前"的。
HKLM 的持久化文件存储在 %SystemRoot%\System32\Config\ 目录下:
| 子键 | 对应文件名 |
|---|---|
| SAM | SAM |
| Security | SECURITY |
| Software | SOFTWARE |
| System | SYSTEM |
完整的 hive 文件映射可以在 HKLM\System\CurrentControlSet\Control\hivelist 中找到。
HKEY_USERS (HKU)
HKEY_USERS(简称 HKU)存储所有曾在本地登录过的用户的特定信息。每个用户由其 SID(Security Identifier,安全标识符)唯一标识。其中 .DEFAULT 子键保存新用户的默认配置值。SYSTEM、本地服务(Local Service)、网络服务(Network Service)账户的短 SID 也在 HKU 中。普通用户的 SID 下包含桌面设置、控制台配置、环境变量、键盘布局、打印机设置等信息。
HKEY_CURRENT_USER (HKCU)
HKEY_CURRENT_USER(简称 HKCU)是指向 HKEY_USERS 中当前登录用户对应 SID 子键的链接。其持久化文件为 C:\Users\<用户名>\NtUser.dat。
HKEY_CLASSES_ROOT (HKCR)
HKEY_CLASSES_ROOT(简称 HKCR)由 HKLM\Software\Classes 和 HKCU\Software\Classes 组合而成,其中用户设置会覆盖计算机默认设置。HKCR 包含两类信息:
- 资源管理器外壳数据(Explorer Shell Data):文件类型关联(File Type Association)和外壳扩展(Shell Extension)。例如搜索
.txt会找到默认值txtfile,该键下的shell\open\command值为%SystemRoot%\System32\NOTEPAD.EXE %1,表示用记事本打开 .txt 文件。 - COM 相关信息:将在第 21 章详细讨论。
HKEY_CURRENT_CONFIG (HKCC)
HKEY_CURRENT_CONFIG(简称 HKCC)是指向 HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\Current 的链接,包含当前硬件配置文件的信息。
HKEY_PERFORMANCE_DATA
HKEY_PERFORMANCE_DATA 在 Regedit.exe 中不可见,是使用性能计数器(Performance Counter)的旧有机制。从 Windows 2000 起,已有更新的 API 替代了它。
32 位特定的注册表项
在 64 位 Windows 系统上,注册表的某些部分必须对 32 位进程和 64 位进程加以区分。当 32 位进程尝试打开 HKLM\Software 时,会被透明重定向到 HKLM\Software\Wow6432Node。这种机制称为注册表虚拟化(Registry Virtualization)。
其他重定向示例:
HKCR\CLSID→HKCR\Wow6432Node\CLSID(影响进程内 DLL COM 组件)HKCR\AppID、HKCR\Interface、HKCR\TypeLib同样被重定向
32 位进程可通过在 RegCreateKeyEx 或 RegOpenKeyEx 中指定 KEY_WOW64_64KEY 标志选择不进行重定向,直接访问 64 位视图;64 位进程也可通过 KEY_WOW64_32KEY 标志访问 32 位视图。
操作键和值
打开键:RegOpenKeyEx
打开一个已存在的注册表键:
LSTATUS RegOpenKeyEx(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpSubKey,
_In_opt_ DWORD ulOptions,
_In_ REGSAM samDesired,
_Out_ PHKEY phkResult);参数说明:
- hKey:已打开的键的句柄,可以是预定义根键(如
HKEY_CURRENT_USER)或其他 API 返回的句柄。 - lpSubKey:要打开的子键名称,不区分大小写。
- ulOptions:保留参数,通常设为 0;也可指定
REG_OPTION_OPEN_LINK以打开符号链接本身而非其目标。 - samDesired:访问掩码(Access Mask),常用值包括
KEY_READ(查询和枚举)和KEY_WRITE(写入和修改)。 - phkResult:输出参数,接收打开的键的句柄。
函数的返回值是错误码(如 ERROR_SUCCESS 即 0 表示成功),调用失败时不应使用 GetLastError 获取错误信息。使用完毕后需调用 RegCloseKey 关闭句柄。
创建键:RegCreateKeyEx
创建或打开一个注册表键:
LSTATUS RegCreateKeyEx(
_In_ HKEY hKey, _In_ LPCTSTR lpSubKey,
_Reserved_ DWORD Reserved, _In_opt_ LPTSTR lpClass,
_In_ DWORD dwOptions, _In_ REGSAM samDesired,
_In_opt_ CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_Out_ PHKEY phkResult, _Out_opt_ LPDWORD lpdwDisposition);参数说明:
- hKey / lpSubKey:与
RegOpenKeyEx相同。如果中间的子键不存在,该函数会自动创建。 - dwOptions:控制键的行为:
REG_OPTION_NON_VOLATILE:默认值,键持久保存到磁盘。REG_OPTION_VOLATILE:键仅存在于内存中,重启后消失。REG_OPTION_CREATE_LINK:创建符号链接而非普通键。REG_OPTION_BACKUP_RESTORE:使用备份/恢复权限打开,此时忽略samDesired参数。
- lpSecurityAttributes:可选的安全属性,指定键的安全描述符。
- lpdwDisposition:输出参数,指示是新建还是打开已有键:
REG_CREATED_NEW_KEY:键是新创建的。REG_OPENED_EXISTING_KEY:键已存在,被打开。
读取值:RegQueryValueEx
从指定键中读取一个命名值的数据:
LSTATUS RegQueryValueEx(
_In_ HKEY hKey, _In_opt_ LPCTSTR lpValueName,
_Reserved_ LPDWORD lpReserved, _Out_opt_ LPDWORD lpType,
_Out_ LPBYTE lpData, _Inout_opt_ LPDWORD lpcbData);参数说明:
- lpValueName:要读取的值的名称。传入
NULL或空字符串时读取默认值(Default Value)。 - lpType:输出参数,返回值的类型(可为
NULL忽略)。 - lpData:接收数据的缓冲区。可先传入
NULL以获取所需缓冲区大小。 - lpcbData:输入时为缓冲区大小,输出时为实际数据大小。
注册表值支持的数据类型如下:
| 常量 | 值 | 描述 |
|---|---|---|
REG_NONE | 0 | 无类型 |
REG_SZ | 1 | 以 NULL 结尾的 Unicode 字符串 |
REG_EXPAND_SZ | 2 | 含未展开环境变量(用 % 包围)的 Unicode 字符串 |
REG_BINARY | 3 | 任意二进制数据 |
REG_DWORD | 4 | 32 位无符号整数(小端序,Little-Endian) |
REG_DWORD_BIG_ENDIAN | 5 | 32 位无符号整数(大端序,Big-Endian) |
REG_LINK | 6 | 符号链接(Unicode 字符串) |
REG_MULTI_SZ | 7 | 多个以 NULL 分隔的 Unicode 字符串,以双 NULL 结尾 |
REG_RESOURCE_LIST | 8 | 仅内核模式使用 |
REG_FULL_RESOURCE_DESCRIPTOR | 9 | 仅内核模式使用 |
REG_RESOURCE_REQUIREMENTS_LIST | 10 | 仅内核模式使用 |
REG_QWORD | 11 | 64 位无符号整数(小端序) |
读取示例——获取控制台窗口中使用的字体名称:
HKEY hKey;
::RegOpenKeyEx(HKEY_CURRENT_USER, L"Console", 0, KEY_READ, &hKey);
DWORD type, size;
::RegQueryValueEx(hKey, L"FaceName", nullptr, &type, nullptr, &size);
assert(type == REG_SZ);
auto value = std::make_unique<BYTE[]>(size);
::RegQueryValueEx(hKey, L"FaceName", nullptr, &type, value.get(), &size);
::RegCloseKey(hKey);
printf("Value: %ws\n", (PCWSTR)value.get());增强读取:RegGetValue
RegGetValue 是对 RegQueryValueEx 的增强版本,通过 dwFlags 参数可以直接限制允许的数据类型:
LSTATUS RegGetValue(
_In_ HKEY hkey, _In_opt_ LPCSTR lpSubKey, _In_opt_ LPCSTR lpValue,
_In_ DWORD dwFlags, _Out_opt_ LPDWORD pdwType,
_Out_ PVOID pvData, _Inout_opt_ LPDWORD pcbData);常用的 dwFlags 标志:
RRF_RT_REG_SZ/RRF_RT_REG_EXPAND_SZ/RRF_RT_REG_BINARY/RRF_RT_REG_DWORD/RRF_RT_REG_MULTI_SZ/RRF_RT_REG_QWORD:仅接受对应类型。RRF_RT_DWORD:接受REG_DWORD或REG_BINARY组合。RRF_RT_QWORD:接受REG_QWORD或REG_BINARY组合。RRF_RT_ANY:不限制类型。RRF_SUBKEY_WOW6464KEY/RRF_SUBKEY_WOW6432KEY:控制 WoW64 重定向。RRF_NOEXPAND:不展开REG_EXPAND_SZ中的环境变量。RRF_ZEROONFAILURE:失败时将缓冲区清零。
写入值:RegSetValueEx
向指定键写入一个命名值:
LSTATUS RegSetValueEx(
_In_ HKEY hKey, _In_opt_ LPCTSTR lpValueName,
_Reserved_ DWORD Reserved, _In_ DWORD dwType,
_In_ CONST BYTE* lpData, _In_ DWORD cbData);注意事项:
hKey需要具有KEY_SET_VALUE访问掩码。- 字符串类型(
REG_SZ、REG_EXPAND_SZ)必须以 NULL 结尾;REG_MULTI_SZ需以双 NULL 结尾。 cbData始终以字节为单位,对于字符串类型,大小必须包含终止 NULL 字符。
写入示例——修改控制台字体名称:
HKEY hKey;
::RegOpenKeyEx(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE, &hKey);
WCHAR value[] = L"Arial";
::RegSetValueEx(hKey, L"FaceName", 0, REG_SZ, (const BYTE*)value, sizeof(value));
::RegCloseKey(hKey);替代函数 RegSetKeyValue 允许直接指定子键路径进行写入,无需预先显式打开子键,简化了调用流程。
删除键和值
删除键:RegDeleteKey / RegDeleteKeyEx
LSTATUS RegDeleteKey(_In_ HKEY hKey, _In_ LPCTSTR lpSubKey);
LSTATUS RegDeleteKeyEx(_In_ HKEY hKey, _In_ LPCTSTR lpSubKey,
_In_ REGSAM samDesired, _Reserved_ DWORD Reserved);注意事项:
- 键的安全描述符决定删除权限,而非打开句柄时使用的访问掩码。
- 键被标记为删除(Marked for Deletion),但直到所有打开的句柄被关闭后才真正从磁盘移除。
- 只能删除不含任何子键的键,否则返回
ERROR_ACCESS_DENIED (5)。
删除包含子键的树:RegDeleteTree
LSTATUS RegDeleteTree(_In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey);该函数递归删除指定键下所有子键和值。hKey 需要具有 DELETE、KEY_ENUMERATE_SUB_KEYS、KEY_QUERY_VALUE 和 KEY_SET_VALUE 权限。当 lpSubKey 为 NULL 时,删除 hKey 下的所有键和值。
删除值:RegDeleteValue / RegDeleteKeyValue
LSTATUS RegDeleteValue(_In_ HKEY hKey, _In_opt_ LPCTSTR lpValueName);
LSTATUS RegDeleteKeyValue(_In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey,
_In_opt_ LPCTSTR lpValueName);hKey 需要具有 KEY_SET_VALUE 访问掩码。在性能方面,RegDeleteValue 优于 RegDeleteKeyValue,因为后者内部需要额外打开子键。
创建注册表链接
注册表中的链接(Registry Link)是指向其他键的键。典型的例子是 HKLM\System\CurrentControlSet,它是一个指向 ControlSet001 的符号链接。
创建符号链接的步骤:
- 调用
RegCreateKeyEx时指定REG_OPTION_CREATE_LINK标志。 - 在创建的键中写入名为
SymbolicLinkValue、类型为REG_LINK的值。 - 值的路径必须是内核视角的绝对路径,格式如
\REGISTRY\USER\{SID}\...。
获取当前用户 SID,用于构造内核绝对路径:
HANDLE hToken;
::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken);
BYTE buffer[sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE];
DWORD len;
::GetTokenInformation(hToken, TokenUser, buffer, sizeof(buffer), &len);
::CloseHandle(hToken);
auto user = (TOKEN_USER*)buffer;
PWSTR stringSid;
::ConvertSidToStringSid(user->User.Sid, &stringSid);
// 使用 stringSid 构造路径...
::LocalFree(stringSid);构造路径并写入链接(注意 cbData 不含 NULL 终止符):
std::wstring path = L"\\REGISTRY\\USER\\";
path += stringSid;
path += L"\\Control Panel\\Desktop\\Colors";
::RegSetValueEx(hKey, L"SymbolicLinkValue", 0, REG_LINK,
(const BYTE*)path.c_str(), path.size() * sizeof(WCHAR));删除符号链接:不能使用 RegDeleteKey(Ex)(否则会删除链接所指向的目标键),必须使用原生 API NtDeleteKey:
extern "C" int NTAPI NtDeleteKey(HKEY);
#pragma comment(lib, "ntdll")
HKEY hKey;
::RegOpenKeyEx(HKEY_CURRENT_USER, L"DesktopColors", REG_OPTION_OPEN_LINK,
DELETE, &hKey);
::NtDeleteKey(hKey);
::RegCloseKey(hKey);打开现有链接时必须使用 RegOpenKeyEx 并指定 REG_OPTION_OPEN_LINK 标志,否则默认会跟随链接并打开目标键。
枚举键和值
枚举子键:RegEnumKeyEx
LSTATUS RegEnumKeyEx(
_In_ HKEY hKey, _In_ DWORD dwIndex,
_Out_ LPTSTR lpName, _Inout_ LPDWORD lpcchName,
_Reserved_ LPDWORD lpReserved, _Out_ LPTSTR lpClass,
_Inout_opt_ LPDWORD lpcchClass, _Out_opt_ PFILETIME lpftLastWriteTime);参数说明:
- hKey:需要具有
KEY_ENUMERATE_SUB_KEYS访问掩码。 - dwIndex:从 0 开始递增,直到返回
ERROR_NO_MORE_ITEMS。 - lpName / lpcchName:接收键名及其长度。键名最大长度为 255 个字符。
- lpClass / lpcchClass:可选,接收类名。
- lpftLastWriteTime:可选,接收最后修改时间。
枚举值:RegEnumValue
LSTATUS RegEnumValue(
_In_ HKEY hKey, _In_ DWORD dwIndex,
_Out_ LPTSTR lpValueName, _Inout_ LPDWORD lpcchValueName,
_Reserved_ LPDWORD lpReserved, _Out_opt_ LPDWORD lpType,
_Out_opt_ LPBYTE lpData, _Inout_opt_ LPDWORD lpcbData);参数说明:
- hKey:需要具有
KEY_QUERY_VALUE访问掩码。 - dwIndex:从 0 开始递增。
- lpValueName / lpcchValueName:接收值名及其长度,值名最大长度为 16383 个字符。
- lpType:可选,接收值的数据类型。
- lpData / lpcbData:可选,接收值的数据。
获取键的元信息:RegQueryInfoKey
该函数可一次性获取子键数量、值的数量、最大值名长度、最大值数据大小等信息,用于在枚举前预先分配足够大小的缓冲区,含 13 个参数。
完整枚举示例——实现一个递归转储注册表键的 DumpKey 工具函数:
void DumpKey(HKEY hKey, bool dumpKeys, bool dumpValues, bool recurse) {
DWORD nsubkeys, nvalues, maxValueSize, maxValueNameLen;
FILETIME modified;
::RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &nsubkeys, nullptr,
nullptr, &nvalues, &maxValueNameLen, &maxValueSize, nullptr, &modified);
if (dumpValues) {
auto value = std::make_unique<BYTE[]>(maxValueSize);
auto name = std::make_unique<WCHAR[]>(maxValueNameLen + 1);
for (DWORD i = 0; ; i++) {
DWORD cname = maxValueNameLen + 1;
DWORD size = maxValueSize;
DWORD type;
auto error = ::RegEnumValue(hKey, i, name.get(), &cname,
nullptr, &type, value.get(), &size);
if (error == ERROR_NO_MORE_ITEMS) break;
// 显示名称、类型、大小和值
}
}
// 递归枚举子键(略)...
}此外,可借助 GetValueAsString 辅助函数将不同类型的数据转换为可读文本,支持 REG_DWORD、REG_QWORD、REG_SZ、REG_EXPAND_SZ、REG_BINARY 等多种类型。
注册表通知
RegNotifyChangeKeyValue
RegNotifyChangeKeyValue 用于监视注册表键的变化,但存在局限性:它不指明具体是什么发生了变化或变化发生在哪个确切位置。
LSTATUS RegNotifyChangeKeyValue(
_In_ HKEY hKey, _In_ BOOL bWatchSubtree,
_In_ DWORD dwNotifyFilter, _In_opt_ HANDLE hEvent,
_In_ BOOL fAsynchronous);参数说明:
- hKey:必须用
KEY_NOTIFY(即REG_NOTIFY)访问掩码打开。 - bWatchSubtree:
TRUE监控整棵子树(包含所有后代键);FALSE仅监控指定的单个键。 - dwNotifyFilter:指定监控的变化类型,可以是以下标志的组合:
REG_NOTIFY_CHANGE_NAME (1):子键被添加或删除。REG_NOTIFY_CHANGE_ATTRIBUTES (2):键属性发生变化。REG_NOTIFY_CHANGE_LAST_SET (4):最后修改时间变化(值被添加、更改或删除)。REG_NOTIFY_CHANGE_SECURITY (8):安全描述符发生变化。REG_NOTIFY_THREAD_AGNOSTIC (0x10000000):Windows 8+,通知与调用线程无关。
- hEvent:异步模式下使用的事件句柄。
- fAsynchronous:
FALSE时阻塞调用线程直到变化发生;TRUE时立即返回,变化发生时触发hEvent。
同步模式监视示例:
HKEY hKey;
::RegOpenKeyEx(root, path, 0, KEY_NOTIFY, &hKey);
DWORD notifyFlags = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET;
while (ERROR_SUCCESS == ::RegNotifyChangeKeyValue(
hKey, recurse, notifyFlags, nullptr, FALSE)) {
printf("Change occurred.\n");
}
::RegCloseKey(hKey);ETW 注册表监视(RegWatch2 示例)
通过 Windows 事件跟踪(ETW,Event Tracing for Windows)可以获取更详细的注册表活动信息,使用内核提供程序(Kernel Provider)。相比 RegNotifyChangeKeyValue,ETW 能够精确地告诉你哪个键被创建、打开、删除或修改。
核心设置是将 ETW 会话的启用标志设为 EVENT_TRACE_FLAG_REGISTRY:
_properties->EnableFlags = EVENT_TRACE_FLAG_REGISTRY;EventParser 负责解析事件记录,OnEvent 回调根据不同的操作码(Opcode)进行处理:
void OnEvent(PEVENT_RECORD rec) {
EventParser parser(rec);
switch (parser.GetEventHeader().EventDescriptor.Opcode) {
case EVENT_TRACE_TYPE_REGCREATE: printf("Create key"); break;
case EVENT_TRACE_TYPE_REGOPEN: printf("Open key"); break;
case EVENT_TRACE_TYPE_REGDELETE: printf("Delete key"); break;
case EVENT_TRACE_TYPE_REGQUERY: printf("Query key"); break;
case EVENT_TRACE_TYPE_REGSETVALUE: printf("Set value"); break;
// ... 还有更多操作码
}
auto prop = parser.GetProperty(L"KeyName");
if (prop) printf(" %ws", prop->GetUnicodeString());
}该方式需要管理员权限运行。可通过 SetConsoleCtrlHandler 捕获 Ctrl+C 信号以优雅地停止 ETW 会话。
事务性注册表(Transactional Registry)
注册表操作可以作为事务(Transaction)的一部分执行,与文件事务操作配合使用。事务性注册表意味着:打开事务句柄后,在事务范围内进行的所有注册表操作要么全部成功提交(Commit),要么全部回滚(Rollback)。
创建/打开事务性键:RegCreateKeyTransacted / RegOpenKeyTransacted
LSTATUS RegCreateKeyTransacted(
// ... 与 RegCreateKeyEx 相同的参数 ...
_In_ HANDLE hTransaction, _Reserved_ PVOID pExtendedParemeter);
LSTATUS RegOpenKeyTransacted(
// ... 与 RegOpenKeyEx 相同的参数 ...
_In_ HANDLE hTransaction, _Reserved_ PVOID pExtendedParemeter);这两个函数在原有函数的基础上增加了最后两个参数:
- hTransaction:事务句柄,通过
CreateTransaction创建。 - pExtendedParemeter:保留参数,必须传入
NULL。
使用返回的 hKey 句柄进行的所有后续注册表操作都属于该事务的范围。事务的详细内容将在第 9 章展开。
远程注册表(Remote Registry)
RegConnectRegistry
连接到远程计算机的注册表,前提是目标计算机上的远程注册表服务(Remote Registry Service)正在运行(默认为手动启动):
LSTATUS RegConnectRegistry(
_In_opt_ LPCTSTR lpMachineName, // 格式:\\computername
_In_ HKEY hKey, // HKEY_USERS、HKEY_LOCAL_MACHINE 或 HKEY_PERFORMANCE_DATA
_Out_ PHKEY phkResult); // 返回的句柄可像本地句柄一样使用获取远程句柄后,可以执行本地的常规注册表操作。操作完成后使用 RegCloseKey 关闭句柄。
RegConnectRegistryEx
RegConnectRegistryEx 是对 RegConnectRegistry 的扩展版本:
LSTATUS RegConnectRegistryEx(
_In_opt_ LPCTSTR lpMachineName, _In_ HKEY hKey,
_In_ ULONG Flags, _Out_ PHKEY phkResult);目前唯一支持的标志是 REG_SECURE_CONNECTION (1),用于加密 RPC 调用,提升远程连接的安全性。
其他注册表函数
安全管理
LSTATUS RegGetKeySecurity(_In_ HKEY hKey,
_In_ SECURITY_INFORMATION SecurityInformation,
_Out_ PSECURITY_DESCRIPTOR pSecurityDescriptor,
_Inout_ LPDWORD lpcbSecurityDescriptor);
LSTATUS RegSetKeySecurity(_In_ HKEY hKey,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_ PSECURITY_DESCRIPTOR pSecurityDescriptor);这两个函数用于获取和设置注册表键的安全描述符,比通用的 GetSecurityInfo / SetSecurityInfo 使用更简便。
保存和恢复注册表项
RegSaveKey / RegSaveKeyEx——将注册表键保存到文件(专有二进制格式,非 .REG 文本格式):
LSTATUS RegSaveKey(_In_ HKEY hKey, _In_ LPCTSTR lpFile,
_In_opt_ CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes);lpFile 指定的文件不能已存在。可选的安全属性为输出文件提供安全描述符。
扩展版本 RegSaveKeyEx 增加 Flags 参数:
LSTATUS RegSaveKeyEx(_In_ HKEY hKey, _In_ LPCTSTR lpFile,
_In_opt_ CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD Flags);标志取值:
REG_STANDARD_FORMAT (1):原始格式。REG_LATEST_FORMAT (2):最新格式。REG_NO_COMPRESSION (4):不压缩(仅适用于真正的配置单元 hive 文件)。
调用者需要具有 SeBackupPrivilege 权限。
RegRestoreKey——从文件恢复注册表键:
LSTATUS RegRestoreKey(_In_ HKEY hKey, _In_ LPCTSTR lpFile, _In_ DWORD dwFlags);注意事项:
- 保留根键名称,但替换所有其他属性、子键和值。
REG_FORCE_RESTORE (8):强制恢复,即使存在打开的句柄。REG_WHOLE_HIVE_VOLATILE (1):仅在内存中创建,重启后消失。- 调用者需要具有
SeRestorePrivilege权限。
加载和卸载配置单元
RegLoadAppKey——将配置单元加载到不可枚举的不可见根:
LSTATUS RegLoadAppKey(_In_ LPCTSTR lpFile, _Out_ PHKEY phkResult,
_In_ REGSAM samDesired, _In_ DWORD dwOptions, _Reserved_ DWORD Reserved);该函数加载的 hive 不属于标准注册表树,只能通过返回的根键句柄访问。调用者无需 SeRestorePrivilege 权限。REG_PROCESS_APPKEY 标志防止其他调用方同时加载同一文件。
RegLoadKey / RegUnloadKey——从文件加载配置单元作为新的 hive:
LSTATUS RegLoadKey(_In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey, _In_ LPCTSTR lpFile);hKey 只能是 HKEY_LOCAL_MACHINE 或 HKEY_USERS(两个"真正的"根键)。加载后,对子键的更改会持久保存到文件中。
LSTATUS RegUnLoadKey(_In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey);卸载配置单元前必须关闭所有打开的句柄。
总结
注册表是 Windows 用于存储系统和用户设置的核心分层数据库。本章覆盖了以下内容:
- 注册表的逻辑结构:六个主要 hive(HKLM、HKU、HKCU、HKCR、HKCC、HKEY_PERFORMANCE_DATA)及其相互关系——本质上只有
HKEY_USERS和HKEY_LOCAL_MACHINE两个真正的根键,其余均由这两个组合或映射而成。 - 32/64 位注册表虚拟化:WoW64 重定向机制,以及
KEY_WOW64_64KEY/KEY_WOW64_32KEY标志的使用。 - 最常用的 API 操作:打开/创建键(
RegOpenKeyEx/RegCreateKeyEx)、读取值(RegQueryValueEx/RegGetValue)、写入值(RegSetValueEx/RegSetKeyValue)、删除键和值(RegDeleteKey/RegDeleteTree/RegDeleteValue)、符号链接的创建和删除、键和值的枚举(RegEnumKeyEx/RegEnumValue/RegQueryInfoKey)。 - 高级功能:使用
RegNotifyChangeKeyValue和 ETW 实现注册表变化通知、事务性注册表(RegCreateKeyTransacted/RegOpenKeyTransacted)、远程注册表连接(RegConnectRegistry/RegConnectRegistryEx)。 - 维护相关函数:保存/恢复/加载/卸载配置单元(
RegSaveKey/RegRestoreKey/RegLoadKey/RegUnloadKey/RegLoadAppKey),以及安全描述符管理(RegGetKeySecurity/RegSetKeySecurity)。