1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """ This plugin contains CORE classes used by lots of other plugins """
24
25
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
38 KERNEL_NAMES = set(
39 ["ntkrnlmp.pdb", "ntkrnlpa.pdb", "ntoskrnl.pdb",
40 "ntkrpamp.pdb"])
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
61
62 mode = "mode_windows_memory"
63
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):
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):
115
126
139
145
164
166 """Check the eprocess for sanity."""
167
168
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
176 address_space = super(WinFindDTB, self).GetAddressSpaceImplementation()(
177 session=self.session, dtb=dtb,
178 base=self.session.physical_address_space)
179
180
181
182
183
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
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
201 architecture = self.profile.metadata("arch")
202 performance = self.session.GetParameter("performance")
203 if architecture == "AMD64":
204
205
206 if performance == "fast":
207 impl = "AMD64PagedMemory"
208 else:
209 impl = "WindowsAMD64PagedMemory"
210
211
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
234
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
247
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):
259
260 - def check(self, buffer_as, offset):
263
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):
276
277 - def skip(self, buffer_as, offset):
280
281 - def check(self, buffer_as, offset):
284
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
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
324 """ Checks the pool index """
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
337 """A scanner for pool allocations."""
338
339 - def scan(self, offset=0, maxlen=None):
346
347
348 -class KDBGHook(AbstractWindowsParameterHook):
349 """A Hook to calculate the KDBG when needed."""
350
351 name = "kdbg"
352
354
355 kdbg = self.session.profile.get_constant_object(
356 "KdDebuggerDataBlock", "_KDDEBUGGER_DATA64",
357 vm=self.session.kernel_address_space)
358
359
360 if kdbg.Header.OwnerTag == "KDBG":
361 return kdbg
362
363
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
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
379 """The PsActiveProcessHead is actually found in the profile symbols."""
380
381 name = "PsActiveProcessHead"
382
388
391 """The PsLoadedModuleList is actually found in the profile symbols."""
392
393 name = "PsLoadedModuleList"
394
400
403 """A windows plugin which requires the kernel address space."""
404 __abstract = True
405
408 """A class for filtering processes."""
409
410 __abstract = True
411
412
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
439
460
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
486
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
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
537
538 if not self.plugin_args.scan_kernel:
539 pool_plugin = self.session.plugins.pools()
540
541
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
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
634
637 name = "pslist_PsActiveProcessHead"
638
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
652 name = "pslist_CSRSS"
653
655 """Enumerate processes using the csrss.exe handle table"""
656 result = set()
657
658
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
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
676 name = "pslist_PspCidTable"
677
679 """Enumerate processes by walking the PspCidTable"""
680 result = set()
681
682
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
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
704 name = "pslist_Sessions"
705
729
732 name = "pslist_Handles"
733
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