1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 @author: Andrew Case
21 @license: GNU General Public License 2.0 or later
22 @contact: atcuno@gmail.com
23 @organization: Digital Forensics Solutions
24 """
25 import os
26 import re
27
28 from rekall import addrspace
29 from rekall import kb
30 from rekall import plugin
31 from rekall import obj
32 from rekall_lib import utils
33
34 from rekall.plugins import core
38 """A parser for KAllSyms files."""
39
40
41 KALLSYMS_FILE = "/proc/kallsyms"
42
43
44 KALLSYMS_REGEXP = (r"(?P<offset>[0-9a-fA-F]+) "
45 r"(?P<type>[a-zA-Z]) "
46 r"(?P<symbol>[^ \t]+)"
47 r"(\t(?P<module>[^ ]+))?$")
48
52
54 """Parses a single symbol line from a symbols file.
55
56 This is the result of obtaining symbols from nm:
57
58 0000000000 t linux_proc_banner
59 0000000010 s other_symbol [module]
60
61 Yields:
62 Tuple of offset, symbol_name, type, module
63 """
64
65 matches = None
66 if line:
67 matches = re.match(self.KALLSYMS_REGEXP, line.rstrip("\n"))
68
69 if not matches:
70 raise ValueError("Invalid line: %s", line)
71
72
73 offset = matches.group("offset")
74 symbol_type = matches.group("type")
75 symbol = matches.group("symbol")
76 try:
77 module = matches.group("module")
78 except IndexError:
79 module = None
80
81 try:
82 offset = obj.Pointer.integer_to_address(int(offset, 16))
83 except ValueError:
84 pass
85
86 return offset, symbol, symbol_type, module
87
89 """Obtain symbol names and values for a live machine.
90
91 Yields:
92 Tuples of offset, symbol_name, type, module
93 """
94 kallsyms_as = self._OpenLiveSymbolsFile(
95 self.physical_address_space)
96
97 if not kallsyms_as:
98 return []
99
100
101
102 if kallsyms_as.end() != 0:
103 data = kallsyms_as.read(0, kallsyms_as.end())
104 else:
105
106 read_length = 1*1024*1024
107 data = kallsyms_as.read(0, read_length).strip("\x00")
108 while len(data) == read_length and read_length < 2**30:
109 read_length *= 2
110 data = kallsyms_as.read(0, read_length).strip("\x00")
111
112 return self.parse_data(data)
113
115 self.session.logging.debug(
116 "Found %s of size: %d", self.KALLSYMS_FILE, len(data))
117
118 match_failures = 0
119 for line in data.split(os.linesep):
120 if match_failures >= 50:
121 break
122
123 try:
124 yield self._ParseKallsym(line)
125 except ValueError:
126 match_failures += 1
127 continue
128
134
139 """A base class for all linux based plugins."""
140 __abstract = True
141 mode = "mode_linux_memory"
142
147
148
149 -class LinuxFindDTB(AbstractLinuxCommandPlugin, core.FindDTB):
150 """A scanner for DTB values. Handles both 32 and 64 bits.
151
152 The plugin also autodetects when the guest is running as a XEN
153 ParaVirtualized guest and returns a compatible address space.
154 """
155
156 __name = "find_dtb"
157
171
203
213
220
221
222 -class LinuxPlugin(plugin.KernelASMixin, AbstractLinuxCommandPlugin):
223 """Plugin which requires the kernel Address space to be loaded."""
224 __abstract = True
225
228 """A class for filtering processes."""
229
230 __abstract = True
231
232 METHODS = [
233 "InitTask"
234 ]
235
236 __args = [
237 dict(name="pids", type="ArrayIntParser", positional=True,
238 help="One or more pids of processes to select."),
239
240 dict(name="proc_regex", type="RegEx",
241 help="A regex to select a process by name."),
242
243 dict(name="task", type="ArrayIntParser",
244 help="Kernel addresses of task structs."),
245
246 dict(name="method", choices=METHODS, type="ChoiceArray",
247 default=METHODS,
248 help="Method to list processes (Default uses all methods)."),
249 ]
250
251 @utils.safe_property
255
262
278
299
301 """Tries to return an task in virtual space from a physical offset.
302
303 We do this by reflecting off the list elements.
304
305 Args:
306 physical_offset: The physical offset of the process.
307
308 Returns:
309 an _TASK object or a NoneObject on failure.
310 """
311 physical_task = self.profile.eprocess(offset=int(physical_offset),
312 vm=self.kernel_address_space.base)
313
314
315
316
317 our_list_entry = physical_task.tasks.next.dereference(
318 vm=self.kernel_address_space).prev.dereference()
319
320
321 return our_list_entry.dereference_as("task_struct", "tasks")
322
325 """A mixin for converting a scanner into a heap only scanner."""
326
327 - def __init__(self, task=None, **kwargs):
330
331 - def scan(self, offset=0, maxlen=2**64):
332 for vma in self.task.mm.mmap.walk_list("vm_next"):
333 start = max(vma.vm_start, self.task.mm.start_brk)
334 end = min(vma.vm_end, self.task.mm.brk)
335
336
337 for hit in super(HeapScannerMixIn, self).scan(
338 offset=start, maxlen=end-start):
339 yield hit
340
343 """A plugin mixin which does kernel address checks."""
344
351
352
353 -class Hostname(AbstractLinuxCommandPlugin):
354 __name = "hostname"
355
378
382
383
384
385 -class LinuxPageOffset(AbstractLinuxParameterHook):
386 """The highest address for user mode/kernel mode division."""
387
388 name = "linux_page_offset"
389
390 - def calculate(self):
391 """Returns PAGE_OFFSET."""
392 return self.session.profile.GetPageOffset()
393
396 """The Kernel Address Space Randomization constant.
397
398 Note that this function assumes the profile is already correct. It is
399 not called during the profile guessing phase. So in reality this will
400 only come into play when the user provided the profile specifically.
401 """
402 name = "kernel_slide"
403
405 if self.session.GetCache("execution_phase") == "ProfileAutodetect":
406 raise RuntimeError(
407 "Attempting to get KASLR slide during profile "
408 "autodetection.")
409
410
411 for offset, symbol, type, module in KAllSyms(
412 self.session).ObtainSymbols():
413 if not module and type in ["t", "T"]:
414 offset_from_profile = self.session.profile.get_constant(
415 symbol)
416
417
418
419 return offset - offset_from_profile
420
421
422
423 return 0
424
427 name = "pslist_InitTask"
428
430 seen = set()
431 task_head = self.session.profile.get_constant_object(
432 "init_task", "task_struct")
433
434 for task in task_head.tasks:
435 if task.obj_offset not in seen:
436 seen.add(task.obj_offset)
437
438 return seen
439