mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-05 01:07:38 +02:00
Move all core plugins over to framework/plugins.
This should have no impact functionality-wise. The statistics plugin was left out a) as an example and b) because it was committed by mistake in the first place and was never meant to be a real plugin.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import struct
|
||||
from typing import List
|
||||
|
||||
from volatility.framework import constants, renderers, symbols, interfaces
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.layers import scanners
|
||||
from volatility.framework.objects import utility
|
||||
from volatility.framework.symbols.linux.bash import BashIntermedSymbols
|
||||
from volatility.plugins import timeliner
|
||||
from volatility.plugins.linux import pslist
|
||||
|
||||
|
||||
class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Recovers bash command history from memory"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
def _generator(self, tasks):
|
||||
is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["vmlinux"])
|
||||
if is_32bit:
|
||||
pack_format = "I"
|
||||
bash_json_file = "bash32"
|
||||
else:
|
||||
pack_format = "Q"
|
||||
bash_json_file = "bash64"
|
||||
|
||||
bash_table_name = BashIntermedSymbols.create(self.context,
|
||||
self.config_path,
|
||||
"linux",
|
||||
bash_json_file)
|
||||
|
||||
ts_offset = self.context.symbol_space.get_type(
|
||||
bash_table_name + constants.BANG + "hist_entry").relative_child_offset("timestamp")
|
||||
|
||||
for task in tasks:
|
||||
task_name = utility.array_to_string(task.comm)
|
||||
if task_name not in ["bash", "sh", "dash"]:
|
||||
continue
|
||||
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if not proc_layer_name:
|
||||
continue
|
||||
|
||||
proc_layer = self.context.memory[proc_layer_name]
|
||||
|
||||
bang_addrs = []
|
||||
|
||||
# find '#' values on the heap
|
||||
for address in proc_layer.scan(self.context,
|
||||
scanners.BytesScanner(b"#"),
|
||||
sections = task.get_process_memory_sections(heap_only = True)):
|
||||
bang_addrs.append(struct.pack(pack_format, address))
|
||||
|
||||
history_entries = []
|
||||
|
||||
for address, _ in proc_layer.scan(self.context,
|
||||
scanners.MultiStringScanner(bang_addrs),
|
||||
sections = task.get_process_memory_sections(heap_only = True)):
|
||||
hist = self.context.object(bash_table_name + constants.BANG + "hist_entry",
|
||||
offset = address - ts_offset,
|
||||
layer_name = proc_layer_name)
|
||||
|
||||
if hist.is_valid():
|
||||
history_entries.append(hist)
|
||||
|
||||
for hist in sorted(history_entries, key = lambda x: x.get_time_as_integer()):
|
||||
yield (0, (task.pid, task_name, hist.get_time_object(), hist.get_command()))
|
||||
|
||||
def run(self):
|
||||
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
|
||||
|
||||
plugin = pslist.PsList.list_tasks
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[("PID", int),
|
||||
("Process", str),
|
||||
("CommandTime", datetime.datetime),
|
||||
("Command", str)],
|
||||
self._generator(plugin(self.context,
|
||||
self.config['primary'],
|
||||
self.config['vmlinux'],
|
||||
filter = filt)))
|
||||
|
||||
def generate_timeline(self):
|
||||
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
|
||||
|
||||
plugin = pslist.PsList.list_tasks
|
||||
|
||||
for row in self._generator(plugin(self.context,
|
||||
self.config['primary'],
|
||||
self.config['vmlinux'],
|
||||
filter = filt)):
|
||||
_depth, row_data = row
|
||||
description = "{} ({}): \"{}\"".format(row_data[0], row_data[1], row_data[3])
|
||||
yield (description, timeliner.TimeLinerType.CREATED, row_data[2])
|
||||
@@ -0,0 +1,96 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from volatility.framework import exceptions, interfaces
|
||||
from volatility.framework import renderers
|
||||
from volatility.framework.automagic import linux
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.renderers import format_hints
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Check_afinfo(plugins.PluginInterface):
|
||||
"""Verifies the operation function pointers of network protocols"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
# returns whether the symbol is found within the kernel (system.map) or not
|
||||
def _is_known_address(self, handler_addr):
|
||||
symbols = list(self.context.symbol_space.get_symbols_by_location(handler_addr))
|
||||
|
||||
return len(symbols) > 0
|
||||
|
||||
def _check_members(self, var_ops, var_name, members):
|
||||
for check in members:
|
||||
# redhat-specific garbage
|
||||
if check.startswith("__UNIQUE_ID_rh_kabi_hide"):
|
||||
continue
|
||||
|
||||
if check == "write":
|
||||
addr = var_ops.member(attr = 'write')
|
||||
else:
|
||||
addr = getattr(var_ops, check)
|
||||
|
||||
if addr and addr != 0 and not self._is_known_address(addr):
|
||||
yield check, addr
|
||||
|
||||
def _check_afinfo(self, var_name, var, op_members, seq_members):
|
||||
for hooked_member, hook_address in self._check_members(var.seq_fops, var_name, op_members):
|
||||
yield var_name, hooked_member, hook_address
|
||||
|
||||
# newer kernels
|
||||
if var.has_member("seq_ops"):
|
||||
for hooked_member, hook_address in self._check_members(var.seq_ops, var_name, seq_members):
|
||||
yield var_name, hooked_member, hook_address
|
||||
|
||||
# this is the most commonly hooked member by rootkits, so a force a check on it
|
||||
elif not self._is_known_address(var.seq_show):
|
||||
yield var_name, "show", var.seq_show
|
||||
|
||||
def _generator(self):
|
||||
_, aslr_shift = linux.LinuxUtilities.find_aslr(self.context, self.config['vmlinux'], self.config['primary'])
|
||||
vmlinux = self.context.module(self.config['vmlinux'], self.config['primary'], aslr_shift)
|
||||
|
||||
linux.LinuxUtilities.aslr_mask_symbol_table(self.context,
|
||||
self.config['primary'],
|
||||
self.config['vmlinux'],
|
||||
aslr_shift)
|
||||
|
||||
op_members = vmlinux.get_type('file_operations').members
|
||||
seq_members = vmlinux.get_type('seq_operations').members
|
||||
|
||||
tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"])
|
||||
udp = ("udp_seq_afinfo", ["udplite6_seq_afinfo", "udp6_seq_afinfo", "udplite4_seq_afinfo", "udp4_seq_afinfo"])
|
||||
protocols = [tcp, udp]
|
||||
|
||||
for (struct_type, global_vars) in protocols:
|
||||
for global_var_name in global_vars:
|
||||
# this will lookup fail for the IPv6 protocols on kernels without IPv6 support
|
||||
try:
|
||||
global_var = vmlinux.get_symbol(global_var_name)
|
||||
except exceptions.SymbolError:
|
||||
continue
|
||||
|
||||
global_var = vmlinux.object(type_name = struct_type, offset = global_var.address)
|
||||
|
||||
for name, member, address in self._check_afinfo(global_var_name, global_var, op_members, seq_members):
|
||||
yield 0, (name, member, format_hints.Hex(address))
|
||||
|
||||
def run(self):
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[("Symbol Name", str),
|
||||
("Member", str),
|
||||
("Handler Address", format_hints.Hex)],
|
||||
self._generator())
|
||||
@@ -0,0 +1,187 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from volatility.framework import exceptions, interfaces
|
||||
from volatility.framework import renderers, constants
|
||||
from volatility.framework.automagic import linux
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.renderers import format_hints
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import capstone
|
||||
|
||||
has_capstone = True
|
||||
except ImportError:
|
||||
has_capstone = False
|
||||
|
||||
|
||||
class Check_syscall(plugins.PluginInterface):
|
||||
"""Check system call table for hooks"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
def _get_table_size_next_symbol(self, table_addr, ptr_sz, vmlinux):
|
||||
"""
|
||||
Returns the size of the table based on the next symbol
|
||||
"""
|
||||
ret = 0
|
||||
|
||||
sym_table = self.context.symbol_space[vmlinux.name]
|
||||
|
||||
sorted_symbols = sorted([(sym_table.get_symbol(sn).address, sn) for sn in sym_table.symbols])
|
||||
|
||||
sym_address = 0
|
||||
|
||||
for tmp_sym_address, sym_name in sorted_symbols:
|
||||
if tmp_sym_address > table_addr:
|
||||
sym_address = tmp_sym_address
|
||||
break
|
||||
|
||||
if sym_address > 0:
|
||||
ret = int((sym_address - table_addr) / ptr_sz)
|
||||
|
||||
return ret
|
||||
|
||||
def _get_table_size_meta(self, vmlinux):
|
||||
"""
|
||||
returns the number of symbols that start with __syscall_meta__
|
||||
this is a fast way to determine the number of system calls, but not the most accurate
|
||||
"""
|
||||
|
||||
return len(
|
||||
[sym for sym in self.context.symbol_space[vmlinux.name].symbols if sym.startswith("__syscall_meta__")])
|
||||
|
||||
def _get_table_info_other(self, table_addr, ptr_sz, vmlinux):
|
||||
table_size_meta = self._get_table_size_meta(vmlinux)
|
||||
table_size_syms = self._get_table_size_next_symbol(table_addr, ptr_sz, vmlinux)
|
||||
|
||||
sizes = [size for size in [table_size_meta, table_size_syms] if size > 0]
|
||||
|
||||
table_size = min(sizes)
|
||||
|
||||
return table_size
|
||||
|
||||
def _get_table_info_disassembly(self, ptr_sz, vmlinux):
|
||||
"""
|
||||
Find the size of the system call table by disassembling functions
|
||||
that immediately reference it in their first isntruction
|
||||
This is in the form 'cmp reg,NR_syscalls'
|
||||
"""
|
||||
table_size = 0
|
||||
|
||||
if not has_capstone:
|
||||
return table_size
|
||||
|
||||
if ptr_sz == 4:
|
||||
syscall_entry_func = "sysenter_do_call"
|
||||
mode = capstone.CS_MODE_32
|
||||
else:
|
||||
syscall_entry_func = "system_call_fastpath"
|
||||
mode = capstone.CS_MODE_64
|
||||
|
||||
md = capstone.Cs(capstone.CS_ARCH_X86, mode)
|
||||
|
||||
try:
|
||||
func_addr = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + syscall_entry_func).address
|
||||
except exceptions.SymbolError as e:
|
||||
# if we can't find the disassemble function then bail and rely on a different method
|
||||
return 0
|
||||
|
||||
data = self.context.memory.read(self.config['primary'], func_addr, 6)
|
||||
|
||||
for (address, size, mnemonic, op_str) in md.disasm_lite(data, func_addr):
|
||||
if mnemonic == 'CMP':
|
||||
table_size = int(op_str.split(",")[1].strip()) & 0xffff
|
||||
break
|
||||
|
||||
return table_size
|
||||
|
||||
def _get_table_info(self, vmlinux, table_name, ptr_sz):
|
||||
table_sym = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + table_name)
|
||||
|
||||
table_size = self._get_table_info_disassembly(ptr_sz, vmlinux)
|
||||
|
||||
if table_size == 0:
|
||||
table_size = self._get_table_info_other(table_sym.address, ptr_sz, vmlinux)
|
||||
|
||||
if table_size == 0:
|
||||
vollog.error("Unable to get system call table size")
|
||||
return 0, 0
|
||||
|
||||
return table_sym.address, table_size
|
||||
|
||||
# TODO - add finding and parsing unistd.h once cached file enumeration is added
|
||||
def _generator(self):
|
||||
_, aslr_shift = linux.LinuxUtilities.find_aslr(self.context, self.config['vmlinux'], self.config['primary'])
|
||||
vmlinux = self.context.module(self.config['vmlinux'], self.config['primary'], aslr_shift)
|
||||
|
||||
linux.LinuxUtilities.aslr_mask_symbol_table(self.context,
|
||||
self.config['vmlinux'],
|
||||
self.config['primary'],
|
||||
aslr_shift)
|
||||
|
||||
ptr_sz = vmlinux.get_type("pointer").size
|
||||
if ptr_sz == 4:
|
||||
table_name = "32bit"
|
||||
else:
|
||||
table_name = "64bit"
|
||||
|
||||
try:
|
||||
table_info = self._get_table_info(vmlinux, "sys_call_table", ptr_sz)
|
||||
except exceptions.SymbolError:
|
||||
vollog.error("Unable to find the system call table. Exiting.")
|
||||
return
|
||||
|
||||
tables = [(table_name, table_info)]
|
||||
|
||||
# this table is only present on 64 bit systems with 32 bit emulation
|
||||
# enabled in order to support 32 bit programs and libraries
|
||||
# if the symbol isn't there then the support isn't in the kernel and so we skip it
|
||||
try:
|
||||
ia32_symbol = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + "ia32_sys_call_table")
|
||||
except exceptions.SymbolError:
|
||||
ia32_symbol = None
|
||||
|
||||
if ia32_symbol != None:
|
||||
ia32_info = self._get_table_info(vmlinux, "ia32_sys_call_table", ptr_sz)
|
||||
tables.append(("32bit", ia32_info))
|
||||
|
||||
for (table_name, (tableaddr, tblsz)) in tables:
|
||||
table = vmlinux.object(type_name = "array", subtype = vmlinux.get_type("pointer"),
|
||||
offset = tableaddr, count = tblsz)
|
||||
|
||||
for (i, call_addr) in enumerate(table):
|
||||
if not call_addr:
|
||||
continue
|
||||
|
||||
symbols = list(self.context.symbol_space.get_symbols_by_location(call_addr))
|
||||
|
||||
if len(symbols) > 0:
|
||||
sym_name = str(symbols[0].split(constants.BANG)[1]) if constants.BANG in symbols[0] else \
|
||||
str(symbols[0])
|
||||
else:
|
||||
sym_name = "UNKNOWN"
|
||||
|
||||
yield (0, (format_hints.Hex(tableaddr), table_name, i, format_hints.Hex(call_addr), sym_name))
|
||||
|
||||
def run(self):
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[("Table Address", format_hints.Hex),
|
||||
("Table Name", str),
|
||||
("Index", int),
|
||||
("Handler Address", format_hints.Hex),
|
||||
("Handler Symbol", str)],
|
||||
self._generator())
|
||||
@@ -0,0 +1,66 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from volatility.framework import renderers, 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.plugins.linux import pslist
|
||||
|
||||
|
||||
class Elfs(plugins.PluginInterface):
|
||||
"""Lists all memory mapped ELF files for all processes"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
def _generator(self, tasks):
|
||||
for task in tasks:
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if not proc_layer_name:
|
||||
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):
|
||||
filt = 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 = filt)))
|
||||
@@ -0,0 +1,59 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
|
||||
from volatility.framework import renderers, constants, interfaces
|
||||
from volatility.framework.automagic import linux
|
||||
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
|
||||
|
||||
|
||||
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.get_symbol("modules").address
|
||||
|
||||
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())
|
||||
@@ -0,0 +1,51 @@
|
||||
"""A module containing a collection of plugins that produce data
|
||||
typically found in Linux's /proc file system.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from volatility.framework import renderers
|
||||
from volatility.framework.automagic import linux
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.objects import utility
|
||||
from volatility.plugins.linux import pslist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Lsof(plugins.PluginInterface):
|
||||
"""Lists all memory maps for all processes"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
def _generator(self, tasks):
|
||||
for task in tasks:
|
||||
name = utility.array_to_string(task.comm)
|
||||
pid = int(task.pid)
|
||||
|
||||
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):
|
||||
linux.LinuxUtilities.aslr_mask_symbol_table(self.context, self.config['vmlinux'], self.config['primary'])
|
||||
|
||||
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(self.context,
|
||||
self.config['primary'],
|
||||
self.config['vmlinux'],
|
||||
filter = filter)))
|
||||
@@ -0,0 +1,81 @@
|
||||
from typing import List
|
||||
|
||||
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, interfaces
|
||||
from volatility.framework import renderers
|
||||
from volatility.framework.configuration import requirements
|
||||
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) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
def list_injections(self, task):
|
||||
"""Generate memory regions for a process that may contain
|
||||
injected code.
|
||||
"""
|
||||
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if not proc_layer_name:
|
||||
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):
|
||||
filt = 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 = filt)))
|
||||
@@ -0,0 +1,82 @@
|
||||
"""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.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.objects import utility
|
||||
from volatility.framework.renderers import format_hints
|
||||
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 [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
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.get_protection()
|
||||
page_offset = vma.get_page_offset()
|
||||
major = 0
|
||||
minor = 0
|
||||
inode = 0
|
||||
|
||||
if vma.vm_file != 0:
|
||||
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,
|
||||
(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):
|
||||
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),
|
||||
("Flags", str),
|
||||
("PgOff", format_hints.Hex),
|
||||
("Major", int),
|
||||
("Minor", int),
|
||||
("Inode", int),
|
||||
("File Path", str)],
|
||||
self._generator(plugin(self.context,
|
||||
self.config['primary'],
|
||||
self.config['vmlinux'],
|
||||
filter = filter)))
|
||||
@@ -0,0 +1,67 @@
|
||||
from typing import Callable, Iterable, List
|
||||
|
||||
import volatility.framework.interfaces.plugins as interfaces_plugins
|
||||
from volatility.framework import renderers, interfaces
|
||||
from volatility.framework.automagic import linux
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.objects import utility
|
||||
|
||||
|
||||
class PsList(interfaces_plugins.PluginInterface):
|
||||
"""Lists the processes present in a particular linux memory image"""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolRequirement(name = "vmlinux",
|
||||
description = "Linux Kernel")]
|
||||
|
||||
@classmethod
|
||||
def create_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]:
|
||||
# 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:
|
||||
def filter_func(x):
|
||||
return x not in filter_list
|
||||
|
||||
return filter_func
|
||||
else:
|
||||
return lambda _: False
|
||||
|
||||
def _generator(self):
|
||||
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:
|
||||
ppid = task.parent.pid
|
||||
name = utility.array_to_string(task.comm)
|
||||
yield (0, (pid, ppid, name))
|
||||
|
||||
@classmethod
|
||||
def list_tasks(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
vmlinux_symbols: str,
|
||||
filter: Callable[[int], bool] = lambda _: False) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
|
||||
"""Lists all the tasks in the primary layer"""
|
||||
|
||||
_, 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:
|
||||
if not filter(task):
|
||||
yield task
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("PID", int),
|
||||
("PPID", int),
|
||||
("COMM", str)],
|
||||
self._generator())
|
||||
@@ -0,0 +1,51 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user