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.
This commit is contained in:
Nick L. Petroni, Jr
2017-08-16 16:14:33 -04:00
parent efc2d75a86
commit 5bb2aef9fc
5 changed files with 196 additions and 3 deletions
@@ -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
+11 -2
View File
@@ -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)
@@ -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
@@ -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
+72
View File
@@ -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()))