2db26712b4f3bb0496bc8d96e683c621183dd3f3
[libguestfs.git] / tools / virt-win-reg
1 #!/usr/bin/perl -w
2 # virt-win-reg
3 # Copyright (C) 2010 Red Hat Inc.
4 #
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.
9 #
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.
14 #
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.
18
19 use warnings;
20 use strict;
21
22 use Sys::Guestfs;
23 use Sys::Guestfs::Lib qw(open_guest);
24 use Win::Hivex;
25 use Win::Hivex::Regedit qw(reg_import reg_export);
26
27 use Pod::Usage;
28 use Getopt::Long;
29 use File::Temp qw/tempdir/;
30 use File::Basename;
31 use Locale::TextDomain 'libguestfs';
32
33 =encoding utf8
34
35 =head1 NAME
36
37 virt-win-reg - Export and merge Windows Registry entries from a Windows guest
38
39 =head1 SYNOPSIS
40
41  virt-win-reg domname 'HKLM\Path\To\Subkey'
42
43  virt-win-reg domname 'HKLM\Path\To\Subkey' name
44
45  virt-win-reg domname 'HKLM\Path\To\Subkey' @
46
47  virt-win-reg --merge domname [input.reg ...]
48
49  virt-win-reg [--options] disk.img ... # instead of domname
50
51 =head1 WARNING
52
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.
57
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.
62
63 =head1 DESCRIPTION
64
65 This program can export and merge Windows Registry entries from a
66 Windows guest.
67
68 The first parameter is the libvirt guest name or the raw disk image of
69 a Windows guest.
70
71 If C<--merge> is I<not> specified, then the chosen registry
72 key is displayed/exported (recursively).  For example:
73
74  $ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft'
75
76 You can also display single values from within registry keys,
77 for example:
78
79  $ cvkey='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
80  $ virt-win-reg Windows7 $cvkey ProductName
81  Windows 7 Enterprise
82
83 With C<--merge>, you can merge a textual regedit file into
84 the Windows Registry:
85
86  $ virt-win-reg --merge Windows7 changes.reg
87
88 =head2 NOTE
89
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)>.
95
96 =head1 OPTIONS
97
98 =over 4
99
100 =cut
101
102 my $help;
103
104 =item B<--help>
105
106 Display brief help.
107
108 =cut
109
110 my $version;
111
112 =item B<--version>
113
114 Display version number and exit.
115
116 =cut
117
118 my $debug;
119
120 =item B<--debug>
121
122 Enable debugging messages.
123
124 =cut
125
126 my $uri;
127
128 =item B<--connect URI> | B<-c URI>
129
130 If using libvirt, connect to the given I<URI>.  If omitted, then we
131 connect to the default libvirt hypervisor.
132
133 If you specify guest block devices directly, then libvirt is not used
134 at all.
135
136 =cut
137
138 my $format;
139
140 =item B<--format> raw
141
142 Specify the format of disk images given on the command line.  If this
143 is omitted then the format is autodetected from the content of the
144 disk image.
145
146 If disk images are requested from libvirt, then this program asks
147 libvirt for this information.  In this case, the value of the format
148 parameter is ignored.
149
150 If working with untrusted raw-format guest disk images, you should
151 ensure the format is always specified.
152
153 =cut
154
155 my $merge;
156
157 =item B<--merge>
158
159 In merge mode, this merges a textual regedit file into the Windows
160 Registry of the virtual machine.  If this flag is I<not> given then
161 virt-win-reg displays or exports Registry entries instead.
162
163 Note that C<--merge> is I<unsafe> to use on live virtual machines, and
164 will result in disk corruption.  However exporting (without this flag)
165 is always safe.
166
167 =cut
168
169 my $encoding;
170
171 =item B<--encoding> UTF-16LE|ASCII
172
173 When merging (only), you may need to specify the encoding for strings
174 to be used in the hive file.  This is explained in detail in
175 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>.
176
177 The default is to use UTF-16LE, which should work with recent versions
178 of Windows.
179
180 =back
181
182 =cut
183
184 GetOptions ("help|?" => \$help,
185             "version" => \$version,
186             "connect|c=s" => \$uri,
187             "debug|d" => \$debug,
188             "format=s" => \$format,
189             "merge" => \$merge,
190             "encoding=s" => \$encoding,
191     ) or pod2usage (2);
192 pod2usage (1) if $help;
193 if ($version) {
194     my $g = Sys::Guestfs->new ();
195     my %h = $g->version ();
196     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
197     exit
198 }
199
200 # virt-win-reg only takes a single disk image ...
201 die __"no libvirt domain name or disk image given\n" if @ARGV == 0;
202 my $domname_or_image = shift @ARGV;
203
204 warn "launching libguestfs ..." if $debug;
205
206 my @lib_args = ([$domname_or_image]);
207 push @lib_args, address => $uri if $uri;
208 push @lib_args, rw => 1 if $merge;
209 push @lib_args, format => $format if defined $format;
210 my $g = open_guest (@lib_args);
211 $g->launch ();
212
213 warn "inspecting guest ..." if $debug;
214
215 my @roots = $g->inspect_os ();
216 if (@roots == 0) {
217     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",
218             prog => basename ($0));
219 }
220 if (@roots > 1) {
221     die __x("{prog}: multiboot operating systems are not supported.\n",
222             prog => basename ($0))
223 }
224 my %fses = $g->inspect_get_mountpoints ($roots[0]);
225 my @fses = sort { length $a <=> length $b } keys %fses;
226 my $mountopts = $merge ? "" : "ro";
227 foreach (@fses) {
228     $g->mount_options ($mountopts, $fses{$_}, $_);
229 }
230
231 my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
232
233 # Create a working directory to store the downloaded registry files.
234 my $tmpdir = tempdir (CLEANUP => 1);
235
236 # Only used when merging to map downloaded hive names to hive handles.
237 my %hives;
238
239 if (!$merge) {                  # Export mode.
240     die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n"
241         if @ARGV < 1 || @ARGV > 2;
242
243     my $path = shift @ARGV;
244     my $name = shift @ARGV; # or undef
245
246     # Map this to the hive name.  This function dies on failure.
247     my ($hivename, $prefix);
248     ($hivename, $path, $prefix) = map_path_to_hive ($path);
249
250     # Download the chosen hive.
251     download_hive ($hivename);
252
253     # Open it.
254     my $h = Win::Hivex->open ("$tmpdir/$hivename", debug => $debug);
255
256     unless ($name) {
257         # Export it.
258         warn "exporting $path from $hivename with prefix $prefix ..." if $debug;
259         reg_export ($h, $path, \*STDOUT, prefix => $prefix);
260     } else {
261         # Export a single key using hivexget.
262         my @args = ("hivexget", "$tmpdir/$hivename", $path, $name);
263         warn "running ", join (" ", @args), " ..." if $debug;
264         system (@args) == 0 or die "hivexget failed: $?"
265     }
266 }
267 else {                          # Import mode.
268     if (@ARGV == 0) {
269         reg_import (\*STDIN, \&import_mapper, encoding => $encoding);
270     } else {
271         foreach (@ARGV) {
272             open my $fh, $_ or die "open: $_: $!";
273             reg_import ($fh, \&import_mapper, encoding => $encoding);
274         }
275     }
276
277     # Now we've done importing, commit all the hive handles and
278     # close them all.
279     $_->commit (undef) foreach values %hives;
280     %hives = ();
281
282     # Look in the tmpdir for all the hive files which have been
283     # downloaded / modified by the import mapper, and upload
284     # each one.
285     opendir my $dh, $tmpdir or die "$tmpdir: $!";
286     foreach (readdir $dh) {
287         unless (/^\./) {
288             upload_hive ($_)
289         }
290     }
291
292     # Sync everything.
293     $g->umount_all ();
294     $g->sync ();
295 }
296
297 exit 0;
298
299 # map function passed to reg_import.
300 sub import_mapper
301 {
302     local $_ = shift;
303
304     my ($hivename, $path, $prefix) = map_path_to_hive ($_);
305
306     # Need to download this hive?
307     unless (-f "$tmpdir/$hivename") {
308         download_hive ($hivename);
309
310         my $h = Win::Hivex->open ("$tmpdir/$hivename",
311                                   write => 1, debug => $debug);
312         $hives{$hivename} = $h;
313     }
314
315     return ($hives{$hivename}, $path);
316 }
317
318 # Given a path, map that to the name of the hive and the true path
319 # within that hive.
320 sub map_path_to_hive
321 {
322     local $_ = shift;
323     my ($hivename, $prefix);
324
325     if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) {
326         $hivename = "sam";
327         $_ = defined $1 ? $1 : "\\";
328         $prefix = "HKEY_LOCAL_MACHINE\\SAM";
329     }
330     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) {
331         $hivename = "security";
332         $_ = defined $1 ? $1 : "\\";
333         $prefix = "HKEY_LOCAL_MACHINE\\SECURITY";
334     }
335     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) {
336         $hivename = "software";
337         $_ = defined $1 ? $1 : "\\";
338         $prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE";
339     }
340     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) {
341         $hivename = "system";
342         $_ = defined $1 ? $1 : "\\";
343         $prefix = "HKEY_LOCAL_MACHINE\\SYSTEM";
344     }
345     elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) {
346         $hivename = "default";
347         $_ = defined $1 ? $1 : "\\";
348         $prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT";
349     }
350     else {
351         die __x("virt-win-reg: {p}: not a supported Windows Registry path\n",
352                 p => $_)
353     }
354
355     return ($hivename, $_, $prefix);
356 }
357
358 # Download a named hive file.  Die on failure.
359 sub download_hive
360 {
361     local $_;
362     my $hivename = shift;
363
364     my $winfile_before = "$systemroot/system32/config/$hivename";
365     my $winfile;
366     eval { $winfile = $g->case_sensitive_path ($winfile_before); };
367     if ($@) {
368         die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
369                 p => $winfile_before, err => $@);
370     }
371
372     warn "downloading $winfile ..." if $debug;
373     eval { $g->download ($winfile, "$tmpdir/$hivename"); };
374     if ($@) {
375         die __x("virt-win-reg: {p}: could not download registry file: {err}\n",
376                 p => $winfile, err => $@);
377     }
378 }
379
380 # Upload a named hive file.  Die on failure.
381 sub upload_hive
382 {
383     local $_;
384     my $hivename = shift;
385
386     my $winfile_before = "$systemroot/system32/config/$hivename";
387     my $winfile;
388     eval { $winfile = $g->case_sensitive_path ($winfile_before); };
389     if ($@) {
390         die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
391                 p => $winfile_before, err => $@);
392     }
393
394     warn "uploading $winfile ..." if $debug;
395     eval { $g->upload ("$tmpdir/$hivename", $winfile); };
396     if ($@) {
397         die __x("virt-win-reg: {p}: could not upload registry file: {err}\n",
398                 p => $winfile, err => $@);
399     }
400 }
401
402 =head1 SUPPORTED SYSTEMS
403
404 The program currently supports Windows NT-derived guests starting with
405 Windows XP through to at least Windows 7.
406
407 Registry support is done for C<HKEY_LOCAL_MACHINE\SAM>,
408 C<HKEY_LOCAL_MACHINE\SECURITY>, C<HKEY_LOCAL_MACHINE\SOFTWARE>,
409 C<HKEY_LOCAL_MACHINE\SYSTEM> and C<HKEY_USERS\.DEFAULT>.
410
411 You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
412 C<HKU> for C<HKEY_USERS>.
413
414 C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are B<not> supported at
415 this time.
416
417 =head1 ENCODING
418
419 C<virt-win-reg> expects that regedit files have already been reencoded
420 in the local encoding.  Usually on Linux hosts, this means UTF-8 with
421 Unix-style line endings.  Since Windows regedit files are often in
422 UTF-16LE with Windows-style line endings, you may need to reencode the
423 whole file before or after processing.
424
425 To reencode a file from Windows format to Linux (before processing it
426 with the C<--merge> option), you would do something like this:
427
428  iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg
429
430 To go in the opposite direction, after exporting and before sending
431 the file to a Windows user, do something like this:
432
433  unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg
434
435 For more information about encoding, see L<Win::Hivex::Regedit(3)>.
436
437 If you are unsure about the current encoding, use the L<file(1)>
438 command.  Recent versions of Windows regedit.exe produce a UTF-16LE
439 file with Windows-style (CRLF) line endings, like this:
440
441  $ file software.reg
442  software.reg: Little-endian UTF-16 Unicode text, with very long lines,
443  with CRLF line terminators
444
445 This file would need conversion before you could C<--merge> it.
446
447 =head1 CurrentControlSet etc.
448
449 Registry keys like C<CurrentControlSet> don't really exist in the
450 Windows Registry at the level of the hive file, and therefore you
451 cannot modify these.
452
453 C<CurrentControlSet> is usually an alias for C<ControlSet001>.  In
454 some circumstances it might refer to another control set.  The way
455 to find out is to look at the C<HKLM\SYSTEM\Select> key:
456
457  # virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select'
458  [HKEY_LOCAL_MACHINE\SYSTEM\Select]
459  "Current"=dword:00000001
460  "Default"=dword:00000001
461  "Failed"=dword:00000000
462  "LastKnownGood"=dword:00000002
463
464 "Current" is the one which Windows will choose when it boots.
465
466 Similarly, other C<Current...> keys in the path may need to
467 be replaced.
468
469 =head1 WINDOWS TIPS
470
471 Note that some of these tips modify the guest disk image.  The guest
472 I<must> be shut off, else you will get disk corruption.
473
474 =head2 RUNNING A BATCH SCRIPT WHEN A USER LOGS IN
475
476 Prepare a DOS batch script, VBScript or executable.  Upload this using
477 L<guestfish(1)>.  For this example the script is called C<test.bat>
478 and it is uploaded into C<C:\>:
479
480  guestfish -i -d WindowsGuest upload test.bat /test.bat
481
482 Prepare a regedit file containing the registry change:
483
484  cat > test.reg <<'EOF'
485  [HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce]
486  "Test"="c:\\test.bat"
487  EOF
488
489 In this example we use the key C<RunOnce> which means that the script
490 will run precisely once when the first user logs in.  If you want it
491 to run every time a user logs in, replace C<RunOnce> with C<Run>.
492
493 Now update the registry:
494
495  virt-win-reg --merge WindowsGuest test.reg
496
497 =head2 INSTALLING A SERVICE
498
499 This section assumes you are familiar with Windows services, and you
500 either have a program which handles the Windows Service Control
501 Protocol directly or you want to run any program using a service
502 wrapper like SrvAny or the free RHSrvAny.
503
504 First upload the program and optionally the service wrapper.  In this
505 case the test program is called C<test.exe> and we are using the
506 RHSrvAny wrapper:
507
508  guestfish -i -d WindowsGuest <<EOF
509    upload rhsrvany.exe /rhsrvany.exe
510    upload test.exe /test.exe
511  EOF
512
513 Prepare a regedit file containing the registry changes.  In this
514 example, the first registry change is needed for the service itself or
515 the service wrapper (if used).  The second registry change is only
516 needed because I am using the RHSrvAny service wrapper.
517
518  cat > service.reg <<'EOF'
519  [HKLM\SYSTEM\ControlSet001\services\RHSrvAny]
520  "Type"=dword:00000010
521  "Start"=dword:00000002
522  "ErrorControl"=dword:00000001
523  "ImagePath"="c:\\rhsrvany.exe"
524  "DisplayName"="RHSrvAny"
525  "ObjectName"="NetworkService"
526  
527  [HKLM\SYSTEM\ControlSet001\services\RHSrvAny\Parameters]
528  "CommandLine"="c:\\test.exe"
529  "PWD"="c:\\Temp"
530  EOF
531
532 Notes:
533
534 =over 4
535
536 =item *
537
538 For use of C<ControlSet001> see the section above in this manual page.
539 You may need to adjust this according to the control set that is in
540 use by the guest.
541
542 =item *
543
544 C<"ObjectName"> controls the privileges that the service will have.
545 An alternative is C<"ObjectName"="LocalSystem"> which would be the
546 most privileged account.
547
548 =item *
549
550 For the meaning of the magic numbers, see this Microsoft KB article:
551 L<http://support.microsoft.com/kb/103000>.
552
553 =back
554
555 Update the registry:
556
557  virt-win-reg --merge WindowsGuest service.reg
558
559 =head1 SHELL QUOTING
560
561 Be careful when passing parameters containing C<\> (backslash) in the
562 shell.  Usually you will have to use 'single quotes' or double
563 backslashes (but not both) to protect them from the shell.
564
565 Paths and value names are case-insensitive.
566
567 Libvirt guest names can contain arbitrary characters, some of which
568 have meaning to the shell such as C<#> and space.  You may need to
569 quote or escape these characters on the command line.  See the shell
570 manual page L<sh(1)> for details.
571
572 =head1 SEE ALSO
573
574 L<hivex(3)>,
575 L<hivexsh(1)>,
576 L<hivexregedit(1)>,
577 L<guestfs(3)>,
578 L<guestfish(1)>,
579 L<virt-cat(1)>,
580 L<Sys::Guestfs(3)>,
581 L<Sys::Guestfs::Lib(3)>,
582 L<Win::Hivex(3)>,
583 L<Win::Hivex::Regedit(3)>,
584 L<Sys::Virt(3)>,
585 L<http://libguestfs.org/>.
586
587 =head1 BUGS
588
589 When reporting bugs, please enable debugging and capture the
590 I<complete> output:
591
592  export LIBGUESTFS_DEBUG=1
593  virt-win-reg --debug [... rest ...] > /tmp/virt-win-reg.log 2>&1
594
595 Attach /tmp/virt-win-reg.log to a new bug report at
596 L<https://bugzilla.redhat.com/>
597
598 =head1 AUTHOR
599
600 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
601
602 =head1 COPYRIGHT
603
604 Copyright (C) 2010 Red Hat Inc.
605
606 This program is free software; you can redistribute it and/or modify
607 it under the terms of the GNU General Public License as published by
608 the Free Software Foundation; either version 2 of the License, or
609 (at your option) any later version.
610
611 This program is distributed in the hope that it will be useful,
612 but WITHOUT ANY WARRANTY; without even the implied warranty of
613 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
614 GNU General Public License for more details.
615
616 You should have received a copy of the GNU General Public License
617 along with this program; if not, write to the Free Software
618 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.