Определите путь к ключу реестра из дескриптора HKEY в C++

Имея дескриптор ключа реестра Windows, например, те, которые установлены::RegOpenKeyEx(), можно ли определить полный путь к этому ключу?

Я понимаю, что в простом приложении все, что вам нужно сделать, это найти 5 или 10 строк и прочитать... но в сложном приложении, таком как то, которое я отлаживаю, ключ, который меня интересует, может быть открыт из серии звонков.

6 ответов

Решение

Использование LoadLibrary а также NtQueryKey экспортированная функция, как в следующем фрагменте кода.

#include <windows.h>
#include <string>

typedef LONG NTSTATUS;

#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif

#ifndef STATUS_BUFFER_TOO_SMALL
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#endif

std::wstring GetKeyPathFromKKEY(HKEY key)
{
    std::wstring keyPath;
    if (key != NULL)
    {
        HMODULE dll = LoadLibrary(L"ntdll.dll");
        if (dll != NULL) {
            typedef DWORD (__stdcall *NtQueryKeyType)(
                HANDLE  KeyHandle,
                int KeyInformationClass,
                PVOID  KeyInformation,
                ULONG  Length,
                PULONG  ResultLength);

            NtQueryKeyType func = reinterpret_cast<NtQueryKeyType>(::GetProcAddress(dll, "NtQueryKey"));

            if (func != NULL) {
                DWORD size = 0;
                DWORD result = 0;
                result = func(key, 3, 0, 0, &size);
                if (result == STATUS_BUFFER_TOO_SMALL)
                {
                    size = size + 2;
                    wchar_t* buffer = new (std::nothrow) wchar_t[size/sizeof(wchar_t)]; // size is in bytes
                    if (buffer != NULL)
                    {
                        result = func(key, 3, buffer, size, &size);
                        if (result == STATUS_SUCCESS)
                        {
                            buffer[size / sizeof(wchar_t)] = L'\0';
                            keyPath = std::wstring(buffer + 2);
                        }

                        delete[] buffer;
                    }
                }
            }

            FreeLibrary(dll);
        }
    }
    return keyPath;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HKEY key = NULL;
    LONG ret = ERROR_SUCCESS;

    ret = RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft", &key);
    if (ret == ERROR_SUCCESS)
    {
        wprintf_s(L"Key path for %p is '%s'.", key, GetKeyPathFromKKEY(key).c_str());    
        RegCloseKey(key);
    }

    return 0;
}

Это напечатает путь ключа на консоли:

Ключевой путь для 00000FDC - "REGISTRY\MACHINE\SOFTWARE\Microsoft".

Я был взволнован, чтобы найти эту статью и ее любимое решение. Пока я не обнаружил, что NTDLL.DLL моей системы не имеет NtQueryKeyType.

После некоторой охоты я наткнулся на ZwQueryKey на форумах DDK.

Это в C#, но вот решение, которое работает для меня:

enum KEY_INFORMATION_CLASS
{
    KeyBasicInformation,            // A KEY_BASIC_INFORMATION structure is supplied.
    KeyNodeInformation,             // A KEY_NODE_INFORMATION structure is supplied.
    KeyFullInformation,             // A KEY_FULL_INFORMATION structure is supplied.
    KeyNameInformation,             // A KEY_NAME_INFORMATION structure is supplied.
    KeyCachedInformation,           // A KEY_CACHED_INFORMATION structure is supplied.
    KeyFlagsInformation,            // Reserved for system use.
    KeyVirtualizationInformation,   // A KEY_VIRTUALIZATION_INFORMATION structure is supplied.
    KeyHandleTagsInformation,       // Reserved for system use.
    MaxKeyInfoClass                 // The maximum value in this enumeration type.
}
[StructLayout(LayoutKind.Sequential)]
public struct KEY_NAME_INFORMATION
{
    public UInt32 NameLength;     // The size, in bytes, of the key name string in the Name array.
    public char[] Name;           // An array of wide characters that contains the name of the key.
                                  // This character string is not null-terminated.
                                  // Only the first element in this array is included in the
                                  //    KEY_NAME_INFORMATION structure definition.
                                  //    The storage for the remaining elements in the array immediately
                                  //    follows this element.
}

[DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int ZwQueryKey(IntPtr hKey, KEY_INFORMATION_CLASS KeyInformationClass, IntPtr lpKeyInformation, int Length, out int ResultLength);

public static String GetHKeyName(IntPtr hKey)
{
    String result = String.Empty;
    IntPtr pKNI = IntPtr.Zero;

    int needed = 0;
    int status = ZwQueryKey(hKey, KEY_INFORMATION_CLASS.KeyNameInformation, IntPtr.Zero, 0, out needed);
    if ((UInt32)status == 0xC0000023)   // STATUS_BUFFER_TOO_SMALL
    {
        pKNI = Marshal.AllocHGlobal(sizeof(UInt32) + needed + 4 /*paranoia*/);
        status = ZwQueryKey(hKey, KEY_INFORMATION_CLASS.KeyNameInformation, pKNI, needed, out needed);
        if (status == 0)    // STATUS_SUCCESS
        {
            char[] bytes = new char[2 + needed + 2];
            Marshal.Copy(pKNI, bytes, 0, needed);
            // startIndex == 2  skips the NameLength field of the structure (2 chars == 4 bytes)
            // needed/2         reduces value from bytes to chars
            //  needed/2 - 2    reduces length to not include the NameLength
            result = new String(bytes, 2, (needed/2)-2);
        }
    }
    Marshal.FreeHGlobal(pKNI);
    return result;
}

Я только пробовал это во время работы в качестве администратора, что может потребоваться.

Результат немного странно отформатирован: \REGISTRY\MACHINE\SOFTWARE\company\product например, вместо HKEY_LOCAL_MACHINE\SOFTWARE\company\product,

Вы можете использовать RegSaveKey и записать его в файл, а затем посмотреть на файл.

В качестве альтернативы вы можете сохранить глобальную карту HKEYs для LPCWSTR и добавлять записи при их открытии и при каждом поиске.

Вы также можете что-то сделать с помощью команды! Reg в WinDBG / NTSD, но вы не можете просто дать ей HKEY. Вы должны будете сделать некоторые другие хитрости, чтобы получить нужную информацию.

Номинально нет, потому что это просто дескриптор, и я не знаю API, который позволял бы вам делать это в обычных Windows API.

ОДНАКО нативный API имеет множество функций, некоторые из которых могут дать вам возможность открывать дескрипторы для заданных файлов и т. П., Поэтому может быть что-то похожее для реестра. Это и RegMon от SysInternals могут сделать что-то подобное, но я боюсь, что вам придется в Google:/

Сstd::wstringпозволяет построить строку из указателя и количества символов, а строка ядра всегда возвращает количество байтов, нет необходимости завершать строку с помощьюNUL. Я не предлагаю напрямую добавлять размер или смещать указатель на постоянное число, вместо этого лучше использовать реальный тип данных, такой как типы структуры, иstd::vector<UCHAR>вместоnewдля динамического выделения памяти. Я изменил код из высоко оцененного ответа следующим образом.

Устаревший способ динамического получения указателя функции из ntdll.dll :

      #include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windows.h>
#include <winternl.h>
#include <string>
#include <vector>

#define REG_KEY_PATH_LENGTH 1024

typedef enum _KEY_INFORMATION_CLASS {
    KeyBasicInformation,
    KeyNodeInformation,
    KeyFullInformation,
    KeyNameInformation,
    KeyCachedInformation,
    KeyFlagsInformation,
    KeyVirtualizationInformation,
    KeyHandleTagsInformation,
    KeyTrustInformation,
    KeyLayerInformation,
    MaxKeyInfoClass
} KEY_INFORMATION_CLASS;

typedef struct _KEY_NAME_INFORMATION {
    ULONG NameLength;
    WCHAR Name[1];
} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;

typedef NTSTATUS (NTAPI *PFN_NtQueryKey)(
    __in        HANDLE  /* KeyHandle */,
    __in        KEY_INFORMATION_CLASS /* KeyInformationClass */,
    __out_opt   PVOID   /* KeyInformation */,
    __in        ULONG   /* Length */,
    __out       ULONG * /* ResultLength */
);

std::wstring RegQueryKeyPath(HKEY hKey)
{
    std::wstring keyPath;

    if (hKey != NULL)
    {
        HMODULE hinstDLL = GetModuleHandleW(L"ntdll.dll");
        if (hinstDLL != NULL)
        {
            FARPROC pfn = GetProcAddress(hinstDLL, "NtQueryKey");
            if (pfn != NULL)
            {
                NTSTATUS Status;

                std::vector<UCHAR> Buffer(FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + sizeof(WCHAR) * REG_KEY_PATH_LENGTH);
                KEY_NAME_INFORMATION *pkni;
                ULONG Length;

            TryAgain:
                Status = reinterpret_cast<PFN_NtQueryKey>(pfn)(hKey, KeyNameInformation, Buffer.data(), Buffer.size(), &Length);
                switch (Status) {
                case STATUS_BUFFER_TOO_SMALL:
                case STATUS_BUFFER_OVERFLOW:
                    Buffer.resize(Length);
                    goto TryAgain;
                case STATUS_SUCCESS:
                    pkni = reinterpret_cast<KEY_NAME_INFORMATION *>(Buffer.data());
                    keyPath.assign(pkni->Name, pkni->NameLength / sizeof(WCHAR));
                default:
                    break;
                }
            }
        }
    }

    return keyPath;
}

Если вы используете Visual Studio 2015 или более позднюю версию,ntdll.libвключен по умолчанию, поэтому я предлагаю использовать статическое связывание с ntdll.dll :

      #include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll")
#include <string>
#include <vector>

#define REG_KEY_PATH_LENGTH 1024

typedef enum _KEY_INFORMATION_CLASS {
    KeyBasicInformation,
    KeyNodeInformation,
    KeyFullInformation,
    KeyNameInformation,
    KeyCachedInformation,
    KeyFlagsInformation,
    KeyVirtualizationInformation,
    KeyHandleTagsInformation,
    KeyTrustInformation,
    KeyLayerInformation,
    MaxKeyInfoClass
} KEY_INFORMATION_CLASS;

typedef struct _KEY_NAME_INFORMATION {
    ULONG NameLength;
    WCHAR Name[1];
} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;

EXTERN_C NTSYSAPI NTSTATUS NTAPI NtQueryKey(
    __in        HANDLE  /* KeyHandle */,
    __in        KEY_INFORMATION_CLASS /* KeyInformationClass */,
    __out_opt   PVOID   /* KeyInformation */,
    __in        ULONG   /* Length */,
    __out       ULONG * /* ResultLength */
);

std::wstring RegQueryKeyPath(HKEY hKey)
{
    std::wstring keyPath;

    NTSTATUS Status;

    std::vector<UCHAR> Buffer(FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + sizeof(WCHAR) * REG_KEY_PATH_LENGTH);
    KEY_NAME_INFORMATION *pkni;
    ULONG Length;

TryAgain:
    Status = NtQueryKey(hKey, KeyNameInformation, Buffer.data(), Buffer.size(), &Length);
    switch (Status) {
    case STATUS_BUFFER_TOO_SMALL:
    case STATUS_BUFFER_OVERFLOW:
        Buffer.resize(Length);
        goto TryAgain;
    case STATUS_SUCCESS:
        pkni = reinterpret_cast<KEY_NAME_INFORMATION *>(Buffer.data());
        keyPath.assign(pkni->Name, pkni->NameLength / sizeof(WCHAR));
    default:
        break;
    }

    return keyPath;
}

Обратите внимание, чтоNtQueryKeyвернулсяSTATUS_BUFFER_OVERFLOWно нетSTATUS_BUFFER_TOO_SMALLв Windows 10, если предоставленного буфера недостаточно.

За ntsd/windbg:

!handle yourhandle 4

Другие вопросы по тегам