7

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.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • 5
    "object name not found", that of course a *very* common problem. Why on Earth are you doing this, it doesn't add anything to RegOpenKeyEx(), only porting misery and self-inflicted confusion. – Hans Passant Sep 11 '14 at 15:35
  • 1
    It's for educational prupose - so please better try to help me handle this. – AnArrayOfFunctions Sep 11 '14 at 16:11
  • 1
    This doesn't educate anybody, it is harmful. This is not a forum. – Hans Passant Sep 11 '14 at 16:14
  • 1
    Come on - help this poor men. He just wants to get this to work. –  Sep 11 '14 at 16:17
  • 1
    Hmm and why is it harmful? – AnArrayOfFunctions Sep 11 '14 at 16:21
  • 4
    According to [MSDN](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554288%28v=vs.85%29.aspx), `NtOpenKey` is not included in the list of Native API calls that a user-mode application should be using. Therefore, saying "don't use it", _is_ helping. From what I can tell, it should only be used from a driver, via the `ZwOpenKey` routine. – icabod Sep 11 '14 at 16:21
  • 1
    It seems that I'm entering unexplored Windows features then. Thanks for the info anyway. – AnArrayOfFunctions Sep 11 '14 at 16:24
  • 1
    that doesnt make any sense - there are no "unexplored Windows features". You are trying to access a KERNEL-DRIVER API in usermode - thats just nonsensical. Use proper WINAPI-functions and everything will work as expected. http://msdn.microsoft.com/en-us/library/windows/desktop/ms724875(v=vs.85).aspx – specializt Sep 11 '14 at 16:33
  • Aha - I just remember where it all started - look at this project - http://www.codeproject.com/Articles/14508/Registry-Manipulation-Using-NT-Native-APIs - "Registry Manipulation Using NT Native APIs". – AnArrayOfFunctions Sep 11 '14 at 16:40
  • With them you can access Hidden Registry Keys. – AnArrayOfFunctions Sep 11 '14 at 16:41
  • 1
    Neither are there "hidden" keys nor can you access anything microsoft doesnt want you to access - most of the ACTUAL hidden things are encrypted and locked nowadays, only accessible to the kernel itself. Never take advice (or even code) from codeproject, this community is full of BS and whatnot. Every application is able to read every key, there never have been limits (except for mandatory admin-privileges) – specializt Sep 11 '14 at 16:44
  • Will you help me or not? You don't know how *** is Windows to talk like this - Microsoft have no *** idea what is going on it. Do you know that File Explorer can't read paths longer than 256 characters - creating one will result in a file which you can't do anything with. – AnArrayOfFunctions Sep 11 '14 at 16:48
  • 1
    "help" with what, exactly? You have not asked any real question. – specializt Sep 11 '14 at 16:50
  • 1
    I have - please re-read the title. @specializt – AnArrayOfFunctions Sep 11 '14 at 16:52
  • nope, still no question - only you demonstrating that you're trying to use Kerneldriver-functions which are inaccessible. There is no "fix" for something which doesnt work one way but can be replaced another way, that doesnt make any sense. I see you edited your last comment and added quite some rambling about common limitations which are present in pretty much any OS out there ... well ... im guessing we can conclude that your one and only goal is to create a virus or something - have fun failing at that. – specializt Sep 11 '14 at 16:59
  • 1
    And how did you decide that by trying to use one kernel function? – AnArrayOfFunctions Sep 11 '14 at 17:04
  • ... what? I certainly didnt "use a kernel function" today. – specializt Sep 11 '14 at 17:06
  • 4
    @specializt: there are no hidden registry keys shipped with Windows, but malware can (or at least could) create them as described in the article the OP links to. (It is possible that Windows has been updated since then to prevent such keys from being created, but I haven't seen any reports of such a change.) Also, most of the NtXXX functions are in fact accessible from Win32 code; they have to be, because the Win32 API uses them. They're not supported, and may change without notice, but you *can* use them. – Harry Johnston Sep 11 '14 at 22:29
  • You have obviously not understood what is demonstrated on that little website. "Hidden" keys cant exist by design, no matter how much todays youth wants to have them. – specializt Sep 12 '14 at 07:52
  • 3
    @specializt: if you have some actual evidence to that effect, e.g., a document describing the underlying format of the registry, please present it. The fact that the kernel registry functions use counted strings suggests that the design may in fact allow for key names containing embedded nuls, and if so, the fact that the Win32 API uses nul-terminated strings would obviously make such keys inaccessible to it. – Harry Johnston Sep 12 '14 at 23:02
  • 2
    @specializt: OK, I can now confirm that (as of Windows 7) you can indeed create a registry key whose name contains an embedded null. Code available at http://pastebin.com/JrkYgwmK if anyone is interested. (And no, this isn't because "regedit uses UCHAR"; if you create a key name with a non-null Unicode character, regedit handles it perfectly well. The problem only occurs with embedded nulls and is inherent in all code that uses the Win32 API.) – Harry Johnston Sep 13 '14 at 03:56
  • 2
    @specializt: Before saying an article on CodeProject is full of BS, you should actually make sure you understand what it is saying. Because now you've shown your lack of understanding very publicly. – Ben Voigt Sep 14 '14 at 20:56

1 Answers1

20

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.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • 4
    As an aside, with lots of work you can do this in a supported way. Create a driver. From the driver, this API is supported. Relay your communication from user-space to your driver, and have it do this work. – Yakk - Adam Nevraumont Sep 16 '14 at 13:16