Package rekall :: Package plugins :: Package overlays :: Package windows :: Module win7
[frames] | no frames]

Source Code for Module rekall.plugins.overlays.windows.win7

  1  # Rekall Memory Forensics 
  2  # Copyright (c) 2008-2011 Volatile Systems 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  # 
 19   
 20  """ 
 21  @author:       Bradley L Schatz 
 22  @license:      GNU General Public License 2.0 or later 
 23  @contact:      bradley@schatzforensic.com.au 
 24   
 25  This file provides support for windows Windows 7 SP 0. 
 26  """ 
 27   
 28  # pylint: disable=protected-access 
 29  from rekall import addrspace 
 30  from rekall import kb 
 31  from rekall import obj 
 32  from rekall.plugins.overlays.windows import common 
 33  from rekall_lib import utils 
34 35 36 -def TagOffset(x):
37 if x.obj_profile.metadata("arch") == "AMD64": 38 return x.obj_offset - 12 39 return x.obj_offset - 4
40 41 # In windows 7 the VadRoot is actually composed from _MMADDRESS_NODEs instead of 42 # _MMVAD structs. 43 win7_overlays = { 44 '_EPROCESS': [None, { 45 # A symbolic link to the real vad root. 46 'RealVadRoot': lambda x: x.VadRoot.BalancedRoot 47 }], 48 49 '_MMADDRESS_NODE': [None, { 50 'Tag': [TagOffset, ['String', dict(length=4)]], 51 }], 52 53 '_MMVAD_SHORT': [None, { 54 'Tag': [TagOffset, ['String', dict(length=4)]], 55 'Start': lambda x: x.StartingVpn << 12, 56 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1, 57 'Length': lambda x: x.End - x.Start + 1, 58 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge, 59 }], 60 61 '_MMVAD': [None, { 62 'Tag': [TagOffset, ['String', dict(length=4)]], 63 'ControlArea': lambda x: x.Subsection.ControlArea, 64 'Start': lambda x: x.StartingVpn << 12, 65 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1, 66 'Length': lambda x: x.End - x.Start + 1, 67 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge, 68 }], 69 70 '_MMVAD_LONG': [None, { 71 'Tag': [TagOffset, ['String', dict(length=4)]], 72 'ControlArea': lambda x: x.Subsection.ControlArea, 73 'Start': lambda x: x.StartingVpn << 12, 74 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1, 75 'Length': lambda x: x.End - x.Start + 1, 76 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge, 77 }], 78 79 "_CONTROL_AREA": [None, { 80 'FilePointer': [None, ['_EX_FAST_REF', dict( 81 target="_FILE_OBJECT" 82 )]], 83 }], 84 85 "_OBJECT_HEADER": [None, { 86 "InfoMask": [None, ["Flags", dict( 87 maskmap=utils.Invert({ 88 0x01: "CreatorInfo", 89 0x2: "NameInfo", 90 0x4: "HandleInfo", 91 0x8: "QuotaInfo", 92 0x10: "ProcessInfo", 93 0x20: "AuditInfo", 94 0x40: "PaddingInfo", 95 }), 96 target="unsigned char", 97 )]], 98 }], 99 100 '_MM_SESSION_SPACE': [None, { 101 # Specialized iterator to produce all the _IMAGE_ENTRY_IN_SESSION 102 # records. 103 'ImageIterator': lambda x: x.ImageList.list_of_type( 104 "_IMAGE_ENTRY_IN_SESSION", "Link") 105 }], 106 107 '_IMAGE_ENTRY_IN_SESSION': [None, { 108 'ImageBase': lambda x: x.Address.v() & ~7 109 }], 110 }
111 112 113 -class _OBJECT_HEADER(common._OBJECT_HEADER):
114 """A Rekall Memory Forensics object to handle Windows 7 object headers. 115 116 Windows 7 changes the way objects are handled: 117 References: http://www.codemachine.com/article_objectheader.html 118 119 The headers look like this: 120 121 _POOL_HEADER 122 123 # These are optional headers: 124 125 _OBJECT_HEADER_PROCESS_INFO 126 _OBJECT_HEADER_QUOTA_INFO 127 _OBJECT_HEADER_HANDLE_INFO 128 129 _OBJECT_HEADER: 130 ..... 131 InfoMask 132 .... 133 134 When the object manager wants to access a specific optional header, it can 135 use the constant lookup table nt!ObpInfoMaskToOffset to quickly calculate 136 the offset of that header (The headers always appear in the same order): 137 138 table = profile.get_constant_object( 139 "ObpInfoMaskToOffset", 140 target="Array", 141 target_args=dict( 142 target="byte" 143 count=0x80 144 ) 145 ) 146 147 option_header_offset = table[ 148 OBJECT_HEADER->InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))] 149 """ 150 151 # This specifies the order the headers are found below the 152 # _OBJECT_HEADER. It is obtained using "nt!ObpInfoMaskToOffset" which is a 153 # lookup table. 154 optional_header_mask = ( 155 ('CreatorInfo', '_OBJECT_HEADER_CREATOR_INFO', 0x01), 156 ('NameInfo', '_OBJECT_HEADER_NAME_INFO', 0x02), 157 ('HandleInfo', '_OBJECT_HEADER_HANDLE_INFO', 0x04), 158 ('QuotaInfo', '_OBJECT_HEADER_QUOTA_INFO', 0x08), 159 ('ProcessInfo', '_OBJECT_HEADER_PROCESS_INFO', 0x10), 160 ('AuditInfo', '_OBJECT_HEADER_AUDIT_INFO', 0x20), 161 ('PaddingInfo', '_OBJECT_HEADER_PADDING_INFO', 0x40), 162 ) 163
164 - def _GetOptionalHeader(self, struct_name, desired_bit):
165 if not self.InfoMask & desired_bit: 166 return obj.NoneObject("Header not set") 167 168 lookup = self.obj_session.GetParameter("ObpInfoMaskToOffset") 169 offset = lookup[self.InfoMask & (desired_bit | (desired_bit - 1))] 170 return self.obj_profile.Object( 171 struct_name, offset=self.obj_offset - offset, 172 vm=self.obj_vm, parent=self)
173
174 - def get_object_type(self, vm=None):
175 """Return the object's type as a string.""" 176 return self.obj_session.GetParameter("ObjectTypeMap")[ 177 self.TypeIndex].Name.v()
178 179 @utils.safe_property
180 - def TypeIndex(self):
181 """In windows 10 the type index is obfuscated. 182 Windows 10 obfuscates the object type using a cookie: 183 184 ------ nt!ObpRemoveObjectRoutine ------: 0xf801a628e7e0 185 0xf801a628e7e0 MOV [RSP+0x10], RBX 186 0xf801a628e7e5 MOV [RSP+0x18], RBP 187 0xf801a628e7ea MOV [RSP+0x20], RSI 188 0xf801a628e7ef PUSH RDI 189 0xf801a628e7f0 SUB RSP, 0x50 190 0xf801a628e7f4 MOV RBX, RCX // RCX is object header. 191 0xf801a628e7f7 LEA RDI, [RIP-0x48f1e] 0x0 nt!ObTypeIndexTable 192 0xf801a628e7fe MOV RAX, RCX 193 0xf801a628e801 MOVZX ESI, DL 194 0xf801a628e804 SHR RAX, 0x8 // Shift address by 8 195 0xf801a628e808 MOVZX ECX, AL 196 0xf801a628e80b MOVZX EAX, BYTE [RBX+0x18] // _OBJECT_HEADER.TypeIndex 197 0xf801a628e80f XOR RCX, RAX // XOR with object type 198 0xf801a628e812 MOVZX EAX, BYTE [RIP-0x493ed] 0x1dd4015af55 nt!ObHeaderCookie 199 0xf801a628e819 XOR RCX, RAX // XOR with cookie 200 0xf801a628e81c MOV RDI, [RDI+RCX*8] // Dereference table. 201 """ 202 cookie = self.obj_profile.get_constant_object( 203 "ObHeaderCookie", target="byte").v() 204 205 # Windows 7 has no cookie. 206 if cookie == None: 207 return self.m("TypeIndex") 208 209 # Windows 10 xors the virtual address into this field so we need to use 210 # the virtual address to decode it. 211 212 # We are operating on the physical address space. We need to find the 213 # virtual address. 214 if self.obj_vm.metadata("image"): 215 # Resolve the virtual address for this physical address. 216 resolver = self.obj_session.GetParameter( 217 "physical_address_resolver") 218 219 vaddr, _ = resolver.PA2VA_for_DTB( 220 self.obj_offset, 221 self.obj_session.GetParameter("dtb"), 222 userspace=False) 223 224 # This hit does not exist in the kernel Address Space. 225 if vaddr is None: 226 return 0 227 else: 228 vaddr = self.obj_offset 229 230 return ((vaddr >> 8) ^ cookie ^ int(self.m("TypeIndex"))) & 0xFF
231
232 - def is_valid(self):
233 """Determine if the object makes sense.""" 234 # These need to be reasonable. 235 pointer_count = int(self.PointerCount) 236 if pointer_count > 0x100000 or pointer_count < 0: 237 return False 238 239 handle_count = int(self.HandleCount) 240 if handle_count > 0x1000 or handle_count < 0: 241 return False 242 243 # Must be one of the types revealed by the object_types plugins. 244 if self.TypeIndex >= 50 or self.TypeIndex < 1: 245 return False 246 247 return True
248 249 # Build properties for the optional headers 250 for _name, _y, _z in _OBJECT_HEADER.optional_header_mask: 251 setattr(_OBJECT_HEADER, _name, property( 252 lambda x, y=_y, z=_z: x._GetOptionalHeader(y, z)))
253 254 255 -class _MMADDRESS_NODE(common.VadTraverser):
256 """In win7 the base of all Vad objects is _MMADDRESS_NODE. 257 258 The Vad structures can be either _MMVAD_SHORT or _MMVAD or _MMVAD_LONG. At 259 the base of each struct there is an _MMADDRESS_NODE which contains the 260 LeftChild and RightChild members. In order to traverse the tree, we follow 261 the _MMADDRESS_NODE and create the required _MMVAD type at each point 262 depending on their tags. 263 """ 264 265 ## The actual type depends on this tag value. 266 tag_map = {'Vadl': '_MMVAD_LONG', 267 'VadS': '_MMVAD_SHORT', 268 'Vad ': '_MMVAD', 269 'VadF': '_MMVAD_SHORT', 270 'Vadm': '_MMVAD_LONG', 271 }
272
273 274 -class _POOL_HEADER(common._POOL_HEADER):
275 """A class for pool headers""" 276 277 MAX_PREAMBLE_SIZE = 0x50 278 279 @utils.safe_property
280 - def NonPagedPool(self):
281 return self.PoolType.v() % 2 == 0 and self.PoolType.v() > 0
282 283 @utils.safe_property
284 - def PagedPool(self):
285 return self.PoolType.v() % 2 == 1
286 287 @utils.safe_property
288 - def FreePool(self):
289 return self.PoolType.v() == 0
290 291 # A class cached version of the lookup map. This is mutable and shared 292 # between all instances. 293 lookup = {} 294
295 - def _BuildLookupTable(self):
296 """Create a fast lookup table mapping InfoMask -> minimum_offset. 297 298 We are interested in the maximum distance between the _POOL_HEADER and 299 _OBJECT_HEADER. This is dictated by the InfoMask field. Here we build a 300 quick lookup table between the InfoMask field and the offset of the 301 first optional header. 302 """ 303 ObpInfoMaskToOffset = self.obj_session.GetParameter( 304 "ObpInfoMaskToOffset") 305 306 self.lookup["\x00"] = 0 307 308 # Iterate over all the possible InfoMask values (Bytes can take on 256 309 # values). 310 for i in range(0x100): 311 # Locate the largest offset from the start of 312 # _OBJECT_HEADER. Starting with the largest bit position 1 << 7. 313 bit_position = 0x80 314 while bit_position > 0: 315 # This is the optional header with the largest offset. 316 if bit_position & i: 317 self.lookup[chr(i)] = ObpInfoMaskToOffset[ 318 i & (bit_position | (bit_position - 1))] 319 320 break 321 bit_position >>= 1
322
323 - def IterObject(self, type=None, freed=True):
324 """Generates possible _OBJECT_HEADER accounting for optional headers. 325 326 Note that not all pool allocations have an _OBJECT_HEADER - only ones 327 allocated from the the object manager. This means calling this method 328 depends on which pool allocation you are after. 329 330 On windows 8, pool allocations are done from preset sizes. This means 331 that the allocation is never exactly the same size and we can not use 332 the bottom up method like before. 333 334 We therefore, have to build the headers forward by checking the preamble 335 size and validity of each object. This is a little slower than with 336 earlier versions of windows. 337 338 Args: 339 type: The object type name. If not specified we return all objects. 340 """ 341 pool_align = self.obj_profile.get_constant("PoolAlignment") 342 allocation_size = self.BlockSize * pool_align 343 344 # Operate on a cached version of the next page. 345 # We use a temporary buffer for the object to save reads of the image. 346 start = self.obj_end 347 cached_data = self.obj_vm.read(start, allocation_size) 348 cached_vm = addrspace.BufferAddressSpace( 349 base_offset=start, data=cached_data, session=self.obj_session) 350 351 # We search for the _OBJECT_HEADER.InfoMask in close proximity to our 352 # object. We build a lookup table between the values in the InfoMask and 353 # the minimum distance there is between the start of _OBJECT_HEADER and 354 # the end of _POOL_HEADER. This way we can quickly skip unreasonable 355 # values. 356 357 # This is the offset within _OBJECT_HEADER of InfoMask. 358 info_mask_offset = self.obj_profile.get_obj_offset( 359 "_OBJECT_HEADER", "InfoMask") 360 361 # Build the cache if needed. 362 if not self.lookup: 363 self._BuildLookupTable() 364 365 # Walk over all positions in the address space and try to fit an object 366 # header there. 367 for i in utils.xrange(start, 368 start + allocation_size - info_mask_offset, 369 pool_align): 370 possible_info_mask = cached_data[i - start + info_mask_offset] 371 #if possible_info_mask > '\x7f': 372 # continue 373 374 # The minimum amount of space needed before the object header to 375 # hold all the optional headers. 376 minimum_offset = self.lookup[possible_info_mask] 377 378 # Obviously wrong because we need more space than we have. 379 if minimum_offset > i - start: 380 continue 381 382 # Create a test object header from the cached vm to test for 383 # validity. 384 test_object = self.obj_profile._OBJECT_HEADER( 385 offset=i, vm=cached_vm) 386 387 if test_object.is_valid(): 388 if (type is None or 389 test_object.get_object_type() == type or 390 # Freed objects point to index 1 391 #(which is also 0xbad0b0b0). 392 (freed and test_object.TypeIndex <= 2)): 393 yield test_object
394
395 396 -class ObjectTypeMapHook(kb.ParameterHook):
397 """Get and cache the object type map. 398 399 In windows 7, rather than store a pointer to the _OBJECT_TYPE object 400 directly, there is a global table of object types, and the object simply 401 stores an index to it. 402 """ 403 name = "ObjectTypeMap" 404
405 - def calculate(self):
406 return self.session.profile.get_constant_object( 407 "ObTypeIndexTable", 408 target="Array", 409 target_args=dict( 410 target="Pointer", 411 target_args=dict( 412 target="_OBJECT_TYPE" 413 ) 414 ) 415 )
416
417 418 -def InitializeWindows7Profile(profile):
419 profile.add_overlay(win7_overlays) 420 profile.add_classes( 421 _OBJECT_HEADER=_OBJECT_HEADER, 422 _MMADDRESS_NODE=_MMADDRESS_NODE, 423 _POOL_HEADER=_POOL_HEADER, 424 )
425