[Libguestfs] [PATCH] Add read support for "big data" blocks to hivex
Richard W.M. Jones
rjones at redhat.com
Wed Jun 26 12:56:19 UTC 2013
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.1
ACK.
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
More information about the Libguestfs
mailing list