Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 01/14] hivexsh: Document some peculiarities of the "cd" command.
--- hivex/hivexsh.pod | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/hivex/hivexsh.pod b/hivex/hivexsh.pod index 277e3ae..9336798 100644 --- a/hivex/hivexsh.pod +++ b/hivex/hivexsh.pod @@ -100,7 +100,14 @@ or even: Path elements (node names) are matched case insensitively, and characters like space, C<*>, and C<?> have I<no> special significance. -C<..> may be used to go to the parent directory. +C<cd ..> may be used to go to the parent directory. + +C<cd> without any arguments prints the current path. + +Be careful with C<cd \> since the readline library has an undocumented +behaviour where it will think the final backslash is a continuation +(it reads the next line of input and appends it). Put a single space +after the backslash. =item B<close> | B<unload> -- 1.6.5.2
Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 02/14] hivex: Some missing le32toh endianness conversions.
---
hivex/hivex.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index c8257a3..ce3e14c 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -803,7 +803,7 @@ get_children (hive_h *h, hive_node_h node,
/* Count total number of children. */
size_t i, count = 0;
for (i = 0; i < nr_offsets; ++i) {
- hive_node_h offset = ri->offset[i];
+ hive_node_h offset = le32toh (ri->offset[i]);
offset += 0x1000;
if (!IS_VALID_BLOCK (h, offset)) {
if (h->msglvl >= 2)
@@ -839,7 +839,7 @@ get_children (hive_h *h, hive_node_h node,
* something reasonable above.
*/
for (i = 0; i < nr_offsets; ++i) {
- hive_node_h offset = ri->offset[i];
+ hive_node_h offset = le32toh (ri->offset[i]);
offset += 0x1000;
if (!IS_VALID_BLOCK (h, offset)) {
if (h->msglvl >= 2)
--
1.6.5.2
Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 03/14] hivex: Documentation update.
ntreg_lf_record can have id "lf" (old-style hashes) or "lh"
(new-
style hashes).
---
hivex/hivex.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index ce3e14c..b106a10 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -195,7 +195,7 @@ struct ntreg_nk_record {
struct ntreg_lf_record {
int32_t seg_len;
- char id[2]; /* "lf" */
+ char id[2]; /* "lf"|"lh" */
uint16_t nr_keys; /* number of keys in this record */
struct {
uint32_t offset; /* offset of nk-record for this subkey */
--
1.6.5.2
Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 04/14] hivex: More debug messages.
---
hivex/hivex.c | 11 ++++++++++-
1 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index b106a10..6b4b9d8 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -735,7 +735,7 @@ get_children (hive_h *h, hive_node_h node,
subkey_lf += 0x1000;
if (!IS_VALID_BLOCK (h, subkey_lf)) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_node_children: returning EFAULT because
subkey_lf is not a valid block (%zu)\n",
+ fprintf (stderr, "hivex_node_children: returning EFAULT because
subkey_lf is not a valid block (0x%zx)\n",
subkey_lf);
errno = EFAULT;
goto error;
@@ -813,6 +813,9 @@ get_children (hive_h *h, hive_node_h node,
goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h,
offset, "lh")) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "get_children: returning ENOTSUP because
ri-record offset does not point to lf/lh (0x%zx)\n",
+ offset);
errno = ENOTSUP;
goto error;
}
@@ -849,6 +852,9 @@ get_children (hive_h *h, hive_node_h node,
goto error;
}
if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h,
offset, "lh")) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "get_children: returning ENOTSUP because
ri-record offset does not point to lf/lh (0x%zx)\n",
+ offset);
errno = ENOTSUP;
goto error;
}
@@ -876,6 +882,9 @@ get_children (hive_h *h, hive_node_h node,
goto ok;
}
/* else not supported, set errno and fall through */
+ if (h->msglvl >= 2)
+ fprintf (stderr, "get_children: returning ENOTSUP because subkey block
is not lf/lh/ri (0x%zx, %d, %d)\n",
+ subkey_lf, block->id[0], block->id[1]);
errno = ENOTSUP;
error:
free_offset_list (&children);
--
1.6.5.2
Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 05/14] hivex: allocate_block should update valid block bitmap.
The internal allocate_block() function wasn't updating the bitmap,
so if you revisited a block which you had allocated in the same
session, you could get an EFAULT error.
---
hivex/hivex.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index 6b4b9d8..2a094b8 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -1847,6 +1847,8 @@ allocate_block (hive_h *h, size_t seg_len, const char
id[2])
blockhdr->id[1] = id[1];
}
+ BITMAP_SET (h->bitmap, offset);
+
h->endblocks += seg_len;
/* If there is space after the last block in the last page, then we
--
1.6.5.2
Richard Jones
2010-Feb-05 13:06 UTC
[Libguestfs] [PATCH 06/14] hivexsh: cd command: fix error handling
The error behaviour of hivex_node_get_child is subtle, so the 'cd'
command wouldn't always report errors correctly. This fixes it.
---
hivex/hivexsh.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index ceb1153..39ca634 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -559,10 +559,14 @@ cmd_cd (char *path)
continue;
}
+ errno = 0;
new_cwd = hivex_node_get_child (h, new_cwd, elem);
if (new_cwd == 0) {
- fprintf (stderr, _("hivexsh: cd: subkey '%s' not
found\n"),
- elem);
+ if (errno)
+ perror ("hivexsh: cd");
+ else
+ fprintf (stderr, _("hivexsh: cd: subkey '%s' not
found\n"),
+ elem);
return -1;
}
}
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 07/14] hivexsh: lsval: Remove stray quotation mark.
---
hivex/hivexsh.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index 39ca634..1bd3b8b 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -784,7 +784,7 @@ cmd_lsval (char *key)
case hive_t_dword:
case hive_t_dword_be: {
int32_t j = hivex_value_dword (h, values[i]);
- printf ("dword:%08" PRIx32 "\"", j);
+ printf ("dword:%08" PRIx32, j);
break;
}
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 08/14] hivexsh: del command: Fix error message.
---
hivex/hivexsh.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index 1bd3b8b..d5d9ada 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -1069,7 +1069,7 @@ cmd_del (char *args)
hive_node_h new_cwd = hivex_node_parent (h, cwd);
if (hivex_node_delete_child (h, cwd) == -1) {
- perror ("del");
+ perror ("hivexsh: del");
return -1;
}
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 09/14] hivex: Check hash fields in lf/lh records.
---
hivex/tools/visualizer.ml | 81 ++++++++++++++++++++++++++++++++++-----------
1 files changed, 61 insertions(+), 20 deletions(-)
diff --git a/hivex/tools/visualizer.ml b/hivex/tools/visualizer.ml
index da79bee..bc447b7 100644
--- a/hivex/tools/visualizer.ml
+++ b/hivex/tools/visualizer.ml
@@ -636,7 +636,7 @@ let rec visit_nk ?(nk_is_root = false) nk
(* Visit the subkeys of this node. *)
if subkeys <> -1 then (
- let counted, max_name_len = visit_subkeys subkeys in
+ let counted, max_name_len, _ = visit_subkeys subkeys in
if counted <> nr_subkeys then
failwithf "%s: incorrect count of subkeys (%d, counted %d) in
subkey list at %s\n"
@@ -728,11 +728,17 @@ and visit_subkeys subkeys let (_, _, bits) = lookup
"visit_subkeys" subkeys in
mark_visited subkeys;
(bitmatch bits with
- | { ("lf"|"lh") : 2*8 : string;
+ | { "lf" : 2*8 : string;
len : 2*8 : littleendian; (* number of subkeys of this node *)
rest : len*8*8 : bitstring } ->
printf "LF %s %d\n" (print_offset subkeys) len;
- visit_subkeys_in_lf_list subkeys len rest
+ visit_subkeys_in_lf_list false subkeys len rest
+
+ | { "lh" : 2*8 : string;
+ len : 2*8 : littleendian; (* number of subkeys of this node *)
+ rest : len*8*8 : bitstring } ->
+ printf "LF %s %d\n" (print_offset subkeys) len;
+ visit_subkeys_in_lf_list true subkeys len rest
| { "ri" : 2*8 : string;
len : 2*8 : littleendian;
@@ -746,55 +752,90 @@ and visit_subkeys subkeys
| { "nk" : 2*8 : string } ->
visit_nk subkeys;
- let name_len = name_len_of_nk subkeys in
- 1, name_len
+ let name, name_len = name_of_nk subkeys in
+ 1, name_len, name
| {_} ->
failwithf "%s: invalid subkey node found at %s\n"
basename (print_offset subkeys)
)
-and visit_subkeys_in_lf_list subkeys_top len bits +and visit_subkeys_in_lf_list
newstyle_hash subkeys_top len bits if len > 0 then (
bitmatch bits with
| { rest : -1 : bitstring } when bitstring_length rest = 0 ->
assert (len = 0);
- 0, 0
+ 0, 0, ""
| { offset : 4*8 : littleendian, bind (get_offset offset);
- _ (* hash *) : 4*8 : bitstring;
+ hash : 4*8 : bitstring;
rest : -1 : bitstring } ->
- let c1, name_len1 = visit_subkeys offset in
- let c2, name_len2 = visit_subkeys_in_lf_list subkeys_top (len-1) rest
in
- c1 + c2, max name_len1 name_len2
+ let c1, name_len1, name = visit_subkeys offset in
+
+ check_hash offset newstyle_hash hash name;
+
+ let c2, name_len2, _ + visit_subkeys_in_lf_list newstyle_hash
subkeys_top (len-1) rest in
+ c1 + c2, max name_len1 name_len2, ""
| {_} ->
failwithf "%s: invalid subkey in lf/lh list at %s\n"
basename (print_offset subkeys_top)
- ) else 0, 0
+ ) else 0, 0, ""
and visit_subkeys_in_ri_list subkeys_top len bits if len > 0 then (
bitmatch bits with
| { rest : -1 : bitstring } when bitstring_length rest = 0 ->
assert (len = 0);
- 0, 0
+ 0, 0, ""
| { offset : 4*8 : littleendian, bind (get_offset offset);
rest : -1 : bitstring } ->
- let c1, name_len1 = visit_subkeys offset in
- let c2, name_len2 = visit_subkeys_in_ri_list subkeys_top (len-1) rest
in
- c1 + c2, max name_len1 name_len2
+ let c1, name_len1, _ = visit_subkeys offset in
+ let c2, name_len2, _ + visit_subkeys_in_ri_list subkeys_top
(len-1) rest in
+ c1 + c2, max name_len1 name_len2, ""
| {_} ->
failwithf "%s: invalid subkey in ri list at %s\n"
basename (print_offset subkeys_top)
- ) else 0, 0
+ ) else 0, 0, ""
+
+and check_hash offset newstyle_hash hash name + if not newstyle_hash then (
+ (* Old-style lf record hash the first four bytes of the name
+ * as the has.
+ *)
+ let len = String.length name in
+ let name_bits + if len >= 4 then
+ bitstring_of_string (String.sub name 0 4)
+ else (
+ let zeroes = zeroes_bitstring ((4-len)*8) in
+ concat [bitstring_of_string name; zeroes]
+ ) in
+ if not (equals hash name_bits) then
+ eprintf "LF incorrect hash for name %s, expected %s, actual
%s\n"
+ name (print_bitstring name_bits) (print_bitstring hash)
+ ) else (
+ (* New-style lh record has a proper hash. *)
+ let actual = bitmatch hash with { hash : 4*8 : littleendian } -> hash in
+ let h = ref 0_l in
+ String.iter (
+ fun c ->
+ h := Int32.mul !h 37_l;
+ h := Int32.add !h (Int32.of_int (Char.code (Char.uppercase c)))
+ ) name;
+ if actual <> !h then
+ eprintf "LH incorrect hash for name %s, expected 0x%08lx, actual
0x%08lx\n"
+ name !h actual
+ )
-and name_len_of_nk nk - let (_, _, bits) = lookup "name_len_of_nk"
nk in
+and name_of_nk nk + let (_, _, bits) = lookup "name_of_nk" nk in
bitmatch bits with
- | { :nk_fields } -> name_len
+ | { :nk_fields } -> name, name_len
and visit_sk sk let (_, _, bits) = lookup "visit_sk" sk in
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 10/14] hivex: More debugging around nk 'unknown2' field.
---
hivex/tools/visualizer.ml | 36 ++++++++++++++++++++++++++----------
1 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/hivex/tools/visualizer.ml b/hivex/tools/visualizer.ml
index bc447b7..d4a339e 100644
--- a/hivex/tools/visualizer.ml
+++ b/hivex/tools/visualizer.ml
@@ -408,19 +408,27 @@ let bitmatch nk_fields classname : 4*8 :
littleendian, bind (get_offset classname);
(* sentinelchicken.com says this is a single 32 bit field
* containing maximum number of bytes in a subkey name, however
- * that does not seem to be correct. We think it is two 16 bit
+ * that does not seem to be correct. We think it is several
* fields, the first being the maximum number of bytes in the
* UTF16-LE encoded version of the subkey names, (since subkey
* names are usually ASCII, that would be max length of names * 2).
* This is a historical maximum, so it can be greater than the
* current maximum name field.
*
- * The second field is often non-zero, but the purpose is unknown.
- * In the hives we examined it had values 0, 1, 0x20, 0x21, 0xa0,
- * 0xa1, 0xe1, suggesting some sort of flags.
+ * The remaining fields are often non-zero, but the purpose is
+ * unknown.
+ *
+ * In the hives we examined the other fields had values as
+ * follows:
+ * userflags: 0, 2, 0xa, 0xe
+ * virtcontrolflags: 0, 1
+ * debug: always 0
*)
max_subkey_name_len : 2*8 : littleendian;
- unknown2 : 2*8 : littleendian;
+ unknown2_userflags : 4;
+ unknown2_virtcontrolflags : 4;
+ unknown2_debug : 8;
+
(* sentinelchicken.com says: maximum subkey CLASSNAME length,
* however that does not seem to be correct. In hives I looked
* at, it has value 0, 0xc, 0x10, 0x18, 0x1a, 0x28.
@@ -453,7 +461,7 @@ let fprintf_nk chan nk bitmatch bits with
| { :nk_fields } ->
fprintf chan
- "NK %s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s %08lx %s %d %ld %s
%08lx %d %s %s %s %d %04x %08lx %d %d %08lx %d %d %s\n"
+ "NK %s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s %08lx %s %d %ld %s
%08lx %d %s %s %s %d %x %x %x %08lx %d %d %08lx %d %d %s\n"
(print_offset nk)
(if unknownflag8000 then "8" else ".")
(if unknownflag4000 then "4" else ".")
@@ -476,8 +484,9 @@ let fprintf_nk chan nk (print_offset subkeys)
subkeys_vol
nr_values (print_offset vallist)
(print_offset sk) (print_offset classname)
- max_subkey_name_len unknown2 unknown3
- max_vk_name_len max_vk_data_len unknown6
+ max_subkey_name_len
+ unknown2_userflags unknown2_virtcontrolflags unknown2_debug
+ unknown3 max_vk_name_len max_vk_data_len unknown6
name_len classname_len name
type data_t = Inline of bitstring | Offset of int
@@ -601,8 +610,15 @@ let rec visit_nk ?(nk_is_root = false) nk eprintf
"NK %s unknownflag0400 is set\n" (print_offset nk);
if unknown1 <> 0_l then
eprintf "NK %s unknown1 <> 0 (%08lx)\n" (print_offset
nk) unknown1;
- if unknown2 <> 0 then
- eprintf "NK %s unknown2 <> 0 (%04x)\n" (print_offset
nk) unknown2;
+ if unknown2_userflags <> 0 then
+ eprintf "NK %s unknown2_userflags <> 0 (%x)\n"
+ (print_offset nk) unknown2_userflags;
+ if unknown2_virtcontrolflags <> 0 then
+ eprintf "NK %s unknown2_virtcontrolflags <> 0 (%x)\n"
+ (print_offset nk) unknown2_virtcontrolflags;
+ if unknown2_debug <> 0 then
+ eprintf "NK %s unknown2_debug <> 0 (%x)\n"
+ (print_offset nk) unknown2_debug;
if unknown3 <> 0_l then
eprintf "NK %s unknown3 <> 0 (%08lx)\n" (print_offset
nk) unknown3;
if unknown6 <> 0_l then
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 11/14] hivex: Complete the implementation of adding child nodes.
---
hivex/example5 | 54 ++++++++++
hivex/hivex.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++-
hivex/hivex.pod | 19 +++-
hivex/hivexsh.c | 16 +++-
hivex/hivexsh.pod | 14 +++
5 files changed, 378 insertions(+), 12 deletions(-)
create mode 100755 hivex/example5
diff --git a/hivex/example5 b/hivex/example5
new file mode 100755
index 0000000..ccf711c
--- /dev/null
+++ b/hivex/example5
@@ -0,0 +1,54 @@
+#!/bin/bash -
+# Copyright (C) 2009-2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+set -e
+
+# This script adds a new node under \Microsoft in an existing software
+# hive.
+
+if [ $# -ne 2 ]; then
+ echo "$0 software software.new"
+ exit 1
+fi
+
+d=`dirname $0`
+
+$d/hivexsh -w <<EOF
+load $1
+cd \Microsoft
+add TestNode
+cd TestNode
+add Test1
+add Test2
+add Test3
+add Test4
+add Test5
+cd Test1
+setval 2
+@
+string:This is the default key of Test1
+ThisIsTest1
+dword:0x12345678
+cd ..
+cd Test5
+setval 2
+@
+string:This is the default key of Test5
+ThisIsTest5
+dword:0x87654321
+commit $2
+EOF
\ No newline at end of file
diff --git a/hivex/hivex.c b/hivex/hivex.c
index 2a094b8..148d837 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <assert.h>
+#include "c-ctype.h"
#include "full-read.h"
#include "full-write.h"
@@ -49,7 +50,7 @@
//#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
//#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
-//#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
#include "hivex.h"
#include "byte_conversions.h"
@@ -1987,7 +1988,117 @@ hivex_commit (hive_h *h, const char *filename, int
flags)
return 0;
}
-#if 0
+/* Calculate the hash for a lf or lh record offset.
+ */
+static void
+calc_hash (const char *type, const char *name, char *ret)
+{
+ size_t len = strlen (name);
+
+ if (STRPREFIX (type, "lf"))
+ /* Old-style, not used in current registries. */
+ memcpy (ret, name, len < 4 ? len : 4);
+ else {
+ /* New-style for lh-records. */
+ size_t i, c;
+ uint32_t h = 0;
+ for (i = 0; i < len; ++i) {
+ c = c_toupper (name[i]);
+ h *= 37;
+ h += c;
+ }
+ *((uint32_t *) ret) = htole32 (h);
+ }
+}
+
+/* Create a completely new lh-record containing just the single node. */
+static size_t
+new_lh_record (hive_h *h, const char *name, hive_node_h node)
+{
+ static const char id[2] = { 'l', 'h' };
+ size_t seg_len = sizeof (struct ntreg_lf_record);
+ size_t offset = allocate_block (h, seg_len, id);
+ if (offset == 0)
+ return 0;
+
+ struct ntreg_lf_record *lh = (struct ntreg_lf_record *) (h->addr +
offset);
+ lh->nr_keys = htole16 (1);
+ lh->keys[0].offset = htole32 (node - 0x1000);
+ calc_hash ("lh", name, lh->keys[0].hash);
+
+ return offset;
+}
+
+/* Insert node into existing lf/lh-record at position.
+ * This allocates a new record and marks the old one as unused.
+ */
+static size_t
+insert_lf_record (hive_h *h, size_t old_offs, size_t posn,
+ const char *name, hive_node_h node)
+{
+ assert (IS_VALID_BLOCK (h, old_offs));
+
+ /* Work around C stupidity.
+ * http://www.redhat.com/archives/libguestfs/2010-February/msg00056.html
+ */
+ int test = BLOCK_ID_EQ (h, old_offs, "lf") || BLOCK_ID_EQ (h,
old_offs, "lh");
+ assert (test);
+
+ struct ntreg_lf_record *old_lf + (struct ntreg_lf_record *) (h->addr +
old_offs);
+ size_t nr_keys = le16toh (old_lf->nr_keys);
+
+ nr_keys++; /* in new record ... */
+
+ size_t seg_len = sizeof (struct ntreg_lf_record) + (nr_keys-1) * 8;
+ size_t new_offs = allocate_block (h, seg_len, old_lf->id);
+ if (new_offs == 0)
+ return 0;
+
+ struct ntreg_lf_record *new_lf + (struct ntreg_lf_record *) (h->addr +
new_offs);
+ new_lf->nr_keys = htole16 (nr_keys);
+
+ /* Copy the keys until we reach posn, insert the new key there, then
+ * copy the remaining keys.
+ */
+ size_t i;
+ for (i = 0; i < posn; ++i)
+ new_lf->keys[i] = old_lf->keys[i];
+
+ new_lf->keys[i].offset = htole32 (node - 0x1000);
+ calc_hash (new_lf->id, name, new_lf->keys[i].hash);
+
+ for (i = posn+1; i < nr_keys; ++i)
+ new_lf->keys[i] = old_lf->keys[i-1];
+
+ /* Old block is unused, return new block. */
+ mark_block_unused (h, old_offs);
+ return new_offs;
+}
+
+/* Compare name with name in nk-record. */
+static int
+compare_name_with_nk_name (hive_h *h, const char *name, hive_node_h nk_offs)
+{
+ assert (IS_VALID_BLOCK (h, nk_offs));
+ assert (BLOCK_ID_EQ (h, nk_offs, "nk"));
+
+ /* Name in nk is not necessarily nul-terminated. */
+ char *nname = hivex_node_name (h, nk_offs);
+
+ /* Unfortunately we don't have a way to return errors here. */
+ if (!nname) {
+ perror ("compare_name_with_nk_name");
+ return 0;
+ }
+
+ int r = strcasecmp (name, nname);
+ free (nname);
+
+ return r;
+}
+
hive_node_h
hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name)
{
@@ -1998,21 +2109,185 @@ hivex_node_add_child (hive_h *h, hive_node_h parent,
const char *name)
if (!IS_VALID_BLOCK (h, parent) || !BLOCK_ID_EQ (h, parent, "nk"))
{
errno = EINVAL;
- return -1;
+ return 0;
}
- if (name == NULL) {
+ if (name == NULL || strlen (name) == 0) {
errno = EINVAL;
- return -1;
+ return 0;
+ }
+
+ if (hivex_node_get_child (h, parent, name) != 0) {
+ errno = EEXIST;
+ return 0;
}
+ /* Create the new nk-record. */
+ static const char nk_id[2] = { 'n', 'k' };
+ size_t seg_len = sizeof (struct ntreg_nk_record) + strlen (name);
+ hive_node_h node = allocate_block (h, seg_len, nk_id);
+ if (node == 0)
+ return 0;
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: allocated new nk-record for
child at 0x%zx\n", node);
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+ nk->flags = htole16 (0x0020); /* key is ASCII. */
+ nk->parent = htole32 (parent - 0x1000);
+ nk->subkey_lf = htole32 (0xffffffff);
+ nk->subkey_lf_volatile = htole32 (0xffffffff);
+ nk->vallist = htole32 (0xffffffff);
+ nk->classname = htole32 (0xffffffff);
+ nk->name_len = htole16 (strlen (name));
+ strcpy (nk->name, name);
+
+ /* Inherit parent sk. */
+ struct ntreg_nk_record *parent_nk + (struct ntreg_nk_record *) (h->addr
+ parent);
+ size_t parent_sk_offset = le32toh (parent_nk->sk);
+ parent_sk_offset += 0x1000;
+ if (!IS_VALID_BLOCK (h, parent_sk_offset) ||
+ !BLOCK_ID_EQ (h, parent_sk_offset, "sk")) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: returning EFAULT because
parent sk is not a valid block (%zu)\n",
+ parent_sk_offset);
+ errno = EFAULT;
+ return 0;
+ }
+ struct ntreg_sk_record *sk + (struct ntreg_sk_record *) (h->addr +
parent_sk_offset);
+ sk->refcount = htole32 (le32toh (sk->refcount) + 1);
+ nk->sk = htole32 (parent_sk_offset - 0x1000);
+
+ /* Inherit parent timestamp. */
+ memcpy (nk->timestamp, parent_nk->timestamp, sizeof
(parent_nk->timestamp));
+
+ /* What I found out the hard way (not documented anywhere): the
+ * subkeys in lh-records must be kept sorted. If you just add a
+ * subkey in a non-sorted position (eg. just add it at the end) then
+ * Windows won't see the subkey _and_ Windows will corrupt the hive
+ * itself when it modifies or saves it.
+ *
+ * So use get_children() to get a list of intermediate
+ * lf/lh-records. get_children() returns these in reading order
+ * (which is sorted), so we look for the lf/lh-records in sequence
+ * until we find the key name just after the one we are inserting,
+ * and we insert the subkey just before it.
+ *
+ * The only other case is the no-subkeys case, where we have to
+ * create a brand new lh-record.
+ */
+ hive_node_h *unused;
+ size_t *blocks;
+
+ if (get_children (h, parent, &unused, &blocks, 0) == -1)
+ return 0;
+ free (unused);
+
+ size_t i, j;
+ size_t nr_subkeys_in_parent_nk = le32toh (parent_nk->nr_subkeys);
+ if (nr_subkeys_in_parent_nk == 0) { /* No subkeys case. */
+ /* Free up any existing intermediate blocks. */
+ for (i = 0; blocks[i] != 0; ++i)
+ mark_block_unused (h, blocks[i]);
+ size_t lh_offs = new_lh_record (h, name, node);
+ if (lh_offs == 0) {
+ free (blocks);
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: no keys, allocated new
lh-record at 0x%zx\n", lh_offs);
+
+ parent_nk->subkey_lf = htole32 (lh_offs - 0x1000);
+ }
+ else { /* Insert subkeys case. */
+ size_t old_offs = 0, new_offs = 0;
+ struct ntreg_lf_record *old_lf = NULL;
+
+ /* Find lf/lh key name just after the one we are inserting. */
+ for (i = 0; blocks[i] != 0; ++i) {
+ if (BLOCK_ID_EQ (h, blocks[i], "lf") ||
+ BLOCK_ID_EQ (h, blocks[i], "lh")) {
+ old_offs = blocks[i];
+ old_lf = (struct ntreg_lf_record *) (h->addr + old_offs);
+ for (j = 0; j < le16toh (old_lf->nr_keys); ++j) {
+ hive_node_h nk_offs = le32toh (old_lf->keys[j].offset);
+ nk_offs += 0x1000;
+ if (compare_name_with_nk_name (h, name, nk_offs) < 0)
+ goto insert_it;
+ }
+ }
+ }
+
+ /* Insert it at the end.
+ * old_offs points to the last lf record, set j.
+ */
+ assert (old_offs != 0); /* should never happen if nr_subkeys > 0 */
+ j = le16toh (old_lf->nr_keys);
+
+ /* Insert it. */
+ insert_it:
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: insert key in existing
lh-record at 0x%zx, posn %zu\n", old_offs, j);
+
+ new_offs = insert_lf_record (h, old_offs, j, name, node);
+ if (new_offs == 0) {
+ free (blocks);
+ return 0;
+ }
+
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: new lh-record at
0x%zx\n",
+ new_offs);
+
+ /* If the lf/lh-record was directly referenced by the parent nk,
+ * then update the parent nk.
+ */
+ if (le32toh (parent_nk->subkey_lf) + 0x1000 == old_offs)
+ parent_nk->subkey_lf = htole32 (new_offs - 0x1000);
+ /* Else we have to look for the intermediate ri-record and update
+ * that in-place.
+ */
+ else {
+ for (i = 0; blocks[i] != 0; ++i) {
+ if (BLOCK_ID_EQ (h, blocks[i], "ri")) {
+ struct ntreg_ri_record *ri + (struct ntreg_ri_record *)
(h->addr + blocks[i]);
+ for (j = 0; j < le16toh (ri->nr_offsets); ++j)
+ if (le32toh (ri->offset[j] + 0x1000) == old_offs) {
+ ri->offset[j] = htole32 (new_offs - 0x1000);
+ goto found_it;
+ }
+ }
+ }
+ /* Not found .. This is an internal error. */
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex_node_add_child: returning ENOTSUP because
could not find ri->lf link\n");
+ errno = ENOTSUP;
+ free (blocks);
+ return 0;
+
+ found_it:
+ ;
+ }
+ }
+ free (blocks);
+ /* Update nr_subkeys in parent nk. */
+ nr_subkeys_in_parent_nk++;
+ parent_nk->nr_subkeys = htole32 (nr_subkeys_in_parent_nk);
+ /* Update max_subkey_name_len in parent nk. */
+ uint16_t max = le16toh (parent_nk->max_subkey_name_len);
+ if (max < strlen (name) * 2) /* *2 because "recoded" in
UTF16-LE. */
+ parent_nk->max_subkey_name_len = htole16 (strlen (name) * 2);
+ return node;
}
-#endif
/* Decrement the refcount of an sk-record, and if it reaches zero,
* unlink it from the chain and delete it.
diff --git a/hivex/hivex.pod b/hivex/hivex.pod
index 34ff253..275eb42 100644
--- a/hivex/hivex.pod
+++ b/hivex/hivex.pod
@@ -383,9 +383,12 @@ C<hivex_close> after this.
Add a new child node named C<name> to the existing node C<parent>.
The new child initially has no subnodes and contains no keys or
-values. The parent must not have an existing child called C<name>, so
-if you want to overwrite an existing child, call
-C<hivex_node_delete_child> first.
+values. The sk-record (security descriptor) is inherited from
+the parent.
+
+The parent must not have an existing child called C<name>, so if you
+want to overwrite an existing child, call C<hivex_node_delete_child>
+first.
Returns the node handle. On error this returns 0 and sets errno.
@@ -460,8 +463,10 @@ Modifying or deleting single values at a node.
=item *
-Modifying security key (sk) records or classnames. These are not
-well understood.
+Modifying security key (sk) records or classnames.
+Previously we did not understand these records. However now they
+are well-understood and we could add support if it was required
+(but nothing much really uses them).
=back
@@ -591,6 +596,10 @@ Registry contains cycles.
Field in the registry out of range.
+=item EEXIST
+
+Registry key already exists.
+
=item EROFS
Tried to write to a registry which is not opened for writing.
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index d5d9ada..9c54536 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -76,6 +76,7 @@ static char *rl_gets (const char *prompt_string);
static void sort_strings (char **strings, int len);
static int get_xdigit (char c);
static int dispatch (char *cmd, char *args);
+static int cmd_add (char *name);
static int cmd_cd (char *path);
static int cmd_close (char *path);
static int cmd_commit (char *path);
@@ -411,7 +412,9 @@ dispatch (char *cmd, char *args)
return -1;
}
- if (STRCASEEQ (cmd, "cd"))
+ if (STRCASEEQ (cmd, "add"))
+ return cmd_add (args);
+ else if (STRCASEEQ (cmd, "cd"))
return cmd_cd (args);
else if (STRCASEEQ (cmd, "close") || STRCASEEQ (cmd,
"unload"))
return cmd_close (args);
@@ -1077,3 +1080,14 @@ cmd_del (char *args)
set_prompt_string ();
return 0;
}
+
+static int
+cmd_add (char *name)
+{
+ hive_node_h node = hivex_node_add_child (h, cwd, name);
+ if (node == 0) {
+ perror ("hivexsh: add");
+ return -1;
+ }
+ return 0;
+}
diff --git a/hivex/hivexsh.pod b/hivex/hivexsh.pod
index 9336798..a31d9e0 100644
--- a/hivex/hivexsh.pod
+++ b/hivex/hivexsh.pod
@@ -79,6 +79,20 @@ If this option is not given, then write commands are
disabled.
=over 4
+=item B<add> name
+
+Add a subkey named C<name> below the current node. The name may
+contain spaces and punctuation characters, and does not need to be
+quoted.
+
+The new key will have no subkeys and no values (see C<setval>).
+
+There must be no existing subkey called C<name>, or this command will
+fail. To replace an existing subkey, delete it first like this:
+
+ cd name
+ del
+
=item B<cd> path
Change to the subkey C<path>. Use Windows-style backslashes to
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 12/14] hivexsh: Remove unused variable.
This removes an unused variable left over by
commit ab608f3948d903af64e814b2e67949a1a71d93a4.
---
hivex/hivexsh.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index 9c54536..00f33ae 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -858,7 +858,6 @@ cmd_setval (char *nrvals_str)
/* Read nrvals * 2 lines of input, nrvals * (key, value) pairs, as
* explained in the man page.
*/
- int prompt = isatty (0) ? 2 : 0;
int i, j;
for (i = 0; i < nrvals; ++i) {
/* Read key. */
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 13/14] hivex: Make limits into macros.
---
hivex/hivex.c | 14 ++++++++++----
hivex/hivexsh.c | 4 +++-
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index 148d837..5da50ea 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -55,6 +55,12 @@
#include "hivex.h"
#include "byte_conversions.h"
+/* These limits are in place to stop really stupid stuff and/or exploits. */
+#define HIVEX_MAX_SUBKEYS 10000
+#define HIVEX_MAX_VALUES 1000
+#define HIVEX_MAX_VALUE_LEN 1000000
+#define HIVEX_MAX_ALLOCATION 1000000
+
static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len);
struct hive_h {
@@ -719,7 +725,7 @@ get_children (hive_h *h, hive_node_h node,
goto ok;
/* Arbitrarily limit the number of subkeys we will ever deal with. */
- if (nr_subkeys_in_nk > 1000000) {
+ if (nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS) {
errno = ERANGE;
goto error;
}
@@ -989,7 +995,7 @@ get_values (hive_h *h, hive_node_h node,
goto ok;
/* Arbitrarily limit the number of values we will ever deal with. */
- if (nr_values > 100000) {
+ if (nr_values > HIVEX_MAX_VALUES) {
errno = ERANGE;
goto error;
}
@@ -1188,7 +1194,7 @@ hivex_value_value (hive_h *h, hive_value_h value,
*len_rtn = len;
/* Arbitrarily limit the length that we will read. */
- if (len > 1000000) {
+ if (len > HIVEX_MAX_VALUE_LEN) {
errno = ERANGE;
return NULL;
}
@@ -1812,7 +1818,7 @@ allocate_block (hive_h *h, size_t seg_len, const char
id[2])
}
/* Refuse really large allocations. */
- if (seg_len > 1000000) {
+ if (seg_len > HIVEX_MAX_ALLOCATION) {
if (h->msglvl >= 2)
fprintf (stderr, "allocate_block: refusing large allocation (%zu),
returning ERANGE\n",
seg_len);
diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c
index 00f33ae..6d83f0d 100644
--- a/hivex/hivexsh.c
+++ b/hivex/hivexsh.c
@@ -58,6 +58,8 @@
#include "hivex.h"
#include "byte_conversions.h"
+#define HIVEX_MAX_VALUES 1000
+
static int quit = 0;
static int is_tty;
static hive_h *h = NULL;
@@ -840,7 +842,7 @@ cmd_setval (char *nrvals_str)
"setval", "nrvals", "xstrtol",
xerr);
return -1;
}
- if (nrvals < 0 || nrvals > 1000) {
+ if (nrvals < 0 || nrvals > HIVEX_MAX_VALUES) {
fprintf (stderr, _("%s: %s: integer out of range\n"),
"setval", "nrvals");
return -1;
--
1.6.5.2
Richard Jones
2010-Feb-05 13:07 UTC
[Libguestfs] [PATCH 14/14] hivex: Documentation and cleanups.
---
hivex/hivex.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/hivex/hivex.c b/hivex/hivex.c
index 5da50ea..6752da0 100644
--- a/hivex/hivex.c
+++ b/hivex/hivex.c
@@ -1849,7 +1849,7 @@ allocate_block (hive_h *h, size_t seg_len, const char
id[2])
(struct ntreg_hbin_block *) (h->addr + offset);
blockhdr->seg_len = htole32 (- (int32_t) seg_len);
- if (id[0] && id[1] && seg_len >= 6) {
+ if (id[0] && id[1] && seg_len >= sizeof (struct
ntreg_hbin_block)) {
blockhdr->id[0] = id[0];
blockhdr->id[1] = id[1];
}
@@ -2547,6 +2547,7 @@ hivex_node_set_values (hive_h *h, hive_node_h node,
}
if (name_len * 2 > le32toh (nk->max_vk_name_len))
+ /* * 2 for UTF16-LE "reencoding" */
nk->max_vk_name_len = htole32 (name_len * 2);
if (values[i].len > le32toh (nk->max_vk_data_len))
nk->max_vk_data_len = htole32 (values[i].len);
--
1.6.5.2
Seemingly Similar Threads
- [PATCH hivex 00/19] Fix read/write handling of li-records.
- [PATCH 0/2] Fix errors found by Clang static analyzer
- -Woverlength-strings / assert unhelpful interaction
- [PATCH hivex] maint: split long lines
- [PATCH v4 0/5] hivex: handle corrupted hives better.