Dawid Zamirski
2017-Feb-15 18:42 UTC
[Libguestfs] [PATCH v3 0/2] hivex: handle corrupted hives better
The following patches address issues when dealing with hives that have corrupted data in them but are otherwise readable/writable. Those were found on some rather rare Windows installations that seem to work fine but current hivex fails to even open. Those patches change hivex to simply log and ignore such "corrupted" regions instead of aborting because the caller might be looking at keys that are perfectly readable/writable (e.g. to identify Windows version from HKLM/Software/Microsoft/Windows NT/CurrentVersion) and other "corrupted" and irrelevant keys might prevent one from doing so. Changes in v3: * rebase on current master * fix GCC7 -Wunsafe-loop-optimizations warning by incrementing the "off" variable by 1 rather than 0x1000, the latter was assuming "garbage" data in between "hbins" would be aligned on 4096 bytes which does not have to be true and GCC7 is correct. Regards, Dawid Zamirski (2): lib: change how hbin sections are read. lib: allow to walk registry with corrupted blocks lib/handle.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- lib/node.c | 21 ++++++++++++--------- 2 files changed, 58 insertions(+), 18 deletions(-) -- 2.9.3
Dawid Zamirski
2017-Feb-15 18:42 UTC
[Libguestfs] [PATCH v3 1/2] lib: change how hbin sections are read.
* hivex_open: when looping over hbin sections (aka pages), handle a case where following hbin section may not begin at exactly at the end of previous one. If this happens, scan the page section until next one is found and validate it by checking declared offset with actual one - if they match, all is good and we can safely move on. Rationale: there are registry hives there is some garbage data between hbin section but the hive is still perfectly usable as long as the offsets stated in hbin headers are correct. --- lib/handle.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/handle.c b/lib/handle.c index d33c1d0..42fd672 100644 --- a/lib/handle.c +++ b/lib/handle.c @@ -226,11 +226,30 @@ hivex_open (const char *filename, int flags) page->magic[1] != 'b' || page->magic[2] != 'i' || page->magic[3] != 'n') { - SET_ERRNO (ENOTSUP, - "%s: trailing garbage at end of file " - "(at 0x%zx, after %zu pages)", - filename, off, pages); - goto error; + + DEBUG (2, + "page not found at expected offset 0x%zx, " + "seeking until one is found or EOF is reached", + off); + + int found = 0; + while (off < h->endpages) { + off++; + page = (struct ntreg_hbin_page *) ((char *) h->addr + off); + if (page->magic[0] == 'h' && + page->magic[1] == 'b' && + page->magic[2] == 'i' && + page->magic[3] == 'n') { + DEBUG (2, "found next page by seeking at 0x%zx", off); + found = 1; + break; + } + } + + if (!found) { + DEBUG (2, "page not found and end of pages section reached"); + break; + } } size_t page_size = le32toh (page->page_size); @@ -254,6 +273,16 @@ hivex_open (const char *filename, int flags) goto error; } + size_t page_offset = le32toh(page->offset_first) + 0x1000; + + if (page_offset != off) { + SET_ERRNO (ENOTSUP, + "%s: declared page offset (0x%zx) does not match computed " + "offset (0x%zx), bad registry", + filename, page_offset, off); + goto error; + } + /* Read the blocks in this page. */ size_t blkoff; struct ntreg_hbin_block *block; -- 2.9.3
Dawid Zamirski
2017-Feb-15 20:15 UTC
[Libguestfs] [PATCH v3 2/2] lib: allow to walk registry with corrupted blocks
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. --- lib/handle.c | 16 ++++++++++++---- lib/node.c | 21 ++++++++++++--------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/handle.c b/lib/handle.c index 42fd672..29b9747 100644 --- a/lib/handle.c +++ b/lib/handle.c @@ -304,10 +304,18 @@ hivex_open (const char *filename, int flags) #pragma GCC diagnostic ignored "-Wstrict-overflow" if (seg_len <= 4 || (seg_len & 3) != 0) { #pragma GCC diagnostic pop - SET_ERRNO (ENOTSUP, - "%s: block size %" PRIi32 " at 0x%zx, bad registry", - filename, le32toh (block->seg_len), blkoff); - goto error; + if (is_root) { + SET_ERRNO (ENOTSUP, + "%s, the root 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 822c250..a20bbc5 100644 --- a/lib/node.c +++ b/lib/node.c @@ -343,11 +343,10 @@ _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; + 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 +406,10 @@ _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) { + DEBUG (2, "subkey at 0x%zx is not an NK block, skipping", subkey); + continue; + } if (_hivex_add_to_offset_list (children, subkey) == -1) return -1; } @@ -435,8 +436,10 @@ _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) { + DEBUG (2, "subkey at 0x%zx is not an NK block, skipping", subkey); + continue; + } if (_hivex_add_to_offset_list (children, subkey) == -1) return -1; } -- 2.9.3
Reasonably Related Threads
- [PATCH 0/2] hivex: handle corrupted hives better
- [PATCH v2 0/2] hivex: handle corrupted hives better
- [PATCH v4 0/5] hivex: handle corrupted hives better.
- [PATCH hivex 00/19] Fix read/write handling of li-records.
- Re: [PATCH 2/2] lib: allow to walk registry with corrupted blocks