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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 I<--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 I<--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 I<--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 I<--merge>, you can merge a textual regedit file into
86 $ virt-win-reg --merge Windows7 changes.reg
90 This program is only meant for simple access to the registry. If you
91 want to do complicated things with the registry, we suggest you
92 download the Registry hive files from the guest using L<libguestfs(3)>
93 or L<guestfish(1)> and access them locally, eg. using L<hivex(3)>,
94 L<hivexsh(1)> or L<hivexregedit(1)>.
114 Display version number and exit.
122 Enable debugging messages.
130 =item B<--connect URI>
132 If using libvirt, connect to the given I<URI>. If omitted, then we
133 connect to the default libvirt hypervisor.
135 If you specify guest block devices directly, then libvirt is not used
142 =item B<--format> raw
144 Specify the format of disk images given on the command line. If this
145 is omitted then the format is autodetected from the content of the
148 If disk images are requested from libvirt, then this program asks
149 libvirt for this information. In this case, the value of the format
150 parameter is ignored.
152 If working with untrusted raw-format guest disk images, you should
153 ensure the format is always specified.
161 In merge mode, this merges a textual regedit file into the Windows
162 Registry of the virtual machine. If this flag is I<not> given then
163 virt-win-reg displays or exports Registry entries instead.
165 Note that I<--merge> is I<unsafe> to use on live virtual machines, and
166 will result in disk corruption. However exporting (without this flag)
173 =item B<--encoding> UTF-16LE|ASCII
175 When merging (only), you may need to specify the encoding for strings
176 to be used in the hive file. This is explained in detail in
177 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>.
179 The default is to use UTF-16LE, which should work with recent versions
184 my $unsafe_printable_strings;
186 =item B<--unsafe-printable-strings>
188 When exporting (only), assume strings are UTF-16LE and print them as
189 strings instead of hex sequences. Remove the final zero codepoint
190 from strings if present.
192 This is unsafe and does not preserve the fidelity of strings in the
193 original Registry for various reasons:
199 Assumes the original encoding is UTF-16LE. ASCII strings and strings
200 in other encodings will be corrupted by this transformation.
204 Assumes that everything which has type 1 or 2 is really a string
205 and that everything else is not a string, but the type field in
206 real Registries is not reliable.
210 Loses information about whether a zero codepoint followed the string
211 in the Registry or not.
215 This all happens because the Registry itself contains no information
216 about how strings are encoded (see
217 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>).
219 You should only use this option for quick hacking and debugging of the
220 Registry contents, and I<never> use it if the output is going to be
221 passed into another program or stored in another Registry.
227 GetOptions ("help|?" => \$help,
228 "version" => \$version,
229 "connect|c=s" => \$uri,
230 "debug|d" => \$debug,
231 "format=s" => \$format,
233 "encoding=s" => \$encoding,
234 "unsafe-printable-strings" => \$unsafe_printable_strings,
236 pod2usage (1) if $help;
238 my $g = Sys::Guestfs->new ();
239 my %h = $g->version ();
240 print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
244 # virt-win-reg only takes a single disk image ...
245 die __"no libvirt domain name or disk image given\n" if @ARGV == 0;
246 my $domname_or_image = shift @ARGV;
248 warn "launching libguestfs ..." if $debug;
250 my @lib_args = ([$domname_or_image]);
251 push @lib_args, address => $uri if $uri;
252 push @lib_args, rw => 1 if $merge;
253 push @lib_args, format => $format if defined $format;
254 my $g = open_guest (@lib_args);
257 warn "inspecting guest ..." if $debug;
259 my @roots = $g->inspect_os ();
261 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",
262 prog => basename ($0));
265 die __x("{prog}: multiboot operating systems are not supported.\n",
266 prog => basename ($0))
268 my %fses = $g->inspect_get_mountpoints ($roots[0]);
269 my @fses = sort { length $a <=> length $b } keys %fses;
270 my $mountopts = $merge ? "" : "ro";
272 $g->mount_options ($mountopts, $fses{$_}, $_);
275 my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
277 # Create a working directory to store the downloaded registry files.
278 my $tmpdir = tempdir (CLEANUP => 1);
280 # Used when merging (only) to map from the downloaded hiveshortname to
281 # various properties about the hive. The key is hiveshortname. The
282 # value is a hashref containing {h} (hive handle) and {hivefile} (full
283 # hive path on the Windows side).
286 if (!$merge) { # Export mode.
287 die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n"
288 if @ARGV < 1 || @ARGV > 2;
290 my $path = shift @ARGV;
291 my $name = shift @ARGV; # or undef
293 # Map this to the hive name. This function dies on failure.
294 my ($hiveshortname, $hivefile, $prefix);
295 ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
297 # Download the chosen hive.
298 download_hive ($hivefile, $hiveshortname);
301 my $h = Win::Hivex->open ("$tmpdir/$hiveshortname", debug => $debug);
305 warn "exporting $path from $hiveshortname with prefix $prefix ..."
307 reg_export ($h, $path, \*STDOUT,
309 unsafe_printable_strings => $unsafe_printable_strings);
311 # Export a single key using hivexget.
312 my @args = ("hivexget", "$tmpdir/$hiveshortname", $path, $name);
313 warn "running ", join (" ", @args), " ..." if $debug;
314 system (@args) == 0 or die "hivexget failed: $?"
317 else { # Import mode.
319 reg_import (\*STDIN, \&import_mapper, encoding => $encoding);
322 open my $fh, $_ or die "open: $_: $!";
323 reg_import ($fh, \&import_mapper, encoding => $encoding);
327 # Now we've done importing, commit all the hive handles and
329 foreach (values %hives) {
335 # Upload all the downloaded hives.
336 foreach my $hiveshortname (keys %hives) {
337 upload_hive ($hiveshortname, $hives{$hiveshortname}->{hivefile})
347 # map function passed to reg_import.
352 my ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($_);
354 # Need to download this hive?
355 unless (-f "$tmpdir/$hiveshortname") {
356 download_hive ($hivefile, $hiveshortname);
358 my $h = Win::Hivex->open ("$tmpdir/$hiveshortname",
359 write => 1, debug => $debug);
360 my %hash = ( h => $h, hivefile => $hivefile );
361 $hives{$hiveshortname} = \%hash;
364 return ($hives{$hiveshortname}->{h}, $path);
367 # Given a path, map that to the name of the hive and the true path
372 my ($hiveshortname, $hivefile, $path, $prefix);
374 if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) {
375 $hiveshortname = "sam";
376 $hivefile = "$systemroot/system32/config/$hiveshortname";
377 $path = defined $1 ? $1 : "\\";
378 $prefix = "HKEY_LOCAL_MACHINE\\SAM";
380 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) {
381 $hiveshortname = "security";
382 $hivefile = "$systemroot/system32/config/$hiveshortname";
383 $path = defined $1 ? $1 : "\\";
384 $prefix = "HKEY_LOCAL_MACHINE\\SECURITY";
386 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) {
387 $hiveshortname = "software";
388 $hivefile = "$systemroot/system32/config/$hiveshortname";
389 $path = defined $1 ? $1 : "\\";
390 $prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE";
392 elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) {
393 $hiveshortname = "system";
394 $hivefile = "$systemroot/system32/config/$hiveshortname";
395 $path = defined $1 ? $1 : "\\";
396 $prefix = "HKEY_LOCAL_MACHINE\\SYSTEM";
398 elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) {
399 $hiveshortname = "default";
400 $hivefile = "$systemroot/system32/config/$hiveshortname";
401 $path = defined $1 ? $1 : "\\";
402 $prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT";
404 elsif (/^\\?(?:HKEY_USERS|HKU)\\(S-1-5-[-\d]+)(\\.*)?$/i) {
406 $hiveshortname = $sid;
407 $prefix = "HKEY_USERS\\$sid";
408 $path = defined $2 ? $2 : "\\";
409 # This requires a recursive call to download the SOFTWARE hive.
410 $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
412 elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalSystem(\\.*)?$/i) {
413 my $sid = "S-1-5-18";
414 $hiveshortname = $sid;
415 $prefix = "HKEY_USERS\\$sid";
416 $path = defined $1 ? $1 : "\\";
417 # This requires a recursive call to download the SOFTWARE hive.
418 $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
420 elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalService(\\.*)?$/i) {
421 my $sid = "S-1-5-19";
422 $hiveshortname = $sid;
423 $prefix = "HKEY_USERS\\$sid";
424 $path = defined $1 ? $1 : "\\";
425 # This requires a recursive call to download the SOFTWARE hive.
426 $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
428 elsif (/^\\?(?:HKEY_USERS|HKU)\\NetworkService(\\.*)?$/i) {
429 my $sid = "S-1-5-20";
430 $hiveshortname = $sid;
431 $prefix = "HKEY_USERS\\$sid";
432 $path = defined $1 ? $1 : "\\";
433 # This requires a recursive call to download the SOFTWARE hive.
434 $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
436 elsif (/^\\?(?:HKEY_USERS|HKU)\\(.*?)(\\.*)?$/i) {
437 $hiveshortname = "user_$1";
438 $prefix = "HKEY_USERS\\$1";
439 $path = defined $2 ? $2 : "\\";
440 # XXX We should probably look this up properly.
441 if (is_dir_nocase ("/Users/$1")) {
442 $hivefile = "/Users/$1/ntuser.dat"
443 } elsif (is_dir_nocase ("/Documents and Settings/$1")) {
444 $hivefile = "/Documents and Settings/$1/ntuser.dat"
446 die __x("virt-win-reg: {p}: cannot find user directory\n",
451 die __x("virt-win-reg: {p}: not a supported Windows Registry path\n",
455 return ($hiveshortname, $hivefile, $path, $prefix);
458 # Given a User SID, consult
459 # HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid
460 # and return the ProfileImagePath value.
461 sub lookup_pip_of_user_sid
467 "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\".
470 my ($hiveshortname, $hivefile, $prefix);
471 ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
473 download_hive ($hivefile, $hiveshortname)
474 unless -f "$tmpdir/$hiveshortname";
476 my @args = ("$tmpdir/$hiveshortname", $path, "ProfileImagePath");
477 warn "running hivexget ", join (" ", @args), " ..." if $debug;
480 open $fh, "-|", "hivexget", @args
481 or die "hivexget: see earlier errors: $!";
483 close $fh or die "hivexget: see earlier errors: $!";
487 # The contents of the registry are a windows path, possibly
488 # containing %systemroot% and %systemdrive% (on Win XP). Expand
489 # it and remove some other windows-isms. The caller will do
490 # case_sensitive_path for us, so we don't need to do that.
491 s/%systemroot%/$systemroot/i;
505 eval { $windir = $g->case_sensitive_path ($dir); };
509 return $g->is_dir ($windir);
512 # Download a named hive file. Die on failure.
516 my $hivefile = shift;
517 my $hiveshortname = shift;
520 eval { $winfile = $g->case_sensitive_path ($hivefile); };
522 die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
523 p => $hivefile, err => $@);
526 warn "downloading $winfile ..." if $debug;
527 eval { $g->download ($winfile, "$tmpdir/$hiveshortname"); };
529 die __x("virt-win-reg: {p}: could not download registry file: {err}\n",
530 p => $winfile, err => $@);
534 # Upload a named hive file. Die on failure.
538 my $hiveshortname = shift;
539 my $hivefile = shift;
542 eval { $winfile = $g->case_sensitive_path ($hivefile); };
544 die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
545 p => $hivefile, err => $@);
548 warn "uploading $winfile ..." if $debug;
549 eval { $g->upload ("$tmpdir/$hiveshortname", $winfile); };
551 die __x("virt-win-reg: {p}: could not upload registry file: {err}\n",
552 p => $winfile, err => $@);
556 =head1 SUPPORTED SYSTEMS
558 The program currently supports Windows NT-derived guests starting with
559 Windows XP through to at least Windows 7.
561 The following Registry keys are supported:
565 =item C<HKEY_LOCAL_MACHINE\SAM>
567 =item C<HKEY_LOCAL_MACHINE\SECURITY>
569 =item C<HKEY_LOCAL_MACHINE\SOFTWARE>
571 =item C<HKEY_LOCAL_MACHINE\SYSTEM>
573 =item C<HKEY_USERS\.DEFAULT>
575 =item C<HKEY_USERS\I<SID>>
577 where I<SID> is a Windows User SID (eg. C<S-1-5-18>).
579 =item C<HKEY_USERS\I<username>>
581 where I<username> is a local user name (this is a libguestfs extension).
585 You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
586 C<HKU> for C<HKEY_USERS>.
588 The literal keys C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are not
589 supported (there is no "current user").
593 C<virt-win-reg> expects that regedit files have already been reencoded
594 in the local encoding. Usually on Linux hosts, this means UTF-8 with
595 Unix-style line endings. Since Windows regedit files are often in
596 UTF-16LE with Windows-style line endings, you may need to reencode the
597 whole file before or after processing.
599 To reencode a file from Windows format to Linux (before processing it
600 with the I<--merge> option), you would do something like this:
602 iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg
604 To go in the opposite direction, after exporting and before sending
605 the file to a Windows user, do something like this:
607 unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg
609 For more information about encoding, see L<Win::Hivex::Regedit(3)>.
611 If you are unsure about the current encoding, use the L<file(1)>
612 command. Recent versions of Windows regedit.exe produce a UTF-16LE
613 file with Windows-style (CRLF) line endings, like this:
616 software.reg: Little-endian UTF-16 Unicode text, with very long lines,
617 with CRLF line terminators
619 This file would need conversion before you could I<--merge> it.
621 =head1 CurrentControlSet etc.
623 Registry keys like C<CurrentControlSet> don't really exist in the
624 Windows Registry at the level of the hive file, and therefore you
627 C<CurrentControlSet> is usually an alias for C<ControlSet001>. In
628 some circumstances it might refer to another control set. The way
629 to find out is to look at the C<HKLM\SYSTEM\Select> key:
631 # virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select'
632 [HKEY_LOCAL_MACHINE\SYSTEM\Select]
633 "Current"=dword:00000001
634 "Default"=dword:00000001
635 "Failed"=dword:00000000
636 "LastKnownGood"=dword:00000002
638 "Current" is the one which Windows will choose when it boots.
640 Similarly, other C<Current...> keys in the path may need to
643 =head1 DELETING REGISTRY KEYS AND VALUES
645 To delete a whole registry key, use the syntax:
647 [-HKEY_LOCAL_MACHINE\Foo]
649 To delete a single value within a key, use the syntax:
651 [HKEY_LOCAL_MACHINE\Foo]
656 Note that some of these tips modify the guest disk image. The guest
657 I<must> be shut off, else you will get disk corruption.
659 =head2 RUNNING A BATCH SCRIPT WHEN A USER LOGS IN
661 Prepare a DOS batch script, VBScript or executable. Upload this using
662 L<guestfish(1)>. For this example the script is called C<test.bat>
663 and it is uploaded into C<C:\>:
665 guestfish -i -d WindowsGuest upload test.bat /test.bat
667 Prepare a regedit file containing the registry change:
669 cat > test.reg <<'EOF'
670 [HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce]
671 "Test"="c:\\test.bat"
674 In this example we use the key C<RunOnce> which means that the script
675 will run precisely once when the first user logs in. If you want it
676 to run every time a user logs in, replace C<RunOnce> with C<Run>.
678 Now update the registry:
680 virt-win-reg --merge WindowsGuest test.reg
682 =head2 INSTALLING A SERVICE
684 This section assumes you are familiar with Windows services, and you
685 either have a program which handles the Windows Service Control
686 Protocol directly or you want to run any program using a service
687 wrapper like SrvAny or the free RHSrvAny.
689 First upload the program and optionally the service wrapper. In this
690 case the test program is called C<test.exe> and we are using the
693 guestfish -i -d WindowsGuest <<EOF
694 upload rhsrvany.exe /rhsrvany.exe
695 upload test.exe /test.exe
698 Prepare a regedit file containing the registry changes. In this
699 example, the first registry change is needed for the service itself or
700 the service wrapper (if used). The second registry change is only
701 needed because I am using the RHSrvAny service wrapper.
703 cat > service.reg <<'EOF'
704 [HKLM\SYSTEM\ControlSet001\services\RHSrvAny]
705 "Type"=dword:00000010
706 "Start"=dword:00000002
707 "ErrorControl"=dword:00000001
708 "ImagePath"="c:\\rhsrvany.exe"
709 "DisplayName"="RHSrvAny"
710 "ObjectName"="NetworkService"
712 [HKLM\SYSTEM\ControlSet001\services\RHSrvAny\Parameters]
713 "CommandLine"="c:\\test.exe"
723 For use of C<ControlSet001> see the section above in this manual page.
724 You may need to adjust this according to the control set that is in
729 C<"ObjectName"> controls the privileges that the service will have.
730 An alternative is C<"ObjectName"="LocalSystem"> which would be the
731 most privileged account.
735 For the meaning of the magic numbers, see this Microsoft KB article:
736 L<http://support.microsoft.com/kb/103000>.
742 virt-win-reg --merge WindowsGuest service.reg
746 Be careful when passing parameters containing C<\> (backslash) in the
747 shell. Usually you will have to use 'single quotes' or double
748 backslashes (but not both) to protect them from the shell.
750 Paths and value names are case-insensitive.
752 Libvirt guest names can contain arbitrary characters, some of which
753 have meaning to the shell such as C<#> and space. You may need to
754 quote or escape these characters on the command line. See the shell
755 manual page L<sh(1)> for details.
766 L<Sys::Guestfs::Lib(3)>,
768 L<Win::Hivex::Regedit(3)>,
770 L<http://libguestfs.org/>.
774 When reporting bugs, please enable debugging and capture the
777 export LIBGUESTFS_DEBUG=1
778 virt-win-reg --debug [... rest ...] > /tmp/virt-win-reg.log 2>&1
780 Attach /tmp/virt-win-reg.log to a new bug report at
781 L<https://bugzilla.redhat.com/>
785 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
789 Copyright (C) 2010 Red Hat Inc.
791 This program is free software; you can redistribute it and/or modify
792 it under the terms of the GNU General Public License as published by
793 the Free Software Foundation; either version 2 of the License, or
794 (at your option) any later version.
796 This program is distributed in the hope that it will be useful,
797 but WITHOUT ANY WARRANTY; without even the implied warranty of
798 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
799 GNU General Public License for more details.
801 You should have received a copy of the GNU General Public License
802 along with this program; if not, write to the Free Software
803 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.