3 # Copyright (C) 2010 Red Hat Inc.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 use Sys::Guestfs::Lib qw(open_guest);
25 use Win::Hivex::Regedit qw(reg_import reg_export);
29 use File::Temp qw/tempdir/;
31 use Locale::TextDomain 'libguestfs';
37 virt-win-reg - Export and merge Windows Registry entries from a Windows guest
41 virt-win-reg domname 'HKLM\Path\To\Subkey'
43 virt-win-reg domname 'HKLM\Path\To\Subkey' name
45 virt-win-reg domname 'HKLM\Path\To\Subkey' @
47 virt-win-reg --merge domname [input.reg ...]
49 virt-win-reg [--options] disk.img ... # instead of domname
53 You must I<not> use C<virt-win-reg> with the C<--merge> option on live
54 virtual machines. If you do this, you I<will> get irreversible disk
55 corruption in the VM. C<virt-win-reg> tries to stop you from doing
56 this, but doesn't catch all cases.
58 Modifying the Windows Registry is an inherently risky operation. The format
59 is deliberately obscure and undocumented, and Registry changes
60 can leave the system unbootable. Therefore when using the C<--merge>
61 option, make sure you have a reliable backup first.
65 This program can export and merge Windows Registry entries from a
68 The first parameter is the libvirt guest name or the raw disk image of
71 If C<--merge> is I<not> specified, then the chosen registry
72 key is displayed/exported (recursively). For example:
74 $ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft'
76 You can also display single values from within registry keys,
79 $ cvkey='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
80 $ virt-win-reg Windows7 $cvkey ProductName
83 With C<--merge>, you can merge a textual regedit file into
86 $ virt-win-reg --merge Windows7 changes.reg
88 =head2 SUPPORTED SYSTEMS
90 The program currently supports Windows NT-derived guests starting with
91 Windows XP through to at least Windows 7.
93 Registry support is done for C<HKEY_LOCAL_MACHINE\SAM>,
94 C<HKEY_LOCAL_MACHINE\SECURITY>, C<HKEY_LOCAL_MACHINE\SOFTWARE>,
95 C<HKEY_LOCAL_MACHINE\SYSTEM> and C<HKEY_USERS\.DEFAULT>.
97 You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
98 C<HKU> for C<HKEY_USERS>.
100 C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are B<not> supported at
105 This program is only meant for simple access to the registry. If you
106 want to do complicated things with the registry, we suggest you
107 download the Registry hive files from the guest using L<libguestfs(3)>
108 or L<guestfish(1)> and access them locally, eg. using L<hivex(3)>,
109 L<hivexsh(1)> or L<hivexregedit(1)>.
113 C<virt-win-reg> expects that regedit files have already been reencoded
114 in the local encoding. Usually on Linux hosts, this means UTF-8 with
115 Unix-style line endings. Since Windows regedit files are often in
116 UTF-16LE with Windows-style line endings, you may need to reencode the
117 whole file before or after processing.
119 To reencode a file from Windows format to Linux (before processing it
120 with the C<--merge> option), you would do something like this:
122 iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg
124 To go in the opposite direction, after exporting and before sending
125 the file to a Windows user, do something like this:
127 unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg
129 For more information about encoding, see L<Win::Hivex::Regedit(3)>.
131 If you are unsure about the current encoding, use the L<file(1)>
132 command. Recent versions of Windows regedit.exe produce a UTF-16LE
133 file with Windows-style (CRLF) line endings, like this:
136 software.reg: Little-endian UTF-16 Unicode text, with very long lines,
137 with CRLF line terminators
139 This file would need conversion before you could C<--merge> it.
143 Be careful when passing parameters containing C<\> (backslash) in the
144 shell. Usually you will have to use 'single quotes' or double
145 backslashes (but not both) to protect them from the shell.
147 Paths and value names are case-insensitive.
149 =head2 CurrentControlSet etc.
151 Registry keys like C<CurrentControlSet> don't really exist in the
152 Windows Registry at the level of the hive file, and therefore you
155 C<CurrentControlSet> is usually an alias for C<ControlSet001>. In
156 some circumstances it might refer to another control set. The way
157 to find out is to look at the C<HKLM\SYSTEM\Select> key:
159 # virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select'
160 [HKEY_LOCAL_MACHINE\SYSTEM\Select]
161 "Current"=dword:00000001
162 "Default"=dword:00000001
163 "Failed"=dword:00000000
164 "LastKnownGood"=dword:00000002
166 "Current" is the one which Windows will choose when it boots.
168 Similarly, other C<Current...> keys in the path may need to
189 Display version number and exit.
197 Enable debugging messages.
203 =item B<--connect URI> | B<-c URI>
205 If using libvirt, connect to the given I<URI>. If omitted, then we
206 connect to the default libvirt hypervisor.
208 If you specify guest block devices directly, then libvirt is not used
215 =item B<--format> raw
217 Specify the format of disk images given on the command line. If this
218 is omitted then the format is autodetected from the content of the
221 If disk images are requested from libvirt, then this program asks
222 libvirt for this information. In this case, the value of the format
223 parameter is ignored.
225 If working with untrusted raw-format guest disk images, you should
226 ensure the format is always specified.
234 In merge mode, this merges a textual regedit file into the Windows
235 Registry of the virtual machine. If this flag is I<not> given then
236 virt-win-reg displays or exports Registry entries instead.
238 Note that C<--merge> is I<unsafe> to use on live virtual machines, and
239 will result in disk corruption. However exporting (without this flag)
246 =item B<--encoding> UTF-16LE|ASCII
248 When merging (only), you may need to specify the encoding for strings
249 to be used in the hive file. This is explained in detail in
250 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>.
252 The default is to use UTF-16LE, which should work with recent versions
259 GetOptions ("help|?" => \$help,
260 "version" => \$version,
261 "connect|c=s" => \$uri,
262 "debug|d" => \$debug,
263 "format=s" => \$format,
265 "encoding=s" => \$encoding,
267 pod2usage (1) if $help;
269 my $g = Sys::Guestfs->new ();
270 my %h = $g->version ();
271 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
275 # virt-win-reg only takes a single disk image ...
276 die __"no libvirt domain name or disk image given\n" if @ARGV == 0;
277 my $domname_or_image = shift @ARGV;
279 warn "launching libguestfs ..." if $debug;
281 my @lib_args = ([$domname_or_image]);
282 push @lib_args, address => $uri if $uri;
283 push @lib_args, rw => 1 if $merge;
284 push @lib_args, format => $format if defined $format;
285 my $g = open_guest (@lib_args);
288 warn "inspecting guest ..." if $debug;
290 my @roots = $g->inspect_os ();
292 die __x("{prog}: No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n",
293 prog => basename ($0));
296 die __x("{prog}: multiboot operating systems are not supported.\n",
297 prog => basename ($0))
299 my %fses = $g->inspect_get_mountpoints ($roots[0]);
300 my @fses = sort { length $a <=> length $b } keys %fses;
301 my $mountopts = $merge ? "" : "ro";
303 $g->mount_options ($mountopts, $fses{$_}, $_);
306 my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
308 # Create a working directory to store the downloaded registry files.
309 my $tmpdir = tempdir (CLEANUP => 1);
311 # Only used when merging to map downloaded hive names to hive handles.
314 if (!$merge) { # Export mode.
315 die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n"
316 if @ARGV < 1 || @ARGV > 2;
318 my $path = shift @ARGV;
319 my $name = shift @ARGV; # or undef
321 # Map this to the hive name. This function dies on failure.
322 my ($hivename, $prefix);
323 ($hivename, $path, $prefix) = map_path_to_hive ($path);
325 # Download the chosen hive.
326 download_hive ($hivename);
329 my $h = Win::Hivex->open ("$tmpdir/$hivename", debug => $debug);
333 warn "exporting $path from $hivename with prefix $prefix ..." if $debug;
334 reg_export ($h, $path, \*STDOUT, prefix => $prefix);
336 # Export a single key using hivexget.
337 my @args = ("hivexget", "$tmpdir/$hivename", $path, $name);
338 warn "running ", join (" ", @args), " ..." if $debug;
339 system (@args) == 0 or die "hivexget failed: $?"
342 else { # Import mode.
344 reg_import (\*STDIN, \&import_mapper, encoding => $encoding);
347 open my $fh, $_ or die "open: $_: $!";
348 reg_import ($fh, \&import_mapper, encoding => $encoding);
352 # Now we've done importing, commit all the hive handles and
354 $_->commit (undef) foreach values %hives;
357 # Look in the tmpdir for all the hive files which have been
358 # downloaded / modified by the import mapper, and upload
360 opendir my $dh, $tmpdir or die "$tmpdir: $!";
361 foreach (readdir $dh) {
374 # map function passed to reg_import.
379 my ($hivename, $path, $prefix) = map_path_to_hive ($_);
381 # Need to download this hive?
382 unless (-f "$tmpdir/$hivename") {
383 download_hive ($hivename);
385 my $h = Win::Hivex->open ("$tmpdir/$hivename",
386 write => 1, debug => $debug);
387 $hives{$hivename} = $h;
390 return ($hives{$hivename}, $path);
393 # Given a path, map that to the name of the hive and the true path
398 my ($hivename, $prefix);
400 if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) {
402 $_ = defined $1 ? $1 : "\\";
403 $prefix = "HKEY_LOCAL_MACHINE\\SAM";
405 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) {
406 $hivename = "security";
407 $_ = defined $1 ? $1 : "\\";
408 $prefix = "HKEY_LOCAL_MACHINE\\SECURITY";
410 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) {
411 $hivename = "software";
412 $_ = defined $1 ? $1 : "\\";
413 $prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE";
415 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) {
416 $hivename = "system";
417 $_ = defined $1 ? $1 : "\\";
418 $prefix = "HKEY_LOCAL_MACHINE\\SYSTEM";
420 elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) {
421 $hivename = "default";
422 $_ = defined $1 ? $1 : "\\";
423 $prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT";
426 die __x("virt-win-reg: {p}: not a supported Windows Registry path\n",
430 return ($hivename, $_, $prefix);
433 # Download a named hive file. Die on failure.
437 my $hivename = shift;
439 my $winfile_before = "$systemroot/system32/config/$hivename";
441 eval { $winfile = $g->case_sensitive_path ($winfile_before); };
443 die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
444 p => $winfile_before, err => $@);
447 warn "downloading $winfile ..." if $debug;
448 eval { $g->download ($winfile, "$tmpdir/$hivename"); };
450 die __x("virt-win-reg: {p}: could not download registry file: {err}\n",
451 p => $winfile, err => $@);
455 # Upload a named hive file. Die on failure.
459 my $hivename = shift;
461 my $winfile_before = "$systemroot/system32/config/$hivename";
463 eval { $winfile = $g->case_sensitive_path ($winfile_before); };
465 die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
466 p => $winfile_before, err => $@);
469 warn "uploading $winfile ..." if $debug;
470 eval { $g->upload ("$tmpdir/$hivename", $winfile); };
472 die __x("virt-win-reg: {p}: could not upload registry file: {err}\n",
473 p => $winfile, err => $@);
479 Libvirt guest names can contain arbitrary characters, some of which
480 have meaning to the shell such as C<#> and space. You may need to
481 quote or escape these characters on the command line. See the shell
482 manual page L<sh(1)> for details.
493 L<Sys::Guestfs::Lib(3)>,
495 L<Win::Hivex::Regedit(3)>,
497 L<http://libguestfs.org/>.
501 When reporting bugs, please enable debugging and capture the
504 export LIBGUESTFS_DEBUG=1
505 virt-win-reg --debug [... rest ...] > /tmp/virt-win-reg.log 2>&1
507 Attach /tmp/virt-win-reg.log to a new bug report at
508 L<https://bugzilla.redhat.com/>
512 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
516 Copyright (C) 2010 Red Hat Inc.
518 This program is free software; you can redistribute it and/or modify
519 it under the terms of the GNU General Public License as published by
520 the Free Software Foundation; either version 2 of the License, or
521 (at your option) any later version.
523 This program is distributed in the hope that it will be useful,
524 but WITHOUT ANY WARRANTY; without even the implied warranty of
525 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
526 GNU General Public License for more details.
528 You should have received a copy of the GNU General Public License
529 along with this program; if not, write to the Free Software
530 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.