1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """
16 @author: Michael Cohen
17 @license: GNU General Public License 2.0 or later
18 @contact: scudette@gmail.com
19 @organization: Google Inc.
20
21 This file encapsulates various virtual file system operations for supported
22 linux versions. The code is basically copied from the kernel sources of the
23 relevant versions.
24 """
25 import posixpath
26 import re
27 import math
28
29 from rekall import obj
30 from rekall.plugins.overlays import basic
31 from rekall_lib import utils
32
33
34 -class File(object):
35 """Represents a Linux file."""
36
37 - def __init__(self, filename=None, mountpoint=None, dentry=None,
38 is_root=False, session=None):
51
52 @utils.safe_property
54 if self.is_root:
55 return self.mountpoint.name
56 else:
57 return '/'.join([self.mountpoint.name.rstrip("/"),
58 '/'.join(self.filename)])
59
60 @utils.safe_property
66
67 @utils.safe_property
69 return self.dentry.d_inode.i_size
70
71 @utils.safe_property
73 """Returns a list of ranges for which we have data in memory."""
74 page_size = self.session.kernel_address_space.PAGE_SIZE
75 range_start = None
76
77 index = 0
78 while index <= (self.size / page_size):
79 page = self._radix_tree_lookup(index)
80 if page:
81
82 if range_start == None:
83 range_start = index * page_size
84 else:
85
86 if range_start != None:
87 range_end = (index * page_size) -1
88 range_end = min(self.size, range_end)
89 yield (range_start, range_end)
90 range_start = None
91 index += 1
92
93
94
95
96
97
98 if range_start != None:
99 range_end = (index * page_size) - 1
100 range_end = min(self.size, range_end)
101 yield (range_start, range_end)
102
104 """See include/linux/radix-tree.h -> is_indirect_ptr()."""
105 return ptr & 1
106
108 """See lib/radix-tree.c -> indirect_to_ptr()."""
109
110 return self.dentry.obj_profile.Object(
111 "Pointer", target="radix_tree_node",
112 value=node_p.v() & ~1,
113 profile=node_p.obj_profile,
114 vm=node_p.obj_vm)
115
117 """See lib/radix-tree.c -> radix_tree_lookup_element().
118
119 Searches either for the slot or the node of the index of a radix_tree.
120 """
121
122 root = self.dentry.d_inode.i_mapping.page_tree
123 node = root.rnode
124 if not node:
125 return obj.NoneObject()
126
127
128 self.RADIX_TREE_MAP_SIZE = len(node.slots)
129 self.RADIX_TREE_MAP_SHIFT = int(
130 math.log(self.RADIX_TREE_MAP_SIZE) / math.log(2))
131 self.RADIX_TREE_MAP_MASK = self.RADIX_TREE_MAP_SIZE - 1
132
133 if not self._radix_tree_is_indirect_ptr(node):
134 if index > 0:
135 return obj.NoneObject()
136 if is_slot:
137 return node.reference()
138 else:
139 return node
140
141 node = self._radix_tree_indirect_to_ptr(node)
142 height = node.height
143 shift = (height - 1) * self.RADIX_TREE_MAP_SHIFT
144 slot = -1
145
146 while 1:
147 idx = (index >> shift) & self.RADIX_TREE_MAP_MASK
148 node = node.slots[idx]
149 node = node.cast("Pointer", target="radix_tree_node")
150 slot = node.reference()
151 shift -= self.RADIX_TREE_MAP_SHIFT
152 height -= 1
153 if height <= 0:
154 break
155
156 if slot == -1:
157 return obj.NoneObject()
158
159 if is_slot:
160 return slot
161 else:
162 return self._radix_tree_indirect_to_ptr(node)
163
165 """See lib/radix-tree.c ."""
166 return self._radix_tree_lookup_element(index, 1)
167
169 """See lib/radix-tree.c ."""
170 return self._radix_tree_lookup_element(index, 0)
171
172 - def GetPage(self, page_index):
173 page_size = self.session.kernel_address_space.PAGE_SIZE
174 page = self._radix_tree_lookup(page_index)
175 if page:
176 page = page.dereference_as("page")
177 return page.read(0, page_size)
178
179 - def walk(self, recursive=False, unallocated=False):
211
213 if isinstance(other, File):
214 return (self.fullpath == other.fullpath and
215 self.dentry == other.dentry and
216 self.mountpoint == other.mountpoint)
217 return False
218
221
224 """Represents a Linux mount point."""
225
226 - def __init__(self, device="(undefined device)",
227 mount_path="(undefined mount path)",
228 superblock=None, flags=None, session=None):
229 self.device = device
230 self.name = unicode(mount_path)
231 self.sb = superblock
232 self.flags = flags
233 self.session = session
234
235 - def walk(self, recursive=False, unallocated=False):
236 """Yields Files for each file in this mountpoint."""
237
238 if self.sb and self.sb.s_root.d_inode.type.S_IFDIR:
239
240 root_file = File(mountpoint=self,
241 dentry=self.sb.s_root,
242 is_root=True,
243 session=self.session)
244 for sub_file in root_file.walk(recursive=recursive,
245 unallocated=unallocated):
246 yield sub_file
247
248 @utils.safe_property
251
253 if isinstance(other, MountPoint):
254 return (self.sb == other.sb
255 and self.device == other.device
256 and self.name == other.name)
257 return False
258
261 """An object to represent a filename."""
262 MAX_DEPTH = 15
263
264 - def __init__(self, components=None, start_dentry=None):
265 self.start_dentry = start_dentry
266 self.mount_point = "/"
267 self.deleted = False
268 if components is None:
269 components = []
270 self.path_components = components
271
273 self.path_components.insert(0, utils.SmartUnicode(component))
274
296
298 if self.deleted:
299 deleted = " (deleted) "
300 else:
301 deleted = ""
302
303 return u"%s%s%s" % (self.mount_point,
304 "/".join(self.path_components), deleted)
305
308 """This is the implementation specific VFS operations.
309
310 Most of the code here is a direct copy of the methods in linux/fs/dcache.c
311
312 http://lxr.free-electrons.com/source/fs/mount.h?v=3.7#L53
313 http://lxr.free-electrons.com/source/fs/dcache.c?v=3.7#L2576
314 """
315
317
318 self.profile = profile
319 if self.profile.get_constant("set_mphash_entries"):
320 self.prepend_path = self._prepend_path314
321 elif self.profile.has_type("mount"):
322 self.prepend_path = self._prepend_path303
323 else:
324 self.prepend_path = self._prepend_path300
325
327 """Resolve the dentry, vfsmount relative to this task's chroot.
328
329 Returns:
330 An absolute path to the global filesystem mount. (I.e. we do not
331 truncate the path at the chroot point as the kernel does).
332 """
333 return posixpath.normpath(self.prepend_path(filp.f_path, task.fs.root))
334
338
340 return mnt != mnt.mnt_parent
341
343 """Return the path of a dentry for 3.0-3.2 kernels.
344
345 http://lxr.free-electrons.com/source/fs/dcache.c?v=3.2#L2576
346 """
347
348
349 depth = 0
350 dentry = path.dentry
351 vfsmnt = path.mnt
352
353 result = FileName(start_dentry=dentry)
354
355
356 if dentry.d_flags.DCACHE_UNHASHED and not dentry.is_root:
357 result.deleted = True
358
359 while dentry != root.dentry or vfsmnt != root.mnt:
360
361 depth += 1
362 if depth >= result.MAX_DEPTH:
363 break
364
365 if dentry == vfsmnt.mnt_root or dentry.is_root:
366
367 if vfsmnt.mnt_parent == vfsmnt:
368 result.PrependName("")
369 break
370 dentry = vfsmnt.mnt_mountpoint
371 vfsmnt = vfsmnt.mnt_parent
372 continue
373
374 parent = dentry.d_parent
375 if dentry.d_name.name:
376 result.PrependName(dentry.d_name.name.deref())
377 dentry = parent
378
379
380
381
382 result.mount_point = vfsmnt.mnt_mountpoint.d_name.name.deref()
383
384 return result.FormatName(dentry)
385
387 """Return the path of a dentry for 3.3-3.13 kernels.
388
389 Linxu 3.3 introduced the struct mount, and moved some fields between
390 struct mount and struct vfsmount.
391
392 http://lxr.free-electrons.com/source/fs/dcache.c?v=3.7#L2576
393 """
394
395
396 depth = 0
397 dentry = path.dentry
398 vfsmnt = path.mnt
399 mnt = self._real_mount(vfsmnt)
400 slash = False
401
402 result = FileName(start_dentry=dentry)
403
404
405 if dentry.d_flags.DCACHE_UNHASHED and not dentry.is_root:
406 result.deleted = True
407
408 while dentry != root.dentry or vfsmnt != root.mnt:
409
410 depth += 1
411 if depth >= result.MAX_DEPTH:
412 break
413
414 if dentry == vfsmnt.mnt_root or dentry.is_root:
415
416 if not self._mnt_has_parent(mnt):
417 if not slash:
418 result.PrependName("")
419 break
420 dentry = mnt.mnt_mountpoint
421 mnt = mnt.mnt_parent
422 vfsmnt = mnt.mnt
423 continue
424
425 parent = dentry.d_parent
426 if dentry.d_name.name:
427 result.PrependName(dentry.d_name.name.deref())
428 slash = True
429 dentry = parent
430
431
432
433
434 result.mount_point = mnt.mnt_mountpoint.d_name.name.deref()
435
436 return result.FormatName(dentry)
437
439 """Return the path of a dentry for 3.14 kernels.
440
441 http://lxr.free-electrons.com/source/fs/dcache.c?v=3.14#L2867
442 """
443
444
445 depth = 0
446 dentry = path.dentry
447 vfsmnt = path.mnt
448 mnt = self._real_mount(vfsmnt)
449 result = FileName(start_dentry=dentry)
450
451
452 if dentry.d_flags.DCACHE_UNHASHED and not dentry.is_root:
453 result.deleted = True
454
455 while dentry != root.dentry or vfsmnt != root.mnt:
456
457 depth += 1
458 if depth >= result.MAX_DEPTH:
459 break
460
461 if dentry == vfsmnt.mnt_root or dentry.is_root:
462 parent = mnt.mnt_parent
463
464 if mnt != parent:
465 dentry = mnt.mnt_mountpoint
466 mnt = mnt.mnt_parent
467 vfsmnt = mnt.mnt
468 continue
469
470 parent = dentry.d_parent
471 if dentry.d_name.name:
472 result.PrependName(dentry.d_name.name.deref())
473 dentry = parent
474
475
476
477
478 result.mount_point = mnt.mnt_mountpoint.d_name.name.deref()
479
480 return result.FormatName(dentry)
481
484 """This is the implementation specific VFS operations.
485
486 Most of the code here is a direct copy of the methods in linux/fs/dcache.c
487
488 http://lxr.free-electrons.com/source/fs/dcache.c?v=2.6.26#L1782
489 """
490
492 """Resolve the dentry, vfsmount relative to this task's chroot.
493
494 Returns:
495 An absolute path to the global filesystem mount. (I.e. we do not
496 truncate the path at the chroot point as the kernel does).
497 """
498 root = task.fs.root.deref()
499
500
501 if root.obj_type == "dentry":
502 root = task.obj_profile.path()
503 root.dentry = task.fs.root
504 root.mnt = task.fs.rootmnt
505
506 return self.__d_path(filp.f_path, root)
507
509 return dentry.d_flags.DCACHE_UNHASHED
510
550