virt-win-reg: Support HKEY_USERS keys.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 17 May 2011 12:16:17 +0000 (13:16 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 17 May 2011 16:05:11 +0000 (17:05 +0100)
This adds support for various Registry keys under HKEY_USERS (user
preferences).

(1) HKEY_USERS\<SID>
    where <SID> is a User SID.

For example:

  # virt-win-reg Windows 'HKEY_USERS\S-1-5-19'

lists out the LocalService user's registry.

(2) HKEY_USERS\<username>
    where <username> is a Windows local username (this is a
    libguestfs extension).

For example:

  # virt-win-reg Windows 'HKEY_USERS\rjones'

lists out the user preferences of user 'rjones'.

HKU can be used as an abbreviation for HKEY_USERS.  Merging is also
supported.

tools/virt-win-reg

index 1018e11..333bd15 100755 (executable)
@@ -399,6 +399,52 @@ sub map_path_to_hive
         $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 => $_)
@@ -407,6 +453,59 @@ sub map_path_to_hive
     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%.  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/;
+    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
 {
@@ -456,15 +555,35 @@ sub upload_hive
 The program currently supports Windows NT-derived guests starting with
 Windows XP through to at least Windows 7.
 
-Registry support is done for C<HKEY_LOCAL_MACHINE\SAM>,
-C<HKEY_LOCAL_MACHINE\SECURITY>, C<HKEY_LOCAL_MACHINE\SOFTWARE>,
-C<HKEY_LOCAL_MACHINE\SYSTEM> and C<HKEY_USERS\.DEFAULT>.
+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>.
 
-C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are B<not> supported at
-this time.
+The literal keys C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are not
+supported (there is no "current user").
 
 =head1 ENCODING