/* hivex - Windows Registry "hive" extraction library.
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 Red Hat Inc.
* Derived from code by Petter Nordahl-Hagen under a compatible license:
* Copyright (c) 1997-2007 Petter Nordahl-Hagen.
* Derived from code by Markus Stephany under a compatible license:
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-#ifdef HAVE_BYTESWAP_H
-#include <byteswap.h>
+
+#include "full-read.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
#endif
#define STREQ(a,b) (strcmp((a),(b)) == 0)
//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
//#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#ifndef be32toh
-#define be32toh(x) __bswap_32 (x)
-#endif
-#ifndef be64toh
-#define be64toh(x) __bswap_64 (x)
-#endif
-#ifndef le16toh
-#define le16toh(x) (x)
-#endif
-#ifndef le32toh
-#define le32toh(x) (x)
-#endif
-#ifndef le64toh
-#define le64toh(x) (x)
-#endif
-#else
-#ifndef be32toh
-#define be32toh(x) (x)
-#endif
-#ifndef be64toh
-#define be64toh(x) (x)
-#endif
-#ifndef le16toh
-#define le16toh(x) __bswap_16 (x)
-#endif
-#ifndef le32toh
-#define le32toh(x) __bswap_32 (x)
-#endif
-#ifndef le64toh
-#define le64toh(x) __bswap_64 (x)
-#endif
-#endif
-
#include "hivex.h"
+#include "byte_conversions.h"
static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
struct hive_h {
+ char *filename;
int fd;
size_t size;
int msglvl;
+ int writable;
- /* Memory-mapped (readonly) registry file. */
+ /* Registry file, memory mapped if read-only, or malloc'd if writing. */
union {
char *addr;
struct ntreg_header *hdr;
/* Fields from the header, extracted from little-endianness hell. */
size_t rootoffs; /* Root key offset (always an nk-block). */
size_t endpages; /* Offset of end of pages. */
-
- /* Stats. */
- size_t pages; /* Number of hbin pages read. */
- size_t blocks; /* Total number of blocks found. */
- size_t used_blocks; /* Total number of used blocks found. */
- size_t used_size; /* Total size (bytes) of used blocks. */
};
/* NB. All fields are little endian. */
struct ntreg_hbin_page {
char magic[4]; /* "hbin" */
uint32_t offset_first; /* offset from 1st block */
- uint32_t offset_next; /* offset of next (relative to this) */
+ uint32_t page_size; /* size of this page (multiple of 4KB) */
char unknown[20];
/* Linked list of blocks follows here. */
} __attribute__((__packed__));
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[12];
+ char timestamp[8];
+ uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
- uint32_t unknown1;
+ uint32_t nr_subkeys_volatile;
uint32_t subkey_lf; /* lf record containing list of subkeys */
- uint32_t unknown2;
+ uint32_t subkey_lf_volatile;
uint32_t nr_values; /* number of values */
uint32_t vallist; /* value-list record */
uint32_t sk; /* offset of sk-record */
uint32_t classname; /* offset of classname record */
- char unknown3[16];
- uint32_t unknown4;
+ uint16_t max_subkey_name_len; /* maximum length of a subkey name in bytes
+ if the subkey was reencoded as UTF-16LE */
+ uint16_t unknown2;
+ uint32_t unknown3;
+ uint32_t max_vk_name_len; /* maximum length of any vk name in bytes
+ if the name was reencoded as UTF-16LE */
+ uint32_t max_vk_data_len; /* maximum length of any vk data in bytes */
+ uint32_t unknown6;
uint16_t name_len; /* length of name */
uint16_t classname_len; /* length of classname */
char name[1]; /* name follows here */
uint16_t nr_keys; /* number of keys in this record */
struct {
uint32_t offset; /* offset of nk-record for this subkey */
- char name[4]; /* first 4 characters of subkey name */
+ char hash[4]; /* hash of subkey name */
} keys[1];
} __attribute__((__packed__));
*/
uint32_t data_len;
uint32_t data_offset; /* pointer to the data (or data if inline) */
- hive_type data_type; /* type of the data */
+ uint32_t data_type; /* type of the data */
uint16_t flags; /* bit 0 set => key name ASCII,
bit 0 clr => key name UTF-16.
- Only seen ASCII here in the wild. */
+ Only seen ASCII here in the wild.
+ NB: this is CLEAR for default key. */
uint16_t unknown2;
char name[1]; /* key name follows here */
} __attribute__((__packed__));
+static uint32_t
+header_checksum (const hive_h *h)
+{
+ uint32_t *daddr = (uint32_t *) h->addr;
+ size_t i;
+ uint32_t sum = 0;
+
+ for (i = 0; i < 0x1fc / 4; ++i) {
+ sum ^= le32toh (*daddr);
+ daddr++;
+ }
+
+ return sum;
+}
+
hive_h *
hivex_open (const char *filename, int flags)
{
if (h->msglvl >= 2)
fprintf (stderr, "hivex_open: created handle %p\n", h);
- h->fd = open (filename, O_RDONLY);
+ h->writable = !!(flags & HIVEX_OPEN_WRITE);
+ h->filename = strdup (filename);
+ if (h->filename == NULL)
+ goto error;
+
+ h->fd = open (filename, O_RDONLY | O_CLOEXEC);
if (h->fd == -1)
goto error;
h->size = statbuf.st_size;
- h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0);
- if (h->addr == MAP_FAILED)
- goto error;
+ if (!h->writable) {
+ h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0);
+ if (h->addr == MAP_FAILED)
+ goto error;
- if (h->msglvl >= 2)
- fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr);
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr);
+ } else {
+ h->addr = malloc (h->size);
+ if (h->addr == NULL)
+ goto error;
+
+ if (full_read (h->fd, h->addr, h->size) < h->size)
+ goto error;
+ }
/* Check header. */
if (h->hdr->magic[0] != 'r' ||
goto error;
/* Header checksum. */
- uint32_t *daddr = (uint32_t *) h->addr;
- size_t i;
- uint32_t sum = 0;
- for (i = 0; i < 0x1fc / 4; ++i) {
- sum ^= le32toh (*daddr);
- daddr++;
- }
-
+ uint32_t sum = header_checksum (h);
if (sum != le32toh (h->hdr->csum)) {
fprintf (stderr, "hivex: %s: bad checksum in hive header\n", filename);
errno = EINVAL;
*/
int seen_root_block = 0, bad_root_block = 0;
+ /* Collect some stats. */
+ size_t pages = 0; /* Number of hbin pages read. */
+ size_t smallest_page = SIZE_MAX, largest_page = 0;
+ size_t blocks = 0; /* Total number of blocks found. */
+ size_t smallest_block = SIZE_MAX, largest_block = 0, blocks_bytes = 0;
+ size_t used_blocks = 0; /* Total number of used blocks found. */
+ size_t used_size = 0; /* Total size (bytes) of used blocks. */
+
/* Read the pages and blocks. The aim here is to be robust against
* corrupt or malicious registries. So we make sure the loops
* always make forward progress. We add the address of each block
*/
size_t off;
struct ntreg_hbin_page *page;
- for (off = 0x1000; off < h->size; off += le32toh (page->offset_next)) {
+ for (off = 0x1000; off < h->size; off += le32toh (page->page_size)) {
if (off >= h->endpages)
break;
page->magic[2] != 'i' ||
page->magic[3] != 'n') {
fprintf (stderr, "hivex: %s: trailing garbage at end of file (at 0x%zx, after %zu pages)\n",
- filename, off, h->pages);
+ filename, off, pages);
errno = ENOTSUP;
goto error;
}
+ size_t page_size = le32toh (page->page_size);
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_open: page at 0x%zx\n", off);
- h->pages++;
-
- if (le32toh (page->offset_next) <= sizeof (struct ntreg_hbin_page) ||
- (le32toh (page->offset_next) & 3) != 0) {
- fprintf (stderr, "hivex: %s: pagesize %d at %zu, bad registry\n",
- filename, le32toh (page->offset_next), off);
+ fprintf (stderr, "hivex_open: page at 0x%zx, size %zu\n", off, page_size);
+ pages++;
+ if (page_size < smallest_page) smallest_page = page_size;
+ if (page_size > largest_page) largest_page = page_size;
+
+ if (page_size <= sizeof (struct ntreg_hbin_page) ||
+ (page_size & 0x0fff) != 0) {
+ fprintf (stderr, "hivex: %s: page size %zu at 0x%zx, bad registry\n",
+ filename, page_size, off);
errno = ENOTSUP;
goto error;
}
/* Read the blocks in this page. */
size_t blkoff;
struct ntreg_hbin_block *block;
- int32_t seg_len;
+ size_t seg_len;
for (blkoff = off + 0x20;
- blkoff < off + le32toh (page->offset_next);
+ blkoff < off + page_size;
blkoff += seg_len) {
- h->blocks++;
+ blocks++;
int is_root = blkoff == h->rootoffs;
if (is_root)
int used;
seg_len = block_len (h, blkoff, &used);
if (seg_len <= 4 || (seg_len & 3) != 0) {
- fprintf (stderr, "hivex: %s: block size %d at %zu, bad registry\n",
+ fprintf (stderr, "hivex: %s: block size %" PRIu32 " at 0x%zx, bad registry\n",
filename, le32toh (block->seg_len), blkoff);
errno = ENOTSUP;
goto error;
}
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_open: %s block id %d,%d at 0x%zx%s\n",
+ fprintf (stderr, "hivex_open: %s block id %d,%d at 0x%zx size %zu%s\n",
used ? "used" : "free", block->id[0], block->id[1], blkoff,
- is_root ? " (root)" : "");
+ seg_len, is_root ? " (root)" : "");
+
+ blocks_bytes += seg_len;
+ if (seg_len < smallest_block) smallest_block = seg_len;
+ if (seg_len > largest_block) largest_block = seg_len;
if (is_root && !used)
bad_root_block = 1;
if (used) {
- h->used_blocks++;
- h->used_size += seg_len;
+ used_blocks++;
+ used_size += seg_len;
/* Root block must be an nk-block. */
if (is_root && (block->id[0] != 'n' || block->id[1] != 'k'))
if (h->msglvl >= 1)
fprintf (stderr,
"hivex_open: successfully read Windows Registry hive file:\n"
- " pages: %zu\n"
- " blocks: %zu\n"
- " blocks used: %zu\n"
- " bytes used: %zu\n",
- h->pages, h->blocks, h->used_blocks, h->used_size);
+ " pages: %zu [sml: %zu, lge: %zu]\n"
+ " blocks: %zu [sml: %zu, avg: %zu, lge: %zu]\n"
+ " blocks used: %zu\n"
+ " bytes used: %zu\n",
+ pages, smallest_page, largest_page,
+ blocks, smallest_block, blocks_bytes / blocks, largest_block,
+ used_blocks, used_size);
return h;
int err = errno;
if (h) {
free (h->bitmap);
- if (h->addr && h->size && h->addr != MAP_FAILED)
- munmap (h->addr, h->size);
+ if (h->addr && h->size && h->addr != MAP_FAILED) {
+ if (!h->writable)
+ munmap (h->addr, h->size);
+ else
+ free (h->addr);
+ }
if (h->fd >= 0)
close (h->fd);
+ free (h->filename);
free (h);
}
errno = err;
int r;
free (h->bitmap);
- munmap (h->addr, h->size);
+ if (!h->writable)
+ munmap (h->addr, h->size);
+ else
+ free (h->addr);
r = close (h->fd);
+ free (h->filename);
free (h);
return r;
}
#endif
-hive_node_h *
-hivex_node_children (hive_h *h, hive_node_h node)
+/* Structure for returning 0-terminated lists of offsets (nodes,
+ * values, etc).
+ */
+struct offset_list {
+ size_t *offsets;
+ size_t len;
+ size_t alloc;
+};
+
+static void
+init_offset_list (struct offset_list *list)
+{
+ list->len = 0;
+ list->alloc = 0;
+ list->offsets = NULL;
+}
+
+#define INIT_OFFSET_LIST(name) \
+ struct offset_list name; \
+ init_offset_list (&name)
+
+/* Preallocates the offset_list, but doesn't make the contents longer. */
+static int
+grow_offset_list (struct offset_list *list, size_t alloc)
+{
+ assert (alloc >= list->len);
+ size_t *p = realloc (list->offsets, alloc * sizeof (size_t));
+ if (p == NULL)
+ return -1;
+ list->offsets = p;
+ list->alloc = alloc;
+ return 0;
+}
+
+static int
+add_to_offset_list (struct offset_list *list, size_t offset)
+{
+ if (list->len >= list->alloc) {
+ if (grow_offset_list (list, list->alloc ? list->alloc * 2 : 4) == -1)
+ return -1;
+ }
+ list->offsets[list->len] = offset;
+ list->len++;
+ return 0;
+}
+
+static void
+free_offset_list (struct offset_list *list)
+{
+ free (list->offsets);
+}
+
+static size_t *
+return_offset_list (struct offset_list *list)
+{
+ if (add_to_offset_list (list, 0) == -1)
+ return NULL;
+ return list->offsets; /* caller frees */
+}
+
+/* Iterate over children, returning child nodes and intermediate blocks. */
+static int
+get_children (hive_h *h, hive_node_h node,
+ hive_node_h **children_ret, size_t **blocks_ret)
{
if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
errno = EINVAL;
- return NULL;
+ return -1;
}
struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
size_t nr_subkeys_in_nk = le32toh (nk->nr_subkeys);
+ INIT_OFFSET_LIST (children);
+ INIT_OFFSET_LIST (blocks);
+
/* Deal with the common "no subkeys" case quickly. */
- hive_node_h *ret;
- if (nr_subkeys_in_nk == 0) {
- ret = malloc (sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
- ret[0] = 0;
- return ret;
- }
+ if (nr_subkeys_in_nk == 0)
+ goto ok;
/* Arbitrarily limit the number of subkeys we will ever deal with. */
if (nr_subkeys_in_nk > 1000000) {
errno = ERANGE;
- return NULL;
+ goto error;
}
+ /* Preallocate space for the children. */
+ if (grow_offset_list (&children, nr_subkeys_in_nk) == -1)
+ goto error;
+
/* The subkey_lf field can point either to an lf-record, which is
* the common case, or if there are lots of subkeys, to an
* ri-record.
fprintf (stderr, "hivex_node_children: returning EFAULT because subkey_lf is not a valid block (%zu)\n",
subkey_lf);
errno = EFAULT;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, subkey_lf) == -1)
+ goto error;
+
struct ntreg_hbin_block *block =
(struct ntreg_hbin_block *) (h->addr + subkey_lf);
if (nr_subkeys_in_nk != nr_subkeys_in_lf) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
size_t len = block_len (h, subkey_lf, NULL);
fprintf (stderr, "hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n",
nr_subkeys_in_lf, len);
errno = EFAULT;
- return NULL;
+ goto error;
}
- /* Allocate space for the returned values. Note that
- * nr_subkeys_in_lf is limited to a 16 bit value.
- */
- ret = malloc ((1 + nr_subkeys_in_lf) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
size_t i;
for (i = 0; i < nr_subkeys_in_lf; ++i) {
- hive_node_h subkey = lf->keys[i].offset;
+ hive_node_h subkey = le32toh (lf->keys[i].offset);
subkey += 0x1000;
if (!IS_VALID_BLOCK (h, subkey)) {
if (h->msglvl >= 2)
fprintf (stderr, "hivex_node_children: returning EFAULT because subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[i] = subkey;
+ if (add_to_offset_list (&children, subkey) == -1)
+ goto error;
}
- ret[i] = 0;
- return ret;
+ goto ok;
}
/* Points to ri-record? */
else if (block->id[0] == 'r' && block->id[1] == 'i') {
fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, offset) == -1)
+ goto error;
+
struct ntreg_lf_record *lf =
(struct ntreg_lf_record *) (h->addr + offset);
if (nr_subkeys_in_nk != count) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
/* Copy list of children. Note nr_subkeys_in_nk is limited to
* something reasonable above.
*/
- ret = malloc ((1 + nr_subkeys_in_nk) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
- count = 0;
for (i = 0; i < nr_offsets; ++i) {
hive_node_h offset = ri->offset[i];
offset += 0x1000;
fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n",
offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) {
errno = ENOTSUP;
- return NULL;
+ goto error;
}
struct ntreg_lf_record *lf =
size_t j;
for (j = 0; j < le16toh (lf->nr_keys); ++j) {
- hive_node_h subkey = lf->keys[j].offset;
+ hive_node_h subkey = le32toh (lf->keys[j].offset);
subkey += 0x1000;
if (!IS_VALID_BLOCK (h, subkey)) {
if (h->msglvl >= 2)
fprintf (stderr, "hivex_node_children: returning EFAULT because indirect subkey is not a valid block (0x%zx)\n",
subkey);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[count++] = subkey;
+ if (add_to_offset_list (&children, subkey) == -1)
+ goto error;
}
}
- ret[count] = 0;
-
- return ret;
+ goto ok;
}
- else {
- errno = ENOTSUP;
+ /* else not supported, set errno and fall through */
+ errno = ENOTSUP;
+ error:
+ free_offset_list (&children);
+ free_offset_list (&blocks);
+ return -1;
+
+ ok:
+ *children_ret = return_offset_list (&children);
+ *blocks_ret = return_offset_list (&blocks);
+ if (!*children_ret || !*blocks_ret)
+ goto error;
+ return 0;
+}
+
+hive_node_h *
+hivex_node_children (hive_h *h, hive_node_h node)
+{
+ hive_node_h *children;
+ size_t *blocks;
+
+ if (get_children (h, node, &children, &blocks) == -1)
return NULL;
- }
+
+ free (blocks);
+ return children;
}
/* Very inefficient, but at least having a separate API call
return ret;
}
-hive_value_h *
-hivex_node_values (hive_h *h, hive_node_h node)
+static int
+get_values (hive_h *h, hive_node_h node,
+ hive_value_h **values_ret, size_t **blocks_ret)
{
if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
errno = EINVAL;
- return 0;
+ return -1;
}
struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
if (h->msglvl >= 2)
fprintf (stderr, "hivex_node_values: nr_values = %zu\n", nr_values);
+ INIT_OFFSET_LIST (values);
+ INIT_OFFSET_LIST (blocks);
+
/* Deal with the common "no values" case quickly. */
- hive_node_h *ret;
- if (nr_values == 0) {
- ret = malloc (sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
- ret[0] = 0;
- return ret;
- }
+ if (nr_values == 0)
+ goto ok;
/* Arbitrarily limit the number of values we will ever deal with. */
if (nr_values > 100000) {
errno = ERANGE;
- return NULL;
+ goto error;
}
+ /* Preallocate space for the values. */
+ if (grow_offset_list (&values, nr_values) == -1)
+ goto error;
+
/* Get the value list and check it looks reasonable. */
size_t vlist_offset = le32toh (nk->vallist);
vlist_offset += 0x1000;
fprintf (stderr, "hivex_node_values: returning EFAULT because value list is not a valid block (0x%zx)\n",
vlist_offset);
errno = EFAULT;
- return NULL;
+ goto error;
}
+ if (add_to_offset_list (&blocks, vlist_offset) == -1)
+ goto error;
+
struct ntreg_value_list *vlist =
(struct ntreg_value_list *) (h->addr + vlist_offset);
fprintf (stderr, "hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n",
nr_values, len);
errno = EFAULT;
- return NULL;
+ goto error;
}
- /* Allocate return array and copy values in. */
- ret = malloc ((1 + nr_values) * sizeof (hive_node_h));
- if (ret == NULL)
- return NULL;
-
size_t i;
for (i = 0; i < nr_values; ++i) {
hive_node_h value = vlist->offset[i];
fprintf (stderr, "hivex_node_values: returning EFAULT because value is not a valid block (0x%zx)\n",
value);
errno = EFAULT;
- free (ret);
- return NULL;
+ goto error;
}
- ret[i] = value;
+ if (add_to_offset_list (&values, value) == -1)
+ goto error;
}
- ret[i] = 0;
- return ret;
+ ok:
+ *values_ret = return_offset_list (&values);
+ *blocks_ret = return_offset_list (&blocks);
+ if (!*values_ret || !*blocks_ret)
+ goto error;
+ return 0;
+
+ error:
+ free_offset_list (&values);
+ free_offset_list (&blocks);
+ return -1;
+}
+
+hive_value_h *
+hivex_node_values (hive_h *h, hive_node_h node)
+{
+ hive_value_h *values;
+ size_t *blocks;
+
+ if (get_values (h, node, &values, &blocks) == -1)
+ return NULL;
+
+ free (blocks);
+ return values;
}
/* Very inefficient, but at least having a separate API call
return ret;
}
- size_t data_offset = vk->data_offset;
+ size_t data_offset = le32toh (vk->data_offset);
data_offset += 0x1000;
if (!IS_VALID_BLOCK (h, data_offset)) {
if (h->msglvl >= 2)
/* Check that the declared size isn't larger than the block its in. */
size_t blen = block_len (h, data_offset, NULL);
- if (len > blen) {
+ if (len > blen - 4 /* subtract 4 for block header */) {
if (h->msglvl >= 2)
fprintf (stderr, "hivex_value_value: returning EFAULT because data is longer than its block (data 0x%zx, data len %zu, block len %zu)\n",
data_offset, len, blen);
goto error;
}
- switch (t) {
- case hive_t_none:
+ if (vtor->value_any) {
str = hivex_value_value (h, values[i], &t, &len);
if (str == NULL) {
ret = skip_bad ? 0 : -1;
goto error;
}
- if (t != hive_t_none) {
- ret = skip_bad ? 0 : -1;
- goto error;
- }
- if (vtor->value_none &&
- vtor->value_none (h, opaque, node, values[i], t, len, key, str) == -1)
+ if (vtor->value_any (h, opaque, node, values[i], t, len, key, str) == -1)
goto error;
free (str); str = NULL;
- break;
-
- case hive_t_string:
- case hive_t_expand_string:
- case hive_t_link:
- str = hivex_value_string (h, values[i]);
- if (str == NULL) {
- if (errno != EILSEQ && errno != EINVAL) {
+ }
+ else {
+ switch (t) {
+ case hive_t_none:
+ str = hivex_value_value (h, values[i], &t, &len);
+ if (str == NULL) {
ret = skip_bad ? 0 : -1;
goto error;
}
- if (vtor->value_string_invalid_utf16) {
- str = hivex_value_value (h, values[i], &t, &len);
- if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
- goto error;
- free (str); str = NULL;
+ if (t != hive_t_none) {
+ ret = skip_bad ? 0 : -1;
+ goto error;
}
+ if (vtor->value_none &&
+ vtor->value_none (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
break;
- }
- if (vtor->value_string &&
- vtor->value_string (h, opaque, node, values[i], t, len, key, str) == -1)
- goto error;
- free (str); str = NULL;
- break;
-
- case hive_t_dword:
- case hive_t_dword_be: {
- int32_t i32 = hivex_value_dword (h, values[i]);
- if (vtor->value_dword &&
- vtor->value_dword (h, opaque, node, values[i], t, len, key, i32) == -1)
- goto error;
- break;
- }
- case hive_t_qword: {
- int64_t i64 = hivex_value_qword (h, values[i]);
- if (vtor->value_qword &&
- vtor->value_qword (h, opaque, node, values[i], t, len, key, i64) == -1)
- goto error;
- break;
- }
+ case hive_t_string:
+ case hive_t_expand_string:
+ case hive_t_link:
+ str = hivex_value_string (h, values[i]);
+ if (str == NULL) {
+ if (errno != EILSEQ && errno != EINVAL) {
+ ret = skip_bad ? 0 : -1;
+ goto error;
+ }
+ if (vtor->value_string_invalid_utf16) {
+ str = hivex_value_value (h, values[i], &t, &len);
+ if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
+ }
+ break;
+ }
+ if (vtor->value_string &&
+ vtor->value_string (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
+ break;
- case hive_t_binary:
- str = hivex_value_value (h, values[i], &t, &len);
- if (str == NULL) {
- ret = skip_bad ? 0 : -1;
- goto error;
+ case hive_t_dword:
+ case hive_t_dword_be: {
+ int32_t i32 = hivex_value_dword (h, values[i]);
+ if (vtor->value_dword &&
+ vtor->value_dword (h, opaque, node, values[i], t, len, key, i32) == -1)
+ goto error;
+ break;
}
- if (t != hive_t_binary) {
- ret = skip_bad ? 0 : -1;
- goto error;
+
+ case hive_t_qword: {
+ int64_t i64 = hivex_value_qword (h, values[i]);
+ if (vtor->value_qword &&
+ vtor->value_qword (h, opaque, node, values[i], t, len, key, i64) == -1)
+ goto error;
+ break;
}
- if (vtor->value_binary &&
- vtor->value_binary (h, opaque, node, values[i], t, len, key, str) == -1)
- goto error;
- free (str); str = NULL;
- break;
- case hive_t_multiple_strings:
- strs = hivex_value_multiple_strings (h, values[i]);
- if (strs == NULL) {
- if (errno != EILSEQ && errno != EINVAL) {
+ case hive_t_binary:
+ str = hivex_value_value (h, values[i], &t, &len);
+ if (str == NULL) {
ret = skip_bad ? 0 : -1;
goto error;
}
- if (vtor->value_string_invalid_utf16) {
- str = hivex_value_value (h, values[i], &t, &len);
- if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
+ if (t != hive_t_binary) {
+ ret = skip_bad ? 0 : -1;
+ goto error;
+ }
+ if (vtor->value_binary &&
+ vtor->value_binary (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
+ break;
+
+ case hive_t_multiple_strings:
+ strs = hivex_value_multiple_strings (h, values[i]);
+ if (strs == NULL) {
+ if (errno != EILSEQ && errno != EINVAL) {
+ ret = skip_bad ? 0 : -1;
goto error;
- free (str); str = NULL;
+ }
+ if (vtor->value_string_invalid_utf16) {
+ str = hivex_value_value (h, values[i], &t, &len);
+ if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
+ }
+ break;
}
+ if (vtor->value_multiple_strings &&
+ vtor->value_multiple_strings (h, opaque, node, values[i], t, len, key, strs) == -1)
+ goto error;
+ free_strings (strs); strs = NULL;
break;
- }
- if (vtor->value_multiple_strings &&
- vtor->value_multiple_strings (h, opaque, node, values[i], t, len, key, strs) == -1)
- goto error;
- free_strings (strs); strs = NULL;
- break;
- case hive_t_resource_list:
- case hive_t_full_resource_description:
- case hive_t_resource_requirements_list:
- default:
- str = hivex_value_value (h, values[i], &t, &len);
- if (str == NULL) {
- ret = skip_bad ? 0 : -1;
- goto error;
+ case hive_t_resource_list:
+ case hive_t_full_resource_description:
+ case hive_t_resource_requirements_list:
+ default:
+ str = hivex_value_value (h, values[i], &t, &len);
+ if (str == NULL) {
+ ret = skip_bad ? 0 : -1;
+ goto error;
+ }
+ if (vtor->value_other &&
+ vtor->value_other (h, opaque, node, values[i], t, len, key, str) == -1)
+ goto error;
+ free (str); str = NULL;
+ break;
}
- if (vtor->value_other &&
- vtor->value_other (h, opaque, node, values[i], t, len, key, str) == -1)
- goto error;
- free (str); str = NULL;
- break;
}
free (key); key = NULL;