mrc0mmand / rpms / hivex

Forked from rpms/hivex 3 years ago
Clone
Blob Blame History Raw
From 362d5cd9b6527e4f9d3a3729afbe7cd90486c39d Mon Sep 17 00:00:00 2001
From: Dawid Zamirski <dzamirski@datto.com>
Date: Thu, 16 Feb 2017 18:17:26 -0500
Subject: [PATCH 12/12] hivexregedit: allow to pass HIVEX_OPEN_UNSAFE

via new --unsafe flag. Also make --export catpure, log and skip over
errors when reading subkeys/values so that export in unsafe mode does
not abort at first sign of error.

(cherry picked from commit 2610b1e089fa84d294f9925342929e3d7408c35b)
---
 perl/lib/Win/Hivex/Regedit.pm | 59 ++++++++++++++++++++++++++++++++++++++-----
 regedit/hivexregedit          | 20 ++++++++++++---
 2 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/perl/lib/Win/Hivex/Regedit.pm b/perl/lib/Win/Hivex/Regedit.pm
index 8914f9e..355699e 100644
--- a/perl/lib/Win/Hivex/Regedit.pm
+++ b/perl/lib/Win/Hivex/Regedit.pm
@@ -67,7 +67,7 @@ package Win::Hivex::Regedit;
 use strict;
 use warnings;
 
-use Carp qw(croak confess);
+use Carp qw(croak carp confess);
 use Encode qw(encode decode);
 
 require Exporter;
@@ -528,19 +528,51 @@ sub reg_export_node
     print $fh "]\n";
 
     my $unsafe_printable_strings = $params{unsafe_printable_strings};
+    my $unsafe = $params{unsafe};
+
+    my @values;
+    my @safe_values;
 
     # Get the values.
-    my @values = $h->node_values ($node);
+    if ($unsafe) {
+        my $have_vals = 0;
+        eval {
+            @values = $h->node_values ($node);
+            $have_vals = 1;
+        };
+
+        if (!$have_vals) {
+            carp "Failed to read node values at $path";
+        }
+    } else {
+        @values = $h->node_values ($node);
+    }
 
     foreach (@values) {
         use bytes;
 
         my $key = $h->value_key ($_);
-        my ($type, $data) = $h->value_value ($_);
-        $_ = { key => $key, type => $type, data => $data }
+        my ($type, $data);
+
+        if ($unsafe) {
+            my $val_ok = 0;
+            eval {
+                ($type, $data) = $h->value_value ($_);
+                $val_ok = 1;
+            };
+
+            if (!$val_ok) {
+                carp "skipping unreadable value of key: $key in $path";
+                next;
+            }
+        } else {
+            ($type, $data) = $h->value_value ($_);
+        }
+
+        push @safe_values, { key => $key, type => $type, data => $data };
     }
 
-    @values = sort { $a->{key} cmp $b->{key} } @values;
+    @values = sort { $a->{key} cmp $b->{key} } @safe_values;
 
     # Print the values.
     foreach (@values) {
@@ -573,7 +605,22 @@ sub reg_export_node
     }
     print $fh "\n";
 
-    my @children = $h->node_children ($node);
+    my @children;
+
+    if ($unsafe) {
+        my $have_children = 0;
+        eval {
+            @children = $h->node_children ($node);
+            $have_children = 1;
+        };
+
+        if (!$have_children) {
+            carp "Could not get children of $path";
+        }
+    } else {
+        @children = $h->node_children ($node);
+    }
+
     @children = sort { $h->node_name ($a) cmp $h->node_name ($b) } @children;
     reg_export_node ($h, $_, $fh, @_) foreach @children;
 }
diff --git a/regedit/hivexregedit b/regedit/hivexregedit
index 0e534de..aa0ee6e 100755
--- a/regedit/hivexregedit
+++ b/regedit/hivexregedit
@@ -252,6 +252,17 @@ into another program or stored in another hive.
 
 =cut
 
+my $unsafe;
+
+=item B<--unsafe>
+
+Use heuristics to tolerate certain levels of corruption within hives.
+
+This is unsafe but may allow to export/merge valid keys/values in an
+othewise corrupted hive.
+
+=cut
+
 GetOptions ("help|?" => \$help,
             "debug" => \$debug,
             "merge|import" => \$merge,
@@ -259,6 +270,7 @@ GetOptions ("help|?" => \$help,
             "prefix=s" => \$prefix,
             "encoding=s" => \$encoding,
             "unsafe-printable-strings" => \$unsafe_printable_strings,
+            "unsafe" => \$unsafe,
     ) or pod2usage (2);
 pod2usage (1) if $help;
 
@@ -281,7 +293,8 @@ if ($merge) {                   # --merge (reg_import)
 
     my $hivefile = shift @ARGV;
 
-    my $h = Win::Hivex->open ($hivefile, write => 1, debug => $debug);
+    my $h = Win::Hivex->open ($hivefile, write => 1, debug => $debug,
+                              unsafe => $unsafe);
 
     # Read from stdin unless other files have been specified.
     unshift (@ARGV, '-') unless @ARGV;
@@ -312,13 +325,14 @@ if ($merge) {                   # --merge (reg_import)
     my $hivefile = shift @ARGV;
     my $key = shift @ARGV;
 
-    my $h = Win::Hivex->open ($hivefile, debug => $debug);
+    my $h = Win::Hivex->open ($hivefile, debug => $debug, unsafe => $unsafe);
 
     print "Windows Registry Editor Version 5.00\n\n";
 
     reg_export ($h, $key, \*STDOUT,
                 prefix => $prefix,
-                unsafe_printable_strings => $unsafe_printable_strings);
+                unsafe_printable_strings => $unsafe_printable_strings,
+                unsafe => $unsafe);
 }
 
 =head1 SEE ALSO
-- 
1.8.3.1