virt-win-reg is a high level tool for merging Registry changes into a Windows virtual machine, and it’s relatively simple to use. You can also do the same thing using the libguestfs and hivex APIs directly, but it’s a little bit more complicated. This posting explains how to do it the low-level way.
First understand how the Windows Registry is stored in a VM: Although in Windows it appears as a single tree with top level nodes called things like HKEY_LOCAL_MACHINE
and HKEY_USERS
, this is not how Windows actually stores it. The Registry is split across several files called hives. Hivex is a C library for editing this proprietary file format.
A hive file contains a part of the Registry tree. The main hives of interest are called SOFTWARE
, SYSTEM
, SECURITY
and SAM
(without any file extension) and they live in the %systemroot%\System32\Config
directory. These hives correspond to HKEY_LOCAL_MACHINE\SOFTWARE
, HKEY_LOCAL_MACHINE\SYSTEM
etc. There are also per-user hives stored in a location that depends on the version of Windows.
Another thing to understand about the Registry is that when Windows is running it synthesizes “symbolic keys” which don’t really exist in the hive file itself. The most important example is CurrentControlSet
which is really a link to ControlSetXXX
as explained in more detail here.
The plan for modifying a Windows Registry in an offline guest is therefore:
- Locate the hive file containing the key we want to modify.
- Download it to a local temporary directory.
- Modify it with hivex.
- Upload it back to the Windows VM.
(This is essentially what virt-win-reg does).
Below is a Python program which demonstrates how to change the hostname of a Windows VM using the APIs directly. The hostname is stored in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters
in the key Hostname
. Notice from the name that this is located in the SYSTEM
hive file, and in this case I’m going to assume that CurrentControlSet
is a link to ControlSet001
but in reality you’d probably want to read this and do it right.
A final note: Although this changes the Hostname registry key correctly, Windows doesn’t use this key for very much (in particular, this is not the name of the machine), and also it appears that Windows will overwrite this key with whatever name it gets from a DHCP server. It’s only an example though …
#!/usr/bin/python
import guestfs
import hivex
windows_domain = "Win7x32"
# Windows strings stored in the registry have a trailing NUL
new_hostname = "win7x32\0"
# Use libguestfs to download the HKEY_LOCAL_MACHINE\SYSTEM hive.
g = guestfs.GuestFS ()
g.add_domain (windows_domain)
g.launch ()
roots = g.inspect_os ()
root = roots[0]
g.mount_options ("", root, "/")
systemroot = g.inspect_get_windows_systemroot (root)
path = "%s/system32/config/system" % systemroot
path = g.case_sensitive_path (path)
g.download (path, "/tmp/system")
# Open the hive file for writing.
h = hivex.Hivex ("/tmp/system", write=True)
# Navigate down to the TCP/IP parameters.
key = h.root ()
key = h.node_get_child (key, "ControlSet001")
key = h.node_get_child (key, "Services")
key = h.node_get_child (key, "Tcpip")
key = h.node_get_child (key, "Parameters")
# Get the old hostname.
val = h.node_get_value (key, "Hostname")
old_hostname = h.value_value (val)
# Keep the old type (probably 1 = string)
type = old_hostname[0]
# The registry key is encoded as UTF-16LE.
old_hostname = old_hostname[1].decode ('utf-16le').encode ('utf-8')
print "old hostname = %s" % old_hostname
# Change the hostname.
new_hostname = new_hostname.encode ('utf-16le')
new_value = { 'key': "Hostname", 't': type,
'value': new_hostname }
h.node_set_value (key, new_value)
# Commit the changes to the hive.
h.commit (None)
# Upload the hive back to the guest.
g.upload ("/tmp/system", path)
# This is only needed for libguestfs < 1.5.24, but
# it won't hurt for newer versions.
g.sync ()