#include "full-read.h"
#include "full-write.h"
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
#define STREQ(a,b) (strcmp((a),(b)) == 0)
#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
//#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
/* Fields from the header, extracted from little-endianness hell. */
size_t rootoffs; /* Root key offset (always an nk-block). */
size_t endpages; /* Offset of end of pages. */
+ int64_t last_modified; /* mtime of base block. */
/* For writing. */
size_t endblocks; /* Offset to next block allocation (0
char magic[4]; /* "regf" */
uint32_t sequence1;
uint32_t sequence2;
- char last_modified[8];
+ int64_t last_modified;
uint32_t major_ver; /* 1 */
uint32_t minor_ver; /* 3 */
uint32_t unknown5; /* 0 */
int32_t seg_len; /* length (always -ve because used) */
char id[2]; /* "nk" */
uint16_t flags;
- char timestamp[8];
+ int64_t timestamp;
uint32_t unknown1;
uint32_t parent; /* offset of owner/parent */
uint32_t nr_subkeys; /* number of subkeys */
if (h->filename == NULL)
goto error;
+#ifdef O_CLOEXEC
h->fd = open (filename, O_RDONLY | O_CLOEXEC);
+#else
+ h->fd = open (filename, O_RDONLY);
+#endif
if (h->fd == -1)
goto error;
+#ifndef O_CLOEXEC
+ fcntl (h->fd, F_SETFD, FD_CLOEXEC);
+#endif
struct stat statbuf;
if (fstat (h->fd, &statbuf) == -1)
if (full_read (h->fd, h->addr, h->size) < h->size)
goto error;
+
+ /* We don't need the file descriptor along this path, since we
+ * have read all the data.
+ */
+ if (close (h->fd) == -1)
+ goto error;
+ h->fd = -1;
}
/* Check header. */
goto error;
}
+ /* Last modified time. */
+ h->last_modified = le64toh ((int64_t) h->hdr->last_modified);
+
if (h->msglvl >= 2) {
char *name = windows_utf16_to_utf8 (h->hdr->name, 64);
" file version %" PRIu32 ".%" PRIu32 "\n"
" sequence nos %" PRIu32 " %" PRIu32 "\n"
" (sequences nos should match if hive was synched at shutdown)\n"
+ " last modified %" PRIu64 "\n"
+ " (Windows filetime, x 100 ns since 1601-01-01)\n"
" original file name %s\n"
" (only 32 chars are stored, name is probably truncated)\n"
" root offset 0x%x + 0x1000\n"
" checksum 0x%x (calculated 0x%x)\n",
major_ver, le32toh (h->hdr->minor_ver),
le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2),
+ h->last_modified,
name ? name : "(conversion failed)",
le32toh (h->hdr->offset),
le32toh (h->hdr->blocks), h->size,
munmap (h->addr, h->size);
else
free (h->addr);
- r = close (h->fd);
+ if (h->fd >= 0)
+ r = close (h->fd);
+ else
+ r = 0;
free (h->filename);
free (h);
{
hive_node_h ret = h->rootoffs;
if (!IS_VALID_BLOCK (h, ret)) {
- errno = ENOKEY;
+ errno = HIVEX_NO_KEY;
return 0;
}
return ret;
return ret;
}
+static int64_t
+timestamp_check (hive_h *h, hive_node_h node, int64_t timestamp)
+{
+ if (timestamp < 0) {
+ if (h->msglvl >= 2)
+ fprintf (stderr, "hivex: timestamp_check: "
+ "negative time reported at %zu: %" PRIi64 "\n",
+ node, timestamp);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return timestamp;
+}
+
+int64_t
+hivex_last_modified (hive_h *h)
+{
+ return timestamp_check (h, 0, h->last_modified);
+}
+
+int64_t
+hivex_node_timestamp (hive_h *h, hive_node_h node)
+{
+ int64_t ret;
+
+ if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node);
+
+ ret = le64toh (nk->timestamp);
+ return timestamp_check (h, node, ret);
+}
+
#if 0
/* I think the documentation for the sk and classname fields in the nk
* record is wrong, or else the offset field is in the wrong place.
size_t i;
for (i = 0; i < nr_values; ++i) {
- hive_node_h value = vlist->offset[i];
+ hive_node_h value = le32toh (vlist->offset[i]);
value += 0x1000;
if (!IS_VALID_BLOCK (h, value)) {
if (h->msglvl >= 2)
return ret;
}
-char *
-hivex_value_key (hive_h *h, hive_value_h value)
+size_t
+hivex_value_key_len (hive_h *h, hive_value_h value)
{
if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) {
errno = EINVAL;
struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value);
- /* AFAIK the key is always plain ASCII, so no conversion to UTF-8 is
- * necessary. However we do need to nul-terminate the string.
- */
-
/* vk->name_len is unsigned, 16 bit, so this is safe ... However
* we have to make sure the length doesn't exceed the block length.
*/
- size_t len = le16toh (vk->name_len);
+ size_t ret = le16toh (vk->name_len);
size_t seg_len = block_len (h, value, NULL);
- if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) {
+ if (sizeof (struct ntreg_vk_record) + ret - 1 > seg_len) {
if (h->msglvl >= 2)
- fprintf (stderr, "hivex_value_key: returning EFAULT"
+ fprintf (stderr, "hivex_value_key_len: returning EFAULT"
" because key length is too long (%zu, %zu)\n",
- len, seg_len);
+ ret, seg_len);
errno = EFAULT;
- return NULL;
+ return 0;
+ }
+ return ret;
+}
+
+char *
+hivex_value_key (hive_h *h, hive_value_h value)
+{
+ if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) {
+ errno = EINVAL;
+ return 0;
}
+ struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value);
+
+ /* AFAIK the key is always plain ASCII, so no conversion to UTF-8 is
+ * necessary. However we do need to nul-terminate the string.
+ */
+ errno = 0;
+ size_t len = hivex_value_key_len (h, value);
+ if (len == 0 && errno != 0)
+ return NULL;
+
char *ret = malloc (len + 1);
if (ret == NULL)
return NULL;
nk->sk = htole32 (parent_sk_offset - 0x1000);
/* Inherit parent timestamp. */
- memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp));
+ nk->timestamp = 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
leave_partial:
for (int i = 0; i < alloc_ct; i += 2) {
- if (values[i / 2].value != NULL)
- free (values[i / 2].value);
+ free (values[i / 2].value);
if (i + 1 < alloc_ct && values[i / 2].key != NULL)
free (values[i / 2].key);
}