如何从 C# 中删除注册表符号 link 键:"An error is preventing this key from being opened. Details: Access is denied"

How to delete registry symbolic link key from C#: "An error is preventing this key from being opened. Details: Access is denied"

我使用 NtObjectManager library 创建了一个符号注册表项:

using NtApiDotNet;
using System;

namespace poc
{
    class Program
    {
        const string SrcKey = @"HKEY_CURRENT_USER\SOFTWARE\ABC";
        const string TargetKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\XYZ";

        static NtKey CreateSymbolicLink(string name, string target)
        {
            name = NtKeyUtils.Win32KeyNameToNt(name);
            target = NtKeyUtils.Win32KeyNameToNt(target);
            return NtKey.CreateSymbolicLink(name, null, target);
        }

        static void Main(string[] args)
        {
           var link = CreateSymbolicLink(SrcKey, TargetKey)
        }
    }
}

当我尝试从注册表 (Regedit.exe) 中删除密钥时,失败并出现错误:

ABC cannot be opened. An error is preventing this key from being opened. Details: Access is denied

即使有 SYSTEM 权限(使用 psexec 启动 SYSTEM cmd)我也试图删除它,但我仍然收到同样的错误。

函数NtKey.CreateSymbolicLinkcalling SetSymbolicLinkTarget which calls eventuallySetValue这样的:

SetValue(SymbolicLinkValueName, RegistryValueType.Link, Encoding.Unicode.GetBytes(target), throw_on_error);  

我还没想好怎么删除它。
我找到了关于使用 C++ 删除符号注册表项的 ,但它只是调用 lpfnZwDeleteKey,我不知道 C# 的等价物是什么。

我尝试了 NtKey.UnloadKey 函数,我认为它可能有帮助,但没有。

我可以使用 James 的工具 CreateRegSymlink 删除它:

CreateRegSymlink.exe -d "HKCU\Software\XYZ"   

我注意到 calling DeleteRegSymlink 正在做这件事。
当我检查什么是 inside it 时,我注意到它通过调用 RegPathToNative:

将注册表路径转换为真实路径
bstr_t symlink = RegPathToNative(lpSymlink);  

Here 你可以看到 RegPathToNative 的工作。
然后它调用:

InitializeObjectAttributes(&obj_attr, &name, OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, nullptr, nullptr);  

我认为这是神奇的地方。
如果您对如何从符号注册表路径中找到真正的 link 有任何建议,请告诉我。


编辑(10/1/2022)-感谢@RbMm:
我创建了一个使用 REG_OPTION_OPEN_LINK 打开 symlink 的函数,然后使用 ZwDeleteKey 删除它,但重要的是将权限设置为 RegistryRights.Delete 正如@RbMm 提到的:

const int REG_OPTION_OPEN_LINK = 0x0008;

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
static extern int RegOpenKeyExW(SafeRegistryHandle hKey, String lpSubKey,
    int ulOptions, int samDesired, out SafeRegistryHandle hkResult);


[DllImport("ntdll.dll")]  
private static extern int ZwDeleteKey(SafeRegistryHandle hKey);  


public static RegistryKey OpenSubKeySymLink(this RegistryKey key, string name, RegistryRights rights = RegistryRights.ReadKey, RegistryView view = 0)
{
    var error = RegOpenKeyExW(key.Handle, name, REG_OPTION_OPEN_LINK, ((int)rights) | ((int)view), out var subKey);
    if (error != 0)
    {
        subKey.Dispose();
        throw new Win32Exception(error);
    }
    return RegistryKey.FromHandle(subKey);  // RegistryKey will dispose subKey
}

static void Main(string[] args)
{
    RegistryKey key;
    key = OpenSubKeySymLink(Microsoft.Win32.Registry.CurrentUser, @"SOFTWARE\Microsoft\Windows\ABC", RegistryRights.Delete, 0);
    ZwDeleteKey(key.Handle);
}