Hilko Bengen
2013-Jun-25 20:47 UTC
Re: [Libguestfs] [PATCH] Add read support for "big data" blocks to hivex
* Richard W.M. Jones:> diff --git a/lib/hivex.c b/lib/hivex.c > index e3c1e05..9351ac5 100644 > --- a/lib/hivex.c > +++ b/lib/hivex.c > @@ -1471,7 +1471,7 @@ hivex_value_value (hive_h *h, hive_value_h value, > 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)\ > - data_offset, blocklist_offset, subblock_offset); > + data_offset, blocklist_offset, (size_t) subblock_offset); > } > int32_t seg_len = block_len(h, subblock_offset, NULL); > struct ntreg_db_block *subblock > > to make it compile without warnings.Yeah, so I'll just make subblock_offset a size_t, like all the other offset values.> With that addition, * ACK *.I'll post a slightly updated version, with better error messages, just to be sure. Cheers, -Hilko
Hilko Bengen
2013-Jun-25 20:55 UTC
[Libguestfs] [PATCH] Add read support for "big data" blocks to hivex
Large values are split into multiple blocks. References to these sub-blocks are kept in a list whose structure seems to be identical to a value list. A "db" record contains information on the number of sub-blocks and a pointer to the list. It is referenced by the vk record. I came across this when comparing the contents of HKLM\SOFTWARE hives from Windows7 systems and finding that hivex_value_value would only give me identical first 12 bytes for certain records though the data size had changed. If one runs hivexsh with debug messages enabled, it gives a warning when listing these values, for example: SOFTWARE\Microsoft\SystemCertificates\AuthRoot\AutoUpdate> lsval [...] hivex_value_value: warning: declared data length is longer than the block it is in (data 0x28b9b60, data len 115347, block len 16) "EncodedCtl"=hex(3):64,62,08,00,70,8b,8b,02,00,b2,00,00 --- lib/hivex.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/lib/hivex.c b/lib/hivex.c index efc27f8..040b1e7 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,70 @@ 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 block and block is not a db block " + "(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) { + size_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: subblock is not " + "valid (db block 0x%zx, block list 0x%zx, data subblock 0x%zx)\n", + data_offset, blocklist_offset, subblock_offset); + errno = EINVAL; + free (ret); + return NULL; + } + 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; + } + if (off != *len_rtn) { + if (h->msglvl >= 2) + fprintf (stderr, "hivex_value_value: warning: declared data length " + "and amount of data found in sub-blocks differ " + "(db block 0x%zx, data len %zu, found data %zu)\n", + data_offset, *len_rtn, off); + *len_rtn = off; + } + return ret; } - - char *data = (char *) h->addr + data_offset + 4; - memcpy (ret, data, len); - return ret; } static char * -- 1.8.3.1
Richard W.M. Jones
2013-Jun-26 12:56 UTC
Re: [Libguestfs] [PATCH] Add read support for "big data" blocks to hivex
On Tue, Jun 25, 2013 at 10:55:34PM +0200, Hilko Bengen wrote:> Large values are split into multiple blocks. References to these > sub-blocks are kept in a list whose structure seems to be identical to > a value list. > > A "db" record contains information on the number of sub-blocks and a > pointer to the list. It is referenced by the vk record. > > I came across this when comparing the contents of HKLM\SOFTWARE hives > from Windows7 systems and finding that hivex_value_value would only give > me identical first 12 bytes for certain records though the data size had > changed. If one runs hivexsh with debug messages enabled, it gives a > warning when listing these values, for example: > > SOFTWARE\Microsoft\SystemCertificates\AuthRoot\AutoUpdate> lsval > [...] > hivex_value_value: warning: declared data length is longer than the > block it is in (data 0x28b9b60, data len 115347, block len 16) > "EncodedCtl"=hex(3):64,62,08,00,70,8b,8b,02,00,b2,00,00 > --- > lib/hivex.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 76 insertions(+), 15 deletions(-) > > diff --git a/lib/hivex.c b/lib/hivex.c > index efc27f8..040b1e7 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,70 @@ 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 block and block is not a db block " > + "(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) { > + size_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: subblock is not " > + "valid (db block 0x%zx, block list 0x%zx, data subblock 0x%zx)\n", > + data_offset, blocklist_offset, subblock_offset); > + errno = EINVAL; > + free (ret); > + return NULL; > + } > + 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; > + } > + if (off != *len_rtn) { > + if (h->msglvl >= 2) > + fprintf (stderr, "hivex_value_value: warning: declared data length " > + "and amount of data found in sub-blocks differ " > + "(db block 0x%zx, data len %zu, found data %zu)\n", > + data_offset, *len_rtn, off); > + *len_rtn = off; > + } > + return ret; > } > - > - char *data = (char *) h->addr + data_offset + 4; > - memcpy (ret, data, len); > - return ret; > } > > static char * > -- > 1.8.3.1ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 100 libraries supported. http://fedoraproject.org/wiki/MinGW
Maybe Matching Threads
- Re: [PATCH] Add read support for "big data" blocks to hivex
- [PATCH] Add read support for "big data" blocks to hivex
- Re: [PATCH] Add read support for "big data" blocks to hivex
- [PATCH hivex] maint: split long lines
- [hivex][PATCH 3/8] hivex: Add offset-&-length function for long value data