I created a symbolic registry key by using the NtObjectManager library like that:
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)
}
}
}
When I tried to delete the key from Registry (Regedit.exe
) it failed with error:
ABC cannot be opened. An error is preventing this key from being opened. Details: Access is denied
I tried to delete it even with SYSTEM
permissions (using psexec
to launch a SYSTEM
cmd) but I still received the same error.
The function NtKey.CreateSymbolicLink
is calling SetSymbolicLinkTarget
which calls eventually to SetValue
like that:
SetValue(SymbolicLinkValueName, RegistryValueType.Link, Encoding.Unicode.GetBytes(target), throw_on_error);
I didn't figure out yet how to delete it.
I found an answer about deleting symbolic registry key with C++ but it just calls lpfnZwDeleteKey
and I don't know what is the equivalent to C#.
I tried the function NtKey.UnloadKey
function, I thought it might help but it didn't.
I was able to delete it using James's tool CreateRegSymlink
like that:
CreateRegSymlink.exe -d "HKCU\Software\XYZ"
I noticed that it is being done by calling DeleteRegSymlink
.
When I checked what is inside it, I noticed it convert the registry path to a real path by calling RegPathToNative
:
bstr_t symlink = RegPathToNative(lpSymlink);
Here you can see what the RegPathToNative
work.
Then it calls:
InitializeObjectAttributes(&obj_attr, &name, OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, nullptr, nullptr);
Which is I think where the magic happens.
If you have any suggestion how to find the real link from a symbolic registry path, let me know.
EDIT(10/1/2022) - thanks to @RbMm:
I created a function to open symlink using REG_OPTION_OPEN_LINK
and then deletes it with ZwDeleteKey
but the important thing was to set the rights to RegistryRights.Delete
as @RbMm mentioned:
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);
}