mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-25 11:04:52 +02:00
Added documentation and logging added cachedump and lsadump Fixed requested issues fixed encoding issues added requirement Framework: Move cache_clear function to the framework Documentation: Document self.config slightly better Linux/Mac: Refactor *nix Utilities classes Automagic: Fix issue in recent refactor Add elf parsing and symbol retrieval for linux kernel modules Fixes on coding style Linux: Restore accidentally dropped kobject definition Core: Rerun yapf across the codebase. First attempt and better DTB and ASLR validation. Debugging statements left in. Mac: Stash the verified ASLR shift and improve logging Linux: Support stashing the KASLR Remove extra debug prints added hashdump Added documentation and logging Linux - stash the Linux kernel virtual address Hashdump: Reformat and convert to proper byte handling Registry: Fix error message Caching: Only cache remote files Yarascan: Move most of yarascanning into a versionable plugin This refactors common yara tasks, so we can use the plugin versioning to keep track of changes to the YaraScanner class. Core: Refactor versioning and associated requirements Configuration: Improve the VersionableInterface documentation Plugins: Remove unnecessary dependency for yarascan Objects: Add a convenience function for validating enum values Objects: Update enumeration method to is_valid_choice Core: Maintain 3.5.3 compatibility created tty_check.py; edited automagic/linux.py to add kernel tracking abilities fixed some formatting for tty_check.py Fixed tty_check not finding the ttyhook module added some documentation Removed unnecessary code from tty_check.py added docs to automagic methods, fixed missing return types, changed parameters to be more specific added kernel string to linux constants file; changed automagic methods so that they reconstruct the kernel object within the method for consistancy with other methods added parameter type to generate_kernel_handler_info Updated imports to reflect new location of utility class; plugins are no longer outputing anything so commiting for Andrew to take a look at removed debugging print statements fixed bug causing no output when tty_check is run Windows.info: Refactor windows.info as classmethods Linux: Fix plugin case and re-run yapf created keyboard_notifiers removed extra whitespace Yapf: Minor reformats for recent plugins Codebase: Ensure all conversions to bytes handle unicode All conversions using `latin-1` have been converted to `raw_unicode_escape` which is like `latin-1`, but handles unicode characters appropriately (with a `\u` prefix). Since this is like `latin-1` it should have no impact on things that ran previously, but those that would fail with a unicode error now will present an encoded unicode string. There may be situations where the binary representation of unicode would be better (timeliner file output?), but those can be changed when/if it's determined necessary. Fixes #274. Linux: Fix keyboard_notifiers copyright year Renderers: Fix the pretty renderer when no rows are emitted Timeliner: Sort results and provide a filter Sorts the results (as stated). Note that user interfaces may decide to sort their results in an order of their choosing. Also added a parameter that can be provided multiple times to only allow plugins that match (any of) the parameters provided. Timeliner: Actually make use of the TextIoWrapper Windows: Add a version to the info plugin now its got classmethods CLI: Add additional help about 'vol.py plugin --help' created linux_check_idt; plugin currently is not finding the module names for each entry in idt table fix copyright year fixed poor variable name, removed unnecessary code added address mask to fix issue with kernel tracking CLI: Revert epilog changes Update lsadump.py I'm not sure why your are getting this error since it works fine for me, but this may fix it
260 lines
9.2 KiB
Python
260 lines
9.2 KiB
Python
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
|
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
|
#
|
|
from typing import List, Tuple, Iterator
|
|
|
|
from volatility.framework import exceptions, constants, interfaces, objects, contexts
|
|
from volatility.framework.objects import utility
|
|
from volatility.framework.symbols import intermed
|
|
from volatility.framework.symbols.linux import extensions
|
|
from volatility.framework.objects import utility
|
|
|
|
|
|
class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
|
provides = {"type": "interface"}
|
|
|
|
def __init__(self, *args, **kwargs) -> None:
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# 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)
|
|
self.set_type_class('qstr', extensions.qstr)
|
|
self.set_type_class('dentry', extensions.dentry)
|
|
self.set_type_class('fs_struct', extensions.fs_struct)
|
|
self.set_type_class('files_struct', extensions.files_struct)
|
|
self.set_type_class('vfsmount', extensions.vfsmount)
|
|
self.set_type_class('kobject', extensions.kobject)
|
|
|
|
if 'module' in self.types:
|
|
self.set_type_class('module', extensions.module)
|
|
|
|
if 'mount' in self.types:
|
|
self.set_type_class('mount', extensions.mount)
|
|
|
|
|
|
class LinuxUtilities(interfaces.configuration.VersionableInterface):
|
|
"""Class with multiple useful linux functions."""
|
|
|
|
_verison = (1, 0, 0)
|
|
|
|
# based on __d_path from the Linux kernel
|
|
@classmethod
|
|
def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str:
|
|
|
|
ret_path = [] # type: 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 we did not gather any valid dentrys in the path, then the entire file is
|
|
# either 1) smeared out of memory or 2) de-allocated and corresponding structures overwritten
|
|
# we return an empty string in this case to avoid confusion with something like a handle to the root
|
|
# directory (e.g., "/")
|
|
if not 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, context, task, filp) -> str:
|
|
dentry = filp.get_dentry()
|
|
|
|
sym_addr = dentry.d_op.d_dname
|
|
|
|
symbol_table_arr = sym_addr.vol.type_name.split("!")
|
|
symbol_table = None
|
|
if len(symbol_table_arr) == 2:
|
|
symbol_table = symbol_table_arr[0]
|
|
|
|
symbs = list(context.symbol_space.get_symbols_by_location(sym_addr, table_name = symbol_table))
|
|
|
|
if len(symbs) == 1:
|
|
sym = symbs[0].split(constants.BANG)[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 = cls._get_path_file(task, 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, context, 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 dentry.d_op.has_member("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(context, task, filp)
|
|
else:
|
|
ret = LinuxUtilities._get_path_file(task, filp)
|
|
|
|
return ret
|
|
|
|
@classmethod
|
|
def files_descriptors_for_process(cls, context: interfaces.context.ContextInterface, symbol_table: str,
|
|
task: interfaces.objects.ObjectInterface):
|
|
|
|
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 = symbol_table + constants.BANG + 'file'
|
|
|
|
fds = objects.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(context, task, filp)
|
|
|
|
yield fd_num, filp, full_path
|
|
|
|
@classmethod
|
|
def mask_mods_list(cls, context: interfaces.context.ContextInterface, layer_name: str,
|
|
mods: Iterator[interfaces.objects.ObjectInterface]) -> List[Tuple[str, int, int]]:
|
|
"""
|
|
A helper function to mask the starting and end address of kernel modules
|
|
"""
|
|
mask = context.layers[layer_name].address_mask
|
|
|
|
return [(utility.array_to_string(mod.name), mod.get_module_base() & mask,
|
|
(mod.get_module_base() & mask) + mod.get_core_size()) for mod in mods]
|
|
|
|
@classmethod
|
|
def generate_kernel_handler_info(
|
|
cls, context: interfaces.context.ContextInterface, layer_name: str, kernel_name: str,
|
|
mods_list: Iterator[interfaces.objects.ObjectInterface]) -> List[Tuple[str, int, int]]:
|
|
"""
|
|
A helper function that gets the beginning and end address of the kernel module
|
|
"""
|
|
|
|
kernel = contexts.Module(context, kernel_name, layer_name, 0)
|
|
|
|
mask = context.layers[layer_name].address_mask
|
|
|
|
start_addr = kernel.object_from_symbol("_text")
|
|
start_addr = start_addr.vol.offset & mask
|
|
|
|
end_addr = kernel.object_from_symbol("_etext")
|
|
end_addr = end_addr.vol.offset & mask
|
|
|
|
return [(constants.linux.KERNEL_NAME, start_addr, end_addr)] + \
|
|
LinuxUtilities.mask_mods_list(context, layer_name, mods_list)
|
|
|
|
@classmethod
|
|
def lookup_module_address(cls, context: interfaces.context.ContextInterface, handlers: List[Tuple[str, int, int]],
|
|
target_address: int):
|
|
"""
|
|
Searches between the start and end address of the kernel module using target_address.
|
|
Returns the module and symbol name of the address provided.
|
|
"""
|
|
|
|
mod_name = "UNKNOWN"
|
|
symbol_name = "N/A"
|
|
|
|
for name, start, end in handlers:
|
|
if start <= target_address <= end:
|
|
mod_name = name
|
|
if name == constants.linux.KERNEL_NAME:
|
|
symbols = list(context.symbol_space.get_symbols_by_location(target_address))
|
|
|
|
if len(symbols):
|
|
symbol_name = symbols[0].split(constants.BANG)[1] if constants.BANG in symbols[0] else \
|
|
symbols[0]
|
|
|
|
break
|
|
|
|
return mod_name, symbol_name
|
|
|
|
@classmethod
|
|
def walk_internal_list(cls, vmlinux, struct_name, list_member, list_start):
|
|
while list_start:
|
|
list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset)
|
|
yield list_struct
|
|
list_start = getattr(list_struct, list_member)
|