Package rekall :: Package plugins :: Package addrspaces :: Module hibernate
[frames] | no frames]

Source Code for Module rekall.plugins.addrspaces.hibernate

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright (c) 2008 Volatile Systems 
  4  # Copyright (c) 2008 Brendan Dolan-Gavitt <bdolangavitt@wesleyan.edu> 
  5  # Copyright 2013 Google Inc. All Rights Reserved. 
  6  # 
  7  # This program is free software; you can redistribute it and/or modify 
  8  # it under the terms of the GNU General Public License as published by 
  9  # the Free Software Foundation; either version 2 of the License, or (at 
 10  # your option) any later version. 
 11  # 
 12  # This program is distributed in the hope that it will be useful, but 
 13  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 15  # General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License 
 18  # along with this program; if not, write to the Free Software 
 19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 20  # 
 21  # 
 22  # Code found in WindowsHiberFileSpace32 for parsing meta information 
 23  # is inspired by the work of Matthieu Suiche:  http://sandman.msuiche.net/. 
 24  # A special thanks to Matthieu for all his help integrating 
 25  # this code in Rekall Memory Forensics. 
 26   
 27  """ A Hiber file Address Space """ 
 28  from rekall import addrspace 
 29  from rekall import obj 
 30  from rekall import utils 
 31  from rekall.plugins.addrspaces import xpress 
 32  import struct 
 33   
 34   
 35  # pylint: disable=C0111 
 36   
 37  PAGE_SIZE = 0x1000 
 38  page_shift = 12 
39 40 41 -class HibernationSupport(obj.ProfileModification):
42 """Support hibernation file structures for different versions of windows.""" 43 44 vtypes = { 45 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x10, { 46 'NextTable' : [ 0x4, ['unsigned long']], 47 'EntryCount' : [ 0xc, ['unsigned long']], 48 } ], 49 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x10, { 50 'StartPage' : [ 0x4, ['unsigned long']], 51 'EndPage' : [ 0x8, ['unsigned long']], 52 } ], 53 '_PO_MEMORY_RANGE_ARRAY' : [ 0x20, { 54 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 55 'RangeTable': [ 0x10, ['array', lambda x: x.MemArrayLink.EntryCount, 56 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 57 } ], 58 59 '_IMAGE_XPRESS_HEADER' : [ 0x20 , { 60 'u09' : [ 0x9, ['unsigned char']], 61 'u0A' : [ 0xA, ['unsigned char']], 62 'u0B' : [ 0xB, ['unsigned char']], 63 } ] 64 } 65 66 vistasp01_vtypes = { 67 '_PO_MEMORY_RANGE_ARRAY' : [ 0x20, { 68 'RangeTable': [ 0x10, ['array', lambda x: x.Link.EntryCount, 69 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 70 } ], 71 } 72 73 vistasp2_vtypes = { 74 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x10, { 75 'NextTable' : [ 0x4, ['unsigned long']], 76 'EntryCount' : [ 0x8, ['unsigned long']], 77 } ], 78 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x8, { 79 'StartPage' : [ 0x0, ['unsigned long']], 80 'EndPage' : [ 0x4, ['unsigned long']], 81 } ], 82 '_PO_MEMORY_RANGE_ARRAY' : [ 0x20, { 83 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 84 'RangeTable': [ 0xc, ['array', lambda x: x.MemArrayLink.EntryCount, 85 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 86 } ], 87 } 88 89 win7_vtypes = { 90 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x10, { 91 'NextTable' : [ 0x0, ['unsigned long']], 92 'EntryCount' : [ 0x4, ['unsigned long']], 93 } ], 94 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x8, { 95 'StartPage' : [ 0x0, ['unsigned long']], 96 'EndPage' : [ 0x4, ['unsigned long']], 97 } ], 98 '_PO_MEMORY_RANGE_ARRAY' : [ 0x20, { 99 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 100 'RangeTable': [ 0x8, ['array', lambda x: x.MemArrayLink.EntryCount, 101 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 102 } ], 103 } 104 105 win7_x64_vtypes = { 106 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x10, { 107 'NextTable' : [ 0x0, ['unsigned long long']], 108 'EntryCount' : [ 0x8, ['unsigned long']], 109 } ], 110 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x10, { 111 'StartPage' : [ 0x0, ['unsigned long long']], 112 'EndPage' : [ 0x8, ['unsigned long long']], 113 } ], 114 '_PO_MEMORY_RANGE_ARRAY' : [ 0x20, { 115 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 116 'RangeTable': [ 0x10, ['array', lambda x: x.MemArrayLink.EntryCount, 117 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 118 } ], 119 } 120 121 x64_vtypes = { 122 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x20, { 123 'NextTable' : [ 0x8, ['unsigned long long']], 124 'EntryCount' : [ 0x14, ['unsigned long']], 125 } ], 126 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x20, { 127 'StartPage' : [ 0x8, ['unsigned long long']], 128 'EndPage' : [ 0x10, ['unsigned long long']], 129 } ], 130 '_PO_MEMORY_RANGE_ARRAY' : [ 0x40, { 131 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 132 'RangeTable': [ 0x20, ['array', lambda x: x.MemArrayLink.EntryCount, 133 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 134 } ], 135 } 136 137 vistaSP2_x64_vtypes = { 138 '_PO_MEMORY_RANGE_ARRAY_LINK' : [ 0x18, { 139 'NextTable' : [ 0x8, ['unsigned long long']], 140 'EntryCount' : [ 0x10, ['unsigned long']], 141 } ], 142 '_PO_MEMORY_RANGE_ARRAY_RANGE' : [ 0x10, { 143 'StartPage' : [ 0x0, ['unsigned long long']], 144 'EndPage' : [ 0x8, ['unsigned long long']], 145 } ], 146 '_PO_MEMORY_RANGE_ARRAY' : [ 0x28, { 147 'MemArrayLink' : [ 0x0, ['_PO_MEMORY_RANGE_ARRAY_LINK']], 148 'RangeTable': [ 0x18, ['array', lambda x: x.MemArrayLink.EntryCount, 149 ['_PO_MEMORY_RANGE_ARRAY_RANGE']]], 150 } ], 151 } 152 153 @classmethod
154 - def modify(cls, profile):
155 profile.add_overlay(cls.vtypes) 156 profile.add_constants(HibrProcPage=0x2, HibrEntryCount=0xff) 157 158 major = profile.metadata("major") 159 minor = profile.metadata("minor") 160 build = profile.metadata("build") 161 architecture = profile.metadata("arch") 162 163 if architecture == "I386": 164 if major == 6 and minor == 0: 165 if build < 6000: 166 profile.add_overlay(cls.vistasp01_vtypes) 167 168 elif build == 6000: 169 profile.add_overlay(cls.vistasp01_vtypes) 170 profile.add_constants(HibrProcPage=0x4, HibrEntryCount=0xff) 171 172 elif build == 6001: 173 profile.add_overlay(cls.vistasp01_vtypes) 174 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0xff) 175 176 elif build == 6002: 177 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0x1fe) 178 profile.add_overlay(cls.vistasp2_vtypes) 179 180 elif major == 6 and minor == 1: 181 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0x1ff) 182 183 if build <= 7601: 184 profile.add_overlay(cls.win7_vtypes) 185 186 elif architecture == "AMD64": 187 # Windows 2003 188 if major == 5 and minor == 2 and build <= 3790: 189 profile.add_constants(HibrProcPage=0x2, HibrEntryCount=0x7f) 190 profile.add_overlay(cls.x64_vtypes) 191 192 elif major == 6 and minor == 0: 193 if build <= 6000: 194 profile.add_constants(HibrProcPage=0x4, HibrEntryCount=0x7f) 195 profile.add_overlay(cls.x64_vtypes) 196 197 elif build == 6001: 198 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0x7f) 199 profile.add_overlay(cls.x64_vtypes) 200 201 elif build == 6002: 202 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0xfe) 203 profile.add_overlay(cls.vistaSP2_x64_vtypes) 204 205 elif major == 6 and minor == 1: 206 profile.add_constants(HibrProcPage=0x1, HibrEntryCount=0xff) 207 208 if build <= 7601: 209 profile.add_overlay(cls.win7_x64_vtypes)
210
211 212 -class WindowsHiberFileSpace(addrspace.BaseAddressSpace):
213 """ This is a hibernate address space for windows hibernation files. 214 215 In order for us to work we need to: 216 1) have a valid baseAddressSpace 217 2) the first 4 bytes must be 'hibr' 218 """ 219 220 __name = "hiber" 221 __image = True 222 223 order = 100 224
225 - def __init__(self, **kwargs):
226 self.as_assert(self.base == None, "No base Address Space") 227 self.as_assert(self.base.read(0, 4).lower() in ["hibr", "wake"]) 228 self.runs = [] 229 self.PageDict = {} 230 self.HighestPage = 0 231 self.PageIndex = 0 232 self.AddressList = [] 233 self.LookupCache = {} 234 self.PageCache = utils.FastStore(500) 235 self.MemRangeCnt = 0 236 self.offset = 0 237 self.entry_count = 0xFF 238 239 # Modify the profile by adding version specific definitions. 240 self.profile = HibernationSupport(self.profile) 241 242 # Extract header information 243 self.as_assert(self.profile.has_type("PO_MEMORY_IMAGE"), 244 "PO_MEMORY_IMAGE is not available in profile") 245 246 self.header = self.profile.Object('PO_MEMORY_IMAGE', offset=0, vm=self.base) 247 self.entry_count = self.profile.get_constant("HibrEntryCount") 248 249 proc_page = self.profile.get_constant("HibrProcPage") 250 251 # Check it's definitely a hibernation file 252 self.as_assert(self._get_first_table_page() is not None, 253 "No xpress signature found") 254 255 # Extract processor state 256 self.ProcState = self.profile.Object( 257 "_KPROCESSOR_STATE", offset=proc_page * 4096, vm=base) 258 259 ## This is a pointer to the page table - any ASs above us dont 260 ## need to search for it. 261 self.dtb = self.ProcState.SpecialRegisters.Cr3.v() 262 263 # This is a lengthy process, it was cached, but it may be best to delay this 264 # until it's absolutely necessary and/or convert it into a generator... 265 self.build_page_cache() 266 super(WindowsHiberFileSpace, self).__init__(**kwargs)
267
268 - def _get_first_table_page(self):
269 if self.header: 270 return self.header.FirstTablePage 271 272 for i in range(10): 273 if self.base.read(i * PAGE_SIZE, 8) == "\x81\x81xpress": 274 return i - 1
275
276 - def build_page_cache(self):
277 XpressIndex = 0 278 279 XpressHeader = self.profile.Object("_IMAGE_XPRESS_HEADER", 280 offset=(self._get_first_table_page() + 1) * 4096, 281 vm=self.base) 282 283 XpressBlockSize = self.get_xpress_block_size(XpressHeader) 284 285 MemoryArrayOffset = self._get_first_table_page() * 4096 286 287 while MemoryArrayOffset: 288 MemoryArray = self.profile.Object( 289 '_PO_MEMORY_RANGE_ARRAY', MemoryArrayOffset, self.base) 290 291 EntryCount = MemoryArray.MemArrayLink.EntryCount.v() 292 for i in MemoryArray.RangeTable: 293 start = i.StartPage.v() 294 end = i.EndPage.v() 295 LocalPageCnt = end - start 296 297 if end > self.HighestPage: 298 self.HighestPage = end 299 300 self.AddressList.append((start * 0x1000, # virtual address 301 start * 0x1000, # physical address 302 LocalPageCnt * 0x1000)) 303 304 for j in range(0, LocalPageCnt): 305 if (XpressIndex and ((XpressIndex % 0x10) == 0)): 306 XpressHeader, XpressBlockSize = \ 307 self.next_xpress(XpressHeader, XpressBlockSize) 308 309 PageNumber = start + j 310 XpressPage = XpressIndex % 0x10 311 if XpressHeader.obj_offset not in self.PageDict: 312 self.PageDict[XpressHeader.obj_offset] = [ 313 (PageNumber, XpressBlockSize, XpressPage)] 314 else: 315 self.PageDict[XpressHeader.obj_offset].append( 316 (PageNumber, XpressBlockSize, XpressPage)) 317 318 ## Update the lookup cache 319 self.LookupCache[PageNumber] = ( 320 XpressHeader.obj_offset, XpressBlockSize, XpressPage) 321 322 self.PageIndex += 1 323 XpressIndex += 1 324 325 NextTable = MemoryArray.MemArrayLink.NextTable.v() 326 327 # This entry count (EntryCount) should probably be calculated 328 if (NextTable and (EntryCount == self.entry_count)): 329 MemoryArrayOffset = NextTable * 0x1000 330 self.MemRangeCnt += 1 331 332 XpressHeader, XpressBlockSize = self.next_xpress( 333 XpressHeader, XpressBlockSize) 334 335 # Make sure the xpress block is after the Memory Table 336 while (XpressHeader.obj_offset < MemoryArrayOffset): 337 XpressHeader, XpressBlockSize = self.next_xpress( 338 XpressHeader, 0) 339 340 XpressIndex = 0 341 else: 342 MemoryArrayOffset = 0
343
344 - def convert_to_raw(self, ofile):
345 page_count = 0 346 for _i, xb in enumerate(self.PageDict.keys()): 347 size = self.PageDict[xb][0][1] 348 data_z = self.base.read(xb + 0x20, size) 349 if size == 0x10000: 350 data_uz = data_z 351 else: 352 data_uz = xpress.xpress_decode(data_z) 353 for page, size, offset in self.PageDict[xb]: 354 ofile.seek(page * 0x1000) 355 ofile.write(data_uz[offset * 0x1000:offset * 0x1000 + 0x1000]) 356 page_count += 1 357 del data_z, data_uz 358 yield page_count
359
360 - def next_xpress(self, XpressHeader, XpressBlockSize):
361 XpressHeaderOffset = int(XpressBlockSize) + XpressHeader.obj_offset + \ 362 XpressHeader.size() 363 364 ## We only search this far 365 BLOCKSIZE = 1024 366 original_offset = XpressHeaderOffset 367 while 1: 368 data = self.base.read(XpressHeaderOffset, BLOCKSIZE) 369 Magic_offset = data.find("\x81\x81xpress") 370 if Magic_offset >= 0: 371 XpressHeaderOffset += Magic_offset 372 break 373 374 else: 375 XpressHeaderOffset += len(data) 376 377 ## Only search this far in advance 378 if XpressHeaderOffset - original_offset > 10240: 379 return None, None 380 381 XpressHeader = self.profile.Object( 382 "_IMAGE_XPRESS_HEADER", XpressHeaderOffset, self.base) 383 XpressBlockSize = self.get_xpress_block_size(XpressHeader) 384 385 return XpressHeader, XpressBlockSize
386
387 - def get_xpress_block_size(self, xpress_header):
388 u0B = xpress_header.u0B.v() << 24 389 u0A = xpress_header.u0A.v() << 16 390 u09 = xpress_header.u09.v() << 8 391 392 Size = u0B + u0A + u09 393 Size = Size >> 10 394 Size = Size + 1 395 396 if ((Size % 8) == 0): 397 return Size 398 return (Size & ~7) + 8
399
400 - def get_header(self):
401 return self.header
402
403 - def get_base(self):
404 return self.base
405
406 - def get_signature(self):
407 return self.header.Signature
408
409 - def get_system_time(self):
410 return self.header.SystemTime
411
412 - def is_paging(self):
413 return (self.ProcState.SpecialRegisters.Cr0.v() >> 31) & 1
414
415 - def is_pse(self):
416 return (self.ProcState.SpecialRegisters.Cr4.v() >> 4) & 1
417
418 - def is_pae(self):
419 return (self.ProcState.SpecialRegisters.Cr4.v() >> 5) & 1
420
421 - def get_number_of_memranges(self):
422 return self.MemRangeCnt
423
424 - def get_number_of_pages(self):
425 return self.PageIndex
426
427 - def get_addr(self, addr):
428 page = addr >> page_shift 429 if page in self.LookupCache: 430 (hoffset, size, pageoffset) = self.LookupCache[page] 431 return hoffset, size, pageoffset 432 return None, None, None
433
434 - def get_block_offset(self, _xb, addr):
435 page = addr >> page_shift 436 if page in self.LookupCache: 437 (_hoffset, _size, pageoffset) = self.LookupCache[page] 438 return pageoffset 439 return None
440
441 - def is_valid_address(self, addr):
442 XpressHeaderOffset, _XpressBlockSize, _XpressPage = self.get_addr(addr) 443 return XpressHeaderOffset != None
444
445 - def read_xpress(self, baddr, BlockSize):
446 data_uz = self.PageCache.Get(baddr) 447 if data_uz is None: 448 data_read = self.base.read(baddr, BlockSize) 449 if BlockSize == 0x10000: 450 data_uz = data_read 451 else: 452 data_uz = xpress.xpress_decode(data_read) 453 454 self.PageCache.Put(baddr, data_uz) 455 456 return data_uz
457
458 - def fread(self, length):
459 data = self.read(self.offset, length) 460 self.offset += len(data) 461 return data
462
463 - def _partial_read(self, addr, len):
464 """ A function which reads as much as possible from the current page. 465 466 May return a short read. 467 """ 468 ## The offset within the page where we start 469 page_offset = (addr & 0x00000FFF) 470 471 ## How much data can we satisfy? 472 available = min(PAGE_SIZE - page_offset, len) 473 474 ImageXpressHeader, BlockSize, XpressPage = self.get_addr(addr) 475 if not ImageXpressHeader: 476 return None 477 478 baddr = ImageXpressHeader + 0x20 479 480 data = self.read_xpress(baddr, BlockSize) 481 482 ## Each block decompressed contains 2**page_shift pages. We 483 ## need to know which page to use here. 484 offset = XpressPage * 0x1000 + page_offset 485 486 return data[offset:offset + available]
487
488 - def read(self, addr, length):
489 result = '' 490 while length > 0: 491 data = self._partial_read(addr, length) 492 if not data: 493 break 494 495 addr += len(data) 496 length -= len(data) 497 result += data 498 499 if result == '': 500 result = obj.NoneObject("Unable to read data at %s for length %s." % ( 501 addr, length)) 502 503 return result
504
505 - def read_long(self, addr):
506 _baseaddr = self.get_addr(addr) 507 string = self.read(addr, 4) 508 if not string: 509 return obj.NoneObject("Could not read long at %s" % addr) 510 (longval,) = struct.unpack('=I', string) 511 return longval
512
513 - def get_available_pages(self):
514 page_list = [] 515 for _i, xb in enumerate(self.PageDict.keys()): 516 for page, _size, _offset in self.PageDict[xb]: 517 page_list.append([page * 0x1000, page * 0x1000, 0x1000]) 518 return page_list
519
520 - def get_address_range(self):
521 """ This relates to the logical address range that is indexable """ 522 size = self.HighestPage * 0x1000 + 0x1000 523 return [0, size]
524
525 - def check_address_range(self, addr):
526 memrange = self.get_address_range() 527 if addr < memrange[0] or addr > memrange[1]: 528 raise IOError
529
530 - def get_available_addresses(self):
531 """ This returns the ranges of valid addresses """ 532 for i in self.AddressList: 533 yield i
534
535 - def close(self):
536 self.base.close()
537