Reputation: 23
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
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
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