X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=hivex%2Fhivex.c;h=44f29984c055ebb1e5b036a37e06cf2a186e301b;hp=a7603007edc5c05f919649d4f39aa33eb98e2f7f;hb=4718aeb9f0f743a05dfa342bc797617f3b9f96b3;hpb=1e4614ca8f8d2b3d758fef733f1294c611502b94 diff --git a/hivex/hivex.c b/hivex/hivex.c index a760300..44f2998 100644 --- a/hivex/hivex.c +++ b/hivex/hivex.c @@ -1,5 +1,5 @@ /* 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: @@ -33,11 +33,11 @@ #include #include #include -#ifdef HAVE_ENDIAN_H -#include -#endif -#ifdef HAVE_BYTESWAP_H -#include + +#include "full-read.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 #endif #define STREQ(a,b) (strcmp((a),(b)) == 0) @@ -50,41 +50,8 @@ //#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); @@ -93,8 +60,9 @@ struct hive_h { 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; @@ -120,12 +88,6 @@ struct hive_h { /* 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. */ @@ -167,7 +129,7 @@ struct ntreg_header { 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__)); @@ -202,18 +164,25 @@ struct ntreg_nk_record { 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 */ @@ -225,7 +194,7 @@ struct ntreg_lf_record { 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__)); @@ -253,14 +222,30 @@ struct ntreg_vk_record { */ 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) { @@ -282,11 +267,12 @@ hivex_open (const char *filename, int flags) if (h->msglvl >= 2) fprintf (stderr, "hivex_open: created handle %p\n", h); + h->writable = !!(flags & HIVEX_OPEN_WRITE); h->filename = strdup (filename); if (h->filename == NULL) goto error; - h->fd = open (filename, O_RDONLY); + h->fd = open (filename, O_RDONLY | O_CLOEXEC); if (h->fd == -1) goto error; @@ -296,12 +282,21 @@ hivex_open (const char *filename, int flags) 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' || @@ -329,14 +324,7 @@ hivex_open (const char *filename, int flags) 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; @@ -376,6 +364,14 @@ hivex_open (const char *filename, int flags) */ 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 @@ -384,7 +380,7 @@ hivex_open (const char *filename, int flags) */ 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; @@ -394,19 +390,22 @@ hivex_open (const char *filename, int flags) 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; } @@ -414,11 +413,11 @@ hivex_open (const char *filename, int flags) /* 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) @@ -428,23 +427,27 @@ hivex_open (const char *filename, int flags) 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')) @@ -471,11 +474,13 @@ hivex_open (const char *filename, int flags) 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; @@ -483,8 +488,12 @@ hivex_open (const char *filename, int flags) 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); @@ -500,7 +509,10 @@ hivex_close (hive_h *h) 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); @@ -601,34 +613,97 @@ hivex_node_classname (hive_h *h, hive_node_h node) } #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. @@ -640,9 +715,12 @@ hivex_node_children (hive_h *h, hive_node_h node) 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); @@ -663,7 +741,7 @@ hivex_node_children (hive_h *h, hive_node_h node) if (nr_subkeys_in_nk != nr_subkeys_in_lf) { errno = ENOTSUP; - return NULL; + goto error; } size_t len = block_len (h, subkey_lf, NULL); @@ -672,32 +750,24 @@ hivex_node_children (hive_h *h, hive_node_h node) 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') { @@ -715,13 +785,16 @@ hivex_node_children (hive_h *h, hive_node_h node) 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); @@ -734,17 +807,12 @@ hivex_node_children (hive_h *h, hive_node_h node) 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; @@ -753,11 +821,11 @@ hivex_node_children (hive_h *h, hive_node_h node) 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 = @@ -765,27 +833,47 @@ hivex_node_children (hive_h *h, hive_node_h node) 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 @@ -840,12 +928,13 @@ hivex_node_parent (hive_h *h, hive_node_h node) 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); @@ -855,22 +944,23 @@ hivex_node_values (hive_h *h, hive_node_h 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; @@ -879,9 +969,12 @@ hivex_node_values (hive_h *h, hive_node_h node) 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); @@ -891,14 +984,9 @@ hivex_node_values (hive_h *h, hive_node_h node) 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]; @@ -908,14 +996,36 @@ hivex_node_values (hive_h *h, hive_node_h node) 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 @@ -1056,7 +1166,7 @@ hivex_value_value (hive_h *h, hive_value_h value, 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) @@ -1069,7 +1179,7 @@ hivex_value_value (hive_h *h, hive_value_h value, /* 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); @@ -1378,114 +1488,126 @@ hivex__visit_node (hive_h *h, hive_node_h node, 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;