mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
775 lines
22 KiB
Perl
Executable File
775 lines
22 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
# virt-win-reg
|
|
# Copyright (C) 2010 Red Hat Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use Sys::Guestfs;
|
|
use Sys::Guestfs::Lib qw(open_guest);
|
|
use Win::Hivex;
|
|
use Win::Hivex::Regedit qw(reg_import reg_export);
|
|
|
|
use Pod::Usage;
|
|
use Getopt::Long;
|
|
use File::Temp qw/tempdir/;
|
|
use File::Basename;
|
|
use Locale::TextDomain 'libguestfs';
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
virt-win-reg - Export and merge Windows Registry entries from a Windows guest
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
virt-win-reg domname 'HKLM\Path\To\Subkey'
|
|
|
|
virt-win-reg domname 'HKLM\Path\To\Subkey' name
|
|
|
|
virt-win-reg domname 'HKLM\Path\To\Subkey' @
|
|
|
|
virt-win-reg --merge domname [input.reg ...]
|
|
|
|
virt-win-reg [--options] disk.img ... # instead of domname
|
|
|
|
=head1 WARNING
|
|
|
|
You must I<not> use C<virt-win-reg> with the I<--merge> option on live
|
|
virtual machines. If you do this, you I<will> get irreversible disk
|
|
corruption in the VM. C<virt-win-reg> tries to stop you from doing
|
|
this, but doesn't catch all cases.
|
|
|
|
Modifying the Windows Registry is an inherently risky operation. The format
|
|
is deliberately obscure and undocumented, and Registry changes
|
|
can leave the system unbootable. Therefore when using the I<--merge>
|
|
option, make sure you have a reliable backup first.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This program can export and merge Windows Registry entries from a
|
|
Windows guest.
|
|
|
|
The first parameter is the libvirt guest name or the raw disk image of
|
|
a Windows guest.
|
|
|
|
If I<--merge> is I<not> specified, then the chosen registry
|
|
key is displayed/exported (recursively). For example:
|
|
|
|
$ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft'
|
|
|
|
You can also display single values from within registry keys,
|
|
for example:
|
|
|
|
$ cvkey='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
|
|
$ virt-win-reg Windows7 $cvkey ProductName
|
|
Windows 7 Enterprise
|
|
|
|
With I<--merge>, you can merge a textual regedit file into
|
|
the Windows Registry:
|
|
|
|
$ virt-win-reg --merge Windows7 changes.reg
|
|
|
|
=head2 NOTE
|
|
|
|
This program is only meant for simple access to the registry. If you
|
|
want to do complicated things with the registry, we suggest you
|
|
download the Registry hive files from the guest using L<libguestfs(3)>
|
|
or L<guestfish(1)> and access them locally, eg. using L<hivex(3)>,
|
|
L<hivexsh(1)> or L<hivexregedit(1)>.
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 4
|
|
|
|
=cut
|
|
|
|
my $help;
|
|
|
|
=item B<--help>
|
|
|
|
Display brief help.
|
|
|
|
=cut
|
|
|
|
my $version;
|
|
|
|
=item B<--version>
|
|
|
|
Display version number and exit.
|
|
|
|
=cut
|
|
|
|
my $debug;
|
|
|
|
=item B<--debug>
|
|
|
|
Enable debugging messages.
|
|
|
|
=cut
|
|
|
|
my $uri;
|
|
|
|
=item B<-c URI>
|
|
|
|
=item B<--connect URI>
|
|
|
|
If using libvirt, connect to the given I<URI>. If omitted, then we
|
|
connect to the default libvirt hypervisor.
|
|
|
|
If you specify guest block devices directly, then libvirt is not used
|
|
at all.
|
|
|
|
=cut
|
|
|
|
my $format;
|
|
|
|
=item B<--format> raw
|
|
|
|
Specify the format of disk images given on the command line. If this
|
|
is omitted then the format is autodetected from the content of the
|
|
disk image.
|
|
|
|
If disk images are requested from libvirt, then this program asks
|
|
libvirt for this information. In this case, the value of the format
|
|
parameter is ignored.
|
|
|
|
If working with untrusted raw-format guest disk images, you should
|
|
ensure the format is always specified.
|
|
|
|
=cut
|
|
|
|
my $merge;
|
|
|
|
=item B<--merge>
|
|
|
|
In merge mode, this merges a textual regedit file into the Windows
|
|
Registry of the virtual machine. If this flag is I<not> given then
|
|
virt-win-reg displays or exports Registry entries instead.
|
|
|
|
Note that I<--merge> is I<unsafe> to use on live virtual machines, and
|
|
will result in disk corruption. However exporting (without this flag)
|
|
is always safe.
|
|
|
|
=cut
|
|
|
|
my $encoding;
|
|
|
|
=item B<--encoding> UTF-16LE|ASCII
|
|
|
|
When merging (only), you may need to specify the encoding for strings
|
|
to be used in the hive file. This is explained in detail in
|
|
L<Win::Hivex::Regedit(3)/ENCODING STRINGS>.
|
|
|
|
The default is to use UTF-16LE, which should work with recent versions
|
|
of Windows.
|
|
|
|
=cut
|
|
|
|
my $unsafe_printable_strings;
|
|
|
|
=item B<--unsafe-printable-strings>
|
|
|
|
When exporting (only), assume strings are UTF-16LE and print them as
|
|
strings instead of hex sequences. Remove the final zero codepoint
|
|
from strings if present.
|
|
|
|
This is unsafe and does not preserve the fidelity of strings in the
|
|
original Registry for various reasons:
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
Assumes the original encoding is UTF-16LE. ASCII strings and strings
|
|
in other encodings will be corrupted by this transformation.
|
|
|
|
=item *
|
|
|
|
Assumes that everything which has type 1 or 2 is really a string
|
|
and that everything else is not a string, but the type field in
|
|
real Registries is not reliable.
|
|
|
|
=item *
|
|
|
|
Loses information about whether a zero codepoint followed the string
|
|
in the Registry or not.
|
|
|
|
=back
|
|
|
|
This all happens because the Registry itself contains no information
|
|
about how strings are encoded (see
|
|
L<Win::Hivex::Regedit(3)/ENCODING STRINGS>).
|
|
|
|
You should only use this option for quick hacking and debugging of the
|
|
Registry contents, and I<never> use it if the output is going to be
|
|
passed into another program or stored in another Registry.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
GetOptions ("help|?" => \$help,
|
|
"version" => \$version,
|
|
"connect|c=s" => \$uri,
|
|
"debug|d" => \$debug,
|
|
"format=s" => \$format,
|
|
"merge" => \$merge,
|
|
"encoding=s" => \$encoding,
|
|
"unsafe-printable-strings" => \$unsafe_printable_strings,
|
|
) or pod2usage (2);
|
|
pod2usage (1) if $help;
|
|
if ($version) {
|
|
my $g = Sys::Guestfs->new ();
|
|
my %h = $g->version ();
|
|
print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
|
|
exit
|
|
}
|
|
|
|
# virt-win-reg only takes a single disk image ...
|
|
die __"no libvirt domain name or disk image given\n" if @ARGV == 0;
|
|
my $domname_or_image = shift @ARGV;
|
|
|
|
warn "launching libguestfs ..." if $debug;
|
|
|
|
my @lib_args = ([$domname_or_image]);
|
|
push @lib_args, address => $uri if $uri;
|
|
push @lib_args, rw => 1 if $merge;
|
|
push @lib_args, format => $format if defined $format;
|
|
my $g = open_guest (@lib_args);
|
|
$g->launch ();
|
|
|
|
warn "inspecting guest ..." if $debug;
|
|
|
|
my @roots = $g->inspect_os ();
|
|
if (@roots == 0) {
|
|
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",
|
|
prog => basename ($0));
|
|
}
|
|
if (@roots > 1) {
|
|
die __x("{prog}: multiboot operating systems are not supported.\n",
|
|
prog => basename ($0))
|
|
}
|
|
my %fses = $g->inspect_get_mountpoints ($roots[0]);
|
|
my @fses = sort { length $a <=> length $b } keys %fses;
|
|
my $mountopts = $merge ? "" : "ro";
|
|
foreach (@fses) {
|
|
$g->mount_options ($mountopts, $fses{$_}, $_);
|
|
}
|
|
|
|
my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
|
|
|
|
# Create a working directory to store the downloaded registry files.
|
|
my $tmpdir = tempdir (CLEANUP => 1);
|
|
|
|
# Used when merging (only) to map from the downloaded hiveshortname to
|
|
# various properties about the hive. The key is hiveshortname. The
|
|
# value is a hashref containing {h} (hive handle) and {hivefile} (full
|
|
# hive path on the Windows side).
|
|
my %hives;
|
|
|
|
if (!$merge) { # Export mode.
|
|
die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n"
|
|
if @ARGV < 1 || @ARGV > 2;
|
|
|
|
my $path = shift @ARGV;
|
|
my $name = shift @ARGV; # or undef
|
|
|
|
# Map this to the hive name. This function dies on failure.
|
|
my ($hiveshortname, $hivefile, $prefix);
|
|
($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
|
|
|
|
# Download the chosen hive.
|
|
download_hive ($hivefile, $hiveshortname);
|
|
|
|
# Open it.
|
|
my $h = Win::Hivex->open ("$tmpdir/$hiveshortname", debug => $debug);
|
|
|
|
unless ($name) {
|
|
# Export it.
|
|
warn "exporting $path from $hiveshortname with prefix $prefix ..."
|
|
if $debug;
|
|
reg_export ($h, $path, \*STDOUT,
|
|
prefix => $prefix,
|
|
unsafe_printable_strings => $unsafe_printable_strings);
|
|
} else {
|
|
# Export a single key using hivexget.
|
|
my @args = ("hivexget", "$tmpdir/$hiveshortname", $path, $name);
|
|
warn "running ", join (" ", @args), " ..." if $debug;
|
|
system (@args) == 0 or die "hivexget failed: $?"
|
|
}
|
|
}
|
|
else { # Import mode.
|
|
if (@ARGV == 0) {
|
|
reg_import (\*STDIN, \&import_mapper, encoding => $encoding);
|
|
} else {
|
|
foreach (@ARGV) {
|
|
open my $fh, $_ or die "open: $_: $!";
|
|
reg_import ($fh, \&import_mapper, encoding => $encoding);
|
|
}
|
|
}
|
|
|
|
# Now we've done importing, commit all the hive handles and
|
|
# close them all.
|
|
foreach (values %hives) {
|
|
my $h = $_->{h};
|
|
delete $_->{h};
|
|
$h->commit (undef);
|
|
}
|
|
|
|
# Upload all the downloaded hives.
|
|
foreach my $hiveshortname (keys %hives) {
|
|
upload_hive ($hiveshortname, $hives{$hiveshortname}->{hivefile})
|
|
}
|
|
|
|
# Close.
|
|
$g->shutdown ();
|
|
$g->close ();
|
|
}
|
|
|
|
exit 0;
|
|
|
|
# map function passed to reg_import.
|
|
sub import_mapper
|
|
{
|
|
local $_ = shift;
|
|
|
|
my ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($_);
|
|
|
|
# Need to download this hive?
|
|
unless (-f "$tmpdir/$hiveshortname") {
|
|
download_hive ($hivefile, $hiveshortname);
|
|
|
|
my $h = Win::Hivex->open ("$tmpdir/$hiveshortname",
|
|
write => 1, debug => $debug);
|
|
my %hash = ( h => $h, hivefile => $hivefile );
|
|
$hives{$hiveshortname} = \%hash;
|
|
}
|
|
|
|
return ($hives{$hiveshortname}->{h}, $path);
|
|
}
|
|
|
|
# Given a path, map that to the name of the hive and the true path
|
|
# within that hive.
|
|
sub map_path_to_hive
|
|
{
|
|
local $_ = shift;
|
|
my ($hiveshortname, $hivefile, $path, $prefix);
|
|
|
|
if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) {
|
|
$hiveshortname = "sam";
|
|
$hivefile = "$systemroot/system32/config/$hiveshortname";
|
|
$path = defined $1 ? $1 : "\\";
|
|
$prefix = "HKEY_LOCAL_MACHINE\\SAM";
|
|
}
|
|
elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) {
|
|
$hiveshortname = "security";
|
|
$hivefile = "$systemroot/system32/config/$hiveshortname";
|
|
$path = defined $1 ? $1 : "\\";
|
|
$prefix = "HKEY_LOCAL_MACHINE\\SECURITY";
|
|
}
|
|
elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) {
|
|
$hiveshortname = "software";
|
|
$hivefile = "$systemroot/system32/config/$hiveshortname";
|
|
$path = defined $1 ? $1 : "\\";
|
|
$prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE";
|
|
}
|
|
elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) {
|
|
$hiveshortname = "system";
|
|
$hivefile = "$systemroot/system32/config/$hiveshortname";
|
|
$path = defined $1 ? $1 : "\\";
|
|
$prefix = "HKEY_LOCAL_MACHINE\\SYSTEM";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) {
|
|
$hiveshortname = "default";
|
|
$hivefile = "$systemroot/system32/config/$hiveshortname";
|
|
$path = defined $1 ? $1 : "\\";
|
|
$prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\(S-1-5-[-\d]+)(\\.*)?$/i) {
|
|
my $sid = $1;
|
|
$hiveshortname = $sid;
|
|
$prefix = "HKEY_USERS\\$sid";
|
|
$path = defined $2 ? $2 : "\\";
|
|
# This requires a recursive call to download the SOFTWARE hive.
|
|
$hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalSystem(\\.*)?$/i) {
|
|
my $sid = "S-1-5-18";
|
|
$hiveshortname = $sid;
|
|
$prefix = "HKEY_USERS\\$sid";
|
|
$path = defined $1 ? $1 : "\\";
|
|
# This requires a recursive call to download the SOFTWARE hive.
|
|
$hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalService(\\.*)?$/i) {
|
|
my $sid = "S-1-5-19";
|
|
$hiveshortname = $sid;
|
|
$prefix = "HKEY_USERS\\$sid";
|
|
$path = defined $1 ? $1 : "\\";
|
|
# This requires a recursive call to download the SOFTWARE hive.
|
|
$hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\NetworkService(\\.*)?$/i) {
|
|
my $sid = "S-1-5-20";
|
|
$hiveshortname = $sid;
|
|
$prefix = "HKEY_USERS\\$sid";
|
|
$path = defined $1 ? $1 : "\\";
|
|
# This requires a recursive call to download the SOFTWARE hive.
|
|
$hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
|
|
}
|
|
elsif (/^\\?(?:HKEY_USERS|HKU)\\(.*?)(\\.*)?$/i) {
|
|
$hiveshortname = "user_$1";
|
|
$prefix = "HKEY_USERS\\$1";
|
|
$path = defined $2 ? $2 : "\\";
|
|
# XXX We should probably look this up properly.
|
|
if (is_dir_nocase ("/Users/$1")) {
|
|
$hivefile = "/Users/$1/ntuser.dat"
|
|
} elsif (is_dir_nocase ("/Documents and Settings/$1")) {
|
|
$hivefile = "/Documents and Settings/$1/ntuser.dat"
|
|
} else {
|
|
die __x("virt-win-reg: {p}: cannot find user directory\n",
|
|
p => $1)
|
|
}
|
|
}
|
|
else {
|
|
die __x("virt-win-reg: {p}: not a supported Windows Registry path\n",
|
|
p => $_)
|
|
}
|
|
|
|
return ($hiveshortname, $hivefile, $path, $prefix);
|
|
}
|
|
|
|
# Given a User SID, consult
|
|
# HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid
|
|
# and return the ProfileImagePath value.
|
|
sub lookup_pip_of_user_sid
|
|
{
|
|
local $_;
|
|
my $sid = shift;
|
|
|
|
my $path =
|
|
"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\".
|
|
$sid;
|
|
|
|
my ($hiveshortname, $hivefile, $prefix);
|
|
($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
|
|
|
|
download_hive ($hivefile, $hiveshortname)
|
|
unless -f "$tmpdir/$hiveshortname";
|
|
|
|
my @args = ("$tmpdir/$hiveshortname", $path, "ProfileImagePath");
|
|
warn "running hivexget ", join (" ", @args), " ..." if $debug;
|
|
|
|
my $fh;
|
|
open $fh, "-|", "hivexget", @args
|
|
or die "hivexget: see earlier errors: $!";
|
|
$_ = <$fh>;
|
|
close $fh or die "hivexget: see earlier errors: $!";
|
|
|
|
chomp;
|
|
|
|
# The contents of the registry are a windows path, possibly
|
|
# containing %systemroot% and %systemdrive% (on Win XP). Expand
|
|
# it and remove some other windows-isms. The caller will do
|
|
# case_sensitive_path for us, so we don't need to do that.
|
|
s/%systemroot%/$systemroot/i;
|
|
s/%systemdrive%//i;
|
|
s/^c://i;
|
|
s,\\,/,g;
|
|
|
|
$_;
|
|
}
|
|
|
|
sub is_dir_nocase
|
|
{
|
|
local $_;
|
|
my $dir = shift;
|
|
|
|
my $windir;
|
|
eval { $windir = $g->case_sensitive_path ($dir); };
|
|
if ($@) {
|
|
return 0;
|
|
}
|
|
return $g->is_dir ($windir);
|
|
}
|
|
|
|
# Download a named hive file. Die on failure.
|
|
sub download_hive
|
|
{
|
|
local $_;
|
|
my $hivefile = shift;
|
|
my $hiveshortname = shift;
|
|
|
|
my $winfile = $g->case_sensitive_path ($hivefile);
|
|
|
|
warn "downloading $winfile ..." if $debug;
|
|
eval { $g->download ($winfile, "$tmpdir/$hiveshortname"); };
|
|
if ($@) {
|
|
die __x("virt-win-reg: {p}: could not download registry file: {err}\n",
|
|
p => $winfile, err => $@);
|
|
}
|
|
}
|
|
|
|
# Upload a named hive file. Die on failure.
|
|
sub upload_hive
|
|
{
|
|
local $_;
|
|
my $hiveshortname = shift;
|
|
my $hivefile = shift;
|
|
|
|
my $winfile = $g->case_sensitive_path ($hivefile);
|
|
|
|
warn "uploading $winfile ..." if $debug;
|
|
eval { $g->upload ("$tmpdir/$hiveshortname", $winfile); };
|
|
if ($@) {
|
|
die __x("virt-win-reg: {p}: could not upload registry file: {err}\n",
|
|
p => $winfile, err => $@);
|
|
}
|
|
}
|
|
|
|
=head1 SUPPORTED SYSTEMS
|
|
|
|
The program currently supports Windows NT-derived guests starting with
|
|
Windows XP through to at least Windows 8.
|
|
|
|
The following Registry keys are supported:
|
|
|
|
=over 4
|
|
|
|
=item C<HKEY_LOCAL_MACHINE\SAM>
|
|
|
|
=item C<HKEY_LOCAL_MACHINE\SECURITY>
|
|
|
|
=item C<HKEY_LOCAL_MACHINE\SOFTWARE>
|
|
|
|
=item C<HKEY_LOCAL_MACHINE\SYSTEM>
|
|
|
|
=item C<HKEY_USERS\.DEFAULT>
|
|
|
|
=item C<HKEY_USERS\I<SID>>
|
|
|
|
where I<SID> is a Windows User SID (eg. C<S-1-5-18>).
|
|
|
|
=item C<HKEY_USERS\I<username>>
|
|
|
|
where I<username> is a local user name (this is a libguestfs extension).
|
|
|
|
=back
|
|
|
|
You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
|
|
C<HKU> for C<HKEY_USERS>.
|
|
|
|
The literal keys C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are not
|
|
supported (there is no "current user").
|
|
|
|
=head2 WINDOWS 8
|
|
|
|
Windows 8 "fast startup" can prevent virt-win-reg from being
|
|
able to edit the Registry. See
|
|
L<guestfs(3)/WINDOWS HIBERNATION AND WINDOWS 8 FAST STARTUP>.
|
|
|
|
=head1 ENCODING
|
|
|
|
C<virt-win-reg> expects that regedit files have already been reencoded
|
|
in the local encoding. Usually on Linux hosts, this means UTF-8 with
|
|
Unix-style line endings. Since Windows regedit files are often in
|
|
UTF-16LE with Windows-style line endings, you may need to reencode the
|
|
whole file before or after processing.
|
|
|
|
To reencode a file from Windows format to Linux (before processing it
|
|
with the I<--merge> option), you would do something like this:
|
|
|
|
iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg
|
|
|
|
To go in the opposite direction, after exporting and before sending
|
|
the file to a Windows user, do something like this:
|
|
|
|
unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg
|
|
|
|
For more information about encoding, see L<Win::Hivex::Regedit(3)>.
|
|
|
|
If you are unsure about the current encoding, use the L<file(1)>
|
|
command. Recent versions of Windows regedit.exe produce a UTF-16LE
|
|
file with Windows-style (CRLF) line endings, like this:
|
|
|
|
$ file software.reg
|
|
software.reg: Little-endian UTF-16 Unicode text, with very long lines,
|
|
with CRLF line terminators
|
|
|
|
This file would need conversion before you could I<--merge> it.
|
|
|
|
=head1 CurrentControlSet etc.
|
|
|
|
Registry keys like C<CurrentControlSet> don't really exist in the
|
|
Windows Registry at the level of the hive file, and therefore you
|
|
cannot modify these.
|
|
|
|
C<CurrentControlSet> is usually an alias for C<ControlSet001>. In
|
|
some circumstances it might refer to another control set. The way
|
|
to find out is to look at the C<HKLM\SYSTEM\Select> key:
|
|
|
|
# virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select'
|
|
[HKEY_LOCAL_MACHINE\SYSTEM\Select]
|
|
"Current"=dword:00000001
|
|
"Default"=dword:00000001
|
|
"Failed"=dword:00000000
|
|
"LastKnownGood"=dword:00000002
|
|
|
|
"Current" is the one which Windows will choose when it boots.
|
|
|
|
Similarly, other C<Current...> keys in the path may need to
|
|
be replaced.
|
|
|
|
=head1 DELETING REGISTRY KEYS AND VALUES
|
|
|
|
To delete a whole registry key, use the syntax:
|
|
|
|
[-HKEY_LOCAL_MACHINE\Foo]
|
|
|
|
To delete a single value within a key, use the syntax:
|
|
|
|
[HKEY_LOCAL_MACHINE\Foo]
|
|
"Value"=-
|
|
|
|
=head1 WINDOWS TIPS
|
|
|
|
Note that some of these tips modify the guest disk image. The guest
|
|
I<must> be shut off, else you will get disk corruption.
|
|
|
|
=head2 RUNNING A BATCH SCRIPT WHEN A USER LOGS IN
|
|
|
|
Prepare a DOS batch script, VBScript or executable. Upload this using
|
|
L<guestfish(1)>. For this example the script is called C<test.bat>
|
|
and it is uploaded into C<C:\>:
|
|
|
|
guestfish -i -d WindowsGuest upload test.bat /test.bat
|
|
|
|
Prepare a regedit file containing the registry change:
|
|
|
|
cat > test.reg <<'EOF'
|
|
[HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce]
|
|
"Test"="c:\\test.bat"
|
|
EOF
|
|
|
|
In this example we use the key C<RunOnce> which means that the script
|
|
will run precisely once when the first user logs in. If you want it
|
|
to run every time a user logs in, replace C<RunOnce> with C<Run>.
|
|
|
|
Now update the registry:
|
|
|
|
virt-win-reg --merge WindowsGuest test.reg
|
|
|
|
=head2 INSTALLING A SERVICE
|
|
|
|
This section assumes you are familiar with Windows services, and you
|
|
either have a program which handles the Windows Service Control
|
|
Protocol directly or you want to run any program using a service
|
|
wrapper like SrvAny or the free RHSrvAny.
|
|
|
|
First upload the program and optionally the service wrapper. In this
|
|
case the test program is called C<test.exe> and we are using the
|
|
RHSrvAny wrapper:
|
|
|
|
guestfish -i -d WindowsGuest <<EOF
|
|
upload rhsrvany.exe /rhsrvany.exe
|
|
upload test.exe /test.exe
|
|
EOF
|
|
|
|
Prepare a regedit file containing the registry changes. In this
|
|
example, the first registry change is needed for the service itself or
|
|
the service wrapper (if used). The second registry change is only
|
|
needed because I am using the RHSrvAny service wrapper.
|
|
|
|
cat > service.reg <<'EOF'
|
|
[HKLM\SYSTEM\ControlSet001\services\RHSrvAny]
|
|
"Type"=dword:00000010
|
|
"Start"=dword:00000002
|
|
"ErrorControl"=dword:00000001
|
|
"ImagePath"="c:\\rhsrvany.exe"
|
|
"DisplayName"="RHSrvAny"
|
|
"ObjectName"="NetworkService"
|
|
|
|
[HKLM\SYSTEM\ControlSet001\services\RHSrvAny\Parameters]
|
|
"CommandLine"="c:\\test.exe"
|
|
"PWD"="c:\\Temp"
|
|
EOF
|
|
|
|
Notes:
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
For use of C<ControlSet001> see the section above in this manual page.
|
|
You may need to adjust this according to the control set that is in
|
|
use by the guest.
|
|
|
|
=item *
|
|
|
|
C<"ObjectName"> controls the privileges that the service will have.
|
|
An alternative is C<"ObjectName"="LocalSystem"> which would be the
|
|
most privileged account.
|
|
|
|
=item *
|
|
|
|
For the meaning of the magic numbers, see this Microsoft KB article:
|
|
L<http://support.microsoft.com/kb/103000>.
|
|
|
|
=back
|
|
|
|
Update the registry:
|
|
|
|
virt-win-reg --merge WindowsGuest service.reg
|
|
|
|
=head1 SHELL QUOTING
|
|
|
|
Be careful when passing parameters containing C<\> (backslash) in the
|
|
shell. Usually you will have to use 'single quotes' or double
|
|
backslashes (but not both) to protect them from the shell.
|
|
|
|
Paths and value names are case-insensitive.
|
|
|
|
Libvirt guest names can contain arbitrary characters, some of which
|
|
have meaning to the shell such as C<#> and space. You may need to
|
|
quote or escape these characters on the command line. See the shell
|
|
manual page L<sh(1)> for details.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<hivex(3)>,
|
|
L<hivexsh(1)>,
|
|
L<hivexregedit(1)>,
|
|
L<guestfs(3)>,
|
|
L<guestfish(1)>,
|
|
L<virt-cat(1)>,
|
|
L<Sys::Guestfs(3)>,
|
|
L<Sys::Guestfs::Lib(3)>,
|
|
L<Win::Hivex(3)>,
|
|
L<Win::Hivex::Regedit(3)>,
|
|
L<Sys::Virt(3)>,
|
|
L<http://libguestfs.org/>.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (C) 2010 Red Hat Inc.
|