mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-23 14:42:25 +02:00
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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
Reference in New Issue
Block a user