1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """This is the registry parser.
22
23 We parse registry structures from files or memory.
24 """
25 __author__ = ("Michael Cohen <scudette@gmail.com> based on original code "
26 "by Brendan Dolan-Gavitt")
27
28
29
30 import ntpath
31 import re
32 import struct
33
34 from rekall import addrspace
35 from rekall import obj
36
37 from rekall.plugins.windows import common
38 from rekall_lib import utils
39
40
41 registry_overlays = {
42 '_CM_KEY_NODE': [None, {
43 'Parent': [None, ['Pointer32', dict(
44 target='_CM_KEY_NODE'
45 )]],
46 'Flags': [None, ['Flags', dict(bitmap={
47 "KEY_IS_VOLATILE": 0,
48 "KEY_HIVE_EXIT": 1,
49 "KEY_HIVE_ENTRY": 2,
50 "KEY_NO_DELETE": 3,
51 "KEY_SYM_LINK": 4,
52 "KEY_COMP_NAME": 5,
53 "KEY_PREFEF_HANDLE": 6,
54 "KEY_VIRT_MIRRORED": 7,
55 "KEY_VIRT_TARGET": 8,
56 "KEY_VIRTUAL_STORE": 9,
57 })]],
58
59 'Signature' : [None, ['String', dict(length=2)]],
60 'LastWriteTime' : [None, ['WinFileTime', {}]],
61 'Name' : [None, ['String', dict(length=lambda x: x.NameLength)]],
62 }],
63
64 '_CM_KEY_VALUE': [None, {
65 'Signature' : [None, ['String', dict(length=2)]],
66 'Name' : [None, ['String', dict(length=lambda x: x.NameLength)]],
67
68 'Type': [None, ['Enumeration', dict(choices={
69 0: "REG_NONE",
70 1: "REG_SZ",
71 2: "REG_EXPAND_SZ",
72 3: "REG_BINARY",
73 4: "REG_DWORD",
74 5: "REG_DWORD_BIG_ENDIAN",
75 6: "REG_LINK",
76 7: "REG_MULTI_SZ",
77 8: "REG_RESOURCE_LIST",
78 9: "REG_FULL_RESOURCE_DESCRIPTOR",
79 10: "REG_RESOURCE_REQUIREMENTS_LIST",
80 11: "REG_QWORD",
81 })]]
82 }],
83
84 '_CM_NAME_CONTROL_BLOCK' : [None, {
85 'Name' : [None, ['String', dict(length=lambda x: x.NameLength)]],
86 }],
87
88 '_CHILD_LIST' : [None, {
89 'List' : [None, ['Pointer32', dict(
90 target="Array",
91 target_args=dict(
92 count=lambda x: x.Count,
93 target="Pointer32",
94 target_args=dict(
95 target="_CM_KEY_VALUE"
96 )
97 )
98 )]],
99 }],
100
101 '_CM_KEY_INDEX' : [None, {
102 'Signature' : [None, ['String', dict(length=2)]],
103 'List' : [None, ["Array", dict(
104 count=lambda x: x.Count.v() * 2,
105 target="Pointer32",
106 target_args=dict(
107 target='_CM_KEY_NODE'
108 )
109 )]],
110 }],
111 }
112
113 -class _HMAP_ENTRY(obj.Struct):
114 """Windows uses this to track registry HBIN cells mapped into memory."""
115
116 @utils.safe_property
117 - def BlockAddress(self):
118 """Compatibility field for Windows 7 and Windows 10."""
119 if "BlockAddress" in self.members:
120 return self.m("BlockAddress")
121
122
123 return self.PermanentBinAddress & 0xfffffffffff0
124
129
132 """Translate between hive addresses and a flat file address space.
133
134 This is suitable for reading regular registry files. It should be
135 stacked over the FileAddressSpace.
136 """
142
143 - def vtop(self, vaddr):
145
146 @utils.safe_property
149
152 CI_TYPE_MASK = 0x80000000
153 CI_TYPE_SHIFT = 0x1F
154 CI_TABLE_MASK = 0x7FE00000
155 CI_TABLE_SHIFT = 0x15
156 CI_BLOCK_MASK = 0x1FF000
157 CI_BLOCK_SHIFT = 0x0C
158 CI_OFF_MASK = 0x0FFF
159 CI_OFF_SHIFT = 0x0
160
161 - def __init__(self, hive_addr=None, profile=None, **kwargs):
162 """Translate between hive addresses and virtual memory addresses.
163
164 This must be constructed over the kernel virtual memory.
165 Args:
166 hive_addr: The virtual address of the _CMHIVE object.
167 profile: A profile which holds registry symbols.
168 """
169 super(HiveAddressSpace, self).__init__(**kwargs)
170
171 self.as_assert(hive_addr, "Hive offset not provided.")
172 self.as_assert(self.base, "Must be layered on kernel address space.")
173 self.profile = RekallRegisteryImplementation(
174 profile or self.session.profile)
175
176 self.hive = self.profile._CMHIVE(offset=hive_addr, vm=self.base)
177 self.baseblock = self.hive.Hive.BaseBlock.v()
178 self.flat = self.hive.Hive.Flat.v() > 0
179 self.storage = self.hive.Hive.Storage
180
181
182 self.block_cache = utils.FastStore(max_size=1000)
183
184 self.logging = self.session.logging.getChild("addrspace.hive")
185
186 - def vtop(self, vaddr):
187 vaddr = int(vaddr)
188
189
190
191 if self.flat:
192 return self.baseblock + vaddr + self.BLOCK_SIZE + 4
193
194 ci_type = (vaddr & self.CI_TYPE_MASK) >> self.CI_TYPE_SHIFT
195 ci_table = (vaddr & self.CI_TABLE_MASK) >> self.CI_TABLE_SHIFT
196 ci_block = (vaddr & self.CI_BLOCK_MASK) >> self.CI_BLOCK_SHIFT
197 ci_off = (vaddr & self.CI_OFF_MASK) >> self.CI_OFF_SHIFT
198
199 try:
200 block = self.block_cache.Get((ci_type, ci_table, ci_block))
201 except KeyError:
202 block = self.storage[ci_type].Map.Directory[ci_table].Table[
203 ci_block].BlockAddress
204
205 self.block_cache.Put((ci_type, ci_table, ci_block), block)
206
207 return block + ci_off + 4
208
210 """A generator of registry data in linear form.
211
212 This can be used to write a registry file.
213
214 Yields:
215 blocks of data in order.
216 """
217 baseblock = self.base.read(self.baseblock, self.BLOCK_SIZE)
218 if baseblock:
219 yield baseblock
220 else:
221 yield "\0" * self.BLOCK_SIZE
222
223 last_paddr = 0
224 read_times = 0
225 length = self.hive.Hive.Storage[0].Length.v()
226 for i in xrange(0, length, self.BLOCK_SIZE):
227 paddr = self.vtop(i)
228 if not paddr:
229 self.logging.warn("No mapping found for index {0:x}, "
230 "filling with NULLs".format(i))
231 data = '\0' * self.BLOCK_SIZE
232 else:
233 paddr = paddr - 4
234 if paddr == last_paddr:
235 read_times += 1
236 else:
237 read_times = 0
238 last_paddr = paddr
239 data = self.base.read(paddr + self.BLOCK_SIZE*read_times, self.BLOCK_SIZE)
240 if not data:
241 self.logging.warn("Physical layer returned None for index "
242 "{0:x}, filling with NULL".format(i))
243 data = '\0' * self.BLOCK_SIZE
244
245 yield data
246
247 - def stats(self, stable=True):
248 if stable:
249 stor = 0
250 ci = lambda x: x
251 else:
252 stor = 1
253 ci = lambda x: x | 0x80000000
254
255 length = self.hive.Hive.Storage[stor].Length.v()
256 total_blocks = length / self.BLOCK_SIZE
257 bad_blocks_reg = 0
258 bad_blocks_mem = 0
259 for i in xrange(0, length, self.BLOCK_SIZE):
260 i = ci(i)
261 data = None
262 paddr = self.vtop(i) - 4
263
264 if paddr:
265 data = self.base.read(paddr, self.BLOCK_SIZE)
266 else:
267 bad_blocks_reg += 1
268 continue
269
270 if not data:
271 bad_blocks_mem += 1
272
273 print("{0} bytes in hive.".format(length))
274 print(("{0} blocks not loaded by CM, {1} blocks "
275 "paged out, {2} total blocks.".format(
276 bad_blocks_reg, bad_blocks_mem, total_blocks)))
277
278 if total_blocks:
279 print("Total of {0:.2f}% of hive unreadable.".format(
280 ((bad_blocks_reg + bad_blocks_mem) / float(total_blocks)
281 ) * 100))
282
283 return (bad_blocks_reg, bad_blocks_mem, total_blocks)
284
285 @utils.safe_property
287 return self.hive.Name
288
291 @utils.safe_property
293 name = "[no name]"
294 try:
295 name = (self.FileFullPath.v() or self.FileUserName.v() or
296 self.HiveRootPath.v())
297 except AttributeError:
298 pass
299
300 object_tree_plugin = self.obj_session.plugins.object_tree()
301
302 return u"{0} @ {1:#010x}".format(
303 object_tree_plugin.FileNameWithDrive(name) or "Unnamed",
304 self.obj_offset)
305
308 """A registry key."""
309 NK_SIG = "nk"
310 VK_SIG = "vk"
311
313 """Opens our direct child."""
314 for subkey in self.subkeys():
315 if unicode(subkey.Name).lower() == subkey_name.lower():
316 return subkey
317
318 return obj.NoneObject("Couldn't find subkey {0} of {1}",
319 subkey_name, self.Name)
320
322 """Opens our direct child."""
323 for value in self.values():
324 if value.Name == value_name:
325 return value
326
327 return obj.NoneObject("Couldn't find subkey {0} of {1}",
328 value_name, self.Name)
329
331 """Enumeate all subkeys of this key.
332
333 How are subkeys stored in each key record?
334
335 There are usually two subkey lists - these are pointers to _CM_KEY_INDEX
336 which are just a list of pointers to other subkeys.
337 """
338
339 sk_lists = self.SubKeyLists
340 for list_index, count in enumerate(self.SubKeyCounts):
341 if count > 0:
342 sk_offset = sk_lists[list_index]
343 for subkey in self.obj_profile._CM_KEY_INDEX(
344 offset=sk_offset, vm=self.obj_vm, parent=self):
345 yield subkey
346
348 """Enumerate all the values of the key."""
349 for value_ptr in self.ValueList.List.dereference():
350 value = value_ptr.dereference()
351 if value.Signature == self.VK_SIG:
352 yield value
353
354 @utils.safe_property
356 """Traverse our parent objects to print the full path of this key."""
357 path = []
358 key = self
359 while key:
360 try:
361 path.append(unicode(key.Name))
362 except AttributeError:
363 pass
364 key = key.obj_parent
365
366 return "/".join(reversed(path))
367
368 @utils.safe_property
370 """The name of the key is actually a unicode object.
371 This is encoded either in ascii or utf16 according to the Flags.
372 """
373 if self.Flags.KEY_COMP_NAME:
374 return self.obj_profile.String(
375 vm=self.obj_vm, offset=self.obj_profile.get_obj_offset(
376 self.obj_type, "Name") + self.obj_offset,
377 length=self.NameLength)
378 else:
379 return self.obj_profile.UnicodeString(
380 vm=self.obj_vm, offset=self.obj_profile.get_obj_offset(
381 self.obj_type, "Name") + self.obj_offset,
382 length=self.NameLength, encoding="utf-16")
383
387 """This is a list of pointers to key nodes.
388
389 This work different depending on the Signature.
390 """
391 LH_SIG = "lh"
392 LF_SIG = "lf"
393 RI_SIG = "ri"
394 LI_SIG = "li"
395 NK_SIG = "nk"
396
398 """Iterate over all the keys in the index.
399
400 Depending on our type (from the Signature) we use different methods."""
401 if self.Signature == self.LH_SIG or self.Signature == self.LF_SIG:
402
403
404
405 key_list = self.List
406 for i in xrange(self.Count * 2):
407 nk = key_list[i]
408 if nk.Signature == self.NK_SIG:
409 yield nk
410
411 elif self.Signature == self.RI_SIG:
412 for i in xrange(self.Count):
413
414 for subkey in self.obj_profile.Object(
415 "Pointer32", offset=self.List[i].v(),
416 vm=self.obj_vm, parent=self.obj_parent,
417 target="_CM_KEY_INDEX"):
418 if subkey.Signature == self.NK_SIG:
419 yield subkey
420
421 elif self.Signature == self.LI_SIG:
422 key_list = self.List
423 for i in xrange(self.Count):
424 nk = key_list[i]
425 if nk.Signature == self.NK_SIG:
426 yield nk
427
430 """A registry value."""
431
432 value_formats = {"REG_DWORD": "<L",
433 "REG_DWORD_BIG_ENDIAN": ">L",
434 "REG_QWORD": "<Q"}
435
436 @utils.safe_property
438 """Returns the data for this key decoded according to the type."""
439
440
441 if self.DataLength == 0x80000000:
442 return self.Type.v()
443
444
445 elif self.DataLength & 0x80000000:
446 return self._decode_data(self.obj_vm.read(
447 self.m("Data").obj_offset, self.DataLength & 0x7FFFFFFF))
448
449 elif self.DataLength > 0x4000:
450 return obj.NoneObject("Big data not supported.")
451
452 big_data = self.obj_profile._CM_BIG_DATA(
453 self.Data, vm=self.obj_vm)
454
455 return self._decode_data(big_data.Data)
456
457 else:
458 return self._decode_data(self.obj_vm.read(
459 int(self.m("Data")), self.DataLength))
460
462 """Decode the data according to our type."""
463 valtype = str(self.Type)
464
465 if valtype in ["REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_QWORD"]:
466 if len(data) != struct.calcsize(self.value_formats[valtype]):
467 return obj.NoneObject(
468 "Value data did not match the expected data "
469 "size for a {0}".format(valtype))
470
471 if valtype in ["REG_SZ", "REG_EXPAND_SZ", "REG_LINK"]:
472 data = data.decode('utf-16-le', "ignore")
473
474 elif valtype == "REG_MULTI_SZ":
475 data = data.decode('utf-16-le', "ignore").split('\0')
476
477 elif valtype in ["REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_QWORD"]:
478 data = struct.unpack(self.value_formats[valtype], data)[0]
479
480 return data
481
493
496 """A High level class to abstract access to the registry hive."""
497 ROOT_INDEX = 0x20
498 VK_SIG = "vk"
499
500 BIG_DATA_MAGIC = 0x3fd8
501
502 - def __init__(self, session=None, profile=None, address_space=None,
503 filename=None, stable=True):
532
533 @utils.safe_property
537
539 """Opens a key.
540
541 Args:
542 key: A string path to the key (separated with / or \\) or a list of
543 path components (useful if the keyname contains /).
544 """
545 if isinstance(key, basestring):
546
547 key = filter(None, re.split(r"[\\/]", key))
548
549 result = self.root
550 for component in key:
551 result = result.open_subkey(component)
552
553 return result
554
559
561 """Return the key for the CurrentControlSet."""
562 current = self.open_value("Select/Current").DecodedData
563 if not current:
564 current = 1
565
566 return self.open_key("ControlSet00%s" % current)
567
570 - def __init__(self, hive_offset=None, kernel_address_space=None,
571 profile=None,
572 session=None, **kwargs):
592
595 """A generic registry plugin."""
596
597 __abstract = True
598
599 @classmethod
600 - def args(cls, parser):
601 super(RegistryPlugin, cls).args(parser)
602
603 parser.add_argument(
604 "--hive-offsets", default=None, type="ArrayIntParser",
605 help="A list of hive offsets as found by hivelist. "
606 "If not provided we call hivelist ourselves and list "
607 "the keys on all hives.")
608
609 parser.add_argument("--hive_regex", default=None,
610 help="A regex to filter hive names."
611 "If not provided we use all hives.")
612
613
614 - def __init__(self, hive_offsets=None, hive_regex=None, **kwargs):
615 """Operate on in memory registry hives.
616
617 Args:
618 hive_offset: A list of hive offsets as found by hivelist (virtual
619 address). If not provided we call hivescan ourselves and list the
620 key on all hives.
621 """
622 super(RegistryPlugin, self).__init__(**kwargs)
623
624
625 RekallRegisteryImplementation(self.profile)
626
627 self.hive_offsets = hive_offsets
628 if not self.hive_offsets:
629 self.hive_offsets = list(self.list_hives())
630
631 if hive_regex is not None:
632 hive_offsets = []
633 for hive in self.hive_offsets:
634 m = re.search(hive_regex, utils.SmartUnicode(hive.Name), re.I)
635 if m:
636 hive_offsets.append(hive)
637
638 self.hive_offsets = hive_offsets
639
645
646
647 -class Hives(RegistryPlugin):
648 """List all the registry hives on the system."""
649
650 name = "hives"
651
658