Blob Blame History Raw
From 8e187357f466c31a9e75ac4924b32bbf4823e73f Mon Sep 17 00:00:00 2001
From: Dawid Zamirski <dzamirski@datto.com>
Date: Thu, 16 Feb 2017 18:17:24 -0500
Subject: [PATCH 10/16] lib: allow to walk registry with corrupted blocks

Only when HIVEX_OPEN_UNSAFE flag is set.

There are some corrupted registry files that have invalid hbin cells
but are still readable. This patch makes the following changes:

* hivex_open - do not abort with complete failure if we run across a
  block with invalid size (unless it's the root block). Instead just
  log the event, and move on. This will allow open hives that have
  apparent invalid blocks but the ones of potential interest might be
  perfectly accessible.
* _hivex_get_children - similiarly, if the's invalid subkey, just skip
  it instead of failing so one can continue to browse other valid
  subkeys.

The above is similar to the behavior to Windows regedit where one can
load such corrupted hives with e.g. "reg load HKU\Corrupted" and
browse/change it despite some keys might be missing.

(cherry picked from commit 5345ec8f1df304dc93a6b1e4cbb40e7c1cf6f88d)
---
 lib/handle.c | 16 ++++++++++++----
 lib/node.c   | 46 +++++++++++++++++++++++++++++++++++-----------
 2 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/lib/handle.c b/lib/handle.c
index 8c64b6d..0d2b24b 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -313,10 +313,18 @@ hivex_open (const char *filename, int flags)
       int used;
       seg_len = block_len (h, blkoff, &used);
       if (seg_len <= 4 || (seg_len & 3) != 0) {
-        SET_ERRNO (ENOTSUP,
-                   "%s: block size %" PRIu32 " at 0x%zx, bad registry",
-                   filename, le32toh (block->seg_len), blkoff);
-        goto error;
+        if (is_root || !h->unsafe) {
+          SET_ERRNO (ENOTSUP,
+                     "%s, the block at 0x%zx has invalid size %" PRIi32
+                     ", bad registry",
+                     filename, blkoff, le32toh (block->seg_len));
+          goto error;
+        } else {
+          DEBUG (2,
+                 "%s: block at 0x%zx has invalid size %" PRIi32 ", skipping",
+                 filename, blkoff, le32toh (block->seg_len));
+          break;
+        }
       }
 
       if (h->msglvl >= 2) {
diff --git a/lib/node.c b/lib/node.c
index 22d1861..a90c964 100644
--- a/lib/node.c
+++ b/lib/node.c
@@ -343,11 +343,18 @@ _hivex_get_children (hive_h *h, hive_node_h node,
    */
   size_t nr_children = _hivex_get_offset_list_length (&children);
   if (nr_subkeys_in_nk != nr_children) {
-    SET_ERRNO (ENOTSUP,
-               "nr_subkeys_in_nk = %zu "
-               "is not equal to number of children read %zu",
-               nr_subkeys_in_nk, nr_children);
-    goto error;
+    if (!h->unsafe) {
+      SET_ERRNO (ENOTSUP,
+                 "nr_subkeys_in_nk = %zu "
+                 "is not equal to number of childred read %zu",
+                 nr_subkeys_in_nk, nr_children);
+      goto error;
+    } else {
+      DEBUG (2,
+             "nr_subkeys_in_nk = %zu "
+             "is not equal to number of children read %zu",
+             nr_subkeys_in_nk, nr_children);
+    }
   }
 
  out:
@@ -407,8 +414,14 @@ _get_children (hive_h *h, hive_node_h blkoff,
     for (i = 0; i < nr_subkeys_in_lf; ++i) {
       hive_node_h subkey = le32toh (lf->keys[i].offset);
       subkey += 0x1000;
-      if (check_child_is_nk_block (h, subkey, flags) == -1)
-        return -1;
+      if (check_child_is_nk_block (h, subkey, flags) == -1) {
+        if (h->unsafe) {
+          DEBUG (2, "subkey at 0x%zx is not an NK block, skipping", subkey);
+          continue;
+        } else {
+          return -1;
+        }
+      }
       if (_hivex_add_to_offset_list (children, subkey) == -1)
         return -1;
     }
@@ -435,8 +448,14 @@ _get_children (hive_h *h, hive_node_h blkoff,
     for (i = 0; i < nr_offsets; ++i) {
       hive_node_h subkey = le32toh (ri->offset[i]);
       subkey += 0x1000;
-      if (check_child_is_nk_block (h, subkey, flags) == -1)
-        return -1;
+      if (check_child_is_nk_block (h, subkey, flags) == -1) {
+        if (h->unsafe) {
+          DEBUG (2, "subkey at 0x%zx is not an NK block, skipping", subkey);
+          continue;
+        } else {
+          return -1;
+        }
+      }
       if (_hivex_add_to_offset_list (children, subkey) == -1)
         return -1;
     }
@@ -458,8 +477,13 @@ _get_children (hive_h *h, hive_node_h blkoff,
       hive_node_h offset = le32toh (ri->offset[i]);
       offset += 0x1000;
       if (!IS_VALID_BLOCK (h, offset)) {
-        SET_ERRNO (EFAULT, "ri-offset is not a valid block (0x%zx)", offset);
-        return -1;
+        if (h->unsafe) {
+          DEBUG (2, "ri-offset is not a valid block (0x%zx), skipping", offset);
+          continue;
+        } else {
+          SET_ERRNO (EFAULT, "ri-offset is not a valid block (0x%zx)", offset);
+          return -1;
+        }
       }
 
       if (_get_children (h, offset, children, blocks, flags) == -1)
-- 
1.8.3.1