Support for Windows Registry.
[libguestfs.git] / tools / virt-win-reg
1 #!/usr/bin/perl -w
2 # virt-win-reg
3 # Copyright (C) 2009 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 get_partitions resolve_windows_path
24   inspect_all_partitions inspect_partition
25   inspect_operating_systems mount_operating_system);
26 use Pod::Usage;
27 use Getopt::Long;
28 use File::Temp qw/tempdir/;
29 use Locale::TextDomain 'libguestfs';
30
31 =encoding utf8
32
33 =head1 NAME
34
35 virt-win-reg - Display Windows Registry entries from a Windows guest
36
37 =head1 SYNOPSIS
38
39  virt-win-reg [--options] domname '\Path\To\Subkey' name ['\Path'...]
40
41  virt-win-reg [--options] domname '\Path\To\Subkey' @ ['\Path'...]
42
43  virt-win-reg [--options] domname '\Path\To\Subkey' ['\Path'...]
44
45  virt-win-reg [--options] disk.img [...] '\Path\To\Subkey' (name|@)
46
47 =head1 DESCRIPTION
48
49 This program can display Windows Registry entries from a Windows
50 guest.
51
52 The first parameter is the libvirt guest name or the raw disk image of
53 the Windows guest.
54
55 Then follow one or more sets of path specifiers.  The path must begin
56 with a C<\> (backslash) character, and may be something like
57 C<'\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'>.
58
59 The next parameter after that is either a value name, the single
60 at-character C<@>, or missing.
61
62 If it's a value name, then we print the data associated with that
63 value.  If it's C<@>, then we print the default data associated with
64 the subkey.  If it's missing, then we print all the data associated
65 with the subkey.
66
67 If this is confusing, look at the L</EXAMPLES> section below.
68
69 Usually you should use single quotes to protect backslashes in the
70 path from the shell.
71
72 Paths and value names are case-insensitive.
73
74 =head2 SUPPORTED SYSTEMS
75
76 The program currently supports Windows NT-derived guests starting with
77 Windows XP through to at least Windows 7.
78
79 Registry support is done for C<\HKEY_LOCAL_MACHINE\SAM>,
80 C<\HKEY_LOCAL_MACHINE\SECURITY>, C<\HKEY_LOCAL_MACHINE\SOFTWARE>,
81 C<\HKEY_LOCAL_MACHINE\SYSTEM> and C<\HKEY_USERS\.DEFAULT>.
82
83 C<\HKEY_USERS\$SID> and C<\HKEY_CURRENT_USER> are B<not> supported at
84 this time.
85
86 =head2 NOTES
87
88 This program is only meant for simple access to the registry.  If you
89 want to do complicated things with the registry, we suggest you
90 download the Registry hive files from the guest using C<libguestfs(3)>
91 or C<guestfish(1)> and access them locally, eg. using C<hivex(3)>,
92 C<hivexml(1)> or C<reged(1)>.
93
94 =head1 EXAMPLES
95
96  $ virt-win-reg MyWinGuest \
97    '\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' \
98    ProductName
99  Microsoft Windows Server 2003
100
101  $ virt-win-reg MyWinGuest \
102    '\HKEY_LOCAL_MACHINE\System\ControlSet001\Control' SystemBootDevice
103  multi(0)disk(0)rdisk(0)partition(1)
104
105  $ virt-win-reg MyWinGuest \
106    '\HKEY_LOCAL_MACHINE\System\ControlSet001\Control'
107  "CurrentUser"="USERNAME"
108  "WaitToKillServiceTimeout"="20000"
109  "SystemStartOptions"="NOEXECUTE=OPTOUT  FASTDETECT"
110  "SystemBootDevice"="multi(0)disk(0)rdisk(0)partition(1)"
111
112 (please suggest some more)
113
114 =head1 OPTIONS
115
116 =over 4
117
118 =cut
119
120 my $help;
121
122 =item B<--help>
123
124 Display brief help.
125
126 =cut
127
128 my $version;
129
130 =item B<--version>
131
132 Display version number and exit.
133
134 =cut
135
136 my $uri;
137
138 =item B<--connect URI> | B<-c URI>
139
140 If using libvirt, connect to the given I<URI>.  If omitted, then we
141 connect to the default libvirt hypervisor.
142
143 If you specify guest block devices directly, then libvirt is not used
144 at all.
145
146 =back
147
148 =cut
149
150 GetOptions ("help|?" => \$help,
151             "version" => \$version,
152             "connect|c=s" => \$uri,
153     ) or pod2usage (2);
154 pod2usage (1) if $help;
155 if ($version) {
156     my $g = Sys::Guestfs->new ();
157     my %h = $g->version ();
158     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
159     exit
160 }
161
162 # Split the command line at the first path.  Paths begin with
163 # backslash so this is predictable.
164
165 my @lib_args;
166 my $i;
167
168 for ($i = 0; $i < @ARGV; ++$i) {
169     if (substr ($ARGV[$i], 0, 1) eq "\\") {
170         @lib_args = @ARGV[0 .. ($i-1)];
171         @ARGV = @ARGV[$i .. $#ARGV];
172         last;
173     }
174 }
175
176 pod2usage (__"virt-win-reg: no VM name, disk images or Registry path given") if 0 == @lib_args;
177
178 my $g;
179 if ($uri) {
180     $g = open_guest (\@lib_args, address => $uri);
181 } else {
182     $g = open_guest (\@lib_args);
183 }
184
185 $g->launch ();
186
187 # List of possible filesystems.
188 my @partitions = get_partitions ($g);
189
190 # Now query each one to build up a picture of what's in it.
191 my %fses =
192     inspect_all_partitions ($g, \@partitions,
193       use_windows_registry => 0);
194
195 my $oses = inspect_operating_systems ($g, \%fses);
196
197 my @roots = keys %$oses;
198 die __"no root device found in this operating system image" if @roots == 0;
199 die __"multiboot operating systems are not supported by virt-win-reg" if @roots > 1;
200 my $root_dev = $roots[0];
201
202 my $os = $oses->{$root_dev};
203 mount_operating_system ($g, $os);
204
205 # Create a working directory to store the downloaded registry files.
206 my $tmpdir = tempdir (CLEANUP => 1);
207
208 # Now process each request in turn.
209 my $winfile;
210 my $localhive;
211 my $path;
212
213 for ($i = 0; $i < @ARGV; ++$i) {
214     $_ = $ARGV[$i];
215
216     if (/^\\HKEY_LOCAL_MACHINE\\SAM(\\.*)/i) {
217         $winfile = "/windows/system32/config/sam";
218         $localhive = "$tmpdir/sam";
219         $path = $1;
220     }
221     elsif (/^\\HKEY_LOCAL_MACHINE\\SECURITY(\\.*)/i) {
222         $winfile = "/windows/system32/config/security";
223         $localhive = "$tmpdir/security";
224         $path = $1;
225     }
226     elsif (/^\\HKEY_LOCAL_MACHINE\\SOFTWARE(\\.*)/i) {
227         $winfile = "/windows/system32/config/software";
228         $localhive = "$tmpdir/software";
229         $path = $1;
230     }
231     elsif (/^\\HKEY_LOCAL_MACHINE\\SYSTEM(\\.*)/i) {
232         $winfile = "/windows/system32/config/system";
233         $localhive = "$tmpdir/system";
234         $path = $1;
235     }
236     elsif (/^\\HKEY_USERS\\.DEFAULT(\\.*)/i) {
237         $winfile = "/windows/system32/config/default";
238         $localhive = "$tmpdir/default";
239         $path = $1;
240     }
241     else {
242         die "virt-win-reg: $_: not a supported Windows Registry path\n"
243     }
244
245     unless (-f $localhive) {
246         # Check the hive file exists and get the real name.
247         eval {
248             $winfile = $g->case_sensitive_path ($winfile);
249             $g->download ($winfile, $localhive);
250         };
251         if ($@) {
252             die "virt-win-reg: $winfile: could not download registry file: $@\n"
253         }
254     }
255
256     # What sort of request is it?  Peek at the next arg.
257     my $name; # will be: undefined, @ or a name
258     if ($i+1 < @ARGV) {
259         if (substr ($ARGV[$i+1], 0, 1) ne "\\") {
260             $name = $ARGV[$i+1];
261             $i++;
262         }
263     }
264
265     my @cmd;
266     if (defined $name) {
267         @cmd = ("hivexget", $localhive, $path, $name);
268     } else {
269         @cmd = ("hivexget", $localhive, $path);
270     }
271
272     system (@cmd) == 0
273         or die "hivexget command failed: $?\n";
274 }
275
276 =head1 SEE ALSO
277
278 L<hivex(3)>,
279 L<hivexget(1)>,
280 L<guestfs(3)>,
281 L<guestfish(1)>,
282 L<virt-cat(1)>,
283 L<Sys::Guestfs(3)>,
284 L<Sys::Guestfs::Lib(3)>,
285 L<Sys::Virt(3)>,
286 L<http://libguestfs.org/>.
287
288 =head1 AUTHOR
289
290 Richard W.M. Jones L<http://et.redhat.com/~rjones/>
291
292 =head1 COPYRIGHT
293
294 Copyright (C) 2009 Red Hat Inc.
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.