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

Source Code for Module rekall.plugins.windows.common

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@users.sourceforge.net> 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or (at 
 11  # your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21  # 
 22   
 23  """ This plugin contains CORE classes used by lots of other plugins """ 
 24   
 25  # pylint: disable=protected-access 
 26   
 27  from rekall import addrspace 
 28  from rekall import scan 
 29  from rekall import kb 
 30  from rekall import plugin 
 31   
 32  from rekall.plugins import core 
 33  from rekall.plugins.common import scanners 
 34  from rekall_lib import utils 
 35   
 36   
 37  # Windows kernel pdb filenames. 
 38  KERNEL_NAMES = set( 
 39      ["ntkrnlmp.pdb", "ntkrnlpa.pdb", "ntoskrnl.pdb", 
 40       "ntkrpamp.pdb"]) 
41 42 43 # We require both a physical AS set and a valid profile for 44 # AbstractWindowsCommandPlugins. 45 46 -class AbstractWindowsCommandPlugin(plugin.PhysicalASMixin, 47 plugin.TypedProfileCommand, 48 plugin.ProfileCommand):
49 """A base class for all windows based plugins. 50 51 Windows based plugins require at a minimum a working profile, and a valid 52 physical address space. 53 """ 54 55 __abstract = True 56 57 mode = "mode_windows_memory"
58
59 60 -class AbstractWindowsParameterHook(kb.ParameterHook):
61 62 mode = "mode_windows_memory"
63
64 65 -class WinDTBScanner(scan.BaseScanner):
66 - def __init__(self, process_name=None, **kwargs):
67 super(WinDTBScanner, self).__init__(**kwargs) 68 needle_process_name = process_name or "Idle" 69 needle = needle_process_name + "\x00" * (15 - len(needle_process_name)) 70 self.image_name_offset = self.profile.get_obj_offset( 71 "_EPROCESS", "ImageFileName") 72 self.checks = [["StringCheck", {"needle": needle}]]
73
74 - def scan(self, offset=0, maxlen=None):
75 for offset in super(WinDTBScanner, self).scan(offset, maxlen): 76 self.eprocess = self.profile.Object( 77 "_EPROCESS", offset=offset - self.image_name_offset, 78 vm=self.session.physical_address_space) 79 self.session.logging.debug("Found _EPROCESS @ 0x%X (DTB: 0x%X)", 80 self.eprocess.obj_offset, 81 self.eprocess.Pcb.DirectoryTableBase.v()) 82 83 yield self.eprocess
84
85 86 -class WinFindDTB(AbstractWindowsCommandPlugin, core.FindDTB):
87 """A plugin to search for the Directory Table Base for windows systems. 88 89 There are a number of ways to find the DTB: 90 91 - Scanner method: Scans the image for a known kernel process, and read the 92 DTB from its Process Environment Block (PEB). 93 94 - Get the DTB from the KPCR structure. 95 96 - Note that the kernel is mapped into every process's address space (with 97 the exception of session space which might be different) so using any 98 process's DTB from the same session will work to read kernel data 99 structures. If this plugin fails, try psscan to find potential DTBs. 100 """ 101 102 __name = "find_dtb" 103 104 @classmethod
105 - def args(cls, parser):
106 """Declare the command line args we need.""" 107 super(WinFindDTB, cls).args(parser) 108 parser.add_argument("--process_name", 109 help="The name of the process to search for.")
110
111 - def __init__(self, process_name="Idle", **kwargs):
112 super(WinFindDTB, self).__init__(**kwargs) 113 self.process_name = process_name 114 self.eprocess_index = self.session.LoadProfile("nt/eprocess_index")
115
116 - def scan_for_process(self):
117 """Scan the image for the idle process.""" 118 maxlen = self.session.GetParameter("autodetect_scan_length", 119 10*1024*1024*1024) 120 for process in WinDTBScanner( 121 session=self.session, process_name=self.process_name, 122 profile=self.profile, 123 address_space=self.physical_address_space).scan( 124 0, maxlen=maxlen): 125 yield process
126
127 - def address_space_hits(self):
128 """Finds DTBs and yields virtual address spaces that expose kernel. 129 130 Yields: 131 BaseAddressSpace-derived instances, validated using the VerifyHit() 132 method. 133 """ 134 for dtb, eprocess in self.dtb_eprocess_hits(): 135 address_space = self.VerifyHit(dtb) 136 if address_space is not None and self.TestEProcess( 137 address_space, eprocess): 138 yield address_space
139
140 - def dtb_eprocess_hits(self):
141 for eprocess in self.scan_for_process(): 142 result = eprocess.Pcb.DirectoryTableBase.v() 143 if result: 144 yield result, eprocess
145
146 - def TestEProcess(self, address_space, eprocess):
147 # Reflect through the address space at ourselves. Note that the Idle 148 # process is not usually in the PsActiveProcessHead list, so we use 149 # the ThreadListHead instead. 150 list_head = eprocess.ThreadListHead.Flink 151 152 if list_head == 0: 153 self.session.logging.debug("_EPROCESS.ThreadListHead not valid.") 154 return 155 156 me = list_head.dereference(vm=address_space).Blink.Flink 157 if me.v() != list_head.v(): 158 self.session.logging.debug( 159 "_EPROCESS.ThreadListHead does not reflect.") 160 return 161 162 # We passed the tests.X 163 return True
164
165 - def VerifyHit(self, dtb):
166 """Check the eprocess for sanity.""" 167 # In windows the DTB must be page aligned, except for PAE images where 168 # its aligned to a 0x20 size. 169 if not self.profile.metadata("pae") and dtb & 0xFFF != 0: 170 return 171 172 if self.profile.metadata("pae") and dtb & 0xF != 0: 173 return 174 175 # Select simple address space implementations as test address spaces. 176 address_space = super(WinFindDTB, self).GetAddressSpaceImplementation()( 177 session=self.session, dtb=dtb, 178 base=self.session.physical_address_space) 179 180 # Check that the _KUSER_SHARED_DATA makes sense. This structure is 181 # always at a known offset since it must be shared with user space apps. 182 # Note: We must get the raw value here since image base is not yet 183 # known. 184 kuser_shared = self.eprocess_index._KUSER_SHARED_DATA( 185 offset=self.profile.get_constant("KI_USER_SHARED_DATA_RAW"), 186 vm=address_space) 187 188 # Must be a valid version of windows. 189 if (address_space.vtop(kuser_shared.obj_offset) and 190 (kuser_shared.NtMajorVersion not in [5, 6, 10] or 191 kuser_shared.NtMinorVersion not in [0, 1, 2, 3])): 192 return 193 194 self.session.SetCache("dtb", dtb) 195 196 return self.CreateAS(dtb)
197
199 """Returns the correct address space class for this profile.""" 200 # The virtual address space implementation is chosen by the profile. 201 architecture = self.profile.metadata("arch") 202 performance = self.session.GetParameter("performance") 203 if architecture == "AMD64": 204 # If the user prefers performance we will use the simplest Address 205 # Space Implementation. 206 if performance == "fast": 207 impl = "AMD64PagedMemory" 208 else: 209 impl = "WindowsAMD64PagedMemory" 210 211 # PAE profiles go with the pae address space. 212 elif architecture == "I386" and self.profile.metadata("pae"): 213 if performance == "fast": 214 impl = "IA32PagedMemoryPae" 215 else: 216 impl = "WindowsIA32PagedMemoryPae" 217 218 else: 219 return super(WinFindDTB, self).GetAddressSpaceImplementation() 220 221 as_class = addrspace.BaseAddressSpace.classes[impl] 222 return as_class
223 224 table_header = [ 225 dict(name="_EPROCESS (P)", style="address"), 226 dict(name="dtv", style="address"), 227 dict(name="valid", width=10) 228 ] 229
230 - def collect(self):
231 for dtb, eprocess in self.dtb_eprocess_hits(): 232 yield (eprocess.obj_offset, dtb, 233 self.VerifyHit(dtb) is not None)
234
235 236 ## The following are checks for pool scanners. 237 238 -class PoolTagCheck(scan.StringCheck):
239 """This scanner checks for the occurrence of a pool tag. 240 241 It is basically a StringCheck but it offsets the check with a constant. 242 """
243 - def __init__(self, tag=None, **kwargs):
244 super(PoolTagCheck, self).__init__(needle=tag, **kwargs) 245 246 # The offset from the start of _POOL_HEADER to the tag. (Note we use the 247 # kernel profile for pool definitions.). 248 self.tag_offset = self.session.profile.get_obj_offset( 249 "_POOL_HEADER", "PoolTag") 250 251 if self.tag_offset == None: 252 raise RuntimeError( 253 "Unable to get PoolTag offset in _POOL_HEADER. " 254 "Is the profile correct?")
255
256 - def skip(self, buffer_as, offset):
257 return super(PoolTagCheck, self).skip( 258 buffer_as, offset + self.tag_offset)
259
260 - def check(self, buffer_as, offset):
261 return super(PoolTagCheck, self).check( 262 buffer_as, offset + self.tag_offset)
263
264 265 -class MultiPoolTagCheck(scan.MultiStringFinderCheck):
266 """This scanner checks for the occurrence of a pool tag. 267 268 It is basically a StringCheck but it offsets the check with a constant. 269 """
270 - def __init__(self, tags=None, **kwargs):
271 super(MultiPoolTagCheck, self).__init__(needles=tags, **kwargs) 272 273 # The offset from the start of _POOL_HEADER to the tag. 274 self.tag_offset = self.profile.get_obj_offset( 275 "_POOL_HEADER", "PoolTag")
276
277 - def skip(self, buffer_as, offset):
278 return super(MultiPoolTagCheck, self).skip( 279 buffer_as, offset + self.tag_offset)
280
281 - def check(self, buffer_as, offset):
282 return super(MultiPoolTagCheck, self).check( 283 buffer_as, offset + self.tag_offset)
284
285 286 -class CheckPoolSize(scan.ScannerCheck):
287 """ Check pool block size """
288 - def __init__(self, condition=None, min_size=None, **kwargs):
289 super(CheckPoolSize, self).__init__(**kwargs) 290 self.condition = condition 291 if min_size: 292 self.condition = lambda x: x >= min_size 293 294 self.pool_align = self.session.profile.constants['PoolAlignment'] 295 if self.condition is None: 296 raise RuntimeError("No pool size provided")
297
298 - def check(self, buffer_as, offset):
299 pool_hdr = self.session.profile._POOL_HEADER( 300 vm=buffer_as, offset=offset) 301 302 block_size = pool_hdr.BlockSize.v() 303 return self.condition(block_size * self.pool_align)
304
305 306 -class CheckPoolType(scan.ScannerCheck):
307 """ Check the pool type """
308 - def __init__(self, paged=False, non_paged=False, free=False, **kwargs):
309 super(CheckPoolType, self).__init__(**kwargs) 310 self.non_paged = non_paged 311 self.paged = paged 312 self.free = free
313
314 - def check(self, buffer_as, offset):
315 pool_hdr = self.session.profile._POOL_HEADER( 316 vm=buffer_as, offset=offset) 317 318 return ((self.non_paged and pool_hdr.NonPagedPool) or 319 (self.free and pool_hdr.FreePool) or 320 (self.paged and pool_hdr.PagedPool))
321
322 323 -class CheckPoolIndex(scan.ScannerCheck):
324 """ Checks the pool index """
325 - def __init__(self, value=0, **kwargs):
326 super(CheckPoolIndex, self).__init__(**kwargs) 327 self.value = value
328
329 - def check(self, buffer_as, offset):
330 pool_hdr = self.session.profile._POOL_HEADER( 331 vm=buffer_as, offset=offset) 332 333 return pool_hdr.PoolIndex == self.value
334
335 336 -class PoolScanner(scan.BaseScanner):
337 """A scanner for pool allocations.""" 338
339 - def scan(self, offset=0, maxlen=None):
340 """Yields instances of _POOL_HEADER which potentially match.""" 341 342 maxlen = maxlen or self.session.profile.get_constant("MaxPointer") 343 for hit in super(PoolScanner, self).scan(offset=offset, maxlen=maxlen): 344 yield self.session.profile._POOL_HEADER( 345 vm=self.address_space, offset=hit)
346
347 348 -class KDBGHook(AbstractWindowsParameterHook):
349 """A Hook to calculate the KDBG when needed.""" 350 351 name = "kdbg" 352
353 - def calculate(self):
354 # Try to just get the KDBG address using the profile. 355 kdbg = self.session.profile.get_constant_object( 356 "KdDebuggerDataBlock", "_KDDEBUGGER_DATA64", 357 vm=self.session.kernel_address_space) 358 359 # Verify it. 360 if kdbg.Header.OwnerTag == "KDBG": 361 return kdbg 362 363 # Cant find it from the profile, look for it the old way. 364 self.session.logging.info( 365 "KDBG not provided - Rekall will try to " 366 "automatically scan for it now using plugin.kdbgscan.") 367 368 for kdbg in self.session.plugins.kdbgscan( 369 session=self.session).hits(): 370 # Just return the first one 371 self.session.logging.info( 372 "Found a KDBG hit %r. Hope it works. If not try setting it " 373 "manually.", kdbg) 374 375 return kdbg
376
377 378 -class PsActiveProcessHeadHook(AbstractWindowsParameterHook):
379 """The PsActiveProcessHead is actually found in the profile symbols.""" 380 381 name = "PsActiveProcessHead" 382
383 - def calculate(self):
384 return self.session.profile.get_constant_object( 385 "PsActiveProcessHead", 386 target="_LIST_ENTRY", 387 vm=self.session.kernel_address_space)
388
389 390 -class PsLoadedModuleList(AbstractWindowsParameterHook):
391 """The PsLoadedModuleList is actually found in the profile symbols.""" 392 393 name = "PsLoadedModuleList" 394
395 - def calculate(self):
396 return self.session.profile.get_constant_object( 397 "PsLoadedModuleList", 398 target="_LIST_ENTRY", 399 vm=self.session.kernel_address_space)
400
401 402 -class WindowsCommandPlugin(plugin.KernelASMixin, AbstractWindowsCommandPlugin):
403 """A windows plugin which requires the kernel address space.""" 404 __abstract = True
405
406 407 -class WinProcessFilter(WindowsCommandPlugin):
408 """A class for filtering processes.""" 409 410 __abstract = True 411 412 # Maintain the order of methods. 413 METHODS = [ 414 "PsActiveProcessHead", 415 "CSRSS", 416 "PspCidTable", 417 "Sessions", 418 "Handles", 419 ] 420 421 __args = [ 422 dict(name="eprocess", type="ArrayIntParser", default=[], 423 help="Kernel addresses of eprocess structs."), 424 425 dict(name="pids", positional=True, type="ArrayIntParser", default=[], 426 help="One or more pids of processes to select."), 427 428 dict(name="proc_regex", default=None, type="RegEx", 429 help="A regex to select a process by name."), 430 431 dict(name="method", choices=METHODS, type="ChoiceArray", 432 default=METHODS, help="Method to list processes."), 433 ] 434 435 @utils.safe_property
436 - def filtering_requested(self):
437 return (self.plugin_args.pids or self.plugin_args.proc_regex or 438 self.plugin_args.eprocess)
439
440 - def filter_processes(self):
441 """Filters eprocess list using pids lists.""" 442 # If eprocess are given specifically only use those. 443 if self.plugin_args.eprocess: 444 for task in self.list_from_eprocess(): 445 yield task 446 447 else: 448 for proc in self.list_eprocess(): 449 if not self.filtering_requested: 450 yield proc 451 452 else: 453 if int(proc.pid) in self.plugin_args.pids: 454 yield proc 455 456 elif (self.plugin_args.proc_regex and 457 self.plugin_args.proc_regex.match( 458 utils.SmartUnicode(proc.name))): 459 yield proc
460
461 - def virtual_process_from_physical_offset(self, physical_offset):
462 """Tries to return an eprocess in virtual space from a physical offset. 463 464 We do this by reflecting off the list elements. 465 466 Args: 467 physical_offset: The physcial offset of the process. 468 469 Returns: 470 an _EPROCESS object or a NoneObject on failure. 471 """ 472 physical_eprocess = self.profile._EPROCESS( 473 offset=int(physical_offset), 474 vm=self.physical_address_space) 475 476 return physical_eprocess.ThreadListHead.reflect( 477 vm=self.kernel_address_space).dereference_as( 478 "_EPROCESS", "ThreadListHead")
479
480 - def list_from_eprocess(self):
481 for eprocess_offset in self.plugin_args.eprocess: 482 eprocess = self.profile._EPROCESS( 483 offset=eprocess_offset, vm=self.kernel_address_space) 484 485 yield eprocess
486
487 - def list_eprocess(self):
488 """List processes using chosen methods.""" 489 # We actually keep the results from each method around in case we need 490 # to find out later which process was revealed by which method. 491 seen = set() 492 for proc in self.list_from_eprocess(): 493 seen.add(proc.obj_offset) 494 495 for method in self.plugin_args.method: 496 for proc in self.session.GetParameter("pslist_%s" % method): 497 seen.add(proc) 498 499 result = [] 500 for x in seen: 501 result.append(self.profile._EPROCESS( 502 x, vm=self.session.kernel_address_space)) 503 504 return sorted(result, key=lambda x: x.pid)
505
506 507 -class WinScanner(scanners.BaseScannerPlugin, WinProcessFilter):
508 """Windows specific scanner implementation.""" 509 510 __abstract = True 511 512 __args = [ 513 dict(name="scan_kernel_paged_pool", default=False, type="Boolean", 514 help="Scan the kernel paged pool."), 515 516 dict(name="scan_kernel_nonpaged_pool", default=False, type="Boolean", 517 help="Scan the kernel non-paged pool."), 518 519 dict(name="scan_kernel_code", default=False, type="Boolean", 520 help="Scan the kernel image and loaded drivers."), 521 522 dict(name="scan_kernel_session_pools", default=False, type="Boolean", 523 help="Scan session pools for all processes."), 524 525 dict(name="limit", default=2**64, type="IntParser", 526 help="The length of data to search in each selected region."), 527 ] 528
529 - def generate_memory_ranges(self):
530 for run in super(WinScanner, self).generate_memory_ranges(): 531 run.length = min(run.length, self.plugin_args.limit) 532 yield run 533 534 pools_seen = set() 535 536 # If the user did not just ask to scan the entire kernel space, support 537 # dividing the kernel space into subregions. 538 if not self.plugin_args.scan_kernel: 539 pool_plugin = self.session.plugins.pools() 540 541 # Scan session pools in each process. 542 if self.plugin_args.scan_kernel_session_pools: 543 for pool in pool_plugin.find_session_pool_descriptors(): 544 if pool.PoolStart in pools_seen: 545 continue 546 547 pools_seen.add(pool.PoolStart) 548 549 comment = "%s" % pool.PoolType 550 if pool.Comment: 551 comment += " (%s)" % pool.Comment 552 553 self.session.logging.info( 554 "Scanning in: %s. [%#x-%#x]" % ( 555 comment, pool.PoolStart, pool.PoolEnd)) 556 557 run = addrspace.Run( 558 start=pool.PoolStart, end=pool.PoolEnd, 559 address_space=pool.obj_vm, 560 data=dict(type=comment, pool=pool)) 561 562 run.length = min(run.length, self.plugin_args.limit) 563 yield run 564 565 # Non paged pool selection. 566 if self.plugin_args.scan_kernel_nonpaged_pool: 567 for pool in pool_plugin.find_non_paged_pool(): 568 if pool.PoolStart in pools_seen: 569 continue 570 571 pools_seen.add(pool.PoolStart) 572 comment = "Pool %s" % pool.PoolType 573 if pool.Comment: 574 comment += " (%s)" % pool.Comment 575 576 self.session.logging.info( 577 "Scanning in: %s. [%#x-%#x]" % ( 578 comment, pool.PoolStart, pool.PoolEnd)) 579 580 run = addrspace.Run( 581 start=pool.PoolStart, end=pool.PoolEnd, 582 address_space=pool.obj_vm, 583 data=dict(type=comment, pool=pool)) 584 585 run.length = min(run.length, self.plugin_args.limit) 586 yield run 587 588 if self.plugin_args.scan_kernel_paged_pool: 589 for pool in pool_plugin.find_paged_pool(): 590 if pool.PoolStart in pools_seen: 591 continue 592 593 pools_seen.add(pool.PoolStart) 594 595 comment = "Pool %s" % pool.PoolType 596 if pool.Comment: 597 comment += " (%s)" % pool.Comment 598 599 self.session.logging.info("Scanning in: %s [%#x-%#x]" % ( 600 comment, pool.PoolStart, pool.PoolEnd)) 601 602 run = addrspace.Run( 603 start=pool.PoolStart, end=pool.PoolEnd, 604 address_space=pool.obj_vm, 605 data=dict(type=comment, pool=pool)) 606 607 run.length = min(run.length, self.plugin_args.limit) 608 yield run 609 610 if self.plugin_args.scan_kernel_code: 611 cc = self.session.plugins.cc() 612 with cc: 613 cc.SwitchProcessContext(None) 614 for module in self.session.address_resolver.GetAllModules(): 615 comment = "Module %s" % module.name 616 617 self.session.logging.info( 618 "Scanning in: %s [%#x-%#x]" % ( 619 comment, module.start, module.end)) 620 621 run = addrspace.Run( 622 start=module.start, end=module.end, 623 address_space=self.session.kernel_address_space, 624 data=dict(type=comment, module=module)) 625 run.length = min(run.length, self.plugin_args.limit) 626 yield run 627 628 run.length = min(run.length, self.plugin_args.limit) 629 yield run
630
631 632 -class PoolScannerPlugin(WinScanner, AbstractWindowsCommandPlugin):
633 __abstract = True
634
635 636 -class PsListPsActiveProcessHeadHook(AbstractWindowsParameterHook):
637 name = "pslist_PsActiveProcessHead" 638
639 - def calculate(self):
640 result = set() 641 for x in self.session.GetParameter("PsActiveProcessHead").list_of_type( 642 "_EPROCESS", "ActiveProcessLinks"): 643 result.add(x.obj_offset) 644 645 self.session.logging.debug( 646 "Listed %s processes using PsActiveProcessHead", len(result)) 647 648 return result
649
650 651 -class PsListCSRSSHook(AbstractWindowsParameterHook):
652 name = "pslist_CSRSS" 653
654 - def calculate(self):
655 """Enumerate processes using the csrss.exe handle table""" 656 result = set() 657 658 # First find csrss process using a simpler method. 659 for proc_offset in self.session.GetParameter( 660 "pslist_PsActiveProcessHead"): 661 proc = self.session.profile._EPROCESS(proc_offset) 662 if proc.name == "csrss.exe": 663 # Gather the handles to process objects 664 for handle in proc.ObjectTable.handles(): 665 if handle.get_object_type() == "Process": 666 process = handle.dereference_as("_EPROCESS") 667 result.add(process.obj_offset) 668 669 self.session.logging.debug( 670 "Listed %s processes using CSRSS", len(result)) 671 672 return result
673
674 675 -class PsListPspCidTableHook(AbstractWindowsParameterHook):
676 name = "pslist_PspCidTable" 677
678 - def calculate(self):
679 """Enumerate processes by walking the PspCidTable""" 680 result = set() 681 682 # Follow the pointers to the table base 683 PspCidTable = self.session.profile.get_constant_object( 684 "PspCidTable", 685 target="Pointer", 686 target_args=dict( 687 target="_PSP_CID_TABLE" 688 ) 689 ) 690 691 # Walk the handle table 692 for handle in PspCidTable.handles(): 693 if handle.get_object_type() == "Process": 694 process = handle.dereference_as("_EPROCESS") 695 result.add(process.obj_offset) 696 697 self.session.logging.debug( 698 "Listed %s processes using PspCidTable", len(result)) 699 700 return result
701
702 703 -class PsListSessionsHook(AbstractWindowsParameterHook):
704 name = "pslist_Sessions" 705
706 - def calculate(self):
707 """Enumerate processes by walking the SessionProcessLinks""" 708 result = set() 709 sessions = set() 710 711 # First find unique sessions using a simpler method. 712 for proc_offset in self.session.GetParameter( 713 "pslist_PsActiveProcessHead"): 714 proc = self.session.profile._EPROCESS(proc_offset) 715 if proc.Session in sessions: 716 continue 717 718 sessions.add(proc.Session) 719 720 # Now enumerate all tasks in session list. 721 for task in proc.Session.ProcessList.list_of_type( 722 "_EPROCESS", "SessionProcessLinks"): 723 result.add(task.obj_offset) 724 725 self.session.logging.debug( 726 "Listed %s processes using Sessions", len(result)) 727 728 return result
729
730 731 -class PsListHandlesHook(AbstractWindowsParameterHook):
732 name = "pslist_Handles" 733
734 - def calculate(self):
735 """Enumerate processes by walking the SessionProcessLinks""" 736 result = set() 737 handle_table_list_head = self.session.profile.get_constant_object( 738 "HandleTableListHead", "_LIST_ENTRY") 739 740 for table in handle_table_list_head.list_of_type( 741 "_HANDLE_TABLE", "HandleTableList"): 742 proc = table.QuotaProcess.deref() 743 if proc and proc.pid > 0: 744 result.add(proc.obj_offset) 745 746 self.session.logging.debug( 747 "Listed %s processes using Handles", len(result)) 748 749 750 return result
751