The Windows registry is this weird amalgamation of structured data that is used for anything from tracking system usage statistics to storing user information and serving as a temporary data buffer to be read from and written to. The operating system also relies on the registry for tons of fairly important functionality and information, namely (in the context of malware) establishing what programs are run at startup, what users reside on the system, and so on.
In my reverse-reverse engineering research I've found it incredibly useful to at least understand the basic CRUD (Create Read Update and Delete) methods associated with the registry. I've implemented some basic functionality in the malware I've developed to write to the registry and read from it, and in this article I'm going to talk about how I handle these interactions with the registry and how I use the registry in my malware.
Want to check out the next article in the malware development series? Check out "Simple Registry Operations for Malware Development, Part 2 - RegSetKeyValue."
Step One: Obtaining a Key Handle
Because Microsoft was made by a series of satanic masochists installed by the Dark Overlord himself, pretty much everything about the Windows OS operates using a data structure called handles. I'll probably do a deep dive into just what a handle is from the OS perspective at some point on this blog, because I could honestly do with understanding it a little better, but essentially, a handle is just a data structure that is created, handled, maintained and destroyed by the operating system and is given to the userspace programs whenever they have to interact with various lower-level functionalities built within the API.
The first step to any interaction with the registry is obtaining a handle for a registry key. Now, I'm going to keep dunking on the Windows OS design, because it sucks, but I will say I like the way they deal with obtaining handles: you call the "RegCreateKey" API function and, as long as the permissions for the key you're trying to deal with are valid in the context of your user, it will either return the key handle if the key exists already, or it will create the key and return the handle if it doesn't. This means you don't have to code in explicit checks for the existence of a particular key, you can just write one function (like the one below) and the OS will handle all the checks.
An even more powerful functionality is that the OS handles the creation of nested keys as well. The registry is laid out in a similar way to the file system: keys are nested within folders and subfolders and more subfolders. For example, this is the structure of one of the testing keys I created when developing my malware:
If I call the API correctly, in one swoop it will create the wulf folder, the comms folder, the wulf_comms folder and the commskey key as subfolders of the HKEY_CURRENT_USER hive. This makes it way easier to make complex webs of nested subfolders and registry keys to hide secret values and such, without having to check for the existence of a given folder at every step. Keep in mind, though, that this extensibility also means that the OS will explicitly create those subfolders every time, without asking you, so if you keep messing with the registry values in testing, you're going to make a mess of your registry.
Here is the code required to return a handle to a registry key.
The C++ code necessary to get a handle to a registry key.
I left the MSDN Documentation for the relevant function for your convenience, but I'll also go through it here.
The extended version of the API function (RegCreateKeyExA) takes a fair bit of information in as input.
The first is an initial HKEY value that you can kind of view as a root folder. This doesn't have to be an open key handle. I used the typedef'ed value "HKEY_CURRENT_USER" because I knew I shouldn't have any permissions problems with that registry key, since it's explicitly associated with the context of the currently running user.
The second is a representation of the subkey. This is going to be where you actually decide where your key resides. I'm using the ASCII version of the RegCreateKeyEx function (RegCreateKeyExA) so I just passed a constant character pointer to a string (pictured above, "wulf\comms") so that when I create this key, it will reside at "HKEY_CURRENT_USER\wulf\comms."
After this, we have a reserved variable, which, for whatever demonic reasons Microsoft has determined, just has to be 0. Equally confusing, the next variable is the lpClass input, which MSDN just... doesn't really explain in the article on RegCreateKeyEx, but they said it can be NULL so NULL it is.
After that, we get the dwOptions variable, which just means a DWORD that represents what kind of key we're creating. For malware, we want most of our keys to exist after reboot, meaning we want them to be "non-volatile" so we will use the typedef'ed value "REG_OPTION_NON_VOLATILE" to create a non-volatile key.
I promise, we're almost to the end here... Next, we have the level of desired access. Because I'm planning on using this key to read from and write to, I want to use the KEY_ALL_ACCESS typedef to create a key I have complete access to. After that is the security attributes, which I'm not super worried about in this context.
The last two are the most important. PHKEY is a pointer to a key handle that we pass in to store the eventual open handle. We passed the pointer to an HKEY in to the function by reference, so we will pass this value to the function, as well as a pointer to a DWORD that will hold the disposition (or detailed result) of the function running. Oddly enough, the function isn't void: it also returns a LONG value that will be set to zero if it ran successfully.
Alright. That was a lengthy one. I'm going to leave this article here and we'll return to talk about reading from registry keys, writing to registry keys and how you can implement registry operations in your malware.