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