Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NtOpenKey fails with 0xC0000034 - how to fix this?

Tags:

c++

winapi

wdk

I'm creating a user-mode CMD app using VS 2013 in C++ and I'm trying to use the native registry editing functions in it. I'm trying to open certain key with 'NtOpenKey' but it always fails with 'STATUS_OBJECT_NAME_NOT_FOUND' and I'm sure that the 'object' is in it's place so the reason must be somewhere else. I want to use the native registry API's because they can handle 'Hidden Registry Keys' - look here for more info. Here is a snippet of my code:

#include <Windows.h>
#include <tchar.h>

#include <wininet.h> 

#include <iostream>

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "Nt_Funcs_declr.h" //here I have manually included the native api declarations as normally I'm denied to use them but they're exported in ntdll.dll so basically it is possible

#include <zlib.h>

//Obitain Steam folder path

wchar_t *GetSteamPathBuffer()
{
    //Open the Sofware Steam registry

    OBJECT_ATTRIBUTES objAttrs;

    objAttrs.Length = sizeof(OBJECT_ATTRIBUTES);

    objAttrs.RootDirectory = NULL;

    wchar_t strRegSteam [] = L"\\Registry\\Machine\\SOFTWARE\\Valve";

    UNICODE_STRING uStrTmp = { sizeof(strRegSteam), sizeof(strRegSteam), strRegSteam };

    objAttrs.ObjectName = &uStrTmp;

    objAttrs.Attributes = OBJ_CASE_INSENSITIVE; // 

    objAttrs.SecurityDescriptor = NULL;

    objAttrs.SecurityQualityOfService = NULL;

    HANDLE pKey;

    ULONG tmmp = NtOpenKey(&pKey, GENERIC_READ, &objAttrs); //here it fails with 'STATUS_OBJECT_NAME_NOT_FOUND'
    if(tmmp)
    {
        cout << "Error: " << GetLastError();
        return NULL;
    }

//....
}

And Nt_Funcs_declr.h:

#pragma once


//NTDLL import declarations

#define STATUS_BUFFER_TOO_SMALL          ((NTSTATUS)0xC0000023L)

//
// Unicode strings are counted 16-bit character strings. If they are
// NULL terminated, Length does not include trailing NULL.
//

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
#ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT * Buffer;
#else // MIDL_PASS
    _Field_size_bytes_part_(MaximumLength, Length) PWCH   Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;

//
// Valid values for the Attributes field
//

#define OBJ_INHERIT             0x00000002L
#define OBJ_PERMANENT           0x00000010L
#define OBJ_EXCLUSIVE           0x00000020L
#define OBJ_CASE_INSENSITIVE    0x00000040L
#define OBJ_OPENIF              0x00000080L
#define OBJ_OPENLINK            0x00000100L
#define OBJ_KERNEL_HANDLE       0x00000200L
#define OBJ_FORCE_ACCESS_CHECK  0x00000400L
#define OBJ_VALID_ATTRIBUTES    0x000007F2L

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;        // Points to type SECURITY_DESCRIPTOR
    PVOID SecurityQualityOfService;  // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;


extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtOpenKey(
_Out_ PHANDLE KeyHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes
);

typedef enum _KEY_INFORMATION_CLASS {
    KeyBasicInformation,
    KeyNodeInformation,
    KeyFullInformation,
    KeyNameInformation,
    KeyCachedInformation,
    KeyFlagsInformation,
    KeyVirtualizationInformation,
    KeyHandleTagsInformation,
    KeyTrustInformation,
    MaxKeyInfoClass  // MaxKeyInfoClass should always be the last enum
} KEY_INFORMATION_CLASS;

extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtQueryKey(
_In_ HANDLE KeyHandle,
_In_ KEY_INFORMATION_CLASS KeyInformationClass,
_Out_writes_bytes_opt_(Length) PVOID KeyInformation,
_In_ ULONG Length,
_Out_ PULONG ResultLength
);

typedef enum _KEY_VALUE_INFORMATION_CLASS {
    KeyValueBasicInformation,
    KeyValueFullInformation,
    KeyValuePartialInformation,
    KeyValueFullInformationAlign64,
    KeyValuePartialInformationAlign64,
    MaxKeyValueInfoClass  // MaxKeyValueInfoClass should always be the last enum
} KEY_VALUE_INFORMATION_CLASS;

typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
    ULONG   TitleIndex;
    ULONG   Type;
    ULONG   DataLength;
    _Field_size_bytes_(DataLength) UCHAR Data[1]; // Variable size
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;

extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtQueryValueKey(
_In_ HANDLE KeyHandle,
_In_ PUNICODE_STRING ValueName,
_In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
_Out_writes_bytes_opt_(Length) PVOID KeyValueInformation,
_In_ ULONG Length,
_Out_ PULONG ResultLength
);

NOTE: It's for educational prupose so please don't ask me why I don't use WIN32 API.

like image 829
AnArrayOfFunctions Avatar asked Sep 11 '14 15:09

AnArrayOfFunctions


1 Answers

NB: using the kernel API from user mode is unsupported. I strongly recommend against doing so, unless there is a compelling reason why it is necessary.

Here's the problem:

UNICODE_STRING uStrTmp = { sizeof(strRegSteam), sizeof(strRegSteam), strRegSteam };

From the documentation for UNICODE_STRING:

If the string is null-terminated, Length does not include the trailing null character.

So you should be saying something like

UNICODE_STRING uStrTmp = { sizeof(strRegSteam) - sizeof(wchar_t), 
                           sizeof(strRegSteam), 
                           strRegSteam };

As written, your code was attempting to open a key named L"Valve\0" instead of the key named L"Valve".


Addendum: it has been disputed whether so-called "hidden" keys (an unfortunate name IMO; the keys are visible to Win32 code, they simply can't be manipulated) are actually possible, so here's working code to create one:

#include <Windows.h>

#include <stdio.h>

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWCH   Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;        
    PVOID SecurityQualityOfService;  
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;

#define OBJ_CASE_INSENSITIVE    0x00000040L

#pragma comment(lib, "ntdll.lib")

__declspec(dllimport) NTSTATUS NTAPI NtCreateKey(
    __out PHANDLE KeyHandle,
    __in ACCESS_MASK DesiredAccess,
    __in POBJECT_ATTRIBUTES ObjectAttributes,
    __reserved ULONG TitleIndex,
    __in_opt PUNICODE_STRING Class,
    __in ULONG CreateOptions,
    __out_opt PULONG Disposition
    );

NTSYSAPI NTSTATUS NTAPI NtOpenKey(
    __out PHANDLE KeyHandle,
    __in ACCESS_MASK DesiredAccess,
    __in POBJECT_ATTRIBUTES ObjectAttributes
    );

NTSYSAPI NTSTATUS NTAPI NtDeleteKey(
    __out HANDLE KeyHandle
    );

int main(int argc, char ** argv)
{
    HANDLE pKey;
    NTSTATUS result;

    OBJECT_ATTRIBUTES objAttrs;
    wchar_t strSoftwareKey [] = L"\\Registry\\Machine\\SOFTWARE\\Test\0Key";

// If you use this string instead, the key functions normally, proving that the
// issue isn't because we're using UTF-16 rather than ANSI strings:
//
// wchar_t strSoftwareKey [] = L"\\Registry\\Machine\\SOFTWARE\\Test\u2D80Key";

    UNICODE_STRING uStrSoftwareKey = { 
        sizeof(strSoftwareKey) - sizeof(wchar_t), 
        sizeof(strSoftwareKey), 
        strSoftwareKey };

    objAttrs.Length = sizeof(OBJECT_ATTRIBUTES);
    objAttrs.RootDirectory = NULL;
    objAttrs.ObjectName = &uStrSoftwareKey;
    objAttrs.Attributes = OBJ_CASE_INSENSITIVE;
    objAttrs.SecurityDescriptor = NULL;
    objAttrs.SecurityQualityOfService = NULL;

    result = NtCreateKey(&pKey, GENERIC_ALL, &objAttrs, 0, NULL, 0, NULL);
    if(result)
    {
        printf("NtCreateKey: %x\n", result);
        return NULL;
    }

#if 0  // enable this section to delete the key
       // you won't be able to use regedit!
    result = NtDeleteKey(pKey);
    if(result)
    {
        printf("NtDeleteKey: %x\n", result);
        return NULL;
    }
#endif
}

As of Windows 7, at least, this still works. (You'll need a copy of ntdll.lib, available from the DDK/WDK, in order to build this code.)

Please do not do this in production code or on other people's machines.

like image 108
Harry Johnston Avatar answered Oct 31 '22 14:10

Harry Johnston