While working on a pet project called Registry Explorer (yes, I know there are a bunch of those floating around, most of them pretty old) to be yet another RegEdit.exe replacement, I wanted to add the option to rename a registry key (which RegEdit.exe allows as well). However, looking at the registry APIs (both managed and native) there seems to be no function to rename a key. The closest is the ability to copy a key (with its subkeys and values) via RegCopyTree, so that I can copy the original key with the new name and delete the original one. Although this could work, it seems wasteful; after all – the subkey tree could be very deep with multitude of keys and values, making copy a slow operation and possibly a memory hungry one.
Since regedit allows renaming of a key I wandered whether it has some trick up its sleeve. I’ve opened DependencyWalker and looked at the imported function from AdvApi32.dll (where the Registry APIs reside). And sure enough – there seems to be a renaming key function:
Alas, the function is clearly undocumented in the MSDN docs. I suspect it’s just an oversight, wouldn’t you?
The function is clearly exported, so all I need to do is add the correct prototype. So I guessed the function could have at least two arguments: the handle to the registry key to rename and the new name itself. Let’s see if we can use tools to help get to the correct prototype.
First, it would be nice to know the number of arguments to the function. It may be two (as per my guess) but it may be more – perhaps some flags, or something else. We can use the link.exe tool with the /dump and /export switches to list the mangled function names in the import library advapi32.lib.
If you want to try this, open the Visual Studio command prompt (any version will do) so that the environment variables are set correctly to find the link.exe tool. (alternatively, just go and find that tool somewhere in the VS directory hierarchy) and pass the full path of AdvApi32.lib (typically at c:\Program Files (x86)\Windows kits\10\Lib\10.0.10240.0\um\x86 if using the Windows 10 SDK installation).
C:\Program Files (x86)\Microsoft Visual Studio 12.0>link /dump /exports "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\um\x86\AdvAPI32.Lib"
The result is a long list of functions, but RegRenameKey is there:
Since I’ve used the 32 bit import library this tells us that the arguments take up 12 bytes, which we can guess as being three arguments.
Next, we’ll fire up WinDbg (with admin elevation because RegEdit runs as admin as well) and attach to a RegEdit.Exe instance. Now we can set up a breakpoint at RegRenameKey:
0:000> bp advapi32!regrenamekey
Now we let regedit continue running and when adding a new key or renaming an existing key, we hit the breakpoint:
Breakpoint 0 hit
00007ffd`678c0960 4c8bdc mov r11,rsp
I’ve attached to the 64 bit version of RegEdit. In x64 there is just one calling convention. The first 4 integer/pointer arguments are passed in the RCX, RDX, R8, R9 registers, respectively. Since we suspect the first argument is a handle to a registry key – let’s check our hypothesis:
0:000> !handle @rcx 7
Name \REGISTRY\USER\S-1-5-21-3251691949-2960433927-2607893006-1001\Environment\New Key #1
Indeed, the first argument is a handle to the key with the old name (“New Key 1”). How about the second argument?
0:000> r rdx
It seems RDX is zero which may be some zero flags or a null pointer or a null handle. Let’s try the third argument in R8:
0:000> r r8
There’s definitely something there. Since the new name must be somewhere, let’s see if this is a string:
0:000> du @r8
And it is the new name I’ve set for the key. So these are the 3 arguments.
After some code testing, I’ve determined that the second argument can be the old key name if the handle is to the parent key. It can be null if the key handle points directly to the old key (and not to the parent).
This is the final prototype for the Win32 API:
int RegRenameKey(HANDLE hKey, LPCWSTR oldName, LPCWSTR newName);
The returned value is the error code which is the way all registry API calls work. They don’t return the typical Win32 BOOL; rather, they return the error code directly (zero being ERROR_SUCCESS).
Since my Registry Explorer is a WPF app, I’m using P/Invoke, so the call looks like so:
public static extern int RegRenameKey(SafeRegistryHandle hKey, [MarshalAs(UnmanagedType.LPWStr)] string oldname,
[MarshalAs(UnmanagedType.LPWStr)] string newname);
There you have it – renaming a registry key with a hidden API.