コード例 #1
0
ファイル: value.c プロジェクト: ajnelson-nist/hivex
char *
hivex_value_key (hive_h *h, hive_value_h value)
{
  if (!IS_VALID_BLOCK (h, value) || !block_id_eq (h, value, "vk")) {
    SET_ERRNO (EINVAL, "invalid block or not an 'vk' block");
    return 0;
  }

  struct ntreg_vk_record *vk =
    (struct ntreg_vk_record *) ((char *) h->addr + value);

  size_t flags = le16toh (vk->flags);
  size_t len = le16toh (vk->name_len);

  size_t seg_len = block_len (h, value, NULL);
  if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) {
    SET_ERRNO (EFAULT, "key length is too long (%zu, %zu)", len, seg_len);
    return NULL;
  }
  if (flags & 0x01) {
    return _hivex_windows_latin1_to_utf8 (vk->name, len);
  } else {
    return _hivex_windows_utf16_to_utf8 (vk->name, len);
  }
}
コード例 #2
0
ファイル: bufferarray.cpp プロジェクト: Belxjander/Kirito
size_t BufferArray::write(size_t pos, const void * src, size_t len)
{
	const char * c_src(static_cast<const char *>(src));
	
	if (pos > mLen || 0 == len)
		return 0;
	
	size_t result(0), offset(0);
	const int block_limit(mBlocks.size());
	int block_start(findBlock(pos, &offset));

	if (block_start >= 0)
	{
		// Some or all of the write will be on top of
		// existing data.
		do
		{
			Block & block(*mBlocks[block_start]);
			size_t block_limit(block.mUsed - offset);
			size_t block_len((std::min)(block_limit, len));
		
			memcpy(&block.mData[offset], c_src, block_len);
			result += block_len;
			c_src += block_len;
			len -= block_len;
			offset = 0;
			++block_start;
		}
		while (len && block_start < block_limit);
	}

	// Something left, see if it will fit in the free
	// space of the last block.
	if (len && ! mBlocks.empty())
	{
		Block & last(*mBlocks.back());
		if (last.mUsed < last.mAlloced)
		{
			// Some will fit...
			const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed)));

			memcpy(&last.mData[last.mUsed], c_src, copy_len);
			last.mUsed += copy_len;
			result += copy_len;
			llassert_always(last.mUsed <= last.mAlloced);
			mLen += copy_len;
			c_src += copy_len;
			len -= copy_len;
		}
	}
	
	if (len)
	{
		// Some or all of the remaining write data will
		// be an append.
		result += append(c_src, len);
	}

	return result;
}
コード例 #3
0
ファイル: bufferarray.cpp プロジェクト: Belxjander/Kirito
size_t BufferArray::read(size_t pos, void * dst, size_t len)
{
	char * c_dst(static_cast<char *>(dst));
	
	if (pos >= mLen)
		return 0;
	size_t len_limit(mLen - pos);
	len = (std::min)(len, len_limit);
	if (0 == len)
		return 0;
	
	size_t result(0), offset(0);
	const int block_limit(mBlocks.size());
	int block_start(findBlock(pos, &offset));
	if (block_start < 0)
		return 0;

	do
	{
		Block & block(*mBlocks[block_start]);
		size_t block_limit(block.mUsed - offset);
		size_t block_len((std::min)(block_limit, len));
		
		memcpy(c_dst, &block.mData[offset], block_len);
		result += block_len;
		len -= block_len;
		c_dst += block_len;
		offset = 0;
		++block_start;
	}
	while (len && block_start < block_limit);

	return result;
}
コード例 #4
0
ファイル: write.c プロジェクト: libguestfs/hivex
/* 'offset' must point to a valid, used block.  This function marks
 * the block unused (by updating the seg_len field) and invalidates
 * the bitmap.  It does NOT do this recursively, so to avoid creating
 * unreachable used blocks, callers may have to recurse over the hive
 * structures.  Also callers must ensure there are no references to
 * this block from other parts of the hive.
 */
static void
mark_block_unused (hive_h *h, size_t offset)
{
  assert (h->writable);
  assert (IS_VALID_BLOCK (h, offset));

  DEBUG (2, "marking 0x%zx unused", offset);

  struct ntreg_hbin_block *blockhdr =
    (struct ntreg_hbin_block *) ((char *) h->addr + offset);

  size_t seg_len = block_len (h, offset, NULL);
  blockhdr->seg_len = htole32 (seg_len);

  BITMAP_CLR (h->bitmap, offset);
}
コード例 #5
0
ファイル: value.c プロジェクト: ajnelson-nist/hivex
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")) {
    SET_ERRNO (EINVAL, "invalid block or not an 'vk' block");
    return 0;
  }

  struct ntreg_vk_record *vk =
    (struct ntreg_vk_record *) ((char *) h->addr + value);

  /* 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 seg_len = block_len (h, value, NULL);
  if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) {
    SET_ERRNO (EFAULT, "key length is too long (%zu, %zu)", len, seg_len);
    return 0;
  }
  return _hivex_utf8_strlen (vk->name, len, ! (le16toh (vk->flags) & 0x01));
}
コード例 #6
0
ファイル: value.c プロジェクト: ajnelson-nist/hivex
int
_hivex_get_values (hive_h *h, hive_node_h node,
                   hive_value_h **values_ret, size_t **blocks_ret)
{
  if (!IS_VALID_BLOCK (h, node) || !block_id_eq (h, node, "nk")) {
    SET_ERRNO (EINVAL, "invalid block or not an 'nk' block");
    return -1;
  }

  struct ntreg_nk_record *nk =
    (struct ntreg_nk_record *) ((char *) h->addr + node);

  size_t nr_values = le32toh (nk->nr_values);

  DEBUG (2, "nr_values = %zu", nr_values);

  offset_list values, blocks;
  _hivex_init_offset_list (h, &values);
  _hivex_init_offset_list (h, &blocks);

  /* Deal with the common "no values" case quickly. */
  if (nr_values == 0)
    goto ok;

  /* Arbitrarily limit the number of values we will ever deal with. */
  if (nr_values > HIVEX_MAX_VALUES) {
    SET_ERRNO (ERANGE,
               "nr_values > HIVEX_MAX_VALUES (%zu > %d)",
               nr_values, HIVEX_MAX_VALUES);
    goto error;
  }

  /* Preallocate space for the values. */
  if (_hivex_grow_offset_list (&values, nr_values) == -1)
    goto error;

  /* Get the value list and check it looks reasonable. */
  size_t vlist_offset = le32toh (nk->vallist);
  vlist_offset += 0x1000;
  if (!IS_VALID_BLOCK (h, vlist_offset)) {
    SET_ERRNO (EFAULT,
               "value list is not a valid block (0x%zx)", vlist_offset);
    goto error;
  }

  if (_hivex_add_to_offset_list (&blocks, vlist_offset) == -1)
    goto error;

  struct ntreg_value_list *vlist =
    (struct ntreg_value_list *) ((char *) h->addr + vlist_offset);

  size_t len = block_len (h, vlist_offset, NULL);
  if (4 + nr_values * 4 > len) {
    SET_ERRNO (EFAULT, "value list is too long (%zu, %zu)", nr_values, len);
    goto error;
  }

  size_t i;
  for (i = 0; i < nr_values; ++i) {
    hive_node_h value = le32toh (vlist->offset[i]);
    value += 0x1000;
    if (!IS_VALID_BLOCK (h, value)) {
      SET_ERRNO (EFAULT, "value is not a valid block (0x%zx)", value);
      goto error;
    }
    if (_hivex_add_to_offset_list (&values, value) == -1)
      goto error;
  }

 ok:
  *values_ret = _hivex_return_offset_list (&values);
  *blocks_ret = _hivex_return_offset_list (&blocks);
  if (!*values_ret || !*blocks_ret)
    goto error;
  return 0;

 error:
  _hivex_free_offset_list (&values);
  _hivex_free_offset_list (&blocks);
  return -1;
}
コード例 #7
0
ファイル: value.c プロジェクト: ajnelson-nist/hivex
char *
hivex_value_value (hive_h *h, hive_value_h value,
                   hive_type *t_rtn, size_t *len_rtn)
{
  if (!IS_VALID_BLOCK (h, value) || !block_id_eq (h, value, "vk")) {
    SET_ERRNO (EINVAL, "invalid block or not an 'vk' block");
    return NULL;
  }

  struct ntreg_vk_record *vk =
    (struct ntreg_vk_record *) ((char *) h->addr + value);

  hive_type t;
  size_t len;
  int is_inline;

  t = le32toh (vk->data_type);

  len = le32toh (vk->data_len);
  is_inline = !!(len & 0x80000000);
  len &= 0x7fffffff;

  DEBUG (2, "value=0x%zx, t=%u, len=%zu, inline=%d",
         value, t, len, is_inline);

  if (t_rtn)
    *t_rtn = t;
  if (len_rtn)
    *len_rtn = len;

  if (is_inline && len > 4) {
    SET_ERRNO (ENOTSUP, "inline data with declared length (%zx) > 4", len);
    return NULL;
  }

  /* Arbitrarily limit the length that we will read. */
  if (len > HIVEX_MAX_VALUE_LEN) {
    SET_ERRNO (ERANGE, "data length > HIVEX_MAX_VALUE_LEN (%zu > %d)",
               len, HIVEX_MAX_VALUE_LEN);
    return NULL;
  }

  char *ret = malloc (len);
  if (ret == NULL)
    return NULL;

  if (is_inline) {
    memcpy (ret, (char *) &vk->data_offset, len);
    return ret;
  }

  size_t data_offset = le32toh (vk->data_offset);
  data_offset += 0x1000;
  if (!IS_VALID_BLOCK (h, data_offset)) {
    SET_ERRNO (EFAULT, "data offset is not a valid block (0x%zx)", data_offset);
    free (ret);
    return NULL;
  }

  /* Check that the declared size isn't larger than the block its in.
   *
   * XXX Some apparently valid registries are seen to have this,
   * so turn this into a warning and substitute the smaller length
   * instead.
   */
  size_t blen = block_len (h, data_offset, NULL);
  if (len <= blen - 4 /* subtract 4 for block header */) {
    char *data = (char *) h->addr + data_offset + 4;
    memcpy (ret, data, len);
    return ret;
  } else {
    if (!IS_VALID_BLOCK (h, data_offset) ||
        !block_id_eq (h, data_offset, "db")) {
      SET_ERRNO (EINVAL,
                 "declared data length is longer than the block and "
                 "block is not a db block (data 0x%zx, data len %zu)",
                 data_offset, len);
      free (ret);
      return NULL;
    }
    struct ntreg_db_record *db =
      (struct ntreg_db_record *) ((char *) h->addr + data_offset);
    size_t blocklist_offset = le32toh (db->blocklist_offset);
    blocklist_offset += 0x1000;
    size_t nr_blocks = le16toh (db->nr_blocks);
    if (!IS_VALID_BLOCK (h, blocklist_offset)) {
      SET_ERRNO (EINVAL,
                 "blocklist is not a valid block "
                 "(db block 0x%zx, blocklist 0x%zx)",
                 data_offset, blocklist_offset);
      free (ret);
      return NULL;
    }
    struct ntreg_value_list *bl =
      (struct ntreg_value_list *) ((char *) h->addr + blocklist_offset);
    size_t i, off;
    for (i = off = 0; i < nr_blocks; ++i) {
      size_t subblock_offset = le32toh (bl->offset[i]);
      subblock_offset += 0x1000;
      if (!IS_VALID_BLOCK (h, subblock_offset)) {
        SET_ERRNO (EINVAL,
                   "subblock is not valid "
                   "(db block 0x%zx, block list 0x%zx, data subblock 0x%zx)",
                   data_offset, blocklist_offset, subblock_offset);
        free (ret);
        return NULL;
      }
      int32_t seg_len = block_len(h, subblock_offset, NULL);
      struct ntreg_db_block *subblock =
        (struct ntreg_db_block *) ((char *) h->addr + subblock_offset);
      int32_t sz = seg_len - 8; /* don't copy the last 4 bytes */
      if (off + sz > len) {
        sz = len - off;
      }
      memcpy (ret + off, subblock->data, sz);
      off += sz;
    }
    if (off != *len_rtn) {
      DEBUG (2, "warning: declared data length "
             "and amount of data found in sub-blocks differ "
             "(db block 0x%zx, data len %zu, found data %zu)",
             data_offset, *len_rtn, off);
      *len_rtn = off;
    }
    return ret;
  }
}
コード例 #8
0
ファイル: handle.c プロジェクト: libguestfs/hivex
hive_h *
hivex_open (const char *filename, int flags)
{
  hive_h *h = NULL;

  assert (sizeof (struct ntreg_header) == 0x1000);
  assert (offsetof (struct ntreg_header, csum) == 0x1fc);

  h = calloc (1, sizeof *h);
  if (h == NULL)
    goto error;

  h->msglvl = flags & HIVEX_OPEN_MSGLVL_MASK;

  const char *debug = getenv ("HIVEX_DEBUG");
  if (debug && STREQ (debug, "1"))
    h->msglvl = 2;

  DEBUG (2, "created handle %p", h);

  h->writable = !!(flags & HIVEX_OPEN_WRITE);
  h->unsafe = !!(flags & HIVEX_OPEN_UNSAFE);
  h->filename = strdup (filename);
  if (h->filename == NULL)
    goto error;

#ifdef O_CLOEXEC
  h->fd = open (filename, O_RDONLY | O_CLOEXEC | O_BINARY);
#else
  h->fd = open (filename, O_RDONLY | O_BINARY);
#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)
    goto error;

  h->size = statbuf.st_size;

  if (h->size < 0x2000) {
    SET_ERRNO (EINVAL,
               "%s: file is too small to be a Windows NT Registry hive file",
               filename);
    goto error;
  }

  if (!h->writable) {
    h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0);
    if (h->addr == MAP_FAILED)
      goto error;

    DEBUG (2, "mapped file at %p", h->addr);
  } else {
    h->addr = malloc (h->size);
    if (h->addr == NULL)
      goto error;

    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. */
  if (h->hdr->magic[0] != 'r' ||
      h->hdr->magic[1] != 'e' ||
      h->hdr->magic[2] != 'g' ||
      h->hdr->magic[3] != 'f') {
    SET_ERRNO (ENOTSUP,
               "%s: not a Windows NT Registry hive file", filename);
    goto error;
  }

  /* Check major version. */
  uint32_t major_ver = le32toh (h->hdr->major_ver);
  if (major_ver != 1) {
    SET_ERRNO (ENOTSUP,
               "%s: hive file major version %" PRIu32 " (expected 1)",
               filename, major_ver);
    goto error;
  }

  h->bitmap = calloc (1 + h->size / 32, 1);
  if (h->bitmap == NULL)
    goto error;

  /* Header checksum. */
  uint32_t sum = header_checksum (h);
  if (sum != le32toh (h->hdr->csum)) {
    SET_ERRNO (EINVAL, "%s: bad checksum in hive header", filename);
    goto error;
  }

  for (int t=0; t<nr_recode_types; t++) {
    gl_lock_init (h->iconv_cache[t].mutex);
    h->iconv_cache[t].handle = NULL;
  }

  /* Last modified time. */
  h->last_modified = le64toh ((int64_t) h->hdr->last_modified);

  if (h->msglvl >= 2) {
    char *name = _hivex_recode (h, utf16le_to_utf8,
                                h->hdr->name, 64, NULL);

    fprintf (stderr,
             "hivex_open: header fields:\n"
             "  file version             %" PRIu32 ".%" PRIu32 "\n"
             "  sequence nos             %" PRIu32 " %" PRIu32 "\n"
             "    (sequences nos should match if hive was synched at shutdown)\n"
             "  last modified            %" PRIi64 "\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"
             "  end of last page         0x%x + 0x1000 (total file size 0x%zx)\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,
             le32toh (h->hdr->csum), sum);
    free (name);
  }

  h->rootoffs = le32toh (h->hdr->offset) + 0x1000;
  h->endpages = le32toh (h->hdr->blocks) + 0x1000;

  DEBUG (2, "root offset = 0x%zx", h->rootoffs);

  /* We'll set this flag when we see a block with the root offset (ie.
   * the root block).
   */
  int seen_root_block = 0, bad_root_block = 0;

  /* Collect some stats. */
  size_t pages = 0;           /* Number of hbin pages read. */
  size_t smallest_page = SIZE_MAX, largest_page = 0;
  size_t blocks = 0;          /* Total number of blocks found. */
  size_t smallest_block = SIZE_MAX, largest_block = 0, blocks_bytes = 0;
  size_t used_blocks = 0;     /* Total number of used blocks found. */
  size_t used_size = 0;       /* Total size (bytes) of used blocks. */

  /* Read the pages and blocks.  The aim here is to be robust against
   * corrupt or malicious registries.  So we make sure the loops
   * always make forward progress.  We add the address of each block
   * we read to a hash table so pointers will only reference the start
   * of valid blocks.
   */
  size_t off;
  struct ntreg_hbin_page *page;
  for (off = 0x1000; off < h->size; off += le32toh (page->page_size)) {
    if (off >= h->endpages)
      break;

    page = (struct ntreg_hbin_page *) ((char *) h->addr + off);
    if (page->magic[0] != 'h' ||
        page->magic[1] != 'b' ||
        page->magic[2] != 'i' ||
        page->magic[3] != 'n') {

      if (!h->unsafe) {
        SET_ERRNO (ENOTSUP,
                   "%s: trailing garbage at end of file "
                   "(at 0x%zx, after %zu pages)",
                   filename, off, pages);
        goto error;
      }

      DEBUG (2,
             "page not found at expected offset 0x%zx, "
             "seeking until one is found or EOF is reached",
             off);

      int found = 0;
      while (off < h->size) {
        off += 0x1000;

        if (off >= h->endpages)
          break;

        page = (struct ntreg_hbin_page *) ((char *) h->addr + off);
        if (page->magic[0] == 'h' &&
            page->magic[1] == 'b' &&
            page->magic[2] == 'i' &&
            page->magic[3] == 'n') {
          DEBUG (2, "found next page by seeking at 0x%zx", off);
          found = 1;
          break;
        }
      }

      if (!found) {
        DEBUG (2, "page not found and end of pages section reached");
        break;
      }
    }

    size_t page_size = le32toh (page->page_size);
    DEBUG (2, "page at 0x%zx, size %zu", off, page_size);
    pages++;
    if (page_size < smallest_page) smallest_page = page_size;
    if (page_size > largest_page) largest_page = page_size;

    if (page_size <= sizeof (struct ntreg_hbin_page) ||
        (page_size & 0x0fff) != 0) {
      SET_ERRNO (ENOTSUP,
                 "%s: page size %zu at 0x%zx, bad registry",
                 filename, page_size, off);
      goto error;
    }

    if (off + page_size > h->size) {
      SET_ERRNO (ENOTSUP,
                 "%s: page size %zu at 0x%zx extends beyond end of file, bad registry",
                 filename, page_size, off);
      goto error;
    }

    size_t page_offset = le32toh(page->offset_first) + 0x1000;

    if (page_offset != off) {
      SET_ERRNO (ENOTSUP,
                 "%s: declared page offset (0x%zx) does not match computed "
                 "offset (0x%zx), bad registry",
                 filename, page_offset, off);
      goto error;
    }

    /* Read the blocks in this page. */
    size_t blkoff;
    struct ntreg_hbin_block *block;
    size_t seg_len;
    for (blkoff = off + 0x20;
         blkoff < off + page_size;
         blkoff += seg_len) {
      blocks++;

      int is_root = blkoff == h->rootoffs;
      if (is_root)
        seen_root_block = 1;

      block = (struct ntreg_hbin_block *) ((char *) h->addr + blkoff);
      int used;
      seg_len = block_len (h, blkoff, &used);
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78665 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-overflow"
      if (seg_len <= 4 || (seg_len & 3) != 0) {
#pragma GCC diagnostic pop
        if (is_root || !h->unsafe) {
          SET_ERRNO (ENOTSUP,
                     "%s, the block at 0x%zx has invalid size %" PRIu32
                     ", bad registry",
                     filename, blkoff, le32toh (block->seg_len));
          goto error;
        } else {
          DEBUG (2,
                 "%s: block at 0x%zx has invalid size %" PRIu32 ", skipping",
                 filename, blkoff, le32toh (block->seg_len));
          break;
        }
      }

      if (h->msglvl >= 2) {
        unsigned char *id = (unsigned char *) block->id;
        int id0 = id[0], id1 = id[1];

        fprintf (stderr, "%s: %s: "
                 "%s block id %d,%d (%c%c) at 0x%zx size %zu%s\n",
                 "hivex", __func__,
                 used ? "used" : "free",
                 id0, id1,
                 c_isprint (id0) ? id0 : '.',
                 c_isprint (id1) ? id1 : '.',
                 blkoff,
                 seg_len, is_root ? " (root)" : "");
      }

      blocks_bytes += seg_len;
      if (seg_len < smallest_block) smallest_block = seg_len;
      if (seg_len > largest_block) largest_block = seg_len;

      if (is_root && !used)
        bad_root_block = 1;

      if (used) {
        used_blocks++;
        used_size += seg_len;

        /* Root block must be an nk-block. */
        if (is_root && (block->id[0] != 'n' || block->id[1] != 'k'))
          bad_root_block = 1;

        /* Note this blkoff is a valid address. */
        BITMAP_SET (h->bitmap, blkoff);
      }
    }
  }

  if (!seen_root_block) {
    SET_ERRNO (ENOTSUP, "%s: no root block found", filename);
    goto error;
  }

  if (bad_root_block) {
    SET_ERRNO (ENOTSUP, "%s: bad root block (free or not nk)", filename);
    goto error;
  }

  DEBUG (1, "successfully read Windows Registry hive file:\n"
         "  pages:          %zu [sml: %zu, lge: %zu]\n"
         "  blocks:         %zu [sml: %zu, avg: %zu, lge: %zu]\n"
         "  blocks used:    %zu\n"
         "  bytes used:     %zu",
         pages, smallest_page, largest_page,
         blocks, smallest_block, blocks_bytes / blocks, largest_block,
         used_blocks, used_size);

  return h;

 error:;
  int err = errno;
  if (h) {
    free (h->bitmap);
    if (h->addr && h->size && h->addr != MAP_FAILED) {
      if (!h->writable)
        munmap (h->addr, h->size);
      else
        free (h->addr);
    }
    if (h->fd >= 0)
      close (h->fd);
    free (h->filename);
    free (h);
  }
  errno = err;
  return NULL;
}