X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=hivex%2Fhivex.c;h=f7dbd5fef315f2037bcd264a085c68ee0aa13129;hp=6129017dbc317e4da3b5370f0eb146d520bb897b;hb=a96877f75061ba35efa9fa5dde9fdcf7a1eeaf2a;hpb=0c28fb1a520ac6dea018cf42e6e056fe439f2810 diff --git a/hivex/hivex.c b/hivex/hivex.c index 6129017..f7dbd5f 100644 --- a/hivex/hivex.c +++ b/hivex/hivex.c @@ -89,6 +89,7 @@ static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len); struct hive_h { + char *filename; int fd; size_t size; int msglvl; @@ -119,12 +120,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. */ @@ -166,7 +161,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__)); @@ -260,6 +255,21 @@ struct ntreg_vk_record { char name[1]; /* key name follows here */ } __attribute__((__packed__)); +static uint32_t +header_checksum (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) { @@ -281,6 +291,10 @@ hivex_open (const char *filename, int flags) if (h->msglvl >= 2) fprintf (stderr, "hivex_open: created handle %p\n", h); + h->filename = strdup (filename); + if (h->filename == NULL) + goto error; + h->fd = open (filename, O_RDONLY); if (h->fd == -1) goto error; @@ -324,14 +338,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; @@ -371,6 +378,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 @@ -379,7 +394,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; @@ -389,19 +404,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; } @@ -409,11 +427,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) @@ -430,16 +448,20 @@ hivex_open (const char *filename, int flags) } 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')) @@ -466,11 +488,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; @@ -482,6 +506,7 @@ hivex_open (const char *filename, int flags) munmap (h->addr, h->size); if (h->fd >= 0) close (h->fd); + free (h->filename); free (h); } errno = err; @@ -496,6 +521,7 @@ hivex_close (hive_h *h) free (h->bitmap); munmap (h->addr, h->size); r = close (h->fd); + free (h->filename); free (h); return r; @@ -1371,114 +1397,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;