$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
Sometimes it can be useful to determine what registry key belongs to a registry key handle. And exampling is when hooking RegQueryValue so you can determine the registry key that a value belongs to instead of having to track the registry key when it’s opened.
To obtain the registry key we can pass a handle to the NtQueryKey
API with which has the following signature:
1 2 3 4 5 6 7 | NTSYSAPI NTSTATUS NtQueryKey( [in] HANDLE KeyHandle, [in] KEY_INFORMATION_CLASS KeyInformationClass, [out, optional] PVOID KeyInformation, [in] ULONG Length, [out] PULONG ResultLength ); |
1 2 | [DllImport("ntdll.dll", EntryPoint="NtQueryKey", SetLastError=false)] private static extern UInt32 NtQueryKey(IntPtr KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, IntPtr KeyInformation, UInt32 Length, ref UInt32 ResultLength); |
To get the keyname we need to pass the KeyNameInformation
enum and we can set the ReturnLength
parameter to 0 to obtain the required buffer size:
1 2 3 4 | UInt32 nts; UInt32 resLen = 0; IntPtr buffer = IntPtr.Zero; nts = NtQueryKey(KeyHandle, KEY_INFORMATION_CLASS.KeyNameInformation, IntPtr.Zero, 0, ref resLen); |
Then we call NtQueryKey again passing an allocated buffer (of type KEY_NAME_INFORMATION). However in a managed language like C# there is not really good way to define an “ANYSIZE” array in a struct. So let’s just pass a pointer:
1 2 3 | // allocate memory for the buffer buffer = Marshal.AllocHGlobal((int)resLen); nts = NtQueryKey(KeyHandle, KEY_INFORMATION_CLASS.KeyNameInformation, buffer, resLen, ref resLen); |
And extract the NameLength and Name string like this:
1 2 3 4 5 6 7 8 9 | // read name length from the buffer int nameLen = Marshal.ReadInt32(buffer); // name is an c-style "ANYSIZE" char array, as we don't know the size in advance we cannot use // PtrToStructure, so we get an IntPtr pointing to the char array IntPtr name = new IntPtr(buffer.ToInt64() + sizeof(UInt32)); // and Marshal it to string result = Marshal.PtrToStringUni(name, nameLen / sizeof(char)); |
However the returned names are in a different format than what you might expect. HKLM
keys are returned as \REGISTRY\MACHINE\KEY
and HKCU
as \REGISTRY\USER\SID\KEY
so we use the following logic to alter the string into “normal” format:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if (result.Contains("\\REGISTRY\\MACHINE")) { result = result.Replace("\\REGISTRY\\MACHINE", "HKLM"); } else { using (WindowsIdentity wi = WindowsIdentity.GetCurrent()) { if (result.Contains(wi.User.Value)) { result = result.Replace("\\REGISTRY\\USER\\" + wi.User.Value, "HKCU"); } } } |
The complete code and a sample how to use can be found on my github repo.
Leave a reply