From 77d38592f59f146c5ef249a3f7036cf2a1e45acd Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Thu, 8 Jul 2010 11:12:42 +0100 Subject: [PATCH] Add hivex_set_value API call, and ocaml and perl bindings, and tests. --- generator/generator.ml | 78 ++++++++++++++++++++++++++++++++++++++++++++++-- lib/hivex.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ perl/t/210-setvalue.t | 65 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 perl/t/210-setvalue.t diff --git a/generator/generator.ml b/generator/generator.ml index 5a0ab6e..03f8c20 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_values @@ -304,8 +305,16 @@ 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. Note that this library does not offer -a way to modify just a single key at a node. +stored in C. + +C 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 replace a single (key, value) pair +stored in C. If the key does not already exist, then a +new key is added. Key matching is case insensitive. C is the node to modify."; ] @@ -459,6 +468,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 +816,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 +948,11 @@ Any existing values stored at the node are discarded, and their C handles become invalid. Thus you can remove all values stored at C by passing C.\n\n"; + if List.mem ASetValue (snd style) then + pr "C is a single (key, value) pair. + +Existing C handles become invalid.\n\n"; + (match fst style with | RErr -> pr "\ @@ -1478,6 +1494,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 *) @@ -1548,6 +1565,7 @@ caml_raise_with_args (value tag, int nargs, value args[]) #define Hiveh_val(v) (*((hive_h **)Data_custom_val(v))) static value Val_hiveh (hive_h *); static int HiveOpenFlags_val (value); +static hive_set_value *HiveSetValue_val (value); static hive_set_value *HiveSetValues_val (value); static hive_type HiveType_val (value); static value Val_hive_type (hive_type); @@ -1621,6 +1639,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"; @@ -1688,6 +1708,9 @@ static void raise_closed (const char *) Noreturn; | ASetValues -> pr " free (values);\n"; pr "\n"; + | ASetValue -> + pr " free (val);\n"; + pr "\n"; ) (snd style); (* Check for errors. *) @@ -1750,6 +1773,19 @@ HiveOpenFlags_val (value v) } static hive_set_value * +HiveSetValue_val (value v) +{ + hive_set_value *val = malloc (sizeof (hive_set_value)); + + val->key = String_val (Field (v, 0)); + val->t = HiveType_val (Field (v, 1)); + val->len = caml_string_length (Field (v, 2)); + val->value = String_val (Field (v, 2)); + + return val; +} + +static hive_set_value * HiveSetValues_val (value v) { size_t nr_values = Wosize_val (v); @@ -2113,6 +2149,7 @@ and generate_perl_prototype name style = | AOpenFlags -> pr "[flags]" | AUnusedFlags -> assert false | ASetValues -> pr "\\@values" + | ASetValue -> pr "$val" ) args; pr ")" @@ -2243,6 +2280,39 @@ unpack_pl_set_values (SV *sv) return ret; } +static hive_set_value * +unpack_set_value (SV *sv) +{ + hive_set_value *ret; + + if (!sv || !SvROK (sv) || SvTYPE (SvRV (sv)) != SVt_PVHV) + croak (\"not a hash ref\"); + + ret = malloc (sizeof (hive_set_value)); + if (ret == NULL) + croak (\"malloc failed\"); + + HV *hv = (HV *)SvRV(sv); + + SV **svp; + svp = hv_fetch (hv, \"key\", 3, 0); + if (!svp || !*svp) + croak (\"missing 'key' in hash\"); + ret->key = SvPV_nolen (*svp); + + svp = hv_fetch (hv, \"t\", 1, 0); + if (!svp || !*svp) + croak (\"missing 't' in hash\"); + ret->t = SvIV (*svp); + + svp = hv_fetch (hv, \"value\", 5, 0); + if (!svp || !*svp) + croak (\"missing 'value' in hash\"); + ret->value = SvPV (*svp, ret->len); + + return ret; +} + MODULE = Win::Hivex PACKAGE = Win::Hivex PROTOTYPES: ENABLE @@ -2319,6 +2389,8 @@ DESTROY (h) | AUnusedFlags -> () | ASetValues -> pr " pl_set_values values = unpack_pl_set_values (ST(%d));\n" i + | ASetValue -> + pr " hive_set_value *val = unpack_set_value (ST(%d));\n" i ) (snd style); let free_args () = @@ -2326,6 +2398,8 @@ DESTROY (h) function | ASetValues -> pr " free (values.values);\n" + | ASetValue -> + pr " free (val);\n" | AHive | ANode _ | AValue _ | AString _ | AStringNullable _ | AOpenFlags | AUnusedFlags -> () ) (snd style) diff --git a/lib/hivex.c b/lib/hivex.c index 74a7f55..c65f6de 100644 --- a/lib/hivex.c +++ b/lib/hivex.c @@ -2606,3 +2606,83 @@ 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) +{ + 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; + hive_value_h *prev_val; + for (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 (STRCASEEQ (valkey, val->key)) + 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; +} diff --git a/perl/t/210-setvalue.t b/perl/t/210-setvalue.t new file mode 100644 index 0000000..8e24121 --- /dev/null +++ b/perl/t/210-setvalue.t @@ -0,0 +1,65 @@ +# hivex Perl bindings -*- perl -*- +# Copyright (C) 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. + +use strict; +use warnings; +use Test::More tests => 11; + +use Win::Hivex; + +my $srcdir = $ENV{srcdir} || "."; + +my $h = Win::Hivex->open ("$srcdir/../images/minimal", write => 1); +ok ($h); + +my $root = $h->root (); +ok ($root); + +$h->node_add_child ($root, "B"); +ok (1); + +my $b = $h->node_get_child ($root, "B"); +ok ($b); + +my $values = [ + { key => "Key1", t => 3, value => "ABC" }, + { key => "Key2", t => 3, value => "DEF" } + ]; +$h->node_set_values ($b, $values); +ok (1); + +my $value1 = { key => "Key3", t => 3, value => "GHI" }; +$h->node_set_value ($b, $value1); +ok (1); + +my $value2 = { key => "Key1", t => 3, value => "JKL" }; +$h->node_set_value ($b, $value2); +ok (1); + +my ($val, $t, $data); +$val = $h->node_get_value ($b, "Key1"); +($t, $data) = $h->value_value ($val); +ok ($t == 3); +ok ($data eq "JKL"); + +$val = $h->node_get_value ($b, "Key3"); +($t, $data) = $h->value_value ($val); +ok ($t == 3); +ok ($data eq "GHI"); + +# don't commit because that would overwrite the original file +# $h->commit (); -- 1.8.3.1