Package rekall :: Package plugins :: Package windows :: Package registry :: Module registry
[frames] | no frames]

Source Code for Module rekall.plugins.windows.registry.registry

  1  # Rekall Memory Forensics 
  2  # Copyright (c) 2012 Michael Cohen <scudette@gmail.com> 
  3  # Copyright (c) 2008 Brendan Dolan-Gavitt <bdolangavitt@wesleyan.edu> 
  4  # Copyright 2013 Google Inc. All Rights Reserved. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or (at 
  9  # your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 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  # pylint: disable=protected-access 
 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 # Windows 10 uses a different field. 123 return self.PermanentBinAddress & 0xfffffffffff0
124
125 126 -class HiveBaseAddressSpace(addrspace.PagedReader):
127 __abstract = True 128 BLOCK_SIZE = PAGE_SIZE = 0x1000
129
130 131 -class HiveFileAddressSpace(HiveBaseAddressSpace):
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 """
137 - def __init__(self, **kwargs):
138 super(HiveFileAddressSpace, self).__init__(**kwargs) 139 self.as_assert(self.base, "Must stack on top of a file.") 140 self.as_assert(self.base.read(0, 4) == "regf", "File does not look " 141 "like a registry file.")
142
143 - def vtop(self, vaddr):
144 return vaddr + self.PAGE_SIZE + 4
145 146 @utils.safe_property
147 - def Name(self):
148 return self.base
149
150 151 -class HiveAddressSpace(HiveBaseAddressSpace):
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 # This is a quick lookup for blocks. 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 # If the hive is listed as "flat", it is all contiguous in memory 190 # so we can just calculate it relative to the base block. 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
209 - def save(self):
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
286 - def Name(self):
287 return self.hive.Name
288
289 290 -class _CMHIVE(obj.Struct):
291 @utils.safe_property
292 - def Name(self):
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
306 307 -class _CM_KEY_NODE(obj.Struct):
308 """A registry key.""" 309 NK_SIG = "nk" 310 VK_SIG = "vk" 311
312 - def open_subkey(self, subkey_name):
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
321 - def open_value(self, value_name):
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
330 - def subkeys(self):
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 # There are multiple lists of subkeys: 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
347 - def values(self):
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
355 - def Path(self):
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
369 - def Name(self):
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
384 385 386 -class _CM_KEY_INDEX(obj.Struct):
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
397 - def __iter__(self):
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 # The List contains alternating pointers/hash elements here. We do 403 # not care about the hash at all, so we skip every other entry. See 404 # http://www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf 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 # This is a pointer to another _CM_KEY_INDEX 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
428 429 -class _CM_KEY_VALUE(obj.Struct):
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
437 - def DecodedData(self):
438 """Returns the data for this key decoded according to the type.""" 439 # When the data length is 0x80000000, the value is stored in the type 440 # (as a REG_DWORD). 441 if self.DataLength == 0x80000000: 442 return self.Type.v() 443 444 # If the high bit is set, the data is stored inline 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
461 - def _decode_data(self, data):
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
482 483 -def RekallRegisteryImplementation(profile):
484 """The standard rekall registry parsing subsystem.""" 485 profile.add_classes(dict( 486 _CM_KEY_NODE=_CM_KEY_NODE, _CM_KEY_INDEX=_CM_KEY_INDEX, 487 _CM_KEY_VALUE=_CM_KEY_VALUE, _CMHIVE=_CMHIVE, 488 _HMAP_ENTRY=_HMAP_ENTRY, 489 )) 490 491 profile.add_overlay(registry_overlays) 492 return profile
493
494 495 -class Registry(object):
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):
504 """Abstract a registry hive. 505 506 Args: 507 session: An optional session object. 508 profile: A profile to operate on. If not provided we use 509 session.profile. 510 address_space: An instance of the HiveBaseAddressSpace. 511 filename: If the registry exists in a file, specify the filename to 512 save instantiating the address_space. 513 stable: Should we try to open the unstable registry area? 514 """ 515 self.session = session 516 self.profile = RekallRegisteryImplementation( 517 profile or session.profile) 518 519 if filename is not None: 520 base_as = HiveFileAddressSpace.classes['FileAddressSpace']( 521 filename=filename, session=session) 522 address_space = HiveFileAddressSpace(base=base_as) 523 524 self.address_space = address_space 525 526 root_index = self.ROOT_INDEX 527 if not stable: 528 root_index = self.ROOT_INDEX | 0x80000000 529 530 self.root = self.profile.Object( 531 "_CM_KEY_NODE", offset=root_index, vm=address_space)
532 533 @utils.safe_property
534 - def Name(self):
535 """Return the name of the registry.""" 536 return self.address_space.Name
537
538 - def open_key(self, key=""):
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 # / can be part of the key name... 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
555 - def open_value(self, path):
556 key = self.open_key(ntpath.dirname(path)) 557 558 return key.open_value(ntpath.basename(path))
559
560 - def CurrentControlSet(self):
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
568 569 -class RegistryHive(Registry):
570 - def __init__(self, hive_offset=None, kernel_address_space=None, 571 profile=None, 572 session=None, **kwargs):
573 """A Registry hive instantiated from the hive offsets. 574 575 Args: 576 hive_offset: The virtual offset of the hive. 577 kernel_address_space: The kernel address space. 578 """ 579 if session: 580 profile = profile or session.profile 581 kernel_address_space = (kernel_address_space or 582 session.kernel_address_space) 583 584 hive_address_space = HiveAddressSpace(base=kernel_address_space, 585 hive_addr=hive_offset, 586 profile=profile, 587 session=session) 588 589 super(RegistryHive, self).__init__( 590 session=session, profile=profile, address_space=hive_address_space, 591 **kwargs)
592
593 594 -class RegistryPlugin(common.WindowsCommandPlugin):
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 # Install our specific implementation of registry support. 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
640 - def list_hives(self):
641 hive_list = self.profile.get_constant_object( 642 "CmpHiveListHead", "_LIST_ENTRY") 643 644 return hive_list.list_of_type("_CMHIVE", "HiveList")
645
646 647 -class Hives(RegistryPlugin):
648 """List all the registry hives on the system.""" 649 650 name = "hives" 651
652 - def render(self, renderer):
653 renderer.table_header([("Offset", "offset", "[addrpad]"), 654 ("Name", "name", "")]) 655 656 for hive in self.list_hives(): 657 renderer.table_row(hive, hive.Name)
658