[PATCH] hivex: add hivex_set_value api call
I'm not entirely sure the generator/generator.ml changes are as correct as they could be. I'm not very familiar with Caml. The hivex_node_set_value call builds up a list of hive_set_values by walking the existing values at the node, adding or replacing the passed hive_set_value as necessary, then shoving the list at hivex_node_set_values. Not included: Perl or OCaml binding glue. I'm not on the list, please CC me to replies. Thanks! --- generator/generator.ml | 29 +++++++++++++-- lib/hivex.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/generator/generator.ml b/generator/generator.ml index 5a0ab6e..dbcbd2c 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -71,6 +71,7 @@ and argt = (* Note, cannot be NULL/0 unless it | AOpenFlags (* HIVEX_OPEN_* flags list. *) | AUnusedFlags (* Flags arg that is always 0 *) | ASetValues (* See hivex_node_set_values. *) + | ASetValue (* See hivex_node_set_value. *) (* Hive types, from: * https://secure.wikimedia.org/wikipedia/en/wiki/Windows_Registry#Keys_and_val... @@ -304,8 +305,15 @@ subnodes become invalid. You cannot delete the root node."; "set (key, value) pairs at a node", "\ This call can be used to set all the (key, value) pairs -stored in C<node>. Note that this library does not offer -a way to modify just a single key at a node. +stored in C<node>. + +C<node> is the node to modify."; + + "node_set_value", (RErr, [AHive; ANode "node"; ASetValue; AUnusedFlags]), + "set a single (key, value) pair at a given node", + "\ +This call can be used to set a single (key, value) pair +stored in C<node>. C<node> is the node to modify."; ] @@ -459,6 +467,7 @@ let name_of_argt = function | ANode n | AValue n | AString n | AStringNullable n -> n | AOpenFlags | AUnusedFlags -> "flags" | ASetValues -> "values" + | ASetValue -> "val" (* Check function names etc. for consistency. *) let check_functions () = @@ -806,6 +815,7 @@ and generate_c_prototype ?(extern = false) name style = | AString n | AStringNullable n -> pr "const char *%s" n | AOpenFlags | AUnusedFlags -> pr "int flags" | ASetValues -> pr "size_t nr_values, const hive_set_value *values" + | ASetValue -> pr "const hive_set_value *val" ) (snd style); (match fst style with | RLenType | RLenTypeVal -> pr ", hive_type *t, size_t *len" @@ -937,6 +947,11 @@ Any existing values stored at the node are discarded, and their C<hive_value_h> handles become invalid. Thus you can remove all values stored at C<node> by passing C<nr_values = 0>.\n\n"; + if List.mem ASetValue (snd style) then + pr "C<value> is a single (key, value) pair. + +Existing C<hive_value_h> handles become invalid.\n\n"; + (match fst style with | RErr -> pr "\ @@ -1478,6 +1493,7 @@ and generate_ocaml_prototype ?(is_external = false) name style = | AOpenFlags -> pr "open_flag list -> " | AUnusedFlags -> () | ASetValues -> pr "set_value array -> " + | ASetValue -> pr "set_value -> " ) (snd style); (match fst style with | RErr -> pr "unit" (* all errors are turned into exceptions *) @@ -1621,6 +1637,8 @@ static void raise_closed (const char *) Noreturn; | ASetValues -> pr " int nrvalues = Wosize_val (valuesv);\n"; pr " hive_set_value *values = HiveSetValues_val (valuesv);\n" + | ASetValue -> + pr " hive_set_value *val = HiveSetValue_val (valv);\n" ) (snd style); pr "\n"; @@ -1684,7 +1702,7 @@ static void raise_closed (const char *) Noreturn; List.iter ( function | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ - | AOpenFlags | AUnusedFlags -> () + | AOpenFlags | AUnusedFlags | ASetValue -> () | ASetValues -> pr " free (values);\n"; pr "\n"; @@ -2113,6 +2131,7 @@ and generate_perl_prototype name style = | AOpenFlags -> pr "[flags]" | AUnusedFlags -> assert false | ASetValues -> pr "\\@values" + | ASetValue -> pr "$val" ) args; pr ")" @@ -2319,6 +2338,8 @@ DESTROY (h) | AUnusedFlags -> () | ASetValues -> pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i + | ASetValue -> + pr " pl_set_value val = unpack_pl_set_value (ST(%d));\n" i ) (snd style); let free_args () = @@ -2327,7 +2348,7 @@ DESTROY (h) | ASetValues -> pr " free (values.values);\n" | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ - | AOpenFlags | AUnusedFlags -> () + | AOpenFlags | AUnusedFlags | ASetValue -> () ) (snd style) in diff --git a/lib/hivex.c b/lib/hivex.c index 74a7f55..3879238 100644 --- a/lib/hivex.c +++ b/lib/hivex.c @@ -2606,3 +2606,93 @@ hivex_node_set_values (hive_h *h, hive_node_h node, return 0; } + + +int +hivex_node_set_value (hive_h *h, hive_node_h node, + const hive_set_value *val, int flags) +{ + if (!h->writable) { + errno = EROFS; + return -1; + } + + if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { + errno = EINVAL; + return -1; + } + + hive_value_h *prev_values = hivex_node_values (h, node); + if (prev_values == NULL) + return -1; + + int retval = -1; + + size_t nr_values = 0; + for (hive_value_h *itr = prev_values; *itr != 0; ++itr) + ++nr_values; + + hive_set_value *values = malloc ((nr_values + 1) * (sizeof (hive_set_value))); + if (values == NULL) + goto leave_prev_values; + + int alloc_ct = 0; + int idx_of_val = -1; + for (hive_value_h *prev_val = prev_values; *prev_val != 0; ++prev_val) { + size_t len; + hive_type t; + + hive_set_value *value = &values[prev_val - prev_values]; + + char *valval = hivex_value_value (h, *prev_val, &t, &len); + if (valval == NULL) goto leave_partial; + + ++alloc_ct; + value->value = valval; + value->t = t; + value->len = len; + + char *valkey = hivex_value_key (h, *prev_val); + if (valkey == NULL) goto leave_partial; + + ++alloc_ct; + value->key = valkey; + + if (strcmp (valkey, val->key) == 0) + idx_of_val = prev_val - prev_values; + } + + if (idx_of_val > -1) { + free (values[idx_of_val].key); + free (values[idx_of_val].value); + } else { + idx_of_val = nr_values; + ++nr_values; + } + + hive_set_value *value = &values[idx_of_val]; + *value = (hive_set_value){ + .key = strdup (val->key), + .value = malloc (val->len), + .len = val->len, + .t = val->t + }; + + if (value->key == NULL || value->value == NULL) goto leave_partial; + memcpy (value->value, val->value, val->len); + + retval = hivex_node_set_values (h, node, nr_values, values, 0); + + leave_partial: + for (int i = 0; i < alloc_ct; i += 2) { + if (values[i / 2].value != NULL) + free (values[i / 2].value); + if (i + 1 < alloc_ct && values[i / 2].key != NULL) + free (values[i / 2].key); + } + free (values); + + leave_prev_values: + free (prev_values); + return retval; +} -- 1.7.1
On Sat, Jul 03, 2010 at 09:22:20AM -0700, Conrad Meyer wrote:
I'm not entirely sure the generator/generator.ml changes are as correct as they could be. I'm not very familiar with Caml.
Well you can add OCaml to your CV now, because your code is fine ...
The hivex_node_set_value call builds up a list of hive_set_values by walking the existing values at the node, adding or replacing the passed hive_set_value as necessary, then shoving the list at hivex_node_set_values.
Not included: Perl or OCaml binding glue.
Are you intending to go further with this? The most crucial missing part is the tests. We test via Perl code, so this implies a Perl binding (should be very easy to add). Have you loaded up files that you've modified this way on Windows? Subtle mistakes (more accurately: things which the hacked-together Windows code is not expecting) can cause registries to be silently corrupted. I had a quick look at the code and the only mistake I could spot is:
+ if (strcmp (valkey, val->key) == 0)
First of all use the STR* macros, secondly everything in registry files that is visible externally is matched case *in*sensitively. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
On Sat, 3 Jul 2010 18:42:09 +0100 "Richard W.M. Jones" <rjones@redhat.com> wrote:
On Sat, Jul 03, 2010 at 09:22:20AM -0700, Conrad Meyer wrote:
Not included: Perl or OCaml binding glue.
Are you intending to go further with this? The most crucial missing part is the tests. We test via Perl code, so this implies a Perl binding (should be very easy to add).
Ah, ok. I'll do this.
Have you loaded up files that you've modified this way on Windows? Subtle mistakes (more accurately: things which the hacked-together Windows code is not expecting) can cause registries to be silently corrupted.
No, this is completely untested. I poked around a little but didn't see C language tests -- I guess I'll try my hand at Perl bindings and write some tests.
I had a quick look at the code and the only mistake I could spot is:
+ if (strcmp (valkey, val->key) == 0)
First of all use the STR* macros, secondly everything in registry files that is visible externally is matched case *in*sensitively.
Ah, thanks for spotting that. I'll fix this and resubmit. Regards, -- Conrad Meyer <cemeyer@cs.washington.edu>
participants (2)
-
Conrad Meyer -
Richard W.M. Jones