2 * Dump Windows NT Registry File (REGF)
4 * Copyright 2023 Piotr Caban for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define BLOCK_SIZE 4096
26 #define SECSPERDAY 86400
27 /* 1601 to 1970 is 369 years plus 89 leap days */
28 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
29 #define TICKSPERSEC 10000000
38 unsigned int signature
;
39 unsigned int seq_prim
;
42 unsigned int ver_major
;
43 unsigned int ver_minor
;
44 enum file_type file_type
;
46 unsigned int root_key_off
;
47 unsigned int hive_bins_size
;
48 unsigned int unk2
[116];
49 unsigned int checksum
;
54 KEY_IS_VOLATILE
= 0x01,
56 KEY_HIVE_ENTRY
= 0x04,
69 unsigned int parent_off
;
70 unsigned int sub_keys
;
71 unsigned int volatile_sub_keys
;
72 unsigned int sub_keys_list_off
;
73 unsigned int volatile_sub_keys_list_off
;
75 unsigned int values_list_off
;
76 unsigned int sec_key_off
;
77 unsigned int class_off
;
78 unsigned int max_name_size
;
79 unsigned int max_class_size
;
80 unsigned int max_val_name_size
;
81 unsigned int max_val_size
;
83 unsigned short name_size
;
84 unsigned short class_size
;
95 unsigned short signature
;
96 unsigned short name_size
;
97 unsigned int data_size
;
98 unsigned int data_off
;
99 unsigned int data_type
;
100 unsigned short flags
;
101 unsigned short padding
;
107 unsigned short signature
;
108 unsigned short count
;
111 static unsigned int path_len
;
112 static char path
[512*256];
114 static BOOL
dump_key(unsigned int hive_off
, unsigned int off
);
116 static time_t filetime_to_time_t(FILETIME ft
)
118 ULONGLONG
*ull
= (ULONGLONG
*)&ft
;
119 time_t t
= *ull
/ TICKSPERSEC
- SECS_1601_TO_1970
;
123 static const char *filetime_str(FILETIME ft
)
125 return get_time_str(filetime_to_time_t(ft
));
128 static unsigned int header_checksum(const header
*h
)
130 unsigned int i
, checksum
= 0;
132 for (i
= 0; i
< FIELD_OFFSET(header
, checksum
) / sizeof(int); i
++)
133 checksum
^= ((unsigned int*)h
)[i
];
137 enum FileSig
get_kind_reg(void)
141 hdr
= PRD(0, BLOCK_SIZE
);
142 if (hdr
&& !memcmp(&hdr
->signature
, "regf", sizeof(hdr
->signature
)))
147 static BOOL
dump_subkeys(unsigned int hive_off
, unsigned int off
)
149 const key_list
*key_list
= PRD(hive_off
+ off
, sizeof(*key_list
));
150 const unsigned int *offs
;
156 if (!memcmp(&key_list
->signature
, "lf", 2) ||
157 !memcmp(&key_list
->signature
, "lh", 2) ||
158 !memcmp(&key_list
->signature
, "li", 2))
160 unsigned int elem_size
= memcmp(&key_list
->signature
, "li", 2) ? 2 : 1;
162 offs
= PRD(hive_off
+ off
+ sizeof(*key_list
),
163 key_list
->count
* elem_size
* sizeof(*offs
));
167 for (i
= 0; i
< key_list
->count
; i
++)
169 if (!dump_key(hive_off
, offs
[elem_size
* i
]))
173 else if (!memcmp(&key_list
->signature
, "ri", 2))
175 offs
= PRD(hive_off
+ off
+ sizeof(*key_list
), key_list
->count
* sizeof(*offs
));
179 for (i
= 0; i
< key_list
->count
; i
++)
181 if (!dump_subkeys(hive_off
, offs
[i
]))
193 static BOOL
dump_value(unsigned int hive_off
, unsigned int off
)
195 unsigned int i
, len
, data_size
;
196 const void *data
= NULL
;
197 const char *name
, *str
;
198 const value_key
*val
;
200 val
= PRD(hive_off
+ off
, sizeof(*val
));
201 if (!val
|| memcmp(&val
->signature
, "vk", 2))
204 if (!(val
->data_size
& 0x80000000) && val
->data_size
> 4 * BLOCK_SIZE
)
206 printf("Warning: data blocks not supported\n");
210 if (val
->name_size
&& !(val
->flags
& VAL_COMP_NAME
))
212 name
= PRD(hive_off
+ off
+ sizeof(*val
), val
->name_size
);
215 name
= get_unicode_str((WCHAR
*)name
, val
->name_size
/ sizeof(WCHAR
));
216 len
= strlen(name
) + 1;
220 else if (val
->name_size
)
222 name
= PRD(hive_off
+ off
+ sizeof(*val
), val
->name_size
);
225 len
= val
->name_size
+ 3;
227 printf("\"%.*s\"=", val
->name_size
, name
);
235 data_size
= val
->data_size
;
236 if (data_size
& 0x80000000)
238 data
= &val
->data_off
;
239 data_size
&= ~0x80000000;
243 data
= PRD(hive_off
+ val
->data_off
+ sizeof(unsigned int), data_size
);
248 switch (val
->data_type
)
251 /* TODO: dump as REG_NONE value. */
258 printf("%s", !data
? "\"\"" :
259 get_unicode_str((const WCHAR
*)data
, data_size
/ sizeof(WCHAR
)));
263 printf("hex%s:", val
->data_type
== REG_QWORD
? "(b)" : "");
264 len
+= 4 + (val
->data_type
== REG_QWORD
? 3 : 0);
265 for (i
= 0; i
< data_size
; i
++)
277 printf("%02x", ((BYTE
*)data
)[i
]);
282 assert(data_size
== sizeof(DWORD
) || !data_size
);
284 printf("dword:%08x", *(unsigned int *)data
);
291 while(data_size
> sizeof(WCHAR
))
293 for (len
= 0; len
< data_size
/ sizeof(WCHAR
); len
++)
294 if (!((WCHAR
*)data
)[len
])
296 str
= get_unicode_str(data
, len
);
298 printf("%.*s\\0", (unsigned int)strlen(str
+ 1) - 1, str
+ 1);
299 data
= ((WCHAR
*)data
) + len
+ 1;
300 data_size
-= (len
+ 1) * sizeof(WCHAR
);
305 printf("unhandled data type %d", val
->data_type
);
312 static BOOL
dump_key(unsigned int hive_off
, unsigned int off
)
314 const named_key
*key
;
318 key
= PRD(hive_off
+ off
, sizeof(*key
));
319 if (!key
|| memcmp(&key
->signature
, "nk", 2))
322 if (!(key
->flags
& KEY_COMP_NAME
))
324 printf("unsupported key flags: %x\n", key
->flags
);
328 name
= PRD(hive_off
+ off
+ sizeof(*key
), key
->name_size
);
332 path
[path_len
++] = '\\';
333 memcpy(path
+ path_len
, name
, key
->name_size
);
334 path_len
+= key
->name_size
;
337 if ((!key
->sub_keys
&& !key
->volatile_sub_keys
) || key
->values
)
339 printf("[%s] %u\n", path
, (int)filetime_to_time_t(key
->timestamp
));
340 printf("#time=%x%08x\n", (int)key
->timestamp
.dwHighDateTime
, (int)key
->timestamp
.dwLowDateTime
);
344 const unsigned int *offs
= PRD(hive_off
+ key
->values_list_off
+ sizeof(unsigned int),
345 key
->values
* sizeof(unsigned int));
351 for (i
= 0; i
< key
->values
; i
++)
353 ret
= dump_value(hive_off
, offs
[i
]);
369 ret
= dump_subkeys(hive_off
, key
->sub_keys_list_off
);
371 path_len
-= key
->name_size
+ 1;
380 hdr
= PRD(0, sizeof(BLOCK_SIZE
));
384 printf("File Header\n");
385 printf(" %-20s %.4s\n", "signature:", (char*)&hdr
->signature
);
386 printf(" %-20s %u\n", "primary sequence:", hdr
->seq_prim
);
387 printf(" %-20s %u\n", "secondary sequence:", hdr
->seq_sec
);
388 printf(" %-20s %s\n", "modification time:", filetime_str(hdr
->modif_time
));
389 printf(" %-20s %u.%d\n", "version:", hdr
->ver_major
, hdr
->ver_minor
);
390 printf(" %-20s %u\n", "file type:", hdr
->file_type
);
391 printf(" %-20s %u\n", "root key offset:", hdr
->root_key_off
);
392 printf(" %-20s %u\n", "hive bins size:", hdr
->hive_bins_size
);
393 printf(" %-20s %x (%svalid)\n", "checksum:", hdr
->checksum
,
394 header_checksum(hdr
) == hdr
->checksum
? "" : "in");
397 if (hdr
->ver_major
!= 1 || hdr
->ver_minor
< 2 || hdr
->ver_minor
> 5 ||
398 hdr
->file_type
!= REG_HIVE
)
400 printf("unsupported format, exiting\n");
406 if (!dump_key(BLOCK_SIZE
, hdr
->root_key_off
))
407 printf("error dumping file\n");