From: Piotr Caban piotr@codeweavers.com
--- tools/winedump/Makefile.in | 1 + tools/winedump/dump.c | 1 + tools/winedump/reg.c | 341 +++++++++++++++++++++++++++++++++++++ tools/winedump/winedump.h | 4 +- 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 tools/winedump/reg.c
diff --git a/tools/winedump/Makefile.in b/tools/winedump/Makefile.in index 04dcacf0216..1e58b1a839a 100644 --- a/tools/winedump/Makefile.in +++ b/tools/winedump/Makefile.in @@ -23,6 +23,7 @@ C_SRCS = \ output.c \ pdb.c \ pe.c \ + reg.c \ search.c \ symbol.c \ tlb.c diff --git a/tools/winedump/dump.c b/tools/winedump/dump.c index 7d4a1a320d5..1a97e3bb171 100644 --- a/tools/winedump/dump.c +++ b/tools/winedump/dump.c @@ -261,6 +261,7 @@ dumpers[] = {SIG_FNT, get_kind_fnt, fnt_dump}, {SIG_TLB, get_kind_tlb, tlb_dump}, {SIG_NLS, get_kind_nls, nls_dump}, + {SIG_REG, get_kind_reg, reg_dump}, {SIG_UNKNOWN, NULL, NULL} /* sentinel */ };
diff --git a/tools/winedump/reg.c b/tools/winedump/reg.c new file mode 100644 index 00000000000..1c6c6293415 --- /dev/null +++ b/tools/winedump/reg.c @@ -0,0 +1,341 @@ +/* + * Dump Windows NT Registry File (REGF) + * + * Copyright 2023 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "winedump.h" + +#define BLOCK_SIZE 4096 + +#define SECSPERDAY 86400 +/* 1601 to 1970 is 369 years plus 89 leap days */ +#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) +#define TICKSPERSEC 10000000 + +enum file_type +{ + REG_HIVE +}; + +typedef struct +{ + unsigned int signature; + unsigned int seq_prim; + unsigned int seq_sec; + FILETIME modif_time; + unsigned int ver_major; + unsigned int ver_minor; + enum file_type file_type; + unsigned int unk1; + unsigned int root_key_off; + unsigned int hive_bins_size; + unsigned int unk2[116]; + unsigned int checksum; +} header; + +enum key_flags +{ + KEY_IS_VOLATILE = 0x01, + KEY_HIVE_EXIT = 0x02, + KEY_HIVE_ENTRY = 0x04, + KEY_NO_DELETE = 0x08, + KEY_SYM_LINK = 0x10, + KEY_COMP_NAME = 0x20 +}; + +typedef struct +{ + unsigned int size; + short signature; + short flags; + FILETIME timestamp; + int unk1; + unsigned int parent_off; + unsigned int sub_keys; + unsigned int volatile_sub_keys; + unsigned int sub_keys_list_off; + unsigned int volatile_sub_keys_list_off; + unsigned int values; + unsigned int values_list_off; + unsigned int sec_key_off; + unsigned int class_off; + unsigned int max_name_size; + unsigned int max_class_size; + unsigned int max_val_name_size; + unsigned int max_val_size; + int unk2; + unsigned short name_size; + unsigned short class_size; +} named_key; + +enum val_flags +{ + VAL_COMP_NAME = 0x1 +}; + +typedef struct +{ + unsigned int size; + unsigned short signature; + unsigned short name_size; + unsigned int data_size; + unsigned int data_off; + unsigned int data_type; + unsigned short flags; + unsigned short padding; +} value_key; + +typedef struct +{ + unsigned int size; + unsigned short signature; + unsigned short count; +} key_list; + +static unsigned int path_len; +static char path[512*256]; + +static BOOL dump_key(unsigned int hive_off, unsigned int off); + +static time_t filetime_to_time_t(FILETIME ft) +{ + ULONGLONG *ull = (ULONGLONG *)&ft; + time_t t = *ull / TICKSPERSEC - SECS_1601_TO_1970; + return *ull ? t : 0; +} + +static const char *filetime_str(FILETIME ft) +{ + return get_time_str(filetime_to_time_t(ft)); +} + +static unsigned int header_checksum(const header *h) +{ + unsigned int i, checksum = 0; + + for (i = 0; i < FIELD_OFFSET(header, checksum) / sizeof(int); i++) + checksum ^= ((unsigned int*)h)[i]; + return checksum; +} + +enum FileSig get_kind_reg(void) +{ + const header *hdr; + + hdr = PRD(0, BLOCK_SIZE); + if (hdr && !memcmp(&hdr->signature, "regf", sizeof(hdr->signature))) + return SIG_REG; + return SIG_UNKNOWN; +} + +static BOOL dump_subkeys(unsigned int hive_off, unsigned int off) +{ + const key_list *key_list = PRD(hive_off + off, sizeof(*key_list)); + const unsigned int *offs; + unsigned int i; + + if (!key_list) + return FALSE; + + if (!memcmp(&key_list->signature, "lf", 2) || + !memcmp(&key_list->signature, "lh", 2) || + !memcmp(&key_list->signature, "li", 2)) + { + unsigned int elem_size = memcmp(&key_list->signature, "li", 2) ? 2 : 1; + + offs = PRD(hive_off + off + sizeof(*key_list), + key_list->count * elem_size * sizeof(*offs)); + if (!offs) + return FALSE; + + for (i = 0; i < key_list->count; i++) + { + if (!dump_key(hive_off, offs[elem_size * i])) + return FALSE; + } + } + else if (!memcmp(&key_list->signature, "ri", 2)) + { + offs = PRD(hive_off + off + sizeof(*key_list), key_list->count * sizeof(*offs)); + if (!offs) + return FALSE; + + for (i = 0; i < key_list->count; i++) + { + if (!dump_subkeys(hive_off, offs[i])) + return FALSE; + } + } + else + { + return FALSE; + } + + return TRUE; +} + +static BOOL dump_value(unsigned int hive_off, unsigned int off) +{ + const void *data = NULL; + const value_key *val; + const char *name; + + val = PRD(hive_off + off, sizeof(*val)); + if (!val || memcmp(&val->signature, "vk", 2)) + return FALSE; + + if (!(val->flags & VAL_COMP_NAME)) + { + printf("unsupported value flags: %x\n", val->flags); + return FALSE; + } + + if (val->name_size) + { + name = PRD(hive_off + off + sizeof(*val), val->name_size); + if (!name) + return FALSE; + + printf(""%*s"=", val->name_size, name); + } + else + { + printf("@="); + } + + if (val->data_size) + { + data = PRD(hive_off + val->data_off + sizeof(unsigned int), val->data_size); + if (!data) + return FALSE; + } + + switch (val->data_type) + { + case REG_SZ: + printf("%s", !data ? "" : + get_unicode_str((const WCHAR *)data, val->data_size / sizeof(WCHAR))); + break; + default: + printf("unhandled data type %d", val->data_type); + } + + printf("\n"); + return TRUE; +} + +static BOOL dump_key(unsigned int hive_off, unsigned int off) +{ + const named_key *key; + const char *name; + BOOL ret = TRUE; + + key = PRD(hive_off + off, sizeof(*key)); + if (!key || memcmp(&key->signature, "nk", 2)) + return FALSE; + + if (!(key->flags & KEY_COMP_NAME)) + { + printf("unsupported key flags: %x\n", key->flags); + return FALSE; + } + + name = PRD(hive_off + off + sizeof(*key), key->name_size); + if (!name) + return FALSE; + if (path_len) + path[path_len++] = '\'; + memcpy(path + path_len, name, key->name_size); + path_len += key->name_size; + path[path_len] = 0; + + if ((!key->sub_keys && !key->volatile_sub_keys) || key->values) + { + printf("[%s] %u\n", path, (int)filetime_to_time_t(key->timestamp)); + printf("#time=%x%08x\n", (int)key->timestamp.dwHighDateTime, (int)key->timestamp.dwLowDateTime); + + if (key->values) + { + const unsigned int *offs = PRD(hive_off + key->values_list_off + sizeof(unsigned int), + key->values * sizeof(unsigned int)); + unsigned int i; + + if (!offs) + return FALSE; + + for (i = 0; i < key->values; i++) + { + ret = dump_value(hive_off, offs[i]); + if (!ret) + return ret; + } + } + else + { + printf("@=""\n"); + } + if (!ret) + return FALSE; + + printf("\n"); + } + + if (key->sub_keys) + ret = dump_subkeys(hive_off, key->sub_keys_list_off); + if (ret && key->volatile_sub_keys) + ret = dump_subkeys(hive_off, key->volatile_sub_keys_list_off); + + path_len -= key->name_size + 1; + path[path_len] = 0; + return ret; +} + +void reg_dump(void) +{ + const header *hdr; + + hdr = PRD(0, sizeof(BLOCK_SIZE)); + if (!hdr) + return; + + printf("File Header\n"); + printf(" %-20s %4s\n", "signature:", (char*)&hdr->signature); + printf(" %-20s %u\n", "primary sequence:", hdr->seq_prim); + printf(" %-20s %u\n", "secondary sequence:", hdr->seq_sec); + printf(" %-20s %s\n", "modification time:", filetime_str(hdr->modif_time)); + printf(" %-20s %u.%d\n", "version:", hdr->ver_major, hdr->ver_minor); + printf(" %-20s %u\n", "file type:", hdr->file_type); + printf(" %-20s %u\n", "root key offset:", hdr->root_key_off); + printf(" %-20s %u\n", "hive bins size:", hdr->hive_bins_size); + printf(" %-20s %x (%svalid)\n", "checksum:", hdr->checksum, + header_checksum(hdr) == hdr->checksum ? "" : "in"); + printf("\n"); + + if (hdr->ver_major != 1 || hdr->ver_minor < 2 || hdr->ver_minor > 5 || + hdr->file_type != REG_HIVE) + { + printf("unsupported format, exiting\n"); + return; + } + + path_len = 0; + path[0] = 0; + if (!dump_key(BLOCK_SIZE, hdr->root_key_off)) + printf("error dumping file\n"); +} diff --git a/tools/winedump/winedump.h b/tools/winedump/winedump.h index 4561ca3751a..fe1acca7976 100644 --- a/tools/winedump/winedump.h +++ b/tools/winedump/winedump.h @@ -216,7 +216,7 @@ const char *get_machine_str(int mach);
/* file dumping functions */ enum FileSig {SIG_UNKNOWN, SIG_DOS, SIG_PE, SIG_DBG, SIG_PDB, SIG_NE, SIG_LE, SIG_MDMP, SIG_COFFLIB, SIG_LNK, - SIG_EMF, SIG_EMFSPOOL, SIG_MF, SIG_FNT, SIG_TLB, SIG_NLS}; + SIG_EMF, SIG_EMFSPOOL, SIG_MF, SIG_FNT, SIG_TLB, SIG_NLS, SIG_REG};
const void* PRD(unsigned long prd, unsigned long len); unsigned long Offset(const void* ptr); @@ -265,6 +265,8 @@ enum FileSig get_kind_tlb(void); void tlb_dump(void); enum FileSig get_kind_nls(void); void nls_dump(void); +enum FileSig get_kind_reg(void); +void reg_dump(void);
BOOL codeview_dump_symbols(const void* root, unsigned long start, unsigned long size); BOOL codeview_dump_types_from_offsets(const void* table, const DWORD* offsets, unsigned num_types);