X-Git-Url: http://git.annexia.org/?p=hivex.git;a=blobdiff_plain;f=lib%2Fhivex.c;h=d2ab23d30e0aed122112c4c9d68fa075a19fe5f4;hp=91d3b127de47da13640be2b326c117bf3eb93839;hb=53056244696385299fe0d298bd25053dd7c07dc0;hpb=56feba1260955347a53bd34a37ba295770e1dc5b diff --git a/lib/hivex.c b/lib/hivex.c index 91d3b12..d2ab23d 100644 --- a/lib/hivex.c +++ b/lib/hivex.c @@ -56,12 +56,14 @@ #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_SUBKEYS 15000 +#define HIVEX_MAX_VALUES 10000 #define HIVEX_MAX_VALUE_LEN 1000000 #define HIVEX_MAX_ALLOCATION 1000000 static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len); +static size_t utf16_string_len_in_bytes (const char *str); +static size_t utf16_string_len_in_bytes_max (const char *str, size_t len); struct hive_h { char *filename; @@ -532,6 +534,9 @@ hivex_close (hive_h *h) { int r; + if (h->msglvl >= 1) + fprintf (stderr, "hivex_close\n"); + free (h->bitmap); if (!h->writable) munmap (h->addr, h->size); @@ -1240,6 +1245,10 @@ hivex_value_value (hive_h *h, hive_value_h value, fprintf (stderr, "hivex_value_value: warning: declared data length is longer than the block it is in (data 0x%zx, data len %zu, block len %zu)\n", data_offset, len, blen); len = blen - 4; + + /* Return the smaller length to the caller too. */ + if (len_rtn) + *len_rtn = len; } char *data = h->addr + data_offset + 4; @@ -1275,12 +1284,16 @@ windows_utf16_to_utf8 (/* const */ char *input, size_t len) size_t r = iconv (ic, &inp, &inlen, &outp, &outlen); if (r == (size_t) -1) { if (errno == E2BIG) { + int err = errno; size_t prev = outalloc; /* Try again with a larger output buffer. */ free (out); outalloc *= 2; - if (outalloc < prev) + if (outalloc < prev) { + iconv_close (ic); + errno = err; return NULL; + } goto again; } else { @@ -1315,6 +1328,20 @@ hivex_value_string (hive_h *h, hive_value_h value) return NULL; } + /* Deal with the case where Windows has allocated a large buffer + * full of random junk, and only the first few bytes of the buffer + * contain a genuine UTF-16 string. + * + * In this case, iconv would try to process the junk bytes as UTF-16 + * and inevitably find an illegal sequence (EILSEQ). Instead, stop + * after we find the first \0\0. + * + * (Found by Hilko Bengen in a fresh Windows XP SOFTWARE hive). + */ + size_t slen = utf16_string_len_in_bytes_max (data, len); + if (slen < len) + len = slen; + char *ret = windows_utf16_to_utf8 (data, len); free (data); if (ret == NULL) @@ -1351,6 +1378,21 @@ utf16_string_len_in_bytes (const char *str) return ret; } +/* As for utf16_string_len_in_bytes but only read up to a maximum length. */ +static size_t +utf16_string_len_in_bytes_max (const char *str, size_t len) +{ + size_t ret = 0; + + while (len >= 2 && (str[0] || str[1])) { + str += 2; + ret += 2; + len -= 2; + } + + return ret; +} + /* http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx */ char ** hivex_value_multiple_strings (hive_h *h, hive_value_h value) @@ -1379,7 +1421,8 @@ hivex_value_multiple_strings (hive_h *h, hive_value_h value) char *p = data; size_t plen; - while (p < data + len && (plen = utf16_string_len_in_bytes (p)) > 0) { + while (p < data + len && + (plen = utf16_string_len_in_bytes_max (p, data + len - p)) > 0) { nr_strings++; char **ret2 = realloc (ret, (1 + nr_strings) * sizeof (char *)); if (ret2 == NULL) { @@ -1860,6 +1903,8 @@ allocate_block (hive_h *h, size_t seg_len, const char id[2]) struct ntreg_hbin_block *blockhdr = (struct ntreg_hbin_block *) (h->addr + offset); + memset (blockhdr, 0, seg_len); + blockhdr->seg_len = htole32 (- (int32_t) seg_len); if (id[0] && id[1] && seg_len >= sizeof (struct ntreg_hbin_block)) { blockhdr->id[0] = id[0]; @@ -2604,3 +2649,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; +}