标准的Windows 注册表包含一些键,他们并不是真实的键,而是一种符号链接。比如说,在大多数情况下HKEY_LOCAL_MACHINE\System\CurrentControlSet就是一个链接到HKEY_LOCAL_MACHINE\System\ControlSet001的符号。当我们用注册表编辑器RegEdit.exe查看注册表时,符号链接看起来和正常的键没什么区别,他和链接目标完全一样。下面这张图展示了上面提到的键。他们看上去确实没什么不同。
在注册表中还存在着其他符号链接。另外一个例子就是HIVE HKEY_CURRENT_CONFIG就是一个链接到HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\Current的符号。(HKLM 是 HKEY_LOCAL_MACHINE的缩写)
但是我们如何创建自己的链接呢?微软官方文档给出了如何做到这点的部分细节,但是文档里并未透露两个重要细节来使我们的符号链接生效。
让我们来看看我们能否创建一个符号链接。为了示范,我们在 HKEY_CURRENT_USER里创建一个链接到HKEY_CURRENT_USER\Control Panel\Destop\Colors的符号。
第一步是创建一个键并指定为一个链接而不是一个正常的键。(以下代码错误处理被省略了)
最重要部分是 REG_OPTION_CREATE_LINK 标志,他表明了期望创建的是一个链接,而不是标准的键。KEY_WRITE访问掩码也是必须的,因为我们要指定链接的目标。
接下来到了第一处棘手的部分。文档里指出链接的目标应该写在“SymbolicLinkValue”这个值里面,而且应该指定为一个完整的绝对路径。听上去很简单,对嘛?不,你错了。问题在于这个绝对路径,你可能想他应该是长这样:“HKEY_CURRENT_USER\Control Panel\Desktop\Colors”,和我们期望的一样。也许可以进一步把“HKEY_CURRENT_USER”精简为“HKCU”,毕竟他只是一个字符串而已。
事实证明这两种想法都是错误的。这里要求的绝对路径是一个原始的注册表路径。原始的注册表的样子在RegEdit.exe是看不到的,但是在我自己的注册表编辑工具RegEditX中可以看到,可以从链接里下载到。下面是一个运行截图。
上面的视图才是被Windows 内核实际看到的注册表。可以发现并没有HKEY_CURRENT_USER,只有一个USER键和基于用户会话id在这台机器上创建的用户账户的子键。在 HKEY_USERS Hive下的标准注册表项绝大部分都可见了。
前面提到的绝对路径就是应该是下面这样的。下面的代码写入了基于我当前账户的会话ID的正确路径。
上面的代码展示了第二个未文档化的关键信息,符号链接的路径长度是按字节计算的,而且不包含NULL结束符。运气好,我猜对了
就是这样。现在我们可以安全的收尾了,关闭HKEY,完成了创建。
如果你想尝试使用RegEdit.exe来删除刚才你创建的键话,链接的目标将会被删除,而不是你创建的符号本身。所以你该怎么删除你创建的符号链接呢?我的RegEditX目前还不支持这样的操作。
标准的RegDeleteKey和RegDeleteKeyExAPIs也不能删除这个符号链接。即使你给他以REG_OPTION_OPEN_LINK打开的句柄,他也会忽略这个然后找到链接目标。唯一奏效的API还是来自NtDll.Dll的NtDeleteKey函数。
首先,我们添加一个函数声明,然后导入NtDll。
现在我们可以像这样删除一个符号链接。
最后我想说的是,RegCreateKeyEx不能打开一个存在的符号链接键,他只能进行创建操作。这与可以用RegCreateEx创建或打开标准键完全不同。这意味着如果你想改变存在符号链接的链接目标,你首先必须调用RegOpenKeyEx (REG_OPTION_OPEN_LINK),然后做更改操作(删除键或者重新创建它)
注册表真有趣!不是嘛?