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

Source Code for Module rekall.plugins.windows.pagefile

  1  # Rekall Memory Forensics 
  2  # Copyright 2014 Google Inc. All Rights Reserved. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or (at 
  7  # your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, but 
 10  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 12  # General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program; if not, write to the Free Software 
 16  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 17  # 
 18  """This file adds pagefile support. 
 19   
 20  Although much of the address translation machinery occurs in hardware, when a 
 21  page fault occurs the operating system's pager is called. The pager is 
 22  responsible for faulting in invalid pages, and hence we need operating system 
 23  specific support. 
 24   
 25  Rekall's base paging address spaces emulate the hardware's MMU page translation, 
 26  but when the page is invalid Rekall emulates the operating system's page fault 
 27  handling code. The correct (OS dependent) address space is selected in 
 28  rekall.plugins.core.FindDTB.GetAddressSpaceImplementation() based on the profile 
 29  metadata. 
 30   
 31  This file implements the algorithms described in the paper: 
 32   
 33  Forensic Analysis of Windows User space Applications through Heap allocations. 
 34  Michael Cohen, 3rd IEEE International Workshop on Security and Forensics in 
 35  Communication Systems 2015 [1] 
 36   
 37  http://www.rekall-forensic.com/docs/References/Papers/p1138-cohen.pdf 
 38  """ 
 39   
 40  __author__ = "Michael Cohen <scudette@google.com>" 
 41  import struct 
 42   
 43  from rekall import addrspace 
 44  from rekall import obj 
 45  from rekall.plugins.addrspaces import amd64 
 46  from rekall.plugins.addrspaces import intel 
 47  from rekall.plugins.windows import address_resolver 
 48  from rekall.plugins.windows import common 
 49  from rekall_lib import utils 
50 51 # pylint: disable=protected-access 52 53 54 -def Reentrant(func):
55 def Wrapper(self, *args, **kwargs): 56 lock = "_lock" + func.__name__ 57 if not getattr(self, lock, False): 58 try: 59 setattr(self, lock, True) 60 return func(self, *args, **kwargs) 61 except RuntimeError: 62 pass 63 finally: 64 setattr(self, lock, False)
65 66 return Wrapper 67
68 69 # Windows has some special steps in its address translation. We define some 70 # windows specific descriptors here. 71 -class WindowsPTEDescriptor(intel.AddressTranslationDescriptor):
72 """Print the PTE in exploded view.""" 73 74 default_pte_type = None 75 object_name = "pte" 76
77 - def __init__(self, pte_type=None, pte_value=None, pte_addr=None, 78 object_name=None, session=None):
79 """Define a windows PTE object. 80 81 Valid PTE types are all the members inside the _MMPTE 82 union. e.g. "Hard", "Transition", "Soft", etc). 83 """ 84 super(WindowsPTEDescriptor, self).__init__( 85 object_name=self.object_name, object_value=pte_value, 86 object_address=pte_addr, session=session) 87 self.pte_type = pte_type or self.default_pte_type 88 if object_name is not None: 89 self.object_name = object_name
90
91 - def render(self, renderer):
92 super(WindowsPTEDescriptor, self).render(renderer) 93 94 pte = self.session.profile._MMPTE() 95 pte.u.Long = int(self.object_value) 96 if self.pte_type: 97 specific_pte = pte.u.m(self.pte_type) 98 99 self.session.plugins.dt( 100 specific_pte, offset=self.object_address).render(renderer)
101
102 103 -class WindowsPDEDescriptor(WindowsPTEDescriptor):
104 object_name = "pde"
105
106 107 -class WindowsProtoTypePTEDescriptor(WindowsPTEDescriptor):
108 default_pte_type = "Proto"
109
110 111 -class WindowsSoftwarePTEDescriptor(WindowsPTEDescriptor):
112 default_pte_type = "Soft"
113
114 115 116 -class DemandZeroDescriptor(intel.AddressTranslationDescriptor):
117 """Describe a Demand Zero page.""" 118
119 - def render(self, renderer):
120 renderer.format("Demand Zero")
121
122 123 124 -class WindowsValidPTEDescriptor(WindowsPTEDescriptor):
125 """A descriptor for Valid or in Transition PTEs.""" 126
127 - def __init__(self, **kwargs):
128 super(WindowsValidPTEDescriptor, self).__init__(**kwargs) 129 if self.object_value & 1: 130 self.pte_type = "Hard" 131 else: 132 self.pte_type = "Trans"
133
134 135 -class WindowsPagefileDescriptor(intel.AddressTranslationDescriptor):
136 """A descriptor to mark the final physical address resolution.""" 137
138 - def __init__(self, address=0, pagefile_number=0, protection=0, 139 session=None):
140 super(WindowsPagefileDescriptor, self).__init__(session=session) 141 self.address = address 142 self.pagefile_number = pagefile_number 143 self.protection = protection
144
145 - def render(self, renderer):
146 renderer.format("Pagefile ({0}) @ {1:addr}\n", 147 self.pagefile_number, self.address)
148
149 150 -class WindowsFileMappingDescriptor(intel.AddressTranslationDescriptor):
151 """Describe a file mapping.""" 152
153 - def __init__(self, pte_address=None, page_offset=0, 154 original_pte=None, **kwargs):
155 super(WindowsFileMappingDescriptor, self).__init__( 156 object_address=pte_address, **kwargs) 157 self.pte_address = pte_address 158 self.page_offset = page_offset 159 self.original_pte = original_pte
160
161 - def get_subsection(self):
162 """Find the right subsection object for this pte.""" 163 # Try to find the subsection by looking it up from the list of known 164 # subsections. 165 subsection_lookup = self.session.GetParameter( 166 "prototype_pte_array_subsection_lookup") 167 168 start, _, subsection_offset = subsection_lookup.get_containing_range( 169 self.pte_address) 170 171 if start: 172 return self.session.profile._SUBSECTION(subsection_offset) 173 174 if self.original_pte is not None: 175 return self.original_pte.u.Subsect.Subsection
176
177 - def filename_and_offset(self, subsection=None):
178 """Return the filename of the file mapped (if it is a file mapping).""" 179 if subsection is None: 180 subsection = self.get_subsection() 181 182 if subsection: 183 # File mapping. 184 ca = subsection.ControlArea 185 186 # First PTE in subsection. 187 start = subsection.SubsectionBase.v() 188 189 if ca.u.Flags.File: 190 size_of_pte = self.session.profile.get_obj_size("_MMPTE") 191 mapped_offset_in_file = 0x1000 * ( 192 self.pte_address - start) / size_of_pte + ( 193 subsection.StartingSector * 512) 194 195 return (ca.FilePointer.file_name_with_drive(), 196 mapped_offset_in_file + self.page_offset) 197 198 return None, None
199
200 - def get_owners(self, subsection=None):
201 """Returns a list of _EPROCESS, virtual offsets for owners.""" 202 result = [] 203 if subsection is None: 204 subsection = self.get_subsection() 205 206 if subsection: 207 for details in self.session.GetParameter("subsections").get( 208 subsection.obj_offset, []): 209 task = self.session.profile._EPROCESS(details["task"]) 210 vad = self.session.profile.Object(offset=details["vad"], 211 type_name=details["type"]) 212 213 # Find the virtual address. 214 size_of_pte = self.session.profile.get_obj_size("_MMPTE") 215 relative_offset = ( 216 self.pte_address - vad.FirstPrototypePte.v()) / size_of_pte 217 218 virtual_address = ( 219 relative_offset * 0x1000 + vad.Start + self.page_offset) 220 221 result.append((task, virtual_address)) 222 223 return result
224
225 - def render(self, renderer):
226 subsection = self.get_subsection() 227 filename, offset = self.filename_and_offset(subsection) 228 if filename: 229 renderer.format("File Mapping ({0} @ {1:#x} \n", filename, offset) 230 231 # Private mapping. 232 else: 233 renderer.format( 234 "Private Mapping by {0}\n", 235 subsection.ControlArea.Segment.u1.CreatingProcess.deref()) 236 237 for task, virtual_address in self.get_owners(subsection=subsection): 238 renderer.format( 239 "Mapped in {0} @ {1:#x}\n", task, virtual_address)
240
241 242 -class WindowsSubsectionPTEDescriptor(WindowsPTEDescriptor):
243 """A descriptor for a subsection PTE.""" 244
245 - def metadata(self):
246 pte = self.session.profile._MMPTE(self.object_address) 247 subsection = pte.u.Subsect.Subsection 248 249 # Calculate the file offset. The SubsectionBase is an array pointer 250 # to the linear arrays of section PTEs (one per file sector). 251 file_offset = ( 252 (pte - subsection.SubsectionBase) * 0x1000 / pte.obj_size + 253 subsection.StartingSector * 512) 254 255 return dict( 256 type="File Mapping", 257 filename=subsection.ControlArea.FilePointer.file_name_with_drive(), 258 offset=file_offset)
259
260 - def render(self, renderer):
261 pte = self.session.profile._MMPTE() 262 pte.u.Long = int(self.object_value) 263 specific_pte = pte.u.Subsect 264 265 self.session.plugins.dt( 266 specific_pte, offset=self.object_address).render(renderer) 267 268 metadata = self.metadata() 269 renderer.format("Subsection PTE to file {0} @ {1:addr}\n", 270 metadata["filename"], metadata["offset"])
271
272 273 -class VadPteDescriptor(WindowsPTEDescriptor):
274 """A descriptor which applies specifically for Prototype PTEs from the VAD. 275 276 Windows uses placeholder values in the PTE to trigger a further resolution 277 of the PTE from the VAD. For example a PTE of 0xffffffff00000420 would 278 signal to consult the VAD for the real status of this PTE. 279 """ 280
281 - def __init__(self, virtual_address=None, **kwargs):
282 """Define a windows PTE object. 283 284 Valid PTE types are all the members inside the _MMPTE 285 union. e.g. "Hard", "Transition", "Soft", etc). 286 """ 287 super(VadPteDescriptor, self).__init__(pte_type="Soft", **kwargs) 288 self.virtual_address = virtual_address
289
290 - def render(self, renderer):
291 renderer.format("Prototype PTE is found in VAD\n") 292 task = self.session.GetParameter("process_context") 293 294 # Show the VAD region for the virtual address. 295 vad_plugin = self.session.plugins.vad( 296 eprocess=task, offset=self.virtual_address) 297 vad_plugin.render(renderer) 298 299 resolver = self.session.address_resolver 300 module = resolver.GetContainingModule(self.virtual_address) 301 if isinstance(module, address_resolver.VadModule): 302 mmvad = module.vad 303 304 # The MMVAD does not have any prototypes. 305 if mmvad.m("FirstPrototypePte").deref() == None: 306 renderer.format("Demand Zero page\n") 307 308 else: 309 renderer.format("\n_MMVAD.FirstPrototypePte: {0:#x}\n", 310 mmvad.FirstPrototypePte) 311 pte = mmvad.FirstPrototypePte[ 312 (self.virtual_address - module.start) >> 12] 313 314 renderer.format( 315 "Prototype PTE is at virtual address {0:#x} " 316 "(Physical Address {1:#x})\n", pte, 317 pte.obj_vm.vtop(pte.obj_offset)) 318 else: 319 renderer.format("Demand Zero page\n")
320
321 322 -class WindowsDTBDescriptor(intel.AddressTranslationDescriptor):
323 """A descriptor for DTB values. 324 325 On windows the DTB holds a reference to the _EPROCESS that owns it. This 326 descriptor prints this information too. 327 """ 328 object_name = "DTB" 329
330 - def __init__(self, dtb, **kwargs):
331 super(WindowsDTBDescriptor, self).__init__(object_value=dtb, **kwargs) 332 self.dtb = dtb
333
334 - def owner(self):
335 pfn_database = self.session.profile.get_constant_object("MmPfnDatabase") 336 pfn_obj = pfn_database[self.dtb >> 12] 337 return pfn_obj.u1.Flink.cast("Pointer", target="_EPROCESS").deref()
338
339 - def render(self, renderer):
340 renderer.format("DTB {0:#x} ", self.dtb) 341 owning_process = self.owner() 342 343 if owning_process != None: 344 renderer.format("Owning process: {0}", owning_process) 345 346 renderer.format("\n")
347
348 349 -class WindowsPagedMemoryMixin(object):
350 351 """A mixin to implement windows specific paged memory address spaces. 352 353 This mixin allows us to share code between 32 and 64 bit implementations. 354 """ 355
356 - def __init__(self, **kwargs):
357 super(WindowsPagedMemoryMixin, self).__init__(**kwargs) 358 359 # This is the offset at which the pagefile is mapped into the physical 360 # address space. 361 self._resolve_vads = True 362 self._vad = None 363 364 # We cache these bitfields in order to speed up mask calculations. We 365 # derive them initially from the profile so we do not need to hardcode 366 # any bit positions. 367 pte = self.session.profile._MMPTE() 368 self.prototype_mask = pte.u.Proto.Prototype.mask 369 self.transition_mask = pte.u.Trans.Transition.mask 370 self.valid_mask = pte.u.Hard.Valid.mask 371 self.proto_protoaddress_mask = pte.u.Proto.ProtoAddress.mask 372 self.proto_protoaddress_start = pte.u.Proto.ProtoAddress.start_bit 373 self.soft_pagefilehigh_mask = pte.u.Soft.PageFileHigh.mask 374 375 # Combined masks for faster checking. 376 self.proto_transition_mask = self.prototype_mask | self.transition_mask 377 self.proto_transition_valid_mask = (self.proto_transition_mask | 378 self.valid_mask) 379 self.transition_valid_mask = self.transition_mask | self.valid_mask 380 self.task = None 381 382 self.base_as_can_map_files = self.base.metadata("can_map_files") 383 384 # A Guard flag for protecting against re-entrancy when resolving the 385 # pagefiles. 386 self._resolving_pagefiles = False
387 388 @utils.safe_property
389 - def vad(self):
390 """Returns a cached RangedCollection() of vad ranges.""" 391 392 # If this dtb is the same as the kernel dtb - there are no vads. 393 if self.dtb == self.session.GetParameter("dtb"): 394 return 395 396 # If it is already cached, just return that. 397 if self._vad is not None: 398 return self._vad 399 400 # We can not run plugins in recursive context. 401 if not self._resolve_vads: 402 return obj.NoneObject("vads not available right now") 403 404 try: 405 # Prevent recursively calling ourselves. We might resolve Prototype 406 # PTEs which end up calling plugins (like the VAD plugin) which 407 # might recursively translate another Vad Prototype address. This 408 # safety below ensures we cant get into infinite recursion by 409 # failing more complex PTE resolution on recursive calls. 410 self._resolve_vads = False 411 412 # Try to map the dtb to a task struct so we can look up the vads. 413 if self.task == None: 414 # Find the _EPROCESS for this dtb - we need to consult the VAD 415 # for some of the address transition. 416 self.task = self.session.GetParameter("dtb2task").get(self.dtb) 417 418 self._vad = utils.RangedCollection() 419 task = self.session.profile._EPROCESS(self.task) 420 for vad in task.RealVadRoot.traverse(): 421 self._vad.insert(vad.Start, vad.End, vad) 422 423 return self._vad 424 finally: 425 self._resolve_vads = True
426
427 - def _get_available_PTEs(self, pte_table, vaddr, start=0, end=2**64):
428 """Scan the PTE table and yield address ranges which are valid.""" 429 tmp = vaddr 430 for i in xrange(0, len(pte_table)): 431 pfn = i << 12 432 pte_value = pte_table[i] 433 434 vaddr = tmp | pfn 435 if vaddr > end: 436 return 437 438 next_vaddr = tmp | ((i + 1) << 12) 439 if start >= next_vaddr: 440 continue 441 442 # A PTE value of 0 means to consult the vad, but the vad shows no 443 # mapping at this virtual address, so we can just skip this PTE in 444 # the iteration. 445 if self.vad: 446 start, _, _ = self.vad.get_containing_range(vaddr) 447 if pte_value == 0 and start is None: 448 continue 449 450 elif pte_value == 0: 451 continue 452 453 phys_addr = self._get_phys_addr_from_pte(vaddr, pte_value) 454 455 # Only yield valid physical addresses. This will skip DemandZero 456 # pages and File mappings into the filesystem. 457 if phys_addr is not None: 458 yield addrspace.Run(start=vaddr, 459 end=vaddr + 0x1000, 460 file_offset=phys_addr, 461 address_space=self.base)
462
463 - def _get_phys_addr_from_pte(self, vaddr, pte_value):
464 """Gets the final physical address from the PTE value.""" 465 collection = intel.PhysicalAddressDescriptorCollector(self.session) 466 self.describe_pte(collection, None, pte_value, vaddr) 467 return collection.physical_address
468
469 - def _describe_pde(self, collection, pde_addr, vaddr):
470 """Describe processing of the PDE. 471 472 The PDE is sometimes not present in main memory, we then implement most 473 of the algorithm described in Figure 2 of the paper (except for the 474 prototype state since the PDE can not use a prototype). 475 """ 476 pde_value = self.read_pte(pde_addr) 477 478 # PDE is valid or in transition: 479 if pde_value & self.transition_valid_mask: 480 collection.add(WindowsPDEDescriptor, pte_value=pde_value, 481 pte_addr=pde_addr) 482 483 # PDE refers to a valid large page. 484 if pde_value & self.valid_mask and pde_value & self.page_size_mask: 485 physical_address = ((pde_value & 0xfffffffe00000) | 486 (vaddr & 0x1fffff)) 487 collection.add(intel.CommentDescriptor, "Large page mapped\n") 488 489 collection.add( 490 intel.PhysicalAddressDescriptor, address=physical_address) 491 492 # PDE is mapped in - just read the PTE. 493 else: 494 pte_addr = ((pde_value & 0xffffffffff000) | 495 ((vaddr & 0x1ff000) >> 9)) 496 pte_value = self.read_pte(pte_addr) 497 self.describe_pte(collection, pte_addr, pte_value, vaddr) 498 499 # PDE is paged out into a valid pagefile address. 500 elif pde_value & self.soft_pagefilehigh_mask: 501 collection.add(WindowsPDEDescriptor, pte_value=pde_value, 502 pte_addr=pde_addr, pte_type="Soft") 503 504 pde = self.session.profile._MMPTE() 505 pde.u.Long = pde_value 506 507 # This is the address in the pagefile where the PDE resides. 508 soft_pte = pde.u.Soft 509 pagefile_address = (soft_pte.PageFileHigh * 0x1000 + 510 ((vaddr & 0x1ff000) >> 9)) 511 512 protection = soft_pte.Protection.v() 513 if protection == 0: 514 collection.add(intel.InvalidAddress, "Invalid Soft PTE") 515 else: 516 collection.add(WindowsPagefileDescriptor, 517 address=pagefile_address, 518 protection=protection, 519 pagefile_number=pde.u.Soft.PageFileLow.v()) 520 521 # Try to make the pagefile into the base address space. 522 pte_addr = self._get_pagefile_mapped_address( 523 soft_pte.PageFileLow.v(), pagefile_address) 524 525 if pte_addr is not None: 526 pte_value = self.read_pte(pte_addr) 527 self.describe_pte(collection, pte_addr, pte_value, vaddr) 528 529 else: 530 collection.add(DemandZeroDescriptor)
531 532 @Reentrant
533 - def _get_subsection_mapped_address(self, subsection_pte_address):
534 """Map the subsection into the physical address space. 535 536 Returns: 537 The offset in the physical AS where this subsection PTE is mapped to. 538 """ 539 if self.base_as_can_map_files: 540 pte = self.session.profile._MMPTE(subsection_pte_address) 541 subsection = pte.u.Subsect.Subsection 542 subsection_base = subsection.SubsectionBase.v() 543 544 filename = subsection.ControlArea.FilePointer.file_name_with_drive() 545 if filename: 546 # The offset within the file starts at the beginning sector of 547 # the section object, plus one page for each PTE. A section 548 # object has an array of PTEs - the first one is 0 pages from 549 # the start of the section, and each other PTE is another page 550 # into the file. So we calculate the total number of pages from 551 # the array index of the subsection_pte_address that we were 552 # given. 553 file_offset = ( 554 (subsection_pte_address - 555 subsection_base) * 0x1000 / pte.obj_size + 556 subsection.StartingSector * 512) 557 558 return self.base.get_mapped_offset(filename, file_offset)
559
560 - def _get_pagefile_mapped_address(self, pagefile_number, pagefile_offset):
561 """Map the required pagefile into the physical AS. 562 563 Returns: 564 the mapped address of the required offset in the physical AS. 565 """ 566 if self.base_as_can_map_files: 567 # If we are in the process of resolving the pagefiles, break 568 # re-entrancy. 569 if (self._resolving_pagefiles and 570 not self.session.HasParameter("pagefiles")): 571 return 572 573 # If we have the pagefile we can just read it now. 574 try: 575 self._resolving_pagefiles = True 576 pagefile_name, _ = self.session.GetParameter("pagefiles")[ 577 pagefile_number] 578 except (KeyError, ValueError): 579 return 580 581 except RuntimeError: 582 # Sometimes we cant recover the name of the pagefile because it 583 # is paged. We just take a guess here. 584 pagefile_name = ur"c:\pagefile.sys" 585 586 finally: 587 self._resolving_pagefiles = False 588 589 # Try to make the pagefile into the base address space. 590 return self.base.get_mapped_offset(pagefile_name, pagefile_offset)
591
592 - def _describe_vad_pte(self, collection, pte_addr, pte_value, vaddr):
593 if self.vad: 594 collection.add(intel.CommentDescriptor, "Consulting Vad: ") 595 start, _, mmvad = self.vad.get_containing_range(vaddr) 596 if start is not None: 597 # If the MMVAD has PTEs resolve those.. 598 if "FirstPrototypePte" in mmvad.members: 599 pte = mmvad.FirstPrototypePte[(vaddr - start) >> 12] 600 collection.add(VadPteDescriptor, 601 pte_value=pte_value, pte_addr=pte_addr, 602 virtual_address=vaddr) 603 604 self.describe_proto_pte( 605 collection, pte.obj_offset, pte.u.Long.v(), vaddr) 606 607 return 608 609 else: 610 collection.add(intel.CommentDescriptor, 611 "Vad type {0}\n", mmvad.Tag) 612 613 # Virtual address does not exist in any VAD region. 614 collection.add(DemandZeroDescriptor)
615
616 - def ResolveProtoPTE(self, pte_value, vaddr):
617 collection = intel.PhysicalAddressDescriptorCollector(self.session) 618 self.describe_proto_pte(collection, 0, pte_value, vaddr) 619 620 return collection.physical_address
621
622 - def describe_proto_pte(self, collection, pte_addr, pte_value, vaddr):
623 """Describe the analysis of the prototype PTE. 624 625 This essentially explains how we utilize the flow chart presented in [1] 626 Figure 3. 627 628 NOTE: pte_addr is given here in the kernel's Virtual Address Space since 629 prototype PTEs are always allocated from pool. 630 """ 631 if pte_value & self.transition_valid_mask: 632 physical_address = (pte_value & 0xffffffffff000) | (vaddr & 0xfff) 633 collection.add(WindowsValidPTEDescriptor, 634 pte_value=pte_value, pte_addr=self.vtop(pte_addr)) 635 636 collection.add(intel.PhysicalAddressDescriptor, 637 address=physical_address) 638 639 # File mapping subsection PTE. 640 elif pte_value & self.prototype_mask: 641 collection.add(WindowsSubsectionPTEDescriptor, 642 pte_value=pte_value, pte_addr=pte_addr) 643 644 # Try to map the file into the physical address space. 645 file_mapping = self._get_subsection_mapped_address(pte_addr) 646 if file_mapping is not None: 647 # Add offset within the page. 648 file_mapping += vaddr & 0xFFF 649 collection.add(intel.PhysicalAddressDescriptor, 650 address=file_mapping) 651 652 # PTE is paged out into a valid pagefile address. 653 elif pte_value & self.soft_pagefilehigh_mask: 654 pte = self.session.profile._MMPTE() 655 pte.u.Long = pte_value 656 657 # This is the address in the pagefle where the PTE resides. 658 soft_pte = pte.u.Soft 659 pagefile_address = soft_pte.PageFileHigh * 0x1000 + (vaddr & 0xFFF) 660 protection = soft_pte.Protection.v() 661 662 if protection == 0: 663 collection.add(intel.InvalidAddress, "Invalid Soft PTE") 664 return 665 666 collection.add(WindowsPTEDescriptor, 667 pte_type="Soft", pte_value=pte_value, 668 pte_addr=pte_addr) 669 670 collection.add(WindowsPagefileDescriptor, 671 address=pagefile_address, 672 protection=protection, 673 pagefile_number=pte.u.Soft.PageFileLow.v()) 674 675 # If we have the pagefile we can just read it now. 676 physical_address = self._get_pagefile_mapped_address( 677 soft_pte.PageFileLow.v(), pagefile_address) 678 679 if physical_address is not None: 680 collection.add( 681 intel.PhysicalAddressDescriptor, address=physical_address) 682 683 else: 684 collection.add(DemandZeroDescriptor)
685
686 - def describe_pte(self, collection, pte_addr, pte_value, vaddr):
687 """Describe the initial analysis of the PTE. 688 689 This essentially explains how we utilize the flow chart presented in [1] 690 Figure 2. 691 """ 692 if pte_value & self.transition_valid_mask: 693 physical_address = (pte_value & 0xffffffffff000) | (vaddr & 0xfff) 694 collection.add(WindowsValidPTEDescriptor, 695 pte_value=pte_value, pte_addr=pte_addr) 696 697 collection.add(intel.PhysicalAddressDescriptor, 698 address=physical_address) 699 700 # PTE Type is not known - we need to look it up in the vad. This case is 701 # triggered when the PTE ProtoAddress field is 0xffffffff - it means to 702 # consult the vad. An example PTE value is 0xffffffff00000420. 703 elif pte_value & self.prototype_mask: 704 if ((self.proto_protoaddress_mask & pte_value) >> 705 self.proto_protoaddress_start in (0xffffffff0000, 706 0xffffffff)): 707 708 collection.add(WindowsSoftwarePTEDescriptor, 709 pte_value=pte_value, pte_addr=pte_addr) 710 711 self._describe_vad_pte(collection, pte_addr, pte_value, vaddr) 712 713 else: 714 collection.add(WindowsProtoTypePTEDescriptor, 715 pte_value=pte_value, pte_addr=pte_addr) 716 717 # This PTE points at the prototype PTE in 718 # pte.ProtoAddress. NOTE: The prototype PTE address is specified 719 # in the kernel's address space since it is allocated from pool. 720 pte_addr = pte_value >> self.proto_protoaddress_start 721 pte_value = struct.unpack("<Q", self.read(pte_addr, 8))[0] 722 723 self.describe_proto_pte(collection, pte_addr, pte_value, vaddr) 724 725 # Case 2 of consult VAD: pte.u.Soft.PageFileHigh == 0. 726 elif pte_value & self.soft_pagefilehigh_mask == 0: 727 collection.add(WindowsSoftwarePTEDescriptor, 728 pte_value=pte_value, pte_addr=pte_addr) 729 730 self._describe_vad_pte(collection, pte_addr, pte_value, vaddr) 731 732 # PTE is demand zero. 733 elif (pte_value >> 12) == 0: 734 collection.add(DemandZeroDescriptor) 735 736 # PTE is paged out into a valid pagefile address. 737 elif pte_value & self.soft_pagefilehigh_mask: 738 pte = self.session.profile._MMPTE() 739 pte.u.Long = pte_value 740 741 # This is the address in the pagefle where the PTE resides. 742 soft_pte = pte.u.Soft 743 pagefile_address = soft_pte.PageFileHigh * 0x1000 + (vaddr & 0xFFF) 744 protection = soft_pte.Protection.v() 745 if protection == 0: 746 collection.add(intel.InvalidAddress, "Invalid Soft PTE") 747 return 748 749 collection.add(WindowsPTEDescriptor, 750 pte_type="Soft", pte_value=pte_value, 751 pte_addr=pte_addr) 752 753 collection.add(WindowsPagefileDescriptor, 754 address=pagefile_address, 755 protection=soft_pte.Protection, 756 pagefile_number=pte.u.Soft.PageFileLow.v()) 757 758 physical_address = self._get_pagefile_mapped_address( 759 soft_pte.PageFileLow.v(), pagefile_address) 760 761 # If we have the pagefile we can just read it now. 762 if physical_address is not None: 763 collection.add( 764 intel.PhysicalAddressDescriptor, address=physical_address) 765 766 else: 767 # Fallback 768 collection.add(intel.AddressTranslationDescriptor, 769 object_name="pte", object_value=pte_value, 770 object_address=pte_addr) 771 772 collection.add(intel.CommentDescriptor, "Error! Unknown PTE\n")
773
774 - def _get_pte_addr(self, vaddr, pde_value):
775 if pde_value & self.transition_valid_mask: 776 return (pde_value & 0xffffffffff000) | ((vaddr & 0x1ff000) >> 9) 777 778 if pde_value & self.soft_pagefilehigh_mask: 779 pde = self.session.profile._MMPTE() 780 pde.u.Long = pde_value 781 soft_pte = pde.u.Soft 782 783 # This is the address in the pagefle where the PDE resides. 784 pagefile_address = (soft_pte.PageFileHigh * 0x1000 + 785 ((vaddr & 0x1ff000) >> 9)) 786 787 physical_address = self._get_pagefile_mapped_address( 788 soft_pte.PageFileLow.v(), pagefile_address) 789 790 return physical_address
791
792 793 -class WindowsIA32PagedMemoryPae(WindowsPagedMemoryMixin, 794 intel.IA32PagedMemoryPae):
795 """A Windows specific IA32PagedMemoryPae.""" 796 797 __pae = True
798
799 800 -class WindowsAMD64PagedMemory(WindowsPagedMemoryMixin, 801 amd64.AMD64PagedMemory):
802 """A windows specific AMD64PagedMemory. 803 804 Implements support for reading the pagefile if the base address space 805 contains a pagefile. 806 """
807
808 809 -class Pagefiles(common.WindowsCommandPlugin):
810 """Report all the active pagefiles.""" 811 812 name = "pagefiles" 813 814 table_header = [ 815 dict(name='_MMPAGING_FILE', style="address"), 816 dict(name='number', align="r", width=3), 817 dict(name='size', align="r", width=10), 818 dict(name='filename', width=20), 819 ] 820
821 - def collect(self):
822 for pf_num, (pf_name, pf) in self.session.GetParameter( 823 "pagefiles").items(): 824 pf = self.profile._MMPAGING_FILE(pf) 825 yield (pf, pf_num, pf.Size * 0x1000, pf_name)
826
827 828 -class PagefileHook(common.AbstractWindowsParameterHook):
829 """Map pagefile number to the filename.""" 830 831 name = "pagefiles" 832
833 - def calculate(self):
834 result = {} 835 pagingfiles = self.session.profile.get_constant_object( 836 'MmPagingFile', 837 target='Array', target_args=dict( 838 target='Pointer', 839 count=16, 840 target_args=dict( 841 target='_MMPAGING_FILE' 842 ) 843 ) 844 ) 845 846 # In windows 10, the pagefiles are stored in an AVL Tree. 847 if pagingfiles == None: 848 mistate = self.session.address_resolver.get_constant_object( 849 "nt!MiState", "_MI_SYSTEM_INFORMATION") 850 851 root = mistate.PagingIo.PageFileHead.Root 852 pagingfiles = root.traverse_as_type( 853 "_MMPAGING_FILE", "FileObjectNode") 854 855 for pf in pagingfiles: 856 if pf: 857 result[pf.PageFileNumber.v()] = ( 858 pf.File.file_name_with_drive(), pf.v()) 859 860 return result
861