Containerized registry hives in Windows

If you read my Windows registry file format specification, you might already know about layered keys. Today, let’s talk about them in more detail.

Some editions of Windows 10 are capable of running Windows containers using Docker. Each Docker container is based on an immutable image with all modified data stored in an overlay. When a Windows container is used, the system has to record modifications affecting both the file system and the registry.

In 2016, Microsoft implemented new functionality called layered keys to allow programs access a merged view of keys and values from two or more registry hives! Now, this functionality is utilized by Docker…

The idea behind the concept and most implementation details have been documented in US20170279678A1. The idea is simple: you can load a base hive, then you can load any number of hives that act like stacked overlays (or as a single overlay when only one additional hive is loaded on top of a base hive). Actually, the number of overlay hives is limited to 127, but I guess that nobody will ever hit such a limit. A base hive and its overlay hives will be referred to as a hive stack.

Each key in the overlay hive can state that (let’s assume that only one overlay hive is present in the hive stack):

  1. this and the same key in the base hive must be treated as deleted in the merged view;
  2. this and the same value in the base hive must be treated as deleted in the merged view;
  3. this key supersedes the same key in the base hive, using one of these variants:
    • only the key in question is affected (but not its subkeys) in the merged view,
    • the key in question and its subkeys are affected (the entire tree below this key is overridden) in the merged view;
  4. this key and its values are merged with the same key in the base hive to produce the merged view (values from the overlay hive supersede the same values in the base hive);
  5. and, in addition to the above, there is a flag to indicate that this key must inherit a class name from the same key in the base hive.

Here, the same key means a key having exactly the same path in another hive. And the same value means a value with the same name set within the same key (in another hive). If there are two or more overlay hives, replace the base hive with the base hive and preceding overlay hives (if any).

So, the system can produce cumulative information about keys and their values from stacked hives, mark a key or a value in the stack as deleted without actually removing these entities from the base hive and/or preceding overlay hives (only one overlay hive is affected by such removal), and override keys (with their values), so the merged view for these keys will be based on current overlaid data only.

For example:

  1. A base hive contains the following key: “test_key“, this key has the following value: “test_value_1“.
  2. An overlay hive contains the following key: “test_key“, this key has the following value: “test_value_2“.
  3. If no special flags are set for these keys and values, the merged view will contain one key named “test_key“, this key will have two values: “test_value_1” and “test_value_2“.
  4. If that key in an overlay hive has the supersede flag set, the merged view will contain one key named “test_key“, this key will have one value only: “test_value_2“.

Another example:

  1. A base hive contains the following key: “test_key_1“, this key has the following value: “test_value“.
  2. An overlay hive contains the following key: “test_key_2“, this key has the following value: “test_value“.
  3. If no special flags are set for these keys and values, the merged view will contain two keys named “test_key_1” and “test_key_2“, each key will have one value named “test_value“.

And one more example:

  1. A base hive contains the following key: “test_key“, this key has the following value: “test_value“.
  2. An overlay hive contains the following key: “test_key“, this key has no values.
  3. If that key in an overlay hive has the tombstone flag set, the merged view will not list the key named “test_key” (this key will be treated as deleted). If you create a new key named “test_key“, this key will receive the supersede flag (and this key will be stored in an overlay hive).

Let’s take a look at the data using a HEX editor!

Install Docker in the Windows 10 “1809” Enterprise operating system (running on a physical machine), switch to Windows containers, get the “mcr.microsoft.com/windows:1809” image and start a new container based on this image, then do the following using the reg tool (without committing changes to a new base image):

  • under the “HKEY_LOCAL_MACHINE\System\ControlSet001\services” key, delete the “xboxgip” subkey;
  • under the same key, delete the “xboxgipsvc” subkey and create it again, then create a new subkey under that newly created key;
  • under the “HKEY_LOCAL_MACHINE\System\ControlSet001\services” key, delete the “displayname” value from the “xboxnetapisvc” subkey;
  • delete the “start” value from the same key and create it again.

The overlay files are located in the “/ProgramData/Docker/windowsfilter/<hash>/sandbox.vhdx” image. Let’s mount this image and extract registry hives located in the “/WcSandboxState/Hives/” directory. There are three files related to the SYSTEM hive:

  • System_Delta
  • system_Delta.LOG1
  • system_Delta.LOG2

I will focus on the first one (the primary file). After I open this file in a HEX editor, two things can be observed.

Снимок экрана от 2020-08-15 14-39-52
The header of the System_Delta hive file (primary)

First, the flags field (shown as selected, the offset is 0x90 bytes) has the 0x2 bit mask set. In Windows 10, this bit mask means “the layered keys feature is supported” (in Windows 8, this bit mask had a different meaning, Microsoft reused it for a different purpose in Windows 10).

Second, the version number of the hive format is 1.6 (see the following offsets for major and minor version numbers: 0x14 bytes and 0x18 bytes respectively). (Most hives have the following versions: 1.3 and 1.5.)

Next, navigate to the “xboxgip” subkey (it was deleted in my test). Notice its null timestamp (here is a link to the structure definition):

Key path: ControlSet001\Services\XBOXGIP
Last written timestamp (UTC): 1601-01-01 00:00:00
Access bits: 2
Owner SID: S-1-5-32-544
Снимок экрана от 2020-08-15 14-51-42
The “xboxgip” subkey

The layered key bit fields (stored in one byte) are shown as selected. Their value is 0x1, which stands for the following flag (here is a description): IsTombstone (this key must be treated as deleted). So, there is a key but it’s treated as deleted!

Then, navigate to the “xboxgipsvc” subkey (it was deleted and created again in my test).

Key path: ControlSet001\Services\xboxgipsvc
Last written timestamp (UTC): 2020-08-14 19:30:49.246132
Access bits: 2
Owner SID: S-1-5-93-2-1
Снимок экрана от 2020-08-15 15-03-38
The “xboxgipsvc” subkey

Again, the layered key bit fields are shown as selected, their value is 0x83, which stands for: IsSupersedeTree (0x3; this key and its subkeys supersede the same key in the base hive, the subkeys have the same flag set) and “Inherit class” (0x80).

The IsSupersedeTree flag is also set for the subkey of that key:

Снимок экрана от 2020-08-15 15-09-41
A subkey of the “xboxgipsvc” key

Now, navigate to the following values of the “xboxnetapisvc” subkey: “start” (this value was deleted and created again in my test) and “displayname” (this was deleted in my test). In the overlay hive, this key has only two values (I will show the layout of this key in a live container later):

Key path: ControlSet001\Services\XboxNetApiSvc
Last written timestamp (UTC): 2020-08-14 19:31:27.014190
Access bits: 2
Owner SID: S-1-5-18

Value name: start
Value type: REG_SZ
Data size: 2
Data (decoded):


Value name: displayname
Value type: REG_NONE
Data size: 0
Data (hexdump):


---
Снимок экрана от 2020-08-15 15-14-15
The “start” value
Снимок экрана от 2020-08-15 15-14-42
The “displayname” value

The flags field of each value is shown as selected.

For the “start” value, this flag is set to 0x1, which means that the name string is stored using extended ASCII characters (Latin-1, not UTF-16LE characters).

For the “displayname” value, two bit masks are set: 0x1 and 0x2 (thus, the value is 0x3). The latter one, according to the specification, stands for the following flag: IsTombstone. This means that this value must be treated as deleted.

In the live container, the subkey just mentioned contains more than two values (because two values listed above are merged with other values from the same key in the base hive):

overlay
The “xboxnetapisvc” subkey and its values as seen from the live container (after the test), note the absent “displayname” value

And where is a primary file for the base hive?

It seems that such files are stored (along with one additional layer of overlay hives; those could be initial overlay hives, not an additional layer, I’m not sure) in the “/ProgramData/Docker/windowsfilter/<hash>/Hives/” directory on a host machine (this directory belongs to a base image).


It’s also interesting that Microsoft decided to provide an additional flag to support the class name inheritance, although it looks like that no one seems to remember the real meaning of class names:

Bonus chatter: There’s also this thing called a class. I have no idea what it’s for, so don’t ask.

(Source.)

lpClass is reserved for future use. Just pass NULL for now.

(Source.)

MSDN, in general, does not provide a direct answer to this question. The funny thing is that no one knows the answer to this question, including the current developer responsible for maintaining the registry and his predecessor. 🙂 Or rather, not quite so. They certainly know what functionality is behind this parameter and how it is implemented. But they have no idea what this feature was originally intended for. Moreover, Raymond Chen, known for his excursions into history, also does not remember why it was necessary. The only clue – a letter from another developer, shedding even a little light, was irretrievably lost. And, of course, people who wrote this in retirement for a long time. 🙂

(Source.)

Today, a class name is just a string attached to a key, it has no special meaning for the kernel. An application can treat a class name in any way it wants.

Originally, a class name was intended to define the type of data stored in a key (I’m referring to times when a key could only have one value, which is now called the default or unnamed value); this was never implemented in the Windows NT hives (and an alternative approach was chosen: a key can have more than one value, each one has its own type).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s