📄 regf.c
字号:
/* Samba CIFS implementation Registry backend for REGF files Copyright (C) 2005-2007 Jelmer Vernooij, jelmer@samba.org Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl 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 3 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, see <http://www.gnu.org/licenses/>. */#include "includes.h"#include "system/filesys.h"#include "system/time.h"#include "lib/registry/tdr_regf.h"#include "librpc/gen_ndr/ndr_security.h"#include "librpc/gen_ndr/winreg.h"#include "param/param.h"#include "lib/registry/registry.h"#include "libcli/security/security.h"static struct hive_operations reg_backend_regf;/** * There are several places on the web where the REGF format is explained; * * TODO: Links *//* TODO: * - Return error codes that make more sense * - Locking * - do more things in-memory *//* * Read HBIN blocks into memory */struct regf_data { int fd; struct hbin_block **hbins; struct regf_hdr *header; struct smb_iconv_convenience *iconv_convenience;};static WERROR regf_save_hbin(struct regf_data *data);struct regf_key_data { struct hive_key key; struct regf_data *hive; uint32_t offset; struct nk_block *nk;};static struct hbin_block *hbin_by_offset(const struct regf_data *data, uint32_t offset, uint32_t *rel_offset){ int i; for (i = 0; data->hbins[i]; i++) { if (offset >= data->hbins[i]->offset_from_first && offset < data->hbins[i]->offset_from_first+ data->hbins[i]->offset_to_next) { if (rel_offset != NULL) *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20; return data->hbins[i]; } } return NULL;}/** * Validate a regf header * For now, do nothing, but we should check the checksum */static uint32_t regf_hdr_checksum(const uint8_t *buffer){ uint32_t checksum = 0, x; int i; for (i = 0; i < 0x01FB; i+= 4) { x = IVAL(buffer, i); checksum ^= x; } return checksum;}/** * Obtain the contents of a HBIN block */static DATA_BLOB hbin_get(const struct regf_data *data, uint32_t offset){ DATA_BLOB ret; struct hbin_block *hbin; uint32_t rel_offset; ret.data = NULL; ret.length = 0; hbin = hbin_by_offset(data, offset, &rel_offset); if (hbin == NULL) { DEBUG(1, ("Can't find HBIN containing 0x%04x\n", offset)); return ret; } ret.length = IVAL(hbin->data, rel_offset); if (!(ret.length & 0x80000000)) { DEBUG(0, ("Trying to use dirty block at 0x%04x\n", offset)); return ret; } /* remove high bit */ ret.length = (ret.length ^ 0xffffffff) + 1; ret.length -= 4; /* 4 bytes for the length... */ ret.data = hbin->data + (offset - hbin->offset_from_first - 0x20) + 4; return ret;}static bool hbin_get_tdr(struct regf_data *regf, uint32_t offset, TALLOC_CTX *ctx, tdr_pull_fn_t pull_fn, void *p){ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience); pull->data = hbin_get(regf, offset); if (!pull->data.data) { DEBUG(1, ("Unable to get data at 0x%04x\n", offset)); talloc_free(pull); return false; } if (NT_STATUS_IS_ERR(pull_fn(pull, ctx, p))) { DEBUG(1, ("Error parsing record at 0x%04x using tdr\n", offset)); talloc_free(pull); return false; } talloc_free(pull); return true;}/* Allocate some new data */static DATA_BLOB hbin_alloc(struct regf_data *data, uint32_t size, uint32_t *offset){ DATA_BLOB ret; uint32_t rel_offset = -1; /* Relative offset ! */ struct hbin_block *hbin = NULL; int i; *offset = 0; if (size == 0) return data_blob(NULL, 0); size += 4; /* Need to include int32 for the length */ /* Allocate as a multiple of 8 */ size = (size + 7) & ~7; ret.data = NULL; ret.length = 0; for (i = 0; (hbin = data->hbins[i]); i++) { int j; int32_t my_size; for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) { my_size = IVALS(hbin->data, j); if (my_size == 0x0) { DEBUG(0, ("Invalid zero-length block! File is corrupt.\n")); return ret; } if (my_size % 8 != 0) { DEBUG(0, ("Encountered non-aligned block!\n")); } if (my_size < 0) { /* Used... */ my_size = -my_size; } else if (my_size == size) { /* exact match */ rel_offset = j; DEBUG(4, ("Found free block of exact size %d in middle of HBIN\n", size)); break; } else if (my_size > size) { /* data will remain */ rel_offset = j; /* Split this block and mark the next block as free */ SIVAL(hbin->data, rel_offset+size, my_size-size); DEBUG(4, ("Found free block of size %d (needing %d) in middle of HBIN\n", my_size, size)); break; } } if (rel_offset != -1) break; } /* No space available in previous hbins, * allocate new one */ if (data->hbins[i] == NULL) { DEBUG(4, ("No space available in other HBINs for block of size %d, allocating new HBIN\n", size)); data->hbins = talloc_realloc(data, data->hbins, struct hbin_block *, i+2); hbin = talloc(data->hbins, struct hbin_block); SMB_ASSERT(hbin != NULL); data->hbins[i] = hbin; data->hbins[i+1] = NULL; hbin->HBIN_ID = talloc_strdup(hbin, "hbin"); hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next); hbin->offset_to_next = 0x1000; hbin->unknown[0] = 0; hbin->unknown[0] = 0; unix_to_nt_time(&hbin->last_change, time(NULL)); hbin->block_size = hbin->offset_to_next; hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20); rel_offset = 0x0; SIVAL(hbin->data, size, hbin->block_size - size - 0x20); } /* Set size and mark as used */ SIVAL(hbin->data, rel_offset, -size); ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */ ret.length = size - 0x4; if (offset) { uint32_t new_rel_offset; *offset = hbin->offset_from_first + rel_offset + 0x20; SMB_ASSERT(hbin_by_offset(data, *offset, &new_rel_offset) == hbin); SMB_ASSERT(new_rel_offset == rel_offset); } return ret;}/* Store a data blob. Return the offset at which it was stored */static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob){ uint32_t ret; DATA_BLOB dest = hbin_alloc(data, blob.length, &ret); memcpy(dest.data, blob.data, blob.length); return ret;}static uint32_t hbin_store_tdr(struct regf_data *data, tdr_push_fn_t push_fn, void *p){ struct tdr_push *push = tdr_push_init(data, data->iconv_convenience); uint32_t ret; if (NT_STATUS_IS_ERR(push_fn(push, p))) { DEBUG(0, ("Error during push\n")); return -1; } ret = hbin_store(data, push->data); talloc_free(push); return ret;}/* Free existing data */static void hbin_free (struct regf_data *data, uint32_t offset){ int32_t size; uint32_t rel_offset; int32_t next_size; struct hbin_block *hbin; SMB_ASSERT (offset > 0); hbin = hbin_by_offset(data, offset, &rel_offset); if (hbin == NULL) return; /* Get original size */ size = IVALS(hbin->data, rel_offset); if (size > 0) { DEBUG(1, ("Trying to free already freed block at 0x%04x\n", offset)); return; } /* Mark as unused */ size = -size; /* If the next block is free, merge into big free block */ if (rel_offset + size < hbin->offset_to_next) { next_size = IVALS(hbin->data, rel_offset+size); if (next_size > 0) { size += next_size; } } /* Write block size */ SIVALS(hbin->data, rel_offset, size);}/** * Store a data blob data was already stored, but has changed in size * Will try to save it at the current location if possible, otherwise * does a free + store */static uint32_t hbin_store_resize(struct regf_data *data, uint32_t orig_offset, DATA_BLOB blob){ uint32_t rel_offset; struct hbin_block *hbin = hbin_by_offset(data, orig_offset, &rel_offset); int32_t my_size; int32_t orig_size; int32_t needed_size; int32_t possible_size; int i; SMB_ASSERT(orig_offset > 0); if (!hbin) return hbin_store(data, blob); /* Get original size */ orig_size = -IVALS(hbin->data, rel_offset); needed_size = blob.length + 4; /* Add int32 containing length */ needed_size = (needed_size + 7) & ~7; /* Align */ /* Fits into current allocated block */ if (orig_size >= needed_size) { memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length); /* If the difference in size is greater than 0x4, split the block * and free/merge it */ if (orig_size - needed_size > 0x4) { SIVALS(hbin->data, rel_offset, -needed_size); SIVALS(hbin->data, rel_offset + needed_size, needed_size-orig_size); hbin_free(data, orig_offset + needed_size); } return orig_offset; } possible_size = orig_size; /* Check if it can be combined with the next few free records */ for (i = rel_offset; i < hbin->offset_to_next - 0x20; i += my_size) { if (IVALS(hbin->data, i) < 0) /* Used */ break; my_size = IVALS(hbin->data, i); if (my_size == 0x0) { DEBUG(0, ("Invalid zero-length block! File is corrupt.\n")); break; } else { possible_size += my_size; } if (possible_size >= blob.length) { SIVAL(hbin->data, rel_offset, -possible_size); memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length); return orig_offset; } } hbin_free(data, orig_offset); return hbin_store(data, blob);}static uint32_t hbin_store_tdr_resize(struct regf_data *regf, tdr_push_fn_t push_fn, uint32_t orig_offset, void *p){ struct tdr_push *push = tdr_push_init(regf, regf->iconv_convenience); uint32_t ret; if (NT_STATUS_IS_ERR(push_fn(push, p))) { DEBUG(0, ("Error during push\n")); return -1; } ret = hbin_store_resize(regf, orig_offset, push->data); talloc_free(push); return ret;}static uint32_t regf_create_lh_hash(const char *name){ char *hash_name; uint32_t ret = 0; uint16_t i; hash_name = strupper_talloc(NULL, name); for (i = 0; *(hash_name + i) != 0; i++) { ret *= 37; ret += *(hash_name + i); } talloc_free(hash_name); return ret;}static WERROR regf_get_info(TALLOC_CTX *mem_ctx, const struct hive_key *key, const char **classname, uint32_t *num_subkeys, uint32_t *num_values, NTTIME *last_mod_time, uint32_t *max_subkeynamelen, uint32_t *max_valnamelen, uint32_t *max_valbufsize){ const struct regf_key_data *private_data = (const struct regf_key_data *)key; if (num_subkeys != NULL) *num_subkeys = private_data->nk->num_subkeys; if (num_values != NULL) *num_values = private_data->nk->num_values; if (classname != NULL) { if (private_data->nk->clsname_offset != -1) { DATA_BLOB data = hbin_get(private_data->hive, private_data->nk->clsname_offset); *classname = talloc_strndup(mem_ctx, (char*)data.data, private_data->nk->clsname_length); } else *classname = NULL; } /* TODO: Last mod time */ /* TODO: max valnamelen */ /* TODO: max valbufsize */ /* TODO: max subkeynamelen */ return WERR_OK;}static struct regf_key_data *regf_get_key(TALLOC_CTX *ctx, struct regf_data *regf, uint32_t offset){ struct nk_block *nk; struct regf_key_data *ret; ret = talloc_zero(ctx, struct regf_key_data); ret->key.ops = ®_backend_regf; ret->hive = talloc_reference(ret, regf); ret->offset = offset; nk = talloc(ret, struct nk_block); if (nk == NULL) return NULL; ret->nk = nk; if (!hbin_get_tdr(regf, offset, nk, (tdr_pull_fn_t)tdr_pull_nk_block, nk)) { DEBUG(0, ("Unable to find HBIN data for offset %d\n", offset)); return NULL; } if (strcmp(nk->header, "nk") != 0) { DEBUG(0, ("Expected nk record, got %s\n", nk->header)); talloc_free(ret); return NULL; } return ret;}static WERROR regf_get_value(TALLOC_CTX *ctx, struct hive_key *key, int idx, const char **name, uint32_t *data_type, DATA_BLOB *data){ const struct regf_key_data *private_data = (const struct regf_key_data *)key; struct vk_block *vk; struct regf_data *regf = private_data->hive; uint32_t vk_offset; DATA_BLOB tmp; if (idx >= private_data->nk->num_values) return WERR_NO_MORE_ITEMS; tmp = hbin_get(regf, private_data->nk->values_offset); if (!tmp.data) { DEBUG(0, ("Unable to find value list\n")); return WERR_GENERAL_FAILURE; } if (tmp.length < private_data->nk->num_values * 4) { DEBUG(1, ("Value counts mismatch\n")); } vk_offset = IVAL(tmp.data, idx * 4); vk = talloc(NULL, struct vk_block); W_ERROR_HAVE_NO_MEMORY(vk); if (!hbin_get_tdr(regf, vk_offset, vk, (tdr_pull_fn_t)tdr_pull_vk_block, vk)) { DEBUG(0, ("Unable to get VK block at %d\n", vk_offset)); talloc_free(vk); return WERR_GENERAL_FAILURE; } /* FIXME: name character set ?*/ if (name != NULL) *name = talloc_strndup(ctx, vk->data_name, vk->name_length); if (data_type != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -