From 90781ca7d22a22b9abb367c10707639c7bd31c5b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Fri, 5 Feb 2010 14:50:19 +0000 Subject: [PATCH] hivex: Fix handling of inline VKs. --- hivex/hivex.c | 40 ++++++++++++++++++++-------------------- hivex/tools/visualizer.ml | 26 +++++++++++++++----------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/hivex/hivex.c b/hivex/hivex.c index 6752da0..7d26eeb 100644 --- a/hivex/hivex.c +++ b/hivex/hivex.c @@ -229,8 +229,7 @@ struct ntreg_vk_record { uint16_t name_len; /* length of name */ /* length of the data: * If data_len is <= 4, then it's stored inline. - * If data_len is 0x80000000, then it's an inline dword. - * Top bit may be set or not set at random. + * Top bit is set to indicate inline. */ uint32_t data_len; uint32_t data_offset; /* pointer to the data (or data if inline) */ @@ -1151,11 +1150,7 @@ hivex_value_type (hive_h *h, hive_value_h value, hive_type *t, size_t *len) if (len) { *len = le32toh (vk->data_len); - if (*len == 0x80000000) { /* special case */ - *len = 4; - if (t) *t = hive_t_dword; - } - *len &= 0x7fffffff; + *len &= 0x7fffffff; /* top bit indicates if data is stored inline */ } return 0; @@ -1174,25 +1169,28 @@ hivex_value_value (hive_h *h, hive_value_h value, hive_type t; size_t len; + int is_inline; t = le32toh (vk->data_type); len = le32toh (vk->data_len); - if (len == 0x80000000) { /* special case */ - len = 4; - t = hive_t_dword; - } + is_inline = !!(len & 0x80000000); len &= 0x7fffffff; if (h->msglvl >= 2) - fprintf (stderr, "hivex_value_value: value=0x%zx, t=%d, len=%zu\n", - value, t, len); + fprintf (stderr, "hivex_value_value: value=0x%zx, t=%d, len=%zu, inline=%d\n", + value, t, len, is_inline); if (t_rtn) *t_rtn = t; if (len_rtn) *len_rtn = len; + if (is_inline && len > 4) { + errno = ENOTSUP; + return NULL; + } + /* Arbitrarily limit the length that we will read. */ if (len > HIVEX_MAX_VALUE_LEN) { errno = ERANGE; @@ -1203,8 +1201,7 @@ hivex_value_value (hive_h *h, hive_value_h value, if (ret == NULL) return NULL; - /* If length is <= 4 it's always stored inline. */ - if (len <= 4) { + if (is_inline) { memcpy (ret, (char *) &vk->data_offset, len); return ret; } @@ -1924,12 +1921,12 @@ delete_values (hive_h *h, hive_node_h node) (struct ntreg_vk_record *) (h->addr + values[i]); size_t len; + int is_inline; len = le32toh (vk->data_len); - if (len == 0x80000000) /* special case */ - len = 4; + is_inline = !!(len & 0x80000000); /* top bit indicates is inline */ len &= 0x7fffffff; - if (len > 4) { /* non-inline, so remove data block */ + if (!is_inline) { /* non-inline, so remove data block */ size_t data_offset = le32toh (vk->data_offset); data_offset += 0x1000; mark_block_unused (h, data_offset); @@ -2533,10 +2530,13 @@ hivex_node_set_values (hive_h *h, hive_node_h node, vk->name_len = htole16 (name_len); strcpy (vk->name, values[i].key); vk->data_type = htole32 (values[i].t); - vk->data_len = htole16 (values[i].len); + uint32_t len = values[i].len; + if (len <= 4) /* store it inline => set MSB flag */ + len |= 0x80000000; + vk->data_len = htole32 (len); vk->flags = name_len == 0 ? 0 : 1; - if (values[i].len <= 4) /* Store data inline. */ + if (values[i].len <= 4) /* store it inline */ memcpy (&vk->data_offset, values[i].value, values[i].len); else { size_t offs = allocate_block (h, values[i].len + 4, nul_id); diff --git a/hivex/tools/visualizer.ml b/hivex/tools/visualizer.ml index d4a339e..5b7ac79 100644 --- a/hivex/tools/visualizer.ml +++ b/hivex/tools/visualizer.ml @@ -493,18 +493,18 @@ type data_t = Inline of bitstring | Offset of int let bitmatch vk_fields = { "vk" : 2*8 : string; name_len : 2*8 : littleendian; - (* No one documents the important fact that data_len can have the - * top bit set (randomly or is it meaningful?). The length can - * also be 0 (or 0x80000000) if the data type is NONE. + (* Top bit set means that the data is stored inline. In that case + * the data length must be <= 4. The length can also be 0 (or + * 0x80000000) if the data type is NONE. *) data_len : 4*8 : littleendian, bind ( - let data_len = Int32.logand data_len 0x7fff_ffff_l in - Int32.to_int data_len + let is_inline = Int32.logand data_len 0x8000_0000_l = 0x8000_0000_l in + let data_len = Int32.to_int (Int32.logand data_len 0x7fff_ffff_l) in + if is_inline then assert (data_len <= 4) else assert (data_len > 4); + is_inline, data_len ); - (* Inline data if len <= 4, offset otherwise. - * - * The data itself depends on the type field. + (* The data itself depends on the type field. * * For REG_SZ type, the data always seems to be NUL-terminated, which * means because these strings are often UTF-16LE, that the string will @@ -515,7 +515,8 @@ let bitmatch vk_fields = *) data : 4*8 : bitstring, bind ( - if data_len <= 4 then + let is_inline, data_len = data_len in + if is_inline then Inline (takebits (data_len*8) data) else ( let offset = @@ -546,9 +547,10 @@ let fprintf_vk chan vk = | Offset offset -> let (_, _, bits) = lookup "fprintf_vk (data)" offset in bits in - fprintf chan "VK %s %s %d %s%s %s %08x %s %08x %08x\n" + let is_inline, data_len = data_len in + fprintf chan "VK %s %s %s %d %s%s %s %08x %s %08x %08x\n" (print_offset vk) - name data_len + name (if is_inline then "inline" else "-") data_len (match data with | Inline _ -> "" | Offset offset -> "["^print_offset offset^"]") @@ -707,6 +709,8 @@ and visit_vk vk = | { :vk_fields } -> fprintf_vk stdout vk; + let is_inline, data_len = data_len in + if unknown1 <> 0 then eprintf "VK %s unknown1 flags set (%02x)\n" (print_offset vk) unknown1; -- 1.8.3.1