- Registry Functions
- Obsolete Functions
- Reading and Writing to the Windows Registry in-process from Node.js
- The Problem
- Overview of the Solution
- Interact with Windows APIs
- Creating Node Wrappers for Windows APIs
- Create Windows Data Types in JavaScript
- Enable Your Application
- Installation
- Reading and Writing to the Windows Registry
- Opening a Registry Key
- Creating a Key
- Deleting a Key
- Writing a Value to a Key
- Getting a Value From a Key
- Creating File Associations
- Launching Process as an Admin
- Opportunities for Reuse
Registry Functions
The following are the registry functions.
Function | Description |
---|---|
GetSystemRegistryQuota | Retrieves the current size of the registry and the maximum size that the registry is allowed to attain on the system. |
RegCloseKey | Closes a handle to the specified registry key. |
RegConnectRegistry | Establishes a connection to a predefined registry handle on another computer. |
RegCopyTree | Copies the specified registry key, along with its values and subkeys, to the specified destination key. |
RegCreateKeyEx | Creates the specified registry key. |
RegCreateKeyTransacted | Creates the specified registry key and associates it with a transaction. |
RegDeleteKey | Deletes a subkey and its values. |
RegDeleteKeyEx | Deletes a subkey and its values from the specified platform-specific view of the registry. |
RegDeleteKeyTransacted | Deletes a subkey and its values from the specified platform-specific view of the registry as a transacted operation. |
RegDeleteKeyValue | Removes the specified value from the specified registry key and subkey. |
RegDeleteTree | Deletes the subkeys and values of the specified key recursively. |
RegDeleteValue | Removes a named value from the specified registry key. |
RegDisablePredefinedCache | Disables handle caching for the predefined registry handle for HKEY_CURRENT_USER for the current process. |
RegDisablePredefinedCacheEx | Disables handle caching for all predefined registry handles for the current process. |
RegDisableReflectionKey | Disables registry reflection for the specified key. |
RegEnableReflectionKey | Enables registry reflection for the specified disabled key. |
RegEnumKeyEx | Enumerates the subkeys of the specified open registry key. |
RegEnumValue | Enumerates the values for the specified open registry key. |
RegFlushKey | Writes all attributes of the specified open registry key into the registry. |
RegGetKeySecurity | Retrieves a copy of the security descriptor protecting the specified open registry key. |
RegGetValue | Retrieves the type and data for the specified registry value. |
RegLoadKey | Creates a subkey under HKEY_USERS or HKEY_LOCAL_MACHINE and stores registration information from a specified file into that subkey. |
RegLoadMUIString | Loads the specified string from the specified key and subkey. |
RegNotifyChangeKeyValue | Notifies the caller about changes to the attributes or contents of a specified registry key. |
RegOpenCurrentUser | Retrieves a handle to the HKEY_CURRENT_USER key for the user the current thread is impersonating. |
RegOpenKeyEx | Opens the specified registry key. |
RegOpenKeyTransacted | Opens the specified registry key and associates it with a transaction. |
RegOpenUserClassesRoot | Retrieves a handle to the HKEY_CLASSES_ROOT key for the specified user. |
RegOverridePredefKey | Maps a predefined registry key to a specified registry key. |
RegQueryInfoKey | Retrieves information about the specified registry key. |
RegQueryMultipleValues | Retrieves the type and data for a list of value names associated with an open registry key. |
RegQueryReflectionKey | Determines whether reflection has been disabled or enabled for the specified key. |
RegQueryValueEx | Retrieves the type and data for a specified value name associated with an open registry key. |
RegReplaceKey | Replaces the file backing a registry key and all its subkeys with another file. |
RegRestoreKey | Reads the registry information in a specified file and copies it over the specified key. |
RegSaveKey | Saves the specified key and all of its subkeys and values to a new file. |
RegSaveKeyEx | Saves the specified key and all of its subkeys and values to a new file. You can specify the format for the saved key or hive. |
RegSetKeyValue | Sets the data for the specified value in the specified registry key and subkey. |
RegSetKeySecurity | Sets the security of an open registry key. |
RegSetValueEx | Sets the data and type of a specified value under a registry key. |
RegUnLoadKey | Unloads the specified registry key and its subkeys from the registry. |
The following shell functions can be used with the registry:
The following are the initialization-file functions. They retrieve information from and copy information to a system- or application-defined initialization file. These functions are provided only for compatibility with 16-bit versions of Windows. New applications should use the registry.
Function | Description |
---|---|
GetPrivateProfileInt | Retrieves an integer associated with a key in the specified section of an initialization file. |
GetPrivateProfileSection | Retrieves all the keys and values for the specified section of an initialization file. |
GetPrivateProfileSectionNames | Retrieves the names of all sections in an initialization file. |
GetPrivateProfileString | Retrieves a string from the specified section in an initialization file. |
GetPrivateProfileStruct | Retrieves the data associated with a key in the specified section of an initialization file. |
GetProfileInt | Retrieves an integer from a key in the specified section of the Win.ini file. |
GetProfileSection | Retrieves all the keys and values for the specified section of the Win.ini file. |
GetProfileString | Retrieves the string associated with a key in the specified section of the Win.ini file. |
WritePrivateProfileSection | Replaces the keys and values for the specified section in an initialization file. |
WritePrivateProfileString | Copies a string into the specified section of an initialization file. |
WritePrivateProfileStruct | Copies data into a key in the specified section of an initialization file. |
WriteProfileSection | Replaces the contents of the specified section in the Win.ini file with specified keys and values. |
WriteProfileString | Copies a string into the specified section of the Win.ini file. |
Obsolete Functions
These functions are provided only for compatibility with 16-bit versions of Windows:
Reading and Writing to the Windows Registry in-process from Node.js
November 9th, 2015
A single npm module that enables Node developers to update the Windows Registry, create file associations, and much more!
As cross-platform application developers, we often need the ability to update the Windows Registry when installing our applications on Windows. Common features requested by app developers include reading and writing keys to Windows registry, creating file associations to make Windows assign default programs to a file extension, elevating processes to run as an administrator, and last but not least doing all this within the same process as our application. This code story describes the development of a single npm module that enables Node developers to perform these commonly-requested operations when installing Node.js applications on Windows.
The Problem
Electron is a framework that enables developers to build cross platform desktop apps with web technologies. Electron is behind Microsoft’s Visual Studio Code, Slack’s Apps, GitHub’s Atom, Facebook’s Nucleide, Docker’s Kinematic, and a few others. Prior to our hackathon with GitHub and Visual Studio Code, developers had limited number of resources to work with in order to interact with the Windows Registry. More importantly, there was no library that allowed developers to update the Windows Registry without running a separate shell script. In order to provide a great app install experience for users on Windows, it was obvious that we needed to create an NPM module that can address all these issues. Since this is an NPM module, all Node.js applications can take advantage of this library.
Overview of the Solution
We partnered with GitHub, the maintainer of Electron, and Visual Studio Code, to enable registry operations and file associations for Node application on Windows. As a result of our hackathon, there is now a NPM module for Windows Registry you can include in your application to do just that.
Interact with Windows APIs
This library interacts with native Windows APIs. We leveraged the following node modules to enable Node.js application to communicate with Windows interfaces:
- node-ffi – A Node.js addon for loading and calling dynamic libraries using pure Javascript. It can be used to create bindings to native libraries without writing any C++ code. We used this module to load a list of Windows DLLs to call native Windows APIs.
- ref – A NPM module that turns buffer instances into pointers. We used this module to define our own data types to map to the Windows data types. It also conveniently allowed us to reference and dereference buffers.
- ref-struct – This module allows us to define and implement structures. In order to communicate with Windows APIs, we needed to implement structs that mirror the inputs and outputs of Windows interfaces.
We leveraged the following Windows DLLs to communicate with Windows APIs.
- Advapi32.dll – A Windows library that supports numerous APIs including many security and registry calls. We used this library to perform CRUD operations in Windows Registry.
- Shell32.dll – A Windows library that contains Windows Shell APIs, which are used to open processes on Windows. We used this library to elevate privilege of a process, similar to the experience of running an application as an administrator.
Let’s dive into the code!
Creating Node Wrappers for Windows APIs
We created a Node wrapper adv_api.js for Advapi32.dll to support basic registry manipulation. Using the ffi node module, we can load Advapi32.dll . Then for each Windows API we wanted to call, we created a Javascript API with the expected inputs and outputs.
In C++, the follow example illustrates the use of RegOpenCurrentUser , which is used to retrieve a handle to the HKEY_CURRENT_USER key for the user that the current thread is impersonating.
In our Node.js wrapper, we used the ffi node module to load Advapi32.dll . Then we defined the RegOpenCurrentUser API and other Registry related APIs with their expected inputs and outputs.
Similarly, we created a Node wrapper shell32.js for Shell32.dll , which is used to launch a process on Windows.
In C++, the following example illustrates the use of ShellExecuteEx to launch an application:
In our Node.js wrapper, we used the ffi node module to load Shell32.dll , then we defined the ShellExecuteExA API with its expected input and output.
Create Windows Data Types in JavaScript
As you can see, in order to replicate the exact Windows API call in JavaScript, we needed to create the same function with the exact inputs and outputs of the same data type as the Windows native data types.
For example, let’s look at how we defined the data types for the ShellExecuteExA function.
In C++, here is the definition of the ShellExecuteEx function:
With the above definition, ShellExecuteEx expects a parameter of the type SHELLEXECUTEINFO* , which is a pointer to a SHELLEXECUTEINFO structure that contains and receives information about the application being executed. It also has a return value of type BOOL , which represents the result of executing this call.
In C++, this is the definition of the SHELLEXECUTEINFO structure.
In our Node.js wrapper windef.js, we needed to create the same SHELLEXECUTEINFO struct. Luckily, we had ref , ref struct , and ref union node modules to help us create the same struct in Javascript.
For each struct member, we looked up its data type and its size from the Windows Data Types reference, then created the same data type with the same properties in types.js. We leveraged the ref node module to create types.
Now that we have the SHELLEXECUTEINFO struct in Javascript, we can create an instance of it in utils.js with the runas ShellExecuteEx verb for the lpVerb member and a file path to the process we want to launch for the lpFile member. Let’s call it shellexecuteinfoval . This instance can then be used as a parameter of ShellExecuteExA so that we can launch a process with elevated privilege.
Let’s call ShellExecuteExA with a reference of our new parameter. We implemented this as an asynchronous call so that when the UAC (User Account Control) prompt is launched, the current process can continue to run without waiting on the user to respond.
To wrap everything together, we created an API elevate in our module that takes in a filepath of the process you want to launch, its parameters if any, and a callback to get the user’s response to the UAC prompt.
The process you want to launch with admin access will only be launched after the callback is called and only if the user clicks Yes in the UAC prompt. Otherwise, the process will not be launched. If the user is already running as an admin, the UAC prompt will not be triggered and the process you provided will be launched as an administrator automatically.
Here is an example of this running:
Enable Your Application
To add the NPM module for Windows Registry to your Node application, install the package:
Installation
To install node modules that require compilation on Windows, make sure you have installed the necessary build tools. Specifically, you need npm install -g node-gyp , a cross-platform cli written in Node.js for native addon modules for Node.js.
To install node-gyp , you need to have the following prerequisites installed and configured in your development environment.
- Install python v2.7.3, and add it to your PATH , npm config set python python2.7
- Install VC++ Build Tools Technical Preview. You do not need to install the full Visual Studio, only the build tools are required.
- [Windows 7 only] requires .NET Framework 4.5.1
- Launch cmd, npm config set msvs_version 2015 —global (this is instead of npm install [package name] —msvs_version=2015 every time.)
Once the prerequisites are installed, you should be able to do npm install -g node-gyp .
Reading and Writing to the Windows Registry
This library implements only a few of the basic registry commands, which allow you to do basic CRUD operations for keys in the registry.
Opening a Registry Key
Registry keys can be opened by either opening a predefined registry key defined in the windef module:
Or you can open a sub key from an already opened key:
And don’t forget to close your key when you’re done. Otherwise, you will leak native resources:
Creating a Key
Creating a key just requires that you have a Key object by either using the predefined keys within the windef.HKEY or opening a subkey from an existing key.
Deleting a Key
To delete a key just call the Key.deleteKey() function.
Writing a Value to a Key
To write a value, you will again need a Key object and just need to call the Key.setValue function:
Getting a Value From a Key
To get a value from a key, just call Key.getValue :
The return value depends on the type of the key (REG_SZ for example will give you a string).
Creating File Associations
To create a file association, you can call the fileAssociation.associateExeForFile api, which will make windows assign a default program for an arbitrary file extension:
After running the code above, you will see files with the extension of .zzz will be automatically associated with the Node program and their file icon will be changed to the Node file icon.
Launching Process as an Admin
To launch a process as an Administrator, you can call the utils.elevate api, which will launch a process as an Administrator causing the UAC (User Account Control) elevation prompt to appear if required. This is similar to the Windows Explorer command “Run as administrator”. Pass in FILEPATH to the process you want to elevate. Pass in any PARAMETERS to run with the process. Since this is an asynchronous call, pass in a callback to handle user’s selection.
Opportunities for Reuse
With NPM module for Windows Registry, developers can now read and write keys to Windows registry, create file associations to make Windows assign default programs to a file extension, and elevate processes to run as an administrator for any Node application running on Windows. With the source code of this NPM module in GitHub, our solution also serves as an example for how to interact with native Windows APIs in Node.js.
Anthony Turner
Sr. Software Security Engineer, Security Solutions Area