mike chang
mike chang

Reputation: 23

Obtaining handle to key with NtCreateKey/NtOpenKey

PURPOSE

I'm trying to make a function which will create a given sub key in the HKCU registry hive, or open the sub key if it already exists, then return TRUE.

NOTES

Let RegSidPath represent a fully qualified HKCU registry path with an user SID appended to it such as \\Registry\\User\\S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050

Let KeyToCreate represent a specific registry path such as \\Software\\MyCompany\\MySoftware\\MySubKey

CODE

I have the following function:

BOOL CreateHKCUKey(PWCHAR RegSidPath, PWCHAR KeyToCreate) {

UNICODE_STRING uString;
RtlInitUnicodeString(&uString, RegSidPath);

OBJECT_ATTRIBUTES ObjAttributes;
InitializeObjectAttributes(&ObjAttributes, &uString, OBJ_CASE_INSENSITIVE, 0, 0);

HANDLE BaseKeyHandle = NULL;
NTSTATUS Status = NtOpenKey(&BaseKeyHandle, KEY_CREATE_SUB_KEY, &ObjAttributes);
if (NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {

    UNICODE_STRING KeyString = { 0 };
    do {
        PWCHAR NextSubKey = StrStrW((KeyString.Length == 0 ? KeyToCreate : KeyString.Buffer) + 1, L"\\");
        DWORD CurrentKeyLength = lstrlenW(KeyToCreate) - lstrlenW(NextSubKey);
        PWCHAR CurrentSubKey = PWCHAR(GlobalAlloc(GPTR, CurrentKeyLength + sizeof(WCHAR)));
        if (CurrentSubKey != ERROR) {
            memcpy(CurrentSubKey, KeyToCreate, CurrentKeyLength * sizeof(WCHAR));
            CurrentSubKey[CurrentKeyLength] = UNICODE_NULL;

            RtlInitUnicodeString(&KeyString, CurrentSubKey);

            OBJECT_ATTRIBUTES KeyAttributes;
            InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &BaseKeyHandle, 0);

            HANDLE CurrentHiveEntry = NULL;
            Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
            if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
                InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &CurrentHiveEntry, 0);

                DWORD DefaultDisposition;
                Status = NtCreateKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &DefaultDisposition);
                if (NT_SUCCESS(Status)) {
                    if (StrCmpNW(KeyString.Buffer + uString.Length, KeyString.Buffer, lstrlenW(KeyToCreate) == 0)) 
                        return TRUE;
                    else continue;
                } else break;
            } else break;
            BaseKeyHandle = CurrentHiveEntry;
        }
    } while (TRUE);
 }
 NtClose(BaseKeyHandle);
 return FALSE;
}

PROBLEM

Whenever the code gets to this part of the function

Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
        if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {

The return value is always ERROR_BAD_PATHNAME (161) even if the current sub key already exists.

QUESTION

What is the reason, and what am I doing wrong? Is there anything that I've done which is not correct, and how can I fix it?

Upvotes: 2

Views: 2204

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596407

Unless you are writing a driver, use RegCreateKeyEx() instead. It handles all the logic of creating intermediate keys for you if they don't already exist. 1 function call, no looping needed.

HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW(HKEY_USERS, L"S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050\\Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
    ...
    RegCloseKey(hKey);
}

However, to access the HKEY_CURRENT_USER hive of a specific user, the preferred solution is to use RegOpenCurrentUser() or LoadUserProfile() instead of accessing HKEY_USERS directly:

// impersonate the desired user first, then...

HKEY hRootKey;
LONG lRet = RegOpenCurrentUser(samDesired, &hRootKey);
if (lRet == 0)
{
    HKEY hKey;
    DWORD dwDisposition;
    lRet = RegCreateKeyExW(hRootKey, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
    if (lRet == 0)
    {
        ...
        RegCloseKey(hKey);
    }
    RegCloseKey(hRootKey);
}

// stop impersonating...

// obtain token to desired user first, then...

PROFILEINFO profile = {0};
profile.dwSize = sizeof(profile);
profile.dwFlags = PI_NOUI;

if (LoadUserProfile(hToken, &profile))
{
    HKEY hKey;
    DWORD dwDisposition;
    LONG lRet = RegCreateKeyExW((HKEY)profile.hProfile, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
    if (lRet == 0)
    {
        ...
        RegCloseKey(hKey);
    }
    UnloadUserProfile(hToken, profile.hProfile);
}

// release token ...

Upvotes: 2

RbMm
RbMm

Reputation: 33744

NTSTATUS CreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, PWCHAR RegSidPath, PWCHAR KeyToCreate, PULONG Disposition) 
{
    UNICODE_STRING ObjectName;
    RtlInitUnicodeString(&ObjectName, RegSidPath);

    OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName ,OBJ_CASE_INSENSITIVE };

    NTSTATUS status = ZwOpenKey(&oa.RootDirectory, KEY_CREATE_SUB_KEY, &oa);

    if (0 <= status)
    {
        ObjectName.Buffer = KeyToCreate;

        do 
        {
            ACCESS_MASK Access;

            if (KeyToCreate = wcschr(++ObjectName.Buffer, '\\'))
            {
                ObjectName.Length = (USHORT)RtlPointerToOffset(ObjectName.Buffer, KeyToCreate);
                Access = KEY_CREATE_SUB_KEY;
            }
            else
            {
                ObjectName.Length = (USHORT)wcslen(ObjectName.Buffer) * sizeof(WCHAR);
                Access = DesiredAccess;
            }

            ObjectName.MaximumLength = ObjectName.Length;

            status = ZwCreateKey(KeyHandle, Access, &oa, 0, 0, 0, Disposition);

            NtClose(oa.RootDirectory);

            oa.RootDirectory = *KeyHandle;

        } while (0 <= status && (ObjectName.Buffer = KeyToCreate));
    }

    return status;
}

and use as

  HANDLE hKey;
  NTSTATUS status = CreateKey(&hKey, KEY_ALL_ACCESS,
    L"\\REGISTRY\\USER\\S-***", 
    L"\\Software\\MyCompany\\MySoftware\\MySubKey", 0);

Upvotes: 3

Related Questions