Click here to Skip to main content
13,253,118 members (51,466 online)
Click here to Skip to main content
Add your own
alternative version


92 bookmarked
Posted 19 Jun 2006

Registry Manipulation Using NT Native APIs

, 5 Sep 2006
Rate this:
Please Sign up or sign in to vote.
An article on manipulating the registry using NT Native APIs.

This is the Native Registry Editor (NtRegEdit) article I wrote that uses the CNtRegistry class.

Important notice: Any registry manipulation can cause harm to your system and make it so it doesn't start or run properly. Back-up your registry before using this class. I am not responsible for any damage it causes.


There are many registry classes out there, but none (that I know of) that use NT Native API calls to manipulate the registry. Normally, we use the Microsoft APIs to do the work, but they are a bit uncomfortable to use (to say the least), especially when it comes to copying, searching, and deleting keys and values. I also liked the simple example for RegHide by SysInternals that hides registry keys (in a matter of speaking).

Now, I use Robert Pittenger's CRegistry class (found here at CodeProject) whenever I write something that accesses the registry, because of its simplicity. I also liked the idea of hiding registry keys/values from the Registry Editor (RegEdit) by Microsoft, so I decided to combine the two and write a class that does both but only uses the NT Native Registry APIs to do it. This led to the creation of the CNtRegistry class.

Hidden Registry Keys, you say?

SysInternals says it the best (see below - verbatim from their website):

A subtle but significant difference between the Win32 API and the Native API (see Inside the Native API for more information on this largely undocumented interface) is the way that names are described. In the Win32 API, strings are interpreted as NULL-terminated ANSI (8-bit) or wide character (16-bit) strings. In the Native API, names are counted as Unicode (16-bit) strings. While this distinction is usually not important, it leaves open an interesting situation: there is a class of names that can be referenced using the Native API, but that cannot be described using the Win32 API.

How is this possible? The answer is that a name which is a counted as a Unicode string can explicitly include NULL characters (0) as part of the name. For example, "Key\0". To include the NULL at the end, the length of the Unicode string is specified as 4. There is absolutely no way to specify this name using the Win32 API since if "Key\0" is passed as a name, the API will determine that the name is "Key" (3 characters in length) because the "\0" indicates the end of the name.

When a key (or any other object with a name such as a named Event, Semaphore, or Mutex) is created with such a name, any application using the Win32 API will be unable to open the name, even though they might seem to see it.

This is where you can get a copy of RegHide from SysInternals.

What can it do?

The CNtRegistry class has some useful features ... You can:

  • Copy Keys and Values (including hidden ones) with in the same RootKey or across RootKeys.
    • BOOL CopyKeys(CString csSource, CString csTarget, BOOL bRecursively);
    • BOOL CopyValues(CString csSource, CString csTarget, CString csValueName, CString csNewValueName);
  • Delete Keys and Values (including hidden ones and recursively).
    • BOOL DeleteKey (CString csKey);
    • BOOL DeleteKeysRecursive (CString csKey);
    • BOOL DeleteValue (CString csName);
  • Rename Keys and Values.
    • BOOL RenameKey(CString csFullKey, CString csNewKeyName);
    • BOOL RenameValue(CString csOldName, CString csNewName);
  • Search for Keys, Value names, and Values. This allows you to do searches for certain "String" occurrences. It can also Find hidden Keys. Both functions (Search/Find) can perform them recursively, but only with in the current RootKey.
    • BOOL Search (CString csString, CString csStartKey, CStringArray& csaResults, int nRegSearchType=3, BOOL bCaseSensitive = TRUE);
    • BOOL FindHiddenKeys (CString csKey, BOOL bRecursive, CStringArray& csaResults);
  • Read and Write Values. Possible values/types are:
      • UCHAR* ReadBinary(CString csKey, CString csName, UINT& uiLength);
      • BOOL WriteBinary(CString csKey, CString csName, UCHAR* pValue, UINT uiLength);
      • DWORD ReadDword(CString csKey, CString csName, DWORD dwDefault);
      • BOOL WriteDword(CString csKey, CString csName, DWORD dwValue);
      • CString ReadString(CString csKey, CString csName, CString csDefault);
      • BOOL ReadMultiString(CString csKey, CString csName, CStringArray& csaReturn);
      • BOOL WriteString(CString csKey, CString csName, CString csValue);
      • BOOL WriteExpandString(CString csKey, CString csName, CString csValue);
      • BOOL WriteMultiString(CString csKey, CString csName, CStringArray& csaValue);
    • int (REG_DWORD)
      • int ReadInt(CString csKey, CString csName, int nDefault);
      • BOOL WriteInt(CString csKey, CString csName, int nValue);
    • float (REG_BINARY)
      • double ReadFloat(CString csKey, CString csName, double fDefault);
      • BOOL WriteFloat(CString csKey, CString csName, double fValue);
      • BOOL ReadBool(CString csKey, CString csName, BOOL bDefault);
      • BOOL WriteBool(CString csKey, CString csName, BOOL bValue);
    • COleDateTime (REG_BINARY)
      • COleDateTime ReadDateTime(CString csKey, CString csName, COleDateTime dtDefault);
      • BOOL WriteDateTime(CString csKey, CString csName, COleDateTime dtValue);
      • COLORREF ReadColor(CString csKey, CString csName, COLORREF rgbDefault);
      • BOOL WriteColor(CString csKey, CString csName, COLORREF rgbValue);
    • Objects (REG_BINARY)
      • BOOL ReadFont(CString csKey, CString csName, CFont* pFont);
      • BOOL WriteFont(CString csKey, CString csName, CFont* pFont);
      • BOOL ReadPoint(CString csKey, CString csName, CPoint* pPoint);
      • BOOL WritePoint(CString csKey, CString csName, CPoint* pPoint);
      • BOOL ReadSize(CString csKey, CString csName, CSize* pSize);
      • BOOL WriteSize(CString csKey, CString csName, CSize* pSize);
      • BOOL ReadRect(CString csKey, CString csName, CRect* pRect);
      • BOOL WriteRect(CString csKey, CString csName, CRect* pRect);
  • Display errors that come from the calls to Native APIs in nt.dll.
  • Provide a default Value in case the operation fails.
  • Enable privileges for the current user which gives them the ability (if it doesn't exist already) to backup/restore Hives/Keys.

Using the code

Using the CNtRegistry class is actually very simple. Once you have declared CNtRegistry, initialize the class by calling InitNtRegistry(), call SetRootKey, then SetKey to set "SOFTWARE\MyApp". You can also use SetKey(HKEY hRoot, CString strKey, BOOL bCanCreate, BOOL bCanSaveCurrentKey) which simply combines the two functions (SetRootKey and SetKey). Now call almost any function ... like CreateHiddenKey to create a hidden key (or try a number of other functions).

Here is an example that creates a hidden key, then puts some values in it (which are also hidden).

#define "NtRegistry.h"

void CMyApp::ReadRegistry()
    CNtRegistry ntReg;
    if (ntReg.SetKey(HKEY_LOCAL_MACHINE, 
        _T("Software\\MyApp\\Settings"), FALSE, TRUE))
        if (ntReg.CreateHiddenKey(_T("Software\\MyApp\\Settings\\Hidden")))
            // Write some stuff
            ntReg.WriteInt(_T("Data1"), 777);
            ntReg.WriteFloat(_T("Pi"), 3.14159);
            ntReg.WriteString(_T("UserName"), _T("DMadden61"));

            // Read some stuff
            int nData = ntReg.ReadInt(_T("Data1"), 0);
            pi = ntReg.ReadFloat(_T("Pi"), 0.0);
            CString csUserName = ntReg.ReadString(_T("UserName"), _T("ERR"));
        TRACE("Failed to open/set key\n");

Simple enough? Well, it is, but how it is all put together was another thing.

Differences between Nt...() calls and Reg...() calls

I am going to show you some of the NT Native Registry APIs that CNtRegistry uses (or at least, is ready for use), talk about what makes these different, and how I modified a popular function called "EnablePrivileges" to use the NT Native APIs.

Some Native APIs
Related Win32 APIs
Required Privileges
RegCreateKey, RegCreateKeyEx
RegOpenKey, RegOpenKeyEx
RegEnumerateKey, RegEnumerateKeyEx
RegSetValue, RegSetValueEx
RegQueryValue, RegQueryValueEx
RegEnumKey, RegEnumKeyEx

Sample NtRegistryAPI Image

The parameters used for NT Native Registry APIs are not the same ones you are familiar with. Did you know that there is actually only two (2) Root (main) Keys in the registry? The rest are simply symbolic links. The two Root Keys are "\Registry\Machine (HKEY_LOCAL_MACHINE)" and "\Registry\User (HKEY_USERS)". Look below to see the HKEY and the TEXT equivalent. Where you would normally write a path for a subkey (RegCreateKey) like this "SOFTWARE\MyApp" and also include the HKEY (HKEY_LOCAL_MACHINE), these Native APIs (NtCreateKey) need the entire "full" path to the subkey, like this: "\Registry\Machine\SOFTWARE\MyApp". The CNtRegistry class lets you make the call the way you are used to, but puts it all together for you (internally), simply by calling the two functions (see below) or one that does it both.

  • SetKey(_T("SOFTWARE\\MyApp"),TRUE,TRUE);
  • or just the one below which combines the two
HKEY_USERS           \Registry\User
HKEY_CURRENT_USER    \Registry\User\<Users_SID>
HKEY_LOCAL_MACHINE   \Registry\Machine
HKEY_CLASSES_ROOT    \Registry\Machine\SOFTWARE\Classes
HKEY_CURRENT_CONFIG  \Registry\Machine\SYSTEM\CurrentControlSet\
                                       Hardware Profiles\Current

Others are the UNICODE_STRING (U_S) and OBJECT_ATTRIBUTES (O_A) structures. The U_S structure holds the "full" Key path (Unicode string) and length. The O_A struct contains the properties. InitializeObjectAttributes(...) initializes the O_A structure that specifies the properties of an object handle to be opened. A pointer to this structure is then passed to the routine that actually opens the handle (e.g., NtOpenKey(...)).

// Used to define Unicode strings.
typedef struct _UNICODE_STRING 
    // The length in bytes of the string stored in Buffer.
    USHORT Length;
    // The maximum length in bytes of Buffer.
    USHORT MaximumLength;
    // Points to a buffer used to contain
    // a string of wide characters.
    PWSTR  Buffer;

// Specifies the properties of an object handle.
typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    // Handle to the root object directory             
    HANDLE RootDirectory;
    // Name of the object to open a handle for.
    // Specifies flags (e.g. OBJ_CASE_INSENSITIVE)
    ULONG Attributes;
    // Points to type SECURITY_DESCRIPTOR
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;

#define InitializeObjectAttributes( p, n, a, r, s ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );        \
    (p)->RootDirectory = r;                           \
    (p)->Attributes = a;                              \
    (p)->ObjectName = n;                              \
    (p)->SecurityDescriptor = s;                      \
    (p)->SecurityQualityOfService = NULL;             \

Code example using the above structures to create a registry key (the native way):

// Initialize the Unicode String from an ANSI String



// Initialize the data/properties for the actual call

// We are ready to create the key...
m_NtStatus = NtCreateKey(&hKey, 

if (!NT_SUCCESS(m_NtStatus)) {
    return FALSE;
else {


Because I wanted to use some of the NT Registry Hive APIs (NtSaveKey, etc...), I had to re-write the function EnablePrivileges below to use NT Native APIs (except one function that I couldn't find a native call for, LookupPrivilegeValue).

// The following code can be used to enable or disable the
// privilege. You can use this code to enable or disable privilege 
// Use the following to enable the privilege:
//   EnablePrivilege(SE_BACKUP_NAME, TRUE);
// Use the following to disable the privilege:
//   EnablePrivilege(SE_BACKUP_NAME, FALSE);
NTSTATUS CNtRegistry::EnablePrivilege(CString csPrivilege, BOOL bEnable)
    HANDLE           hToken   = NULL;
    NTSTATUS         NtStatus = STATUS_SUCCESS;

    // Open the process token for this process.
    NtStatus = NtOpenProcessToken(GetCurrentProcess(),
    if (!NT_SUCCESS(NtStatus)) {
        return NtStatus;

    // Get the local unique id for the privilege. This
    // is a Win32 API :-\ I couldn't find a Native one.
    LUID luid;
    if ( !LookupPrivilegeValue(NULL,
        NtClose( hToken );

    // Assign values to the TOKEN_PRIVILEGE structure.
    NewState.PrivilegeCount = 1;
    NewState.Privileges[0].Luid = luid;
    NewState.Privileges[0].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);

    // Adjust the token privilege.
    NtStatus = NtAdjustPrivilegesToken( hToken, 
    // Close the handle.

    return NtStatus;

In closing...

Being an "Intermediate" programmer, the problems I ran into were simply learning curves in the conversion of different types. I commented out the Hive functions because I was having some problems and I didn't want folks out there to screw up their systems. I also do not use all the Native Registry APIs in the nt.dll.

That is all I have got, I don't expect this to be perfect, so please give me your ideas to make it better. One thing to remember in life itself (and all the challenges that come with it) ... if you make a "mistake" and learn from it, then it never was a mistake to begin was a "lesson"!

Thanks to ...

  • CRegistry by Robert Pittenger. The inspiration behind this class!!

Things To-Do

  • Incorporate the "Hive" functions in the code.
  • Ability to "Rename" Keys/Values (shouldn't be too hard with copying/deleting capabilities already there).
  • Take out "un-needed" code (used when I was writing it).
  • Make sure everything is commented (working).
  • There is a lot more...


There is a lot more that can be done to this class, but time is short and I thought that someone out there might like to help :-)

  • August 10, 2006 (
    • Added recursive parameter to the CopyKey function.
    • Added "DeleteKeyRecursive()" function.
  • July 16, 2006 (
    • Added "ShowPermissionsDlg()" common dialog.
    • Added Key path in the statusbar.
  • July 2, 2006 (
    • Changed the parameters of "CopyKeys()/CopyValues()" functions.
      • This makes it easier to copy anything/anywhere.
    • Changed the parameters of the "FindHiddenKeys()" function.
      • This is so the output goes to a CStringArray (instead of a message box) which allowed me to display the output in a ListCtrl for display (thanks to a suggestion from "Tcpip2005" from CodeProject)!!
    • Added the "InitNtRegistry()" function which does all the initialization.
    • Added the CaseSensitive parameter to the "Search()" function.
    • Added "#pragma comment(linker...)" to stdafx.h to show XP themes.
    • Added some "Rtl...()" string functions.
      • RtlInitString()
      • RtlInitAnsiString()
      • RtlInitUnicodeString()
      • RtlAnsiStringToUnicodeString()
      • RtlUnicodeStringToAnsiString()
      • RtlFreeString()
      • RtlFreeAnsiString()
      • RtlFreeUnicodeString()
  • Jun 24, 2006 (
    • Added "RenameKey()" that uses the "NtRenameKey()".
    • Added "RenameValue()" that uses home-bread functions.
    • Reformated the header and source so that the order of the functions in the header match the order in the source.
  • Jun 22, 2006 (
    • Combined "SetRootKey() and SetKey" and added more explanations in the article.
    • Added "GetCurrentUsersTextualSid()" that returns the private variable "m_csSID".
  • Jun 11, 2006 - Release to public.
  • Jun 03, 2004 - Initial playing.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Dan Madden
Product Manager
Germany Germany
I have been programming (as a hobby) for 20+ years (Unix C, Scripting, VB, C/C++, C#). I am getting too old to talk about it and been in the Security line of work (both Military/Civilian) for 25+ years.

You may also be interested in...


Comments and Discussions

QuestionSource code Pin
Michael Haephrati1-Oct-17 9:42
professionalMichael Haephrati1-Oct-17 9:42 
QuestionVery bad code !! Pin
Member 792380318-Oct-12 5:07
memberMember 792380318-Oct-12 5:07 
AnswerRe: Very bad code !! Pin
Assarbad16-Aug-13 10:09
memberAssarbad16-Aug-13 10:09 
GeneralMy vote of 1 Pin
__Andreas__29-May-10 5:09
member__Andreas__29-May-10 5:09 
GeneralGood idea, but code is poor... [modified] Pin
__Andreas__25-May-10 11:34
member__Andreas__25-May-10 11:34 
GeneralRe: Good idea, but code is poor... [modified] Pin
Dan Madden30-May-10 1:50
memberDan Madden30-May-10 1:50 
QuestionCan I use this in my commercial App? Pin
Brian Nguyen18-Jun-09 5:00
memberBrian Nguyen18-Jun-09 5:00 
AnswerRe: Can I use this in my commercial App? Pin
Dan Madden1-Jul-09 12:05
memberDan Madden1-Jul-09 12:05 
GeneralWhy LocateNTDLLEntryPoints returns FALSE when... Pin
Able B.26-Jan-09 1:36
memberAble B.26-Jan-09 1:36 
GeneralDll of the CNtRegistry class Pin
stuart P27-Aug-08 1:22
memberstuart P27-Aug-08 1:22 
GeneralRegistry Cleaner Pin
gayatri.neelema2-Aug-08 0:04
membergayatri.neelema2-Aug-08 0:04 
QuestionUnicode support? [modified] Pin
Hamed Mosavi12-May-08 8:02
memberHamed Mosavi12-May-08 8:02 
GeneralNtLoadKey fails with STATUS_PRIVILEGE_NOT_HELD Pin
Turion20-Nov-07 7:45
memberTurion20-Nov-07 7:45 
QuestionWhere to get latest source code ? Pin
Defenestration5-Nov-07 20:08
memberDefenestration5-Nov-07 20:08 
AnswerRe: Where to get latest source code ? Pin
Dan Madden27-Nov-07 20:44
memberDan Madden27-Nov-07 20:44 
GeneralRe: Where to get latest source code ? Pin
Defenestration28-Nov-07 5:57
memberDefenestration28-Nov-07 5:57 
QuestionNTRegistry DLL? Pin
Ed Hardin23-Sep-07 10:58
memberEd Hardin23-Sep-07 10:58 
AnswerRe: NTRegistry DLL? Pin
Dan Madden30-Sep-07 18:58
memberDan Madden30-Sep-07 18:58 
General32bit app calling NtOpenKey fails on x64 Pin
JeffRoz16-May-07 14:35
memberJeffRoz16-May-07 14:35 
GeneralRe: 32bit app calling NtOpenKey fails on x64 Pin
Dan Madden12-Jun-07 14:49
memberDan Madden12-Jun-07 14:49 
GeneralMemLeaks Pin
ThomT13-Mar-07 17:34
memberThomT13-Mar-07 17:34 
GeneralRe: MemLeaks Pin
Dan Madden14-Mar-07 16:40
memberDan Madden14-Mar-07 16:40 
GeneralRe: MemLeaks Pin
ThomT14-Mar-07 16:59
memberThomT14-Mar-07 16:59 
GeneralRe: MemLeaks [modified] Pin
Dan Madden18-Mar-07 4:52
memberDan Madden18-Mar-07 4:52 
GeneralRe: MemLeaks Pin
ThomT19-Mar-07 16:05
memberThomT19-Mar-07 16:05 
GeneralRe: MemLeaks Pin
Dan Madden18-Mar-07 7:59
memberDan Madden18-Mar-07 7:59 
GeneralNice class, but... Pin
Alcnedlor10-Dec-06 22:01
memberAlcnedlor10-Dec-06 22:01 
GeneralHere is THE solution Pin
Alcnedlor18-Dec-06 3:19
memberAlcnedlor18-Dec-06 3:19 
GeneralRe: Here is THE solution Pin
Dan Madden14-Mar-07 15:59
memberDan Madden14-Mar-07 15:59 
GeneralRe: Here is THE solution Pin
Dan Madden18-Mar-07 8:00
memberDan Madden18-Mar-07 8:00 
GeneralRe: Here is THE solution Pin
Alcnedlor6-Jun-07 6:26
memberAlcnedlor6-Jun-07 6:26 
GeneralVery good job indeed, but ... Pin
SebaM20-Sep-06 11:39
memberSebaM20-Sep-06 11:39 
GeneralRe: Very good job indeed, but ... Pin
Dan Madden20-Sep-06 12:14
memberDan Madden20-Sep-06 12:14 
Answer* DONE * - Regular Expression for Search in the Class Pin
Dan Madden21-Sep-06 8:23
memberDan Madden21-Sep-06 8:23 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
SebaM22-Sep-06 13:31
memberSebaM22-Sep-06 13:31 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
Dan Madden22-Sep-06 16:22
memberDan Madden22-Sep-06 16:22 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
SebaM22-Sep-06 21:20
memberSebaM22-Sep-06 21:20 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
Dan Madden23-Sep-06 14:52
memberDan Madden23-Sep-06 14:52 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
SebaM24-Sep-06 10:40
memberSebaM24-Sep-06 10:40 
GeneralRe: * DONE * - Regular Expression for Search in the Class Pin
SebaM1-Oct-06 5:52
memberSebaM1-Oct-06 5:52 
GeneralQuestion about changing registry values Pin
chris17512-Jul-06 11:02
memberchris17512-Jul-06 11:02 
AnswerRe: Question about changing registry values Pin
Dan Madden12-Jul-06 18:36
memberDan Madden12-Jul-06 18:36 
GeneralRe: Question about changing registry values Pin
chris17513-Jul-06 1:05
memberchris17513-Jul-06 1:05 
AnswerRe: Question about changing registry values Pin
Dan Madden15-Jul-06 10:11
memberDan Madden15-Jul-06 10:11 
GeneralVery good job Pin
42887-Jul-06 2:54
member42887-Jul-06 2:54 
GeneralRe: Very good job Pin
Dan Madden8-Jul-06 14:12
memberDan Madden8-Jul-06 14:12 
QuestionAuthor: New "NtRegEdit" Application Help (requested) Pin
Dan Madden29-Jun-06 9:39
memberDan Madden29-Jun-06 9:39 
GeneralSearch Interface Pin
Tcpip200527-Jun-06 18:53
memberTcpip200527-Jun-06 18:53 
GeneralRe: Search Interface Pin
Dan Madden27-Jun-06 18:59
memberDan Madden27-Jun-06 18:59 
GeneralAuthor: Coming, NtRegistry Editor with a new look (Kinda) Pin
Dan Madden27-Jun-06 15:28
memberDan Madden27-Jun-06 15:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 5 Sep 2006
Article Copyright 2006 by Dan Madden
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid