From 5bb2aef9fc9bca1f5293db1f2c09202e2a0565e1 Mon Sep 17 00:00:00 2001 From: "Nick L. Petroni, Jr" Date: Wed, 16 Aug 2017 00:00:21 -0400 Subject: [PATCH] Created linux.proc.Maps plugin. Added object extensions for the key structures. Also added constants.linux, which will contain Linux-specific constants that can't be extracted via dwarf. --- .../framework/constants/linux/__init__.py | 6 ++ volatility/framework/objects/utility.py | 13 ++- .../framework/symbols/linux/__init__.py | 7 ++ .../symbols/linux/extensions/__init__.py | 101 +++++++++++++++++- volatility/plugins/linux/proc.py | 72 +++++++++++++ 5 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 volatility/framework/constants/linux/__init__.py create mode 100644 volatility/plugins/linux/proc.py diff --git a/volatility/framework/constants/linux/__init__.py b/volatility/framework/constants/linux/__init__.py new file mode 100644 index 000000000..677c3724c --- /dev/null +++ b/volatility/framework/constants/linux/__init__.py @@ -0,0 +1,6 @@ +"""Volatility 3 Linux Constants + +Linux-specific values that aren't found in debug symbols""" + +# arch/x86/include/asm/page_types.h +PAGE_SHIFT = 12 diff --git a/volatility/framework/objects/utility.py b/volatility/framework/objects/utility.py index d8792447f..a6c8fa370 100644 --- a/volatility/framework/objects/utility.py +++ b/volatility/framework/objects/utility.py @@ -1,9 +1,18 @@ -from volatility.framework.objects import Array +from volatility.framework import objects def array_to_string(array, errors = 'replace'): """Takes a volatility Array of characters and returns a string""" # TODO: Consider checking the Array's target is a native char - if not isinstance(array, Array): + if not isinstance(array, objects.Array): raise TypeError("Array_to_string takes an Array of char") return array.cast("string", max_length = array.vol.count, errors = errors) + +def pointer_to_string(pointer, count, errors = 'replace'): + """Takes a volatility Pointer to characters and returns a string""" + if not isinstance(pointer, objects.Pointer): + raise TypeError("pointer_to_string takes a Pointer") + if count < 1: + raise ValueError("pointer_to_string requires a positive count") + char = pointer.dereference() + return char.cast("string", max_length = count, errors=errors) diff --git a/volatility/framework/symbols/linux/__init__.py b/volatility/framework/symbols/linux/__init__.py index e6799d10b..6302a58f8 100644 --- a/volatility/framework/symbols/linux/__init__.py +++ b/volatility/framework/symbols/linux/__init__.py @@ -1,4 +1,6 @@ +from volatility.framework import exceptions from volatility.framework.configuration import requirements +from volatility.framework.interfaces import symbols from volatility.framework.symbols import intermed from volatility.framework.symbols.linux import extensions @@ -10,7 +12,12 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): super().__init__(context = context, config_path = config_path, name = name, isf_filepath = isf_filepath) # Set-up Linux specific types + self.set_type_class('file', extensions.struct_file) self.set_type_class('list_head', extensions.list_head) + self.set_type_class('mm_struct', extensions.mm_struct) + self.set_type_class('super_block', extensions.super_block) + self.set_type_class('task_struct', extensions.task_struct) + self.set_type_class('vm_area_struct', extensions.vm_area_struct) @classmethod diff --git a/volatility/framework/symbols/linux/extensions/__init__.py b/volatility/framework/symbols/linux/extensions/__init__.py index bf35b3928..0dd35755d 100644 --- a/volatility/framework/symbols/linux/extensions/__init__.py +++ b/volatility/framework/symbols/linux/extensions/__init__.py @@ -1,14 +1,113 @@ import collections.abc +import volatility.framework.constants.linux as linux_constants from volatility.framework import objects +from volatility.framework import constants +from volatility.framework.symbols import generic +from volatility.framework.objects import utility # Keep these in a basic module, to prevent import cycles when symbol providers require them +class task_struct(generic.GenericIntelProcess): + def add_process_layer(self, config_prefix = None, preferred_name = None): + """Constructs a new layer based on the process's DTB. + Returns the name of the Layer or None. + """ + + parent_layer = self._context.memory[self.vol.layer_name] + pgd = self.mm.pgd + if not pgd: + return None + + dtb, layer_name = parent_layer.translate(pgd) + if not dtb: + return None + + # Add the constructed layer and return the name + return self._add_process_layer(self._context, dtb, config_prefix, preferred_name) + + +class mm_struct(objects.Struct): + @property + def mmap_iter(self): + """Returns an iterator for the mmap list member of an mm_struct.""" + + if not self.mmap: + return + + yield self.mmap + + seen = {self.mmap.vol.offset} + link = self.mmap.vm_next + + while link != 0 and link.vol.offset not in seen: + yield link + seen.add(link.vol.offset) + link = link.vm_next + + +class super_block(objects.Struct): + # include/linux/kdev_t.h + MINORBITS = 20 + + @property + def major(self): + return self.s_dev >> self.MINORBITS + + @property + def minor(self): + return self.s_dev & ((1 << self.MINORBITS) - 1) + + +class vm_area_struct(objects.Struct): + # include/linux/mm.h + VM_READ = 0x00000001 + VM_WRITE = 0x00000002 + VM_EXEC = 0x00000004 + + @property + def flags(self): + """Returns an rwx string representation of the flags in a vm_area_struct.""" + + retval = "" + vm_flags = self.vm_flags + for (bit, char) in ((self.VM_READ, 'r'), (self.VM_WRITE, 'w'), (self.VM_EXEC, 'x')): + if (vm_flags & bit) == bit: + retval = retval + char + else: + retval = retval + '-' + + return retval + + def page_offset(self): + if self.vm_file == 0: + return 0 + + return self.vm_pgoff << linux_constants.PAGE_SHIFT + +class struct_file(objects.Struct): + @property + def full_path(self): + parts = [] + path = self.f_path + path_dentry = path.dentry + seen = set() + while path_dentry != 0 and path_dentry.vol.offset not in seen: + name = utility.pointer_to_string(path_dentry.d_name.name, path_dentry.d_name.len) + if name == "/": + break + parts.insert(0, name) + seen.add(path_dentry.vol.offset) + path_dentry = path_dentry.d_parent + + return "/" + "/".join(parts) + + class list_head(objects.Struct, collections.abc.Iterable): def to_list(self, symbol_type, member, forward = True, sentinel = True, layer = None): - """Returns an iterator of the entries in the list""" + """Returns an iterator of the entries in the list.""" if layer is None: layer = self.vol.layer_name diff --git a/volatility/plugins/linux/proc.py b/volatility/plugins/linux/proc.py new file mode 100644 index 000000000..38eda858f --- /dev/null +++ b/volatility/plugins/linux/proc.py @@ -0,0 +1,72 @@ +"""A module containing a collection of plugins that produce data +typically found in Linux's /proc file system. +""" + +from volatility.framework.interfaces import plugins +from volatility.framework import renderers +from volatility.framework.renderers import format_hints +from volatility.framework.objects import utility +from volatility.plugins.linux import pslist + + + +class Maps(plugins.PluginInterface): + """Lists all memory maps for all processes""" + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return pslist.PsList.get_requirements() + [] + + + def _generator(self, tasks): + for task in tasks: + if not task.mm: + continue + + name = utility.array_to_string(task.comm) + + for vma in task.mm.mmap_iter: + flags = vma.flags + page_offset = vma.page_offset() + major = 0 + minor = 0 + inode = 0 + path = "" + + if vma.vm_file != 0: + inode_object = vma.vm_file.f_path.dentry.d_inode + major = inode_object.i_sb.major + minor = inode_object.i_sb.minor + inode = inode_object.i_ino + path = vma.vm_file.full_path + + yield( + 0, + (task.pid, + name, + format_hints.Hex(vma.vm_start), + format_hints.Hex(vma.vm_end), + flags, + format_hints.Hex(page_offset), + major, + minor, + inode, + path + )) + + def run(self): + plugin = pslist.PsList(self.context, "plugins.Maps") + + return renderers.TreeGrid( + [("PID", int), + ("Process", str), + ("Start", format_hints.Hex), + ("End", format_hints.Hex), + ("Flags", str), + ("PgOff", format_hints.Hex), + ("Major", int), + ("Minor", int), + ("Inode", int), + ("File Path", str)], + self._generator(plugin.list_tasks()))