summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-02-05 14:50:19 +0000
committerRichard Jones <rjones@redhat.com>2010-02-05 14:50:19 +0000
commit90781ca7d22a22b9abb367c10707639c7bd31c5b (patch)
tree57eec796554e718356a232c4cabf545fe7136f87
parent0764b932676b66ffca9ffa4cc5a45ebac7d81748 (diff)
downloadlibguestfs-90781ca7d22a22b9abb367c10707639c7bd31c5b.tar.gz
libguestfs-90781ca7d22a22b9abb367c10707639c7bd31c5b.tar.xz
libguestfs-90781ca7d22a22b9abb367c10707639c7bd31c5b.zip
hivex: Fix handling of inline VKs.
-rw-r--r--hivex/hivex.c40
-rw-r--r--hivex/tools/visualizer.ml26
2 files changed, 35 insertions, 31 deletions
diff --git a/hivex/hivex.c b/hivex/hivex.c
index 6752da08..7d26eebd 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 d4a339e1..5b7ac79d 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;