How to create a brand new registry hive

First of all, the registry hive is a file that holds the  contents of a whole registry root key on disk, when Windows is not running. Normally there is not much point in creating them manually, you just edit the registry as usual, and it gets automatically saved on disk between the reboots.

However when messing with the installable images, it becomes interesting sometimes to be able to export some registry contents, and then make a new hive out of it. I wrote a PowerShell script for that. It's fairly ugly, and maybe there are some better solutions for its problems, but I haven't found any.

function
ConvertTo-RegistryHive
{
<#
.SYNOPSIS
Convert a registry-exported text (contents of a .reg file) to a binary registry hive file.

.EXAMPLE
PS> ConvertTo-RegistryHive -Text (Get-Content my.reg) -Hive my.hive
#>
    param(
        ## The contents of registry exported (.reg) file to convert into the hive.
        [string[]] $Text,
        ## The hive file name to write the result to.
        [parameter(Mandatory=$true)]
        [string] $Hive
    )

    $basefile = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
    $regfile = $basefile + ".reg"
    $inifile = $basefile + ".ini"
    $subkey = [System.Guid]::NewGuid().ToString()

    &{
        foreach ($chunk in $Text) {
            foreach ($line in ($chunk -split "`r")) {
                $line -replace "^\[\w*\\\w*","[HKEY_LOCAL_MACHINE\$subkey"
            }
        }
    } | Set-Content $regfile

    # Since bcdedit stores its data in the same hives as registry,
    # this is the way to create an almost-empty hive file.
    bcdedit /createstore $Hive
    if (!$?) { throw "failed to create the new hive '$Hive'" }

    reg load "HKLM\$subkey" $Hive
    if (!$?) { throw "failed to load the hive '$Hive' as 'HKLM\$subkey'" }

    try {
        # bcdedit creates some default entries that need to be deleted,
        # but first the permissions on them need to be changed to allow deletion
@"
HKEY_LOCAL_MACHINE\$subkey\Description [1]
HKEY_LOCAL_MACHINE\$subkey\Objects [1]
"@ | Set-Content $inifile
        regini $inifile
        if (!$?) { throw "failed to change permissions on keys in 'HKLM\$subkey'" }
        Remove-Item -LiteralPath "hklm:\$subkey\Description" -Force -Recurse
        Remove-Item -LiteralPath "hklm:\$subkey\Objects" -Force -Recurse

        # now import the file contents
        reg import $regfile
        if (!$?) { throw "failed to import the data from '$regfile'" }
    } finally {
        reg unload "HKLM\$subkey"
        Remove-Item -LiteralPath $inifile -Force
    }

    Remove-Item -LiteralPath $regfile -Force
}

What goes inside:

  • There seems to be no obvious way to create an empty hive file. So bcdedit is abused for this purpose. It's normal purpose is to set the configuration for the Windows boot loader. Which configuration happens to be also stored in the Registry hive format. So a new BCD store is an almost-empty hive. Almost, but that will be dealt with in a moment.
  • To modify this hive, it gets loaded (kind of like mounted) into the active Registry at a temporary key. This is the normal way of modifying the hives. Note that the contents of parameter -Text also got modified to point to this path before writing it to a temporary .reg file.
  • The created hive is not quite empty, there are two keys in it, and if we want to make things nice and clear, they have to be removed. Which is not a straightforward task, since they have no remove access rights on them. I've tried changing the access rights through the .NET classes and I couldn't get them to work. Still not sure, what was going wrong. But there is the command regini that can change every which permissions in the registry. The [1] in its arguments means "grant full access to the Builtin Administrators group". After that the keys can be removed.
  • And finally the contents of the .reg file gets imported and the hive unloaded.

That's hacks upon hacks but in the end it does the job.