[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH] Add read support for "big data" blocks to hivex



---
 lib/hivex.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 66 insertions(+), 15 deletions(-)

diff --git a/lib/hivex.c b/lib/hivex.c
index efc27f8..e3c1e05 100644
--- a/lib/hivex.c
+++ b/lib/hivex.c
@@ -208,6 +208,19 @@ struct ntreg_sk_record {
   char sec_desc[1];             /* security info follows */
 } __attribute__((__packed__));
 
+struct ntreg_db_record {
+  int32_t seg_len;              /* length (always -ve because used) */
+  char id[2];                   /* "db" */
+  uint16_t nr_blocks;
+  uint32_t blocklist_offset;
+  uint32_t unknown1;
+} __attribute__((__packed__));
+
+struct ntreg_db_block {
+  int32_t seg_len;
+  char data[1];
+} __attribute__((__packed__));
+
 static uint32_t
 header_checksum (const hive_h *h)
 {
@@ -1418,22 +1431,60 @@ hivex_value_value (hive_h *h, hive_value_h value,
    * instead.
    */
   size_t blen = block_len (h, data_offset, NULL);
-  if (len > blen - 4 /* subtract 4 for block header */) {
-    if (h->msglvl >= 2)
-      fprintf (stderr, "hivex_value_value: warning: declared data length "
-               "is longer than the block it is in "
-               "(data 0x%zx, data len %zu, block len %zu)\n",
-               data_offset, len, blen);
-    len = blen - 4;
-
-    /* Return the smaller length to the caller too. */
-    if (len_rtn)
-      *len_rtn = len;
+  if (len <= blen - 4 /* subtract 4 for block header */) {
+    char *data = (char *) h->addr + data_offset + 4;
+    memcpy (ret, data, len);
+    return ret;
+  } else {
+    if (!IS_VALID_BLOCK (h, data_offset) || !BLOCK_ID_EQ (h, data_offset, "db")) {
+      if (h->msglvl >= 2)
+        fprintf (stderr, "hivex_value_value: warning: declared data length "
+                 "is longer than the amount of data found in big-data blocks "
+                 "(data 0x%zx, data len %zu)\n",
+                 data_offset, len);
+      errno = EINVAL;
+      free (ret);
+      return NULL;
+    }
+    struct ntreg_db_record *db =
+      (struct ntreg_db_record *) ((char *) h->addr + data_offset);
+    size_t blocklist_offset = le32toh (db->blocklist_offset);
+    blocklist_offset += 0x1000;
+    size_t nr_blocks = le16toh (db->nr_blocks);
+    if (!IS_VALID_BLOCK (h, blocklist_offset)) {
+      if (h->msglvl >= 2)
+        fprintf (stderr, "hivex_value_value: warning: blocklist is not a "
+                 "valid block "
+                 "(db block 0x%zx, blocklist 0x%zx)\n",
+                 data_offset, blocklist_offset);
+      errno = EINVAL;
+      free (ret);
+      return NULL;
+    }
+    struct ntreg_value_list *bl =
+      (struct ntreg_value_list *) ((char *) h->addr + blocklist_offset);
+    size_t i, off;
+    for (i=off=0; i < nr_blocks; ++i) {
+      uint32_t subblock_offset = le32toh (bl->offset[i]);
+      subblock_offset += 0x1000;
+      if (!IS_VALID_BLOCK (h, subblock_offset)) {
+        if (h->msglvl >= 2)
+          fprintf (stderr, "hivex_value_value: warning: big data block is not "
+                   "valid (db block 0x%zx, block list 0x%zx, data block 0x%zx)\n",
+                   data_offset, blocklist_offset, subblock_offset);
+      }
+      int32_t seg_len = block_len(h, subblock_offset, NULL);
+      struct ntreg_db_block *subblock =
+        (struct ntreg_db_block *) ((char *) h->addr + subblock_offset);
+      int32_t sz = seg_len - 8; /* don't copy the last 4 bytes */
+      if (off + sz > len) {
+        sz = len - off;
+      }
+      memcpy (ret + off, subblock->data, sz);
+      off += sz;
+    }
+    return ret;
   }
-
-  char *data = (char *) h->addr + data_offset + 4;
-  memcpy (ret, data, len);
-  return ret;
 }
 
 static char *
-- 
1.8.3.1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]