1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21  __author__ = "Michael Cohen <scudette@google.com>" 
 22   
 23   
 24   
 25   
 26   
 27  from rekall import kb 
 28  from rekall import plugin 
 29  from rekall import scan 
 30   
 31  from rekall.plugins import core 
 32  from rekall_lib import utils 
 33   
 34   
 35   
 36   
 37   
 38   
 39   
 40   
 41   
 42   
 43   
 44   
 45   
 46   
 47   
 48   
 49   
 50   
 51   
 52   
 53   
 54   
 55   
 56   
 57   
 58   
 59   
 60   
 61   
 62   
 63   
 64   
 65   
 66   
 67   
 68   
 69   
 70   
 71   
 72   
 73   
 74   
 75   
 76   
 77  LOW_4GB_MASK = 0x00000000ffffffff 
 78  KERNEL_MIN_ADDRESS = 0xffffff8000000000 
 83   
 84   
 85  X64_POINTER_MASK = 0x0000ffffffffffff 
 89      """Every Darwin-only plugin or hook will have this mixin in their MRO.""" 
 90   
 91      mode = "mode_darwin_memory" 
  92   
 95      """Base class for session parameter hooks on Darwin.""" 
 96      __abstract = True 
  97   
 98   
 99 -class AbstractDarwinCommand(DarwinOnlyMixin, 
100                              plugin.KernelASMixin, 
101                              plugin.PhysicalASMixin, 
102                              plugin.TypedProfileCommand, 
103                              plugin.ProfileCommand): 
 104      """Base class for Darwin profile commands.""" 
105      __abstract = True 
 106   
110      """Base class for Darwin producers using the physical AS.""" 
111      __abstract = True 
 112   
116      """Base class for Darwin producers backed by a session param hook.""" 
117      __abstract = True 
 118   
121      """Find the kernel slide if needed.""" 
122   
123      name = "vm_kernel_slide" 
124   
 131   
134      checks = [ 
135          ("StringCheck", dict(needle="Catfish \x00\x00")) 
136      ] 
 137   
140      """Find the actual offset of the _lowGlo struct.""" 
141   
142      name = "catfish_offset" 
143   
 152   
155      """Ensures that KASLR slide is computed and stored in the session.""" 
156   
157      @classmethod 
158 -    def args(cls, parser): 
 163   
164 -    def __init__(self, vm_kernel_slide=None, **kwargs): 
  178   
179   
180 -class DarwinFindKASLR(plugin.PhysicalASMixin, DarwinOnlyMixin, 
181                        plugin.ProfileCommand): 
 182      """A scanner for KASLR slide values in the Darwin kernel. 
183   
184      The scanner works by looking up a known data structure and comparing 
185      its actual location to its expected location. Verification is a similar 
186      process, using a second constant. This takes advantage of the fact that both 
187      data structures are in a region of kernel memory that maps to the physical 
188      memory in a predictable way (see ID_MAP_VTOP). 
189   
190      Human-readable output includes values of the kernel version string (which is 
191      used for validation) for manual review, in case there are false positives. 
192      """ 
193   
194      name = "find_kaslr" 
195      mode = "mode_darwin_mountain_lion_plus" 
196   
198          """Yields possible lowGlo offsets, starting with session-cached one. 
199   
200          Because the first hit on the catfish string isn't necessarily the right 
201          one, this function will yield subsequent ones by scanning the physical 
202          address space, starting with the offset of the cached first hit. 
203   
204          The caller is responsible for updating the session cache with the 
205          correct offset. 
206          """ 
207          first_hit = self.session.GetParameter("catfish_offset") 
208          yield first_hit 
209   
210          maxlen = self.session.GetParameter("autodetect_scan_length") 
211          for hit in CatfishScanner( 
212                  address_space=self.session.physical_address_space, 
213                  session=self.session).scan(offset=first_hit + 1, 
214                                             maxlen=maxlen): 
215              yield hit 
 216   
218          """Tries to compute the KASLR slide. 
219   
220          In an ideal scenario, this should return exactly one valid result. 
221   
222          Yields: 
223            (int) semi-validated KASLR value 
224          """ 
225   
226          expected_offset = self.profile.get_constant( 
227              "_lowGlo", is_address=False) 
228          expected_offset = ID_MAP_VTOP(expected_offset) 
229   
230          for hit in self.all_catfish_hits(): 
231              vm_kernel_slide = int(hit - expected_offset) 
232   
233              if self._validate_vm_kernel_slide(vm_kernel_slide): 
234                  self.session.SetCache("catfish_offset", hit) 
235                  yield vm_kernel_slide 
 236   
238          """Returns the first result of vm_kernel_slide hits and stops the scan. 
239   
240          This is the idiomatic way of using this plugin if all you need is the 
241          likely KASLR slide value. 
242   
243          Returns: 
244            A value for the KASLR slide that appears sane. 
245          """ 
246          self.session.logging.debug("Searching for KASLR hits.") 
247          for vm_kernel_slide in self.vm_kernel_slide_hits(): 
248              return vm_kernel_slide 
 249   
251          """Uses vm_kernel_slide to look up kernel version string. 
252   
253          This is used for validation only. Physical address space is 
254          asumed to map to kernel virtual address space as expressed by 
255          ID_MAP_VTOP. 
256   
257          Args: 
258            vm_kernel_slide: KASLR slide to be used for lookup. Overrides whatever 
259            may already be set in session. 
260   
261          Returns: 
262            Kernel version string (should start with "Darwin Kernel" 
263          """ 
264          version_offset = self.profile.get_constant( 
265              "_version", is_address=False) 
266          version_offset += vm_kernel_slide 
267          version_offset = ID_MAP_VTOP(version_offset) 
268   
269          return self.profile.String(vm=self.physical_address_space, 
270                                     offset=version_offset) 
 271   
273          """Checks sanity of vm_kernel_slide by looking up kernel version. 
274          If the result a string that looks like the kernel version string the 
275          slide value is assumed to be valid. Note that this can theoretically 
276          give false positives. 
277   
278          Args: 
279            vm_kernel_slide: KASLR slide to be used for validation. Overrides 
280            whatever may already be set in session. 
281   
282          Returns: 
283            True if vm_kernel_slide value appears sane. False otherwise. 
284          """ 
285          version_string = self._lookup_version_string(vm_kernel_slide) 
286          return version_string[0:13] == "Darwin Kernel" 
 287   
 297   
298   
299 -class DarwinFindDTB(DarwinKASLRMixin, DarwinOnlyMixin, core.FindDTB): 
 300      """Tries to find the DTB address for the Darwin/XNU kernel. 
301   
302      As the XNU kernel developed over the years, the best way of deriving this 
303      information changed. This class now offers multiple methods of finding the 
304      DTB. Calling find_dtb should automatically select the best method for the 
305      job, based on the profile. It will also attempt to fall back on less ideal 
306      ways of getting the DTB if the best way fails. 
307      """ 
308   
309      __name = "find_dtb" 
310   
312          """On 10.8 and later, x64, tries to determine the DTB using IdlePML4. 
313   
314          IdlePML4 is the address (in Kernel AS) of the kernel DTB [1]. The DTB 
315          itself happens to be located in a section of kernel memory that sits at 
316          the base of the physical address space [2], and its virtual address can 
317          be converted to its physical address using the ID_MAP_VTOP macro 
318          which kernel defines for this express purpose [3]. 
319   
320          Should work on: 10.8 and later. 
321          Best for: 10.9 and later. 
322   
323          Yields: 
324            The physical address of the DTB, not verified. 
325   
326          1: 
327          github.com/opensource-apple/xnu/blob/10.9/osfmk/i386/i386_init.c#L281 
328   
329          Here the kernel initializes the page register at the address IdlePML4 
330          points to (masked using the bitmask macro). The same function switches 
331          to the newly initialized address space right before returning. 
332   
333          // IdlePML4 single entry for kernel space. 
334          fillkpt(IdlePML4 + KERNEL_PML4_INDEX, 
335                  INTEL_PTE_WRITE, (uintptr_t)ID_MAP_VTOP(IdlePDPT), 0, 1); 
336   
337          2: 
338          The first page of IdlePML4 is allocated by the ALLOCPAGES function 
339          located here: 
340          github.com/opensource-apple/xnu/blob/10.9/osfmk/i386/i386_init.c#L134 
341   
342          3: 
343          ID_MAP_VTOP is defined here, as simple bitmask: 
344          github.com/opensource-apple/xnu/blob/10.9/osfmk/i386/pmap.h#L353 
345          """ 
346          idlepml4 = ID_MAP_VTOP(self.profile.get_constant( 
347              "_IdlePML4", True)) 
348          dtb = self.profile.Object("unsigned int", offset=idlepml4, 
349                                    vm=self.physical_address_space) 
350          yield int(dtb) 
 351   
353          """The original way of getting the DTB, adapted from Volatility. 
354   
355          I have no idea how or why this is intended to work, but it seems to for 
356          old images. 
357   
358          Should work on: 10.7 and earlier. 
359   
360          Yields: 
361            The physical address of the DTB, not verified. 
362          """ 
363          if self.profile.metadata("arch") == "I386": 
364              result = self.profile.get_constant("_IdlePDPT") 
365   
366               
367               
368              if result % 0x1000: 
369                  result = self.profile.get_constant_object( 
370                      "_IdlePDPT", "unsigned int") 
371   
372              yield result 
373          else: 
374              result = self.profile.get_constant("_IdlePML4", is_address=True) 
375              if result > 0xffffff8000000000: 
376                  result -= 0xffffff8000000000 
377   
378              yield result 
 379   
381          """On 64-bit systems, finds the DTB from the kernel pmap struct. 
382   
383          This is a very easy way of getting the DTB on systems where the kernel 
384          pmap is a static symbol (which seems to be most of them.) 
385   
386          Yields: 
387            The physical address of the DTB, not verified. 
388          """ 
389          kernel_pmap_addr = self.profile.get_constant( 
390              "_kernel_pmap_store", is_address=True) 
391          kernel_pmap = self.profile.pmap(offset=ID_MAP_VTOP(kernel_pmap_addr), 
392                                          vm=self.physical_address_space) 
393          yield int(kernel_pmap.pm_cr3) 
 394   
396          """Determines viable methods of getting the DTB based on profile. 
397   
398          Yields: 
399            Callable object that will yield DTB values. 
400          """ 
401          if self.session.GetParameter("mode_darwin_mountain_lion_plus"): 
402              yield self._dtb_hits_idlepml4 
403          else: 
404              yield self._dtb_hits_legacy 
405   
406          if self.profile.metadata("arch") == "AMD64": 
407              yield self._dtb_hits_kernel_pmap 
 408   
410          for method in self._dtb_methods(): 
411              for dtb_hit in method(): 
412                  yield dtb_hit 
 413   
427   
429          renderer.table_header([("DTB", "dtb", "[addrpad]"), 
430                                 ("Verified", "verified", "8"), 
431                                 ("Source", "method", "15")]) 
432          for method in self._dtb_methods(): 
433              for dtb_hit in method(): 
434                  renderer.table_row( 
435                      dtb_hit, 
436                      self.VerifyHit(dtb_hit) is not None, 
437                      method.__name__) 
  438   
441      """Adds methods and arguments that enable easy fitlering by process.""" 
442   
443      __args = [ 
444          dict(name="pids", type="ArrayIntParser", positional=True, 
445               help="One or more pids of processes to select."), 
446   
447          dict(name="proc_regex", type="RegEx", 
448               help="A regex to select a process by name."), 
449   
450          dict(name="proc", type="ArrayIntParser", 
451               help="Kernel addresses of proc structs."), 
452      ] 
453   
454      @classmethod 
467   
468      @utils.safe_property 
472   
496   
498          """Tries to return an proc in virtual space from a physical offset. 
499   
500          We do this by reflecting off the list elements. 
501   
502          Args: 
503             physical_offset: The physcial offset of the process. 
504   
505          Returns: 
506             A proc object or a NoneObject on failure. 
507          """ 
508          physical_proc = self.profile.proc(offset=int(physical_offset), 
509                                            vm=self.kernel_address_space.base) 
510   
511           
512           
513           
514          our_list_entry = physical_proc.procs.next.dereference( 
515              vm=self.kernel_address_space).prev.dereference() 
516   
517           
518          return our_list_entry.dereference_as("proc_struct", "procs") 
  519   
522      """A plugin mixin which does kernel address checks.""" 
523   
 530