major updates and new plugins

This commit is contained in:
Andrew Case
2018-11-08 00:19:17 +00:00
committed by ikelos
parent 63e9cd4604
commit 98e472e211
10 changed files with 561 additions and 214 deletions
+167
View File
@@ -6,6 +6,8 @@ from volatility.framework.automagic import linux_symbol_cache
from volatility.framework.configuration import requirements
from volatility.framework.layers import intel, scanners
from volatility.framework.symbols import linux
from volatility.framework.symbols import utility as symbols_utility
from volatility.framework.objects import utility
vollog = logging.getLogger(__name__)
@@ -166,6 +168,171 @@ class LintelStacker(interfaces.automagic.StackerLayerInterface):
class LinuxUtilities(object):
"""Class with multiple useful linux functions"""
# based on __d_path from the Linux kernel
@classmethod
def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str:
try:
rdentry.validate()
dentry.validate()
except InvalidDataException:
return ""
ret_path = [] # type: typing.List[str]
while dentry != rdentry or vfsmnt != rmnt:
dname = dentry.path()
if dname == "":
break
ret_path.insert(0, dname.strip('/'))
if dentry == vfsmnt.get_mnt_root() or dentry == dentry.d_parent:
if vfsmnt.get_mnt_parent() == vfsmnt:
break
dentry = vfsmnt.get_mnt_mountpoint()
vfsmnt = vfsmnt.get_mnt_parent()
continue
parent = dentry.d_parent
dentry = parent
if ret_path == []:
return ""
ret_val = '/'.join([str(p) for p in ret_path if p != ""])
if ret_val.startswith(("socket:", "pipe:")):
if ret_val.find("]") == -1:
try:
inode = dentry.d_inode
ino = inode.i_ino
except exceptions.InvalidAddressException:
ino = 0
ret_val = ret_val[:-1] + ":[{0}]".format(ino)
else:
ret_val = ret_val.replace("/", "")
elif ret_val != "inotify":
ret_val = '/' + ret_val
return ret_val
# method used by 'older' kernels
# TODO: lookup when dentry_operations->d_name was merged into the mainline kernel for exact version
@classmethod
def _get_path_file(cls, task, filp) -> str:
rdentry = task.fs.get_root_dentry()
rmnt = task.fs.get_root_mnt()
dentry = filp.get_dentry()
vfsmnt = filp.get_vfsmnt()
return LinuxUtilities._do_get_path(rdentry, rmnt, dentry, vfsmnt)
@classmethod
def _get_new_sock_pipe_path(cls, task, filp) -> str:
dentry = filp.get_dentry()
sym_addr = dentry.d_op.d_dname
# IKELOS: _contet.symbol_space has already been masked (including ASLR) by the run() function of the calling plugin. This makes the code super clean
symbols = list(dentry._context.symbol_space.get_symbols_by_location(sym_addr))
if len(symbols) == 1:
sym = symbols[0].split("!")[1]
if sym == "sockfs_dname":
pre_name = "socket"
elif sym == "anon_inodefs_dname":
pre_name = "anon_inode"
elif sym == "pipefs_dname":
pre_name = "pipe"
elif sym == "simple_dname":
pre_name = self._get_path_file(filp)
else:
pre_name = "<unsupported d_op symbol: {0}>".format(sym)
ret = "{0}:[{1:d}]".format(pre_name, dentry.d_inode.i_ino)
else:
ret = "<invalid d_dname pointer> {0:x}".format(sym_addr)
return ret
# a 'file' structure doesn't have enough information to properly restore its full path
# we need the root mount information from task_struct to determine this
@classmethod
def path_for_file(cls, task, filp) -> str:
try:
dentry = filp.get_dentry()
except exceptions.InvalidAddressException:
return ""
if dentry == 0:
return ""
dname_is_valid = False
# TODO COMPARE THIS IN LSOF OUTPUT TO VOL2
try:
if dentry.d_op and hasattr(dentry.d_op, "d_dname") and dentry.d_op.d_dname:
dname_is_valid = True
except exceptions.InvalidAddressException:
dname_is_valid = False
if dname_is_valid:
ret = LinuxUtilities._get_new_sock_pipe_path(task, filp)
else:
ret = LinuxUtilities._get_path_file(task, filp)
return ret
# IKELOS: 'task' will always be a task_struct as defined in the profile json. Do I type this in the parameter list? If so, how?
# IKELOS: what should the type of 'config' be?
@classmethod
def files_descriptors_for_process(cls,
config,
context: interfaces.context.ContextInterface,
task):
fd_table = task.files.get_fds()
if fd_table == 0:
return
max_fds = task.files.get_max_fds()
# corruption check
if max_fds > 500000:
return
file_type = config["vmlinux"] + constants.BANG + 'file'
fds = utility.array_of_pointers(fd_table, count = max_fds, subtype = file_type, context = context)
for (fd_num, filp) in enumerate(fds):
if filp != 0:
full_path = LinuxUtilities.path_for_file(task, filp)
yield fd_num, filp, full_path
@classmethod
def aslr_mask_symbol_table(cls,
config,
context: interfaces.context.ContextInterface):
aslr_layer = config['primary.memory_layer']
_, aslr_shift = LinuxUtilities.find_aslr(context, config["vmlinux"], aslr_layer)
sym_table_name = config["vmlinux"]
sym_layer_name = config["primary"]
symbols_utility.mask_symbol_table(context.symbol_space[sym_table_name], context.memory[sym_layer_name].address_mask, aslr_shift)
@classmethod
def find_aslr(cls,
context: interfaces.context.ContextInterface,
@@ -29,6 +29,7 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable):
self.set_type_class('files_struct', extensions.files_struct)
self.set_type_class('vfsmount', extensions.vfsmount)
self.set_type_class('mount', extensions.mount)
self.set_type_class('module', extensions.module)
@classmethod
def get_requirements(cls) -> typing.List[interfaces.configuration.RequirementInterface]:
@@ -3,12 +3,32 @@ import typing
from volatility.framework import constants
from volatility.framework import exceptions, objects, interfaces
from volatility.framework.objects import utility
from volatility.framework.symbols import generic
from volatility.framework.objects import utility as objects_utility
from volatility.framework.automagic import linux
from volatility.framework.symbols import generic, utility
# Keep these in a basic module, to prevent import cycles when symbol providers require them
class module(generic.GenericIntelProcess):
def get_init_size(self):
if hasattr(self, "init_layout"):
return self.init_layout.size
elif hasattr(self, "init_size"):
return self.init_size
raise AttributeError("module -> get_init_size: Unable to determine .init section size of module")
def get_core_size(self):
if hasattr(self, "core_layout"):
return self.core_layout.size
elif hasattr(self, "core_size"):
return self.core_size
raise AttributeError("module -> get_core_size: Unable to determine initial size of module")
class task_struct(generic.GenericIntelProcess):
def add_process_layer(self,
config_prefix: str = None,
@@ -18,8 +38,9 @@ class task_struct(generic.GenericIntelProcess):
"""
parent_layer = self._context.memory[self.vol.layer_name]
pgd = self.mm.pgd
if not pgd:
try:
pgd = self.mm.pgd
except exceptions.PagedInvalidAddressException:
return None
if not isinstance(parent_layer, interfaces.layers.TranslationLayerInterface):
@@ -32,133 +53,6 @@ class task_struct(generic.GenericIntelProcess):
# Add the constructed layer and return the name
return self._add_process_layer(self._context, dtb, config_prefix, preferred_name)
# based on __d_path from the Linux kernel
def _do_get_path(self, rdentry, rmnt, dentry, vfsmnt) -> str:
try:
rdentry.validate()
dentry.validate()
except InvalidDataException:
return ""
ret_path = [] # type: typing.List[str]
try:
inode = dentry.d_inode
ino = inode.i_ino
except exceptions.InvalidAddressException:
ino = 0
while dentry != rdentry or vfsmnt != rmnt:
dname = dentry.path()
if dname == "":
break
ret_path.insert(0, dname.strip('/'))
if dentry == vfsmnt.get_mnt_root() or dentry == dentry.d_parent:
if vfsmnt.get_mnt_parent() == vfsmnt:
break
dentry = vfsmnt.get_mnt_mountpoint()
vfsmnt = vfsmnt.get_mnt_parent()
continue
parent = dentry.d_parent
dentry = parent
if ret_path == []:
return ""
ret_val = '/'.join([str(p) for p in ret_path if p != ""])
if ret_val.startswith(("socket:", "pipe:")):
if ret_val.find("]") == -1:
ret_val = ret_val[:-1] + ":[{0}]".format(ino)
else:
ret_val = ret_val.replace("/", "")
elif ret_val != "inotify":
ret_val = '/' + ret_val
return ret_val
# old method
def _get_path_file(self, filp) -> str:
rdentry = self.fs.get_root_dentry()
rmnt = self.fs.get_root_mnt()
dentry = filp.dentry
vfsmnt = filp.vfsmnt
return self._do_get_path(rdentry, rmnt, dentry, vfsmnt)
def _get_new_sock_pipe_path(self, filp, layer_name) -> str:
dentry = filp.dentry
sym_addr = dentry.d_op.d_dname
# BUG - ikelos please read
# layer.address_mask is currently a @propery so the code is awkward, such as:
# sym_addr = sym_addr | self._context.memory[layer_name].address_mask
# is there a reason it couldn't just be a normal function so address_mask(sym_addr) would work?
# the second issue is that the mask I am getting is 0x1ffffffff, when I really need it OR'd with 0xffffffff00000000 in order to get the correct value
# third - having to pass in 'layer_name' way from the plugin is pretty ugly, is there a better way to get access to the mask?
# TODO - this is currently not ASLR aware, which makes the lookups fail, after the masking issue is fixed, test for ASLR handling
symbols = list(self._context.symbol_space.get_symbols_by_location(sym_addr))
if len(symbols) == 1:
sym = symbols[0].split("!")[1]
if sym == "sockfs_dname":
pre_name = "socket"
elif sym == "anon_inodefs_dname":
pre_name = "anon_inode"
elif sym == "pipefs_dname":
pre_name = "pipe"
elif sym == "simple_dname":
pre_name = self._get_path_file(filp)
else:
pre_name = "<unsupported d_op symbol: {0}>".format(sym)
ret = "{0}:[{1:d}]".format(pre_name, dentry.d_inode.i_ino)
else:
ret = "<invalid d_dname pointer> {0:d}".format(sym_addr)
return ret
# a 'file' structure doesn't have enough information to properly restore its full path
# we need the root mount information from task_struct to determine this
def path_for_file(self, filp, layer_name) -> str:
try:
dentry = filp.dentry
except exceptions.InvalidAddressException:
return ""
if dentry == 0:
return ""
dname_is_valid = False
# TODO COMPARE THIS IN LSOF OUTPUT TO VOL2
try:
if dentry.d_op and hasattr(dentry.d_op, "d_dname") and dentry.d_op.d_dname:
dname_is_valid = True
except exceptions.InvalidAddressException:
dname_is_valid = False
if dname_is_valid:
ret = self._get_new_sock_pipe_path(filp, layer_name)
else:
ret = self._get_path_file(filp)
return ret
class fs_struct(objects.Struct):
def get_root_dentry(self):
# < 2.6.26
@@ -167,13 +61,11 @@ class fs_struct(objects.Struct):
else:
return self.root.dentry
raise AttributeError("Unable to find the root dentry")
def get_root_mnt(self):
# < 2.6.26
if hasattr(self, "rootmnt"):
return self.rootmnt
else:
elif hasattr(self.root, "mnt"):
return self.root.mnt
raise AttributeError("Unable to find the root mount")
@@ -264,19 +156,47 @@ class vm_area_struct(objects.Struct):
return retval
# only parse the rwx bits
def protection(self) -> str:
def get_protection(self) -> str:
return self._parse_flags(self.vm_flags & 0b1111, vm_area_struct.perm_flags)
# used by malfind
def flags(self) -> str:
def get_flags(self) -> str:
return self._parse_flags(self.vm_flags, extended_flags)
def page_offset(self) -> int:
def get_page_offset(self) -> int:
if self.vm_file == 0:
return 0
return self.vm_pgoff << constants.linux.PAGE_SHIFT
def get_name(self, task):
if self.vm_file != 0:
fname = linux.LinuxUtilities.path_for_file(task, self.vm_file)
elif self.vm_start <= task.mm.start_brk and self.vm_end >= task.mm.brk:
fname = "[heap]"
elif self.vm_start <= task.mm.start_stack and self.vm_end >= task.mm.start_stack:
fname = "[stack]"
elif hasattr(self.vm_mm.context, "vdso") and self.vm_start == self.vm_mm.context.vdso:
fname = "[vdso]"
else:
fname = "Anonymous Mapping"
return fname
# used by malfind
def is_suspicious(self):
ret = True
flags_str = self.get_protection()
if flags_str.find("VM_READ|VM_WRITE|VM_EXEC") != -1:
ret = True
elif flags_str == "VM_READ|VM_EXEC" and self.vm_file != 0:
ret = True
return ret
class qstr(objects.Struct):
def name_as_str(self) -> str:
if hasattr(self, "len"):
@@ -285,7 +205,7 @@ class qstr(objects.Struct):
str_length = 255
try:
ret = utility.pointer_to_string(self.name, str_length)
ret = objects_utility.pointer_to_string(self.name, str_length)
except exceptions.InvalidAddressException:
ret = ""
@@ -296,23 +216,21 @@ class dentry(objects.Struct):
return self.d_name.name_as_str()
class struct_file(objects.Struct):
@property
def dentry(self) -> interfaces.objects.ObjectInterface:
def get_dentry(self) -> interfaces.objects.ObjectInterface:
if hasattr(self, "f_dentry"):
return self.f_dentry
else:
elif hasattr(self, "f_path"):
return self.f_path.dentry
raise AttributeError("Unable to find file -> dentry")
else:
raise AttributeError("Unable to find file -> dentry")
@property
def vfsmnt(self) -> interfaces.objects.ObjectInterface:
def get_vfsmnt(self) -> interfaces.objects.ObjectInterface:
if hasattr(self, "f_vfsmnt"):
return self.f_vfsmnt
else:
elif hasattr(self, "f_path"):
return self.f_path.mnt
raise AttributeError("Unable to find file -> vfs mount")
else:
raise AttributeError("Unable to find file -> vfs mount")
class list_head(objects.Struct, collections.abc.Iterable):
def to_list(self,
@@ -350,44 +268,44 @@ class files_struct(objects.Struct):
def get_fds(self) -> interfaces.objects.ObjectInterface:
if hasattr(self, "fdt"):
return self.fdt.fd.dereference()
else:
elif hasattr(self, "fd"):
return self.fd.dereference()
raise AttributeError("Unable to find files -> file descriptors")
else:
raise AttributeError("Unable to find files -> file descriptors")
def get_max_fds(self) -> interfaces.objects.ObjectInterface:
if hasattr(self, "fdt"):
return self.fdt.max_fds
else:
elif hasattr(self, "max_fds"):
return self.max_fds
raise AttributeError("Unable to find files -> maximum file descriptors")
else:
raise AttributeError("Unable to find files -> maximum file descriptors")
class mount(objects.Struct):
def get_mnt_sb(self):
if hasattr(self, "mnt"):
return self.mnt.mnt_sb
else:
elif hasattr(self, "mnt_sb"):
return self.mnt_sb
raise AttributeError("Unable to find mount -> super block")
else:
raise AttributeError("Unable to find mount -> super block")
def get_mnt_root(self):
if hasattr(self, "mnt"):
return self.mnt.mnt_root
else:
elif hasattr(self, "mnt_root"):
return self.mnt_root
raise AttributeError("Unable to find mount -> mount root")
else:
raise AttributeError("Unable to find mount -> mount root")
def get_mnt_flags(self):
if hasattr(self, "mnt"):
return self.mnt.mnt_flags
else:
elif hasattr(self, "mnt_flags"):
return self.mnt_flags
raise AttributeError("Unable to find mount -> mount flags")
else:
raise AttributeError("Unable to find mount -> mount flags")
def get_mnt_parent(self):
return self.mnt_parent
@@ -402,11 +320,9 @@ class vfsmount(objects.Struct):
self.get_mnt_parent() != 0
def _get_real_mnt(self):
table_name = self.vol.type_name.split(constants.BANG)[0]
table_name = self.vol.type_name.split(constants.BANG)[0]
mount_struct = "{0}{1}mount".format(table_name, constants.BANG)
offset = self._context.symbol_space.get_type(mount_struct).relative_child_offset("mnt")
offset = self._context.symbol_space.get_type(mount_struct).relative_child_offset("mnt")
return self._context.object(mount_struct, self.vol.layer_name, offset = self.vol.offset - offset)
+61
View File
@@ -0,0 +1,61 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
from volatility.framework import renderers
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.framework.automagic import linux
from volatility.plugins.linux import pslist
class Elfs(plugins.PluginInterface):
"""Lists all memory mapped ELF files 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:
proc_layer_name = task.add_process_layer()
if proc_layer_name == None:
continue
proc_layer = self.context.memory[proc_layer_name]
name = utility.array_to_string(task.comm)
for vma in task.mm.mmap_iter:
hdr = proc_layer.read(vma.vm_start, 4, pad = True)
if not (hdr[0] == 0x7f and hdr[1] == 0x45 and hdr[2] == 0x4c and hdr[3] == 0x46):
continue
path = vma.get_name(task)
yield (
0,
(task.pid,
name,
format_hints.Hex(vma.vm_start),
format_hints.Hex(vma.vm_end),
path
))
def run(self):
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("Start", format_hints.Hex),
("End", format_hints.Hex),
("File Path", str)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
+60
View File
@@ -0,0 +1,60 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
import datetime, os
from volatility.framework import renderers, constants, interfaces
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.framework.automagic import linux
from volatility.plugins.linux import pslist
class Lsmod(plugins.PluginInterface):
"""Lists loaded kernel modules"""
@classmethod
def get_requirements(cls):
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
@classmethod
def list_modules(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
vmlinux_symbols: str):
"""Lists all the modules in the primary layer"""
_, aslr_shift = linux.LinuxUtilities.find_aslr(context, vmlinux_symbols, layer_name)
vmlinux = context.module(vmlinux_symbols, layer_name, aslr_shift)
module_head_addr = vmlinux.object(symbol_name = "modules").vol.offset
modules = vmlinux.object(type_name = "list_head", offset = module_head_addr)
table_name = modules.vol.type_name.split(constants.BANG)[0]
for module in modules.to_list("{}{}module".format(table_name, constants.BANG), "list"):
yield module
def _generator(self):
for module in self.list_modules(self.context,
self.config['primary'],
self.config['vmlinux']):
mod_size = module.get_init_size() + module.get_core_size()
mod_name = utility.array_to_string(module.name)
yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size)
def run(self):
return renderers.TreeGrid(
[("Offset", format_hints.Hex),
("Name", str),
("Size", int)],
self._generator())
+18 -29
View File
@@ -3,12 +3,15 @@ typically found in Linux's /proc file system.
"""
import logging
from volatility.framework import interfaces
from volatility.framework.interfaces import plugins
from volatility.framework import renderers
from volatility.framework import constants
from volatility.framework.automagic import linux
from volatility.framework.renderers import format_hints
from volatility.framework.objects import utility
from volatility.framework.symbols import utility as symbols_utility
from volatility.plugins.linux import pslist
vollog = logging.getLogger(__name__)
@@ -21,43 +24,29 @@ class Lsof(plugins.PluginInterface):
# Since we're calling the plugin, make sure we have the plugin's requirements
return pslist.PsList.get_requirements() + []
# yields list of data, e.g.: calculate
def _generator(self, tasks):
layer_name = self.config['primary.memory_layer']
_, aslr_shift = linux.LinuxUtilities.find_aslr(self.context, self.config["vmlinux"], layer_name)
vmlinux = self.context.module(self.config["vmlinux"], self.config["primary"], aslr_shift)
pointer_template = self.context.symbol_space[self.config['vmlinux']].get_type('pointer')
for task in tasks:
fd_table = task.files.get_fds()
if fd_table == 0:
continue
name = str(task.comm)
pid = int(task.pid)
max_fds = task.files.get_max_fds()
proc_name = utility.array_to_string(task.comm)
# corruption check
if max_fds > 500000:
continue
fds = vmlinux.object(type_name="array", offset = fd_table.vol.offset, subtype = pointer_template, count = max_fds)
for (i, fd_ptr) in enumerate(fds):
if fd_ptr:
filp = fd_ptr.dereference().cast(self.config["vmlinux"] + constants.BANG + 'file')
full_path = task.path_for_file(filp, layer_name)
yield (0, (task.pid, proc_name, i, full_path))
for fd_num, _, full_path in linux.LinuxUtilities.files_descriptors_for_process(self.config, self.context, task):
yield (0, (pid, name, fd_num, full_path))
def run(self):
plugin = pslist.PsList(self.context, "plugins.Lsof")
linux.LinuxUtilities.aslr_mask_symbol_table(self.config, self.context)
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("FD", int),
("Path", str)],
self._generator(plugin.list_tasks()))
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
+75
View File
@@ -0,0 +1,75 @@
import volatility.framework.interfaces.plugins as interfaces_plugins
import volatility.framework.interfaces.renderers as interfaces_renderers
import volatility.plugins.linux.pslist as pslist
from volatility.framework import constants
from volatility.framework import renderers
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
class Malfind(interfaces_plugins.PluginInterface):
"""Lists process memory ranges that potentially contain injected code"""
@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 list_injections(self, task):
"""Generate memory regions for a process that may contain
injected code.
"""
proc_layer_name = task.add_process_layer()
if proc_layer_name == None:
return
proc_layer = self.context.memory[proc_layer_name]
for vma in task.mm.mmap_iter:
if vma.is_suspicious() and vma.get_name(task) != "[vdso]":
data = proc_layer.read(vma.vm_start, 64, pad = True)
yield vma, data
def _generator(self, tasks):
# determine if we're on a 32 or 64 bit kernel
if self.context.symbol_space.get_type(self.config["vmlinux"] + constants.BANG + "pointer").size == 4:
is_32bit_arch = True
else:
is_32bit_arch = False
for task in tasks:
process_name = utility.array_to_string(task.comm)
for vma, data in self.list_injections(task):
if is_32bit_arch:
architecture = "intel"
else:
architecture = "intel64"
disasm = interfaces_renderers.Disassembly(data, vma.vm_start, architecture)
yield (0, (task.pid,
process_name,
format_hints.Hex(vma.vm_start),
format_hints.Hex(vma.vm_end),
vma.get_protection(),
format_hints.HexBytes(data),
disasm))
def run(self):
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid([("PID", int),
("Process", str),
("Start", format_hints.Hex),
("End", format_hints.Hex),
("Protection", str),
("Hexdump", format_hints.HexBytes),
("Disasm", interfaces_renderers.Disassembly)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
+20 -12
View File
@@ -7,9 +7,9 @@ from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.framework.automagic import linux
from volatility.plugins.linux import pslist
class Maps(plugins.PluginInterface):
"""Lists all memory maps for all processes"""
@@ -30,20 +30,22 @@ class Maps(plugins.PluginInterface):
name = utility.array_to_string(task.comm)
for vma in task.mm.mmap_iter:
flags = vma.protection()
page_offset = vma.page_offset()
flags = vma.get_protection()
page_offset = vma.get_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
# TODO - update the second parameter to hopefully go away once extension is updated
path = task.path_for_file(vma.vm_file, "")
dentry = vma.vm_file.get_dentry()
if dentry != 0:
inode_object = dentry.d_inode
major = inode_object.i_sb.major
minor = inode_object.i_sb.minor
inode = inode_object.i_ino
path = vma.get_name(task)
yield (
0,
@@ -60,6 +62,10 @@ class Maps(plugins.PluginInterface):
))
def run(self):
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
@@ -71,6 +77,8 @@ class Maps(plugins.PluginInterface):
("Minor", int),
("Inode", int),
("File Path", str)],
self._generator(pslist.PsList.list_tasks(self.context,
self.config['primary'],
self.config['vmlinux'])))
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
+24 -6
View File
@@ -1,5 +1,5 @@
import volatility.framework.interfaces.plugins as interfaces_plugins
from volatility.framework import renderers
from volatility.framework import renderers, interfaces
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
@@ -16,8 +16,21 @@ class PsList(interfaces_plugins.PluginInterface):
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
@classmethod
def create_filter(cls, pid_list: typing.List[int] = None) -> typing.Callable[[int], bool]:
filter = lambda _: False
# FIXME: mypy #4973 or #2608
pid_list = pid_list or []
filter_list = [x for x in pid_list if x is not None]
if filter_list:
filter = lambda x: x not in filter_list
return filter
def _generator(self):
for task in self.list_tasks(self.context, self.config['primary'], self.config['vmlinux']):
for task in self.list_tasks(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = self.create_filter([self.config.get('pid', None)])):
pid = task.pid
ppid = 0
if task.parent:
@@ -26,13 +39,18 @@ class PsList(interfaces_plugins.PluginInterface):
yield (0, (pid, ppid, name))
@classmethod
def list_tasks(cls, context, primary_layer: str, vmlinux_table: str):
def list_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
vmlinux_symbols: str,
filter: typing.Callable[[int], bool] = lambda _: False) -> \
typing.Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer"""
layer_name = context.memory[primary_layer].config['memory_layer']
_, aslr_shift = linux.LinuxUtilities.find_aslr(context, vmlinux_table, layer_name)
vmlinux = context.module(vmlinux_table, primary_layer, aslr_shift)
_, aslr_shift = linux.LinuxUtilities.find_aslr(context, vmlinux_symbols, layer_name)
vmlinux = context.module(vmlinux_symbols, layer_name, aslr_shift)
init_task = vmlinux.object(symbol_name = "init_task")
for task in init_task.tasks:
+52
View File
@@ -0,0 +1,52 @@
from volatility.framework.renderers import format_hints
from volatility.framework.objects import utility
from volatility.plugins.linux import pslist
class PsTree(pslist.PsList):
"""Plugin for listing processes in a tree based on their parent process ID """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._processes = {}
self._levels = {}
self._children = {}
def find_level(self, pid):
"""Finds how deep the pid is in the processes list"""
seen = set([])
seen.add(pid)
level = 0
proc = self._processes.get(pid, None)
while proc is not None and proc.parent != 0 and proc.parent.pid not in seen:
ppid = int(proc.parent.pid)
child_list = self._children.get(ppid, set([]))
child_list.add(proc.pid)
self._children[ppid] = child_list
proc = self._processes.get(ppid, None)
level += 1
self._levels[pid] = level
def _generator(self):
"""Generates the """
for proc in self.list_tasks(self.context, self.config['primary'], self.config['vmlinux']):
self._processes[proc.pid] = proc
# Build the child/level maps
for pid in self._processes:
self.find_level(pid)
def yield_processes(pid):
proc = self._processes[pid]
row = (proc.pid,
proc.parent.pid,
utility.array_to_string(proc.comm))
yield (self._levels[pid] - 1, row)
for child_pid in self._children.get(pid, []):
yield from yield_processes(child_pid)
for pid in self._levels:
if self._levels[pid] == 1:
yield from yield_processes(pid)