File: hivex-internal.h

package info (click to toggle)
hivex 1.3.24-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,924 kB
  • sloc: ansic: 7,385; sh: 5,314; ml: 4,424; makefile: 559; perl: 528; python: 253; ruby: 123; xml: 104; sed: 16
file content (347 lines) | stat: -rw-r--r-- 13,168 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/* hivex internal header
 * Copyright (C) 2009-2011 Red Hat Inc.
 *
 * 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;
 * version 2.1 of the License only.
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef HIVEX_INTERNAL_H_
#define HIVEX_INTERNAL_H_

#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <iconv.h>
#include <pthread.h>

#include "byte_conversions.h"

#define STREQ(a,b) (strcmp((a),(b)) == 0)
#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)

typedef enum {
  utf8_to_latin1 = 0,
  latin1_to_utf8,
  utf8_to_utf16le,
  utf16le_to_utf8,
  nr_recode_types,
} recode_type;

struct hive_h {
  char *filename;
  int fd;
  size_t size;
  int msglvl;                   /* 1 = verbose, 2 or 3 = debug */
  int writable;
  int unsafe;

  /* Registry file, memory mapped if read-only, or malloc'd if writing. */
  union {
    void *addr;
    struct ntreg_header *hdr;
  };

  /* Use a bitmap to store which file offsets are valid (point to a
   * used block).  We only need to store 1 bit per 32 bits of the file
   * (because blocks are 4-byte aligned).  We found that the average
   * block size in a registry file is ~50 bytes.  So roughly 1 in 12
   * bits in the bitmap will be set, making it likely a more efficient
   * structure than a hash table.
   */
  char *bitmap;
#define BITMAP_SET(bitmap,off) (bitmap[(off)>>5] |= 1 << (((off)>>2)&7))
#define BITMAP_CLR(bitmap,off) (bitmap[(off)>>5] &= ~ (1 << (((off)>>2)&7)))
#define BITMAP_TST(bitmap,off) (bitmap[(off)>>5] & (1 << (((off)>>2)&7)))
#define IS_VALID_BLOCK(h,off)               \
  (((off) & 3) == 0 &&                      \
   (off) >= 0x1000 &&                       \
   (off) < (h)->size &&                     \
   BITMAP_TST((h)->bitmap,(off)))

  /* 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
                                   if not allocated anything yet). */

#ifndef HAVE_MMAP
  /* Internal data for mmap replacement */
  void *p_winmap;
#endif

  struct {
    pthread_mutex_t mutex;
    iconv_t *handle;
  } iconv_cache[nr_recode_types];
};

/* Format of registry blocks. NB. All fields are little endian. */
struct ntreg_header {
  char magic[4];                /* "regf" */
  uint32_t sequence1;
  uint32_t sequence2;
  int64_t last_modified;
  uint32_t major_ver;           /* 1 */
  uint32_t minor_ver;           /* 3 */
  uint32_t unknown5;            /* 0 */
  uint32_t unknown6;            /* 1 */
  uint32_t offset;              /* offset of root key record - 4KB */
  uint32_t blocks;              /* pointer AFTER last hbin in file - 4KB */
  uint32_t unknown7;            /* 1 */
  /* 0x30 */
  char name[64];                /* original file name of hive */
  char unknown_guid1[16];
  char unknown_guid2[16];
  /* 0x90 */
  uint32_t unknown8;
  char unknown_guid3[16];
  uint32_t unknown9;
  /* 0xa8 */
  char unknown10[340];
  /* 0x1fc */
  uint32_t csum;                /* checksum: xor of dwords 0-0x1fb. */
  /* 0x200 */
  char unknown11[3528];
  /* 0xfc8 */
  char unknown_guid4[16];
  char unknown_guid5[16];
  char unknown_guid6[16];
  uint32_t unknown12;
  uint32_t unknown13;
  /* 0x1000 */
} __attribute__((__packed__));

struct ntreg_hbin_page {
  char magic[4];                /* "hbin" */
  uint32_t offset_first;        /* offset from 1st block */
  uint32_t page_size;           /* size of this page (multiple of 4KB) */
  char unknown[20];
  /* Linked list of blocks follows here. */
} __attribute__((__packed__));

struct ntreg_hbin_block {
  int32_t seg_len;              /* length of this block (-ve for used block) */
  char id[2];                   /* the block type (eg. "nk" for nk record) */
  /* Block data follows here. */
} __attribute__((__packed__));

static inline int
block_id_eq (const hive_h *h, size_t offs, const char *eqid)
{
  return (STREQLEN (((struct ntreg_hbin_block *)((char *) (h)->addr + (offs)))->id, (eqid), 2));
}

struct ntreg_nk_record {
  int32_t seg_len;              /* length (always -ve because used) */
  char id[2];                   /* "nk" */
  uint16_t flags;               /* bit 1: HiveExit
                                   bit 2: HiveEntry == root key
                                   bit 3: NoDelete
                                   bit 4: SymbolicLink
                                   bit 5: CompressedName: Name is encoded
                                          in ASCII (actually: Latin-1)
                                          rather than UTF-16.
                                   bit 6: PredefinedHandle
                                   bit 7: VirtMirrored
                                   bit 8: VirtTarget
                                   bit 9: VirtualStore */
  /* Information from: Peter Norris: The Internal Structure of the
     Windows Registry, 2008, p.220 ff. */
  int64_t timestamp;
  uint32_t unknown1;
  uint32_t parent;              /* offset of owner/parent */
  uint32_t nr_subkeys;          /* number of subkeys */
  uint32_t nr_subkeys_volatile;
  uint32_t subkey_lf;           /* lf record containing list of subkeys */
  uint32_t subkey_lf_volatile;
  uint32_t nr_values;           /* number of values */
  uint32_t vallist;             /* value-list record */
  uint32_t sk;                  /* offset of sk-record */
  uint32_t classname;           /* offset of classname record */
  uint16_t max_subkey_name_len; /* maximum length of a subkey name in bytes
                                   if the subkey was reencoded as UTF-16LE */
  uint16_t unknown2;
  uint32_t unknown3;
  uint32_t max_vk_name_len;     /* maximum length of any vk name in bytes
                                   if the name was reencoded as UTF-16LE */
  uint32_t max_vk_data_len;     /* maximum length of any vk data in bytes */
  uint32_t unknown6;
  uint16_t name_len;            /* length of name */
  uint16_t classname_len;       /* length of classname */
  char name[1];                 /* name follows here */
} __attribute__((__packed__));

struct ntreg_lf_record {
  int32_t seg_len;
  char id[2];                   /* "lf"|"lh" */
  uint16_t nr_keys;             /* number of keys in this record */
  struct {
    uint32_t offset;            /* offset of nk-record for this subkey */
    char hash[4];               /* hash of subkey name */
  } __attribute__((__packed__)) keys[1];
} __attribute__((__packed__));

struct ntreg_ri_record {
  int32_t seg_len;
  char id[2];                   /* "ri" */
  uint16_t nr_offsets;          /* number of pointers to lh records */
  uint32_t offset[1];           /* list of pointers to lh records */
} __attribute__((__packed__));

/* This has no ID header. */
struct ntreg_value_list {
  int32_t seg_len;
  uint32_t offset[1];           /* list of pointers to vk records */
} __attribute__((__packed__));

struct ntreg_vk_record {
  int32_t seg_len;              /* length (always -ve because used) */
  char id[2];                   /* "vk" */
  uint16_t name_len;            /* length of name */
  /* length of the data:
   * If data_len is <= 4, then it's stored inline.
   * Top bit is set to indicate inline.
   */
  uint32_t data_len;
  uint32_t data_offset;         /* pointer to the data (or data if inline) */
  uint32_t data_type;           /* type of the data */
  uint16_t flags;               /* bit 0 set => key name ASCII,
                                   bit 0 clr => key name UTF-16.
                                   Only seen ASCII here in the wild.
                                   NB: this is CLEAR for default key. */
  uint16_t unknown2;
  char name[1];                 /* key name follows here */
} __attribute__((__packed__));

struct ntreg_sk_record {
  int32_t seg_len;              /* length (always -ve because used) */
  char id[2];                   /* "sk" */
  uint16_t unknown1;
  uint32_t sk_next;             /* linked into a circular list */
  uint32_t sk_prev;
  uint32_t refcount;            /* reference count */
  uint32_t sec_len;             /* length of security info */
  char sec_desc[1];             /* security info follows */
} __attribute__((__packed__));

struct ntreg_db_record {
  int32_t seg_len;              /* length (always -ve because used) */
  char id[2];                   /* "db" */
  uint16_t nr_blocks;
  uint32_t blocklist_offset;
  uint32_t unknown1;
} __attribute__((__packed__));

struct ntreg_db_block {
  int32_t seg_len;
  char data[1];
} __attribute__((__packed__));

static inline size_t
block_len (hive_h *h, size_t blkoff, int *used)
{
  struct ntreg_hbin_block *block;
  block = (struct ntreg_hbin_block *) ((char *) h->addr + blkoff);

  int32_t len = le32toh (block->seg_len);
  if (len < 0) {
    if (used) *used = 1;
    len = -len;
  } else {
    if (used) *used = 0;
  }

  return (size_t) len;
}

/* node.c */
#define GET_CHILDREN_NO_CHECK_NK 1
extern int _hivex_get_children (hive_h *h, hive_node_h node, hive_node_h **children_ret, size_t **blocks_ret, int flags);

/* offset-list.c */
typedef struct offset_list offset_list;
struct offset_list {
  hive_h *h;
  size_t *offsets;
  size_t len;
  size_t alloc;
  size_t limit;
};
extern void _hivex_init_offset_list (hive_h *h, offset_list *list);
extern int _hivex_grow_offset_list (offset_list *list, size_t alloc);
extern int _hivex_add_to_offset_list (offset_list *list, size_t offset);
extern size_t _hivex_get_offset_list_length (offset_list *list);
extern void _hivex_set_offset_list_limit (offset_list *list, size_t limit);
extern void _hivex_free_offset_list (offset_list *list);
extern size_t * _hivex_return_offset_list (offset_list *list);
extern void _hivex_print_offset_list (offset_list *list, FILE *fp);

/* handle.c */
extern iconv_t * _hivex_get_iconv (hive_h *h, recode_type r);
extern void  _hivex_release_iconv (hive_h *h, recode_type r);

/* utf16.c */
extern char * _hivex_recode (hive_h *h, recode_type r,
                             const char *input, size_t input_len, size_t *output_len);
extern char* _hivex_encode_string (hive_h *h, const char *str, size_t *size, int *utf16);
extern size_t _hivex_utf16_string_len_in_bytes_max (const char *str, size_t len);
extern size_t _hivex_utf8_strlen (hive_h *h, const char* str, size_t len, int utf16);

/* util.c */
extern void _hivex_free_strings (char **argv);

/* value.c */
extern int _hivex_get_values (hive_h *h, hive_node_h node, hive_value_h **values_ret, size_t **blocks_ret);

#define DEBUG(lvl,fs,...)                                       \
  do {                                                          \
    if (h->msglvl >= (lvl)) {                                   \
      fprintf (stderr, "%s: %s: " fs "\n",                      \
               "hivex", __func__, ## __VA_ARGS__);              \
    }                                                           \
  } while (0)

#define SET_ERRNO(errval,fs,...)                                        \
  do {                                                                  \
    DEBUG (1, "returning " #errval " because: " fs, ## __VA_ARGS__);    \
    errno = errval;                                                     \
  } while (0)

#define CHECK_WRITABLE(retcode)                                         \
  do {                                                                  \
    if (!h->writable) {                                                 \
      SET_ERRNO (EROFS,                                                 \
                 "HIVEX_OPEN_WRITE flag was not specified when opening this hive"); \
      return (retcode);                                                 \
    }                                                                   \
  } while (0)

/* These limits are in place to stop really stupid stuff and/or exploits. */
#define HIVEX_MAX_SUBKEYS       70000
#define HIVEX_MAX_VALUES       110000
#define HIVEX_MAX_VALUE_LEN   8000000
#define HIVEX_MAX_ALLOCATION  1000000

#endif /* HIVEX_INTERNAL_H_ */