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