diff --git a/doc/source/volshell.rst b/doc/source/volshell.rst index 3d4cad890..1e51f90ba 100644 --- a/doc/source/volshell.rst +++ b/doc/source/volshell.rst @@ -24,13 +24,14 @@ operating system mode for volshell, and the current layer available for use. :: - Volshell (Volatility 3 Framework) 1.0.1 + Volshell (Volatility 3 Framework) 2.0.2 Readline imported successfully PDB scanning finished Call help() to see available functions - Volshell mode: Generic - Current Layer: primary + Volshell mode : Generic + Current Layer : primary + Current Symbol Table: None (primary) >>> @@ -55,9 +56,9 @@ python environment, we can do the following: :: - (primary) >>> proc = ps()[0] - (primary) >>> proc - + (layer_name) >>> proc = ps()[0] + (layer_name) >>> proc + When printing a volatility structure, various information is output, in this case the `type_name`, the `layer` and `offset` that it's been constructed on, and the size of the structure. @@ -70,31 +71,31 @@ automagic). :: - (primary) >>> dt('_EPROCESS') - nt_symbols1!_EPROCESS (2624 bytes) - 0x0 : Pcb nt_symbols1!_KPROCESS - 0x438 : ProcessLock nt_symbols1!_EX_PUSH_LOCK - 0x440 : UniqueProcessId nt_symbols1!pointer - 0x448 : ActiveProcessLinks nt_symbols1!_LIST_ENTRY + (layer_name) >>> dt('_EPROCESS') + symbol_table_name1!_EPROCESS (1968 bytes) + 0x0 : Pcb symbol_table_name1!_KPROCESS + 0x2d8 : ProcessLock symbol_table_name1!_EX_PUSH_LOCK + 0x2e0 : RundownProtect symbol_table_name1!_EX_RUNDOWN_REF + 0x2e8 : UniqueProcessId symbol_table_name1!pointer ... It can also be provided with an object and will interpret the data for each in the process: :: - (primary) >>> dt(proc) - nt_symbols1!_EPROCESS (2624 bytes) - 0x0 : Pcb nt_symbols1!_KPROCESS 0x8c0bccf8d040 - 0x438 : ProcessLock nt_symbols1!_EX_PUSH_LOCK 0x8c0bccf8d478 - 0x440 : UniqueProcessId nt_symbols1!pointer 356 - 0x448 : ActiveProcessLinks nt_symbols1!_LIST_ENTRY 0x8c0bccf8d488 + (layer_name) >>> dt(proc) + symbol_table_name1!_EPROCESS (1968 bytes) + 0x0 : Pcb symbol_table_name1!_KPROCESS 0xe08ff2459040 + 0x2d8 : ProcessLock symbol_table_name1!_EX_PUSH_LOCK 0xe08ff2459318 + 0x2e0 : RundownProtect symbol_table_name1!_EX_RUNDOWN_REF 0xe08ff2459320 + 0x2e8 : UniqueProcessId symbol_table_name1!pointer 4 ... These values can be accessed directory as attributes :: - (primary) >>> proc.UniqueProcessId + (layer_name) >>> proc.UniqueProcessId 356 Pointer structures contain the value they point to, but attributes accessed are forwarded to the object they point to. @@ -102,7 +103,7 @@ This means that pointers do not need to be explicitly dereferenced to access und :: - (primary) >>> proc.Pcb.DirectoryTableBase + (layer_name) >>> proc.Pcb.DirectoryTableBase 4355817472 Running plugins @@ -114,26 +115,26 @@ were required: :: - (primary) >>> from volatility3.plugins.windows import pslist - (primary) >>> display_plugin_output(pslist.PsList) - Unable to validate the plugin requirements: ['plugins.Volshell.9QZLXJKFWESI0BAP3M1U7Y5VCT468GRN.PsList.primary', 'plugins.Volshell.9QZLXJKFWESI0BAP3M1U7Y5VCT468GRN.PsList.nt_symbols'] + (layer_name) >>> from volatility3.plugins.windows import pslist + (layer_name) >>> display_plugin_output(pslist.PsList) + Unable to validate the plugin requirements: ['plugins.Volshell.VH3FSA1JBG0QP9E62Z8OT5UCIMLNYKW4.PsList.kernel'] -We can see that it's made a temporary configuration path for the plugin, and that neither `primary` nor `nt_symbols` -was fulfilled. +We can see that it's made a temporary configuration path for the plugin, and that the `kernel` requirement +was not fulfilled. We can see all the options that the plugin can accept by access the `get_requirements()` method of the plugin. This is a classmethod, so can be called on an uninstantiated copy of the plugin. :: - (primary) >>> pslist.PsList.get_requirements() - [, , , , ] + (layer_name) >>> pslist.PsList.get_requirements() + [, , , ] We can provide arguments via the `dpo` method call: :: - (primary) >>> display_plugin_output(pslist.PsList, primary = self.current_layer, nt_symbols = self.config['nt_symbols']) + (layer_name) >>> display_plugin_output(pslist.PsList, kernel = self.config['kernel']) PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime File output @@ -142,8 +143,9 @@ We can provide arguments via the `dpo` method call: 356 4 smss.exe 0x8c0bccf8d040 3 - N/A False 2021-03-13 17:25:33.000000 N/A Disabled ... -Here's we've provided the current layer as the TranslationLayerRequirement, and used the symbol tables requirement -requested by the volshell plugin itself. A different table could be loaded and provided instead. The context used +Here's we've provided the kernel name that was requested by the volshell plugin itself (the generic volshell does not +load a kernel module, and instead only has a TranslationLayerRequirement). +A different module could be created and provided instead. The context used by the `dpo` method is always `context`. Instead of print the results directly to screen, they can be gathered into a TreeGrid objects for direct access by @@ -151,8 +153,8 @@ using the `generate_treegrid` or `gt` command. :: - (primary) >>> treegrid = gt(pslist.PsList, primary = self.current_layer, nt_symbols = self.config['nt_symbols']) - (primary) >>> treegrid.populate() + (layer_name) >>> treegrid = gt(pslist.PsList, kernel = self.config['kernel']) + (layer_name) >>> treegrid.populate() Treegrids must be populated before the data in them can be accessed. This is where the plugin actually runs and produces data. diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 2c5e13211..4cdbd26e8 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -19,14 +19,14 @@ import os import sys import tempfile import traceback -from typing import Dict, Type, Union, Any +from typing import Any, Dict, Type, Union from urllib import parse, request import volatility3.plugins import volatility3.symbols from volatility3 import framework from volatility3.cli import text_renderer, volargparse -from volatility3.framework import automagic, constants, contexts, exceptions, interfaces, plugins, configuration +from volatility3.framework import automagic, configuration, constants, contexts, exceptions, interfaces, plugins from volatility3.framework.automagic import stacker from volatility3.framework.configuration import requirements diff --git a/volatility3/cli/volshell/__init__.py b/volatility3/cli/volshell/__init__.py index 7b8a759a6..812d44337 100644 --- a/volatility3/cli/volshell/__init__.py +++ b/volatility3/cli/volshell/__init__.py @@ -7,12 +7,11 @@ import json import logging import os import sys -import glob import volatility3.plugins import volatility3.symbols from volatility3 import cli, framework -from volatility3.cli.volshell import generic, windows, linux, mac +from volatility3.cli.volshell import generic, linux, mac, windows from volatility3.framework import automagic, constants, contexts, exceptions, interfaces, plugins # Make sure we log everything diff --git a/volatility3/cli/volshell/generic.py b/volatility3/cli/volshell/generic.py index 8f81a0420..29accb529 100644 --- a/volatility3/cli/volshell/generic.py +++ b/volatility3/cli/volshell/generic.py @@ -8,11 +8,11 @@ import random import string import struct import sys -from typing import Any, Dict, List, Optional, Tuple, Union, Type, Iterable -from urllib import request, parse +from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, Union +from urllib import parse, request from volatility3.cli import text_renderer, volshell -from volatility3.framework import renderers, interfaces, objects, plugins, exceptions +from volatility3.framework import exceptions, interfaces, objects, plugins, renderers from volatility3.framework.configuration import requirements from volatility3.framework.layers import intel, physical, resources @@ -32,6 +32,7 @@ class Volshell(interfaces.plugins.PluginInterface): super().__init__(*args, **kwargs) self.__current_layer: Optional[str] = None self.__console = None + self.__kernel = None def random_string(self, length: int = 32) -> str: return ''.join(random.sample(string.ascii_uppercase + string.digits, length)) @@ -57,8 +58,6 @@ class Volshell(interfaces.plugins.PluginInterface): Return a TreeGrid but this is always empty since the point of this plugin is to run interactively """ - self.__current_layer = self.config['primary'] - # Try to enable tab completion try: import readline @@ -79,9 +78,10 @@ class Volshell(interfaces.plugins.PluginInterface): banner = f""" Call help() to see available functions - Volshell mode: {mode} - Current Layer: {self.current_layer} - """ + Volshell mode : {mode} + Current Layer : {self.current_layer} + Current Symbol Table: {self.current_symbol_table} +""" sys.ps1 = f"({self.current_layer}) >>> " self.__console = code.InteractiveConsole(locals = self._construct_locals_dict()) @@ -174,12 +174,23 @@ class Volshell(interfaces.plugins.PluginInterface): @property def current_layer(self): + if self.__current_layer is None: + self.__current_layer = self.config['primary'] return self.__current_layer + @property + def current_symbol_table(self): + return None + + @property + def kernel(self): + """No default kernel for generic volshell""" + return None + def change_layer(self, layer_name = None): """Changes the current default layer""" if not layer_name: - layer_name = self.config['primary'] + layer_name = self.current_layer self.__current_layer = layer_name sys.ps1 = f"({self.current_layer}) >>> " diff --git a/volatility3/cli/volshell/linux.py b/volatility3/cli/volshell/linux.py index 4338ae06f..a58ff78f6 100644 --- a/volatility3/cli/volshell/linux.py +++ b/volatility3/cli/volshell/linux.py @@ -5,7 +5,7 @@ from typing import Any, List, Tuple, Union from volatility3.cli.volshell import generic -from volatility3.framework import interfaces, constants +from volatility3.framework import constants, interfaces from volatility3.framework.configuration import requirements from volatility3.plugins.linux import pslist @@ -15,8 +15,8 @@ class Volshell(generic.Volshell): @classmethod def get_requirements(cls): - return (super().get_requirements() + [ - requirements.SymbolTableRequirement(name = "vmlinux", description = "Linux kernel symbols"), + return ([ + requirements.ModuleRequirement(name = "kernel", description = "Linux kernel module"), requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)), requirements.IntRequirement(name = 'pid', description = "Process ID", optional = True) ]) @@ -37,14 +37,14 @@ class Volshell(generic.Volshell): def list_tasks(self): """Returns a list of task objects from the primary layer""" # We always use the main kernel memory and associated symbols - return list(pslist.PsList.list_tasks(self.context, self.config['primary'], self.config['vmlinux'])) + return list(pslist.PsList.list_tasks(self.context, self.current_layer, self.current_symbol_table)) def construct_locals(self) -> List[Tuple[List[str], Any]]: result = super().construct_locals() result += [ (['ct', 'change_task', 'cp'], self.change_task), (['lt', 'list_tasks', 'ps'], self.list_tasks), - (['symbols'], self.context.symbol_space[self.config['vmlinux']]), + (['symbols'], self.context.symbol_space[self.current_symbol_table]), ] if self.config.get('pid', None) is not None: self.change_task(self.config['pid']) @@ -64,3 +64,19 @@ class Volshell(generic.Volshell): if symbol_table is None: symbol_table = self.config['vmlinux'] return super().display_symbols(symbol_table) + + @property + def kernel(self): + if self.__kernel is None: + self.__kernel = self.context.modules[self.config['kernel']] + return self.__kernel + + @property + def current_symbol_table(self): + return self.kernel.symbol_table_name + + @property + def current_layer(self): + if self.__current_layer is None: + self.__current_layer = self.kernel.layer_name + return self.__current_layer diff --git a/volatility3/cli/volshell/mac.py b/volatility3/cli/volshell/mac.py index 8218848ba..644a02bd2 100644 --- a/volatility3/cli/volshell/mac.py +++ b/volatility3/cli/volshell/mac.py @@ -15,9 +15,9 @@ class Volshell(generic.Volshell): @classmethod def get_requirements(cls): - return (super().get_requirements() + [ - requirements.SymbolTableRequirement(name = "darwin", description = "Darwin kernel symbols"), - requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)), + return ([ + requirements.ModuleRequirement(name = "kernel", description = "Darwin kernel module"), + requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (3, 0, 0)), requirements.IntRequirement(name = 'pid', description = "Process ID", optional = True) ]) @@ -37,14 +37,14 @@ class Volshell(generic.Volshell): def list_tasks(self): """Returns a list of task objects from the primary layer""" # We always use the main kernel memory and associated symbols - return list(pslist.PsList.list_tasks(self.context, self.config['primary'], self.config['darwin'])) + return list(pslist.PsList.list_tasks(self.context, self.current_layer, self.current_symbol_table)) def construct_locals(self) -> List[Tuple[List[str], Any]]: result = super().construct_locals() result += [ (['ct', 'change_task', 'cp'], self.change_task), (['lt', 'list_tasks', 'ps'], self.list_tasks), - (['symbols'], self.context.symbol_space[self.config['darwin']]), + (['symbols'], self.context.symbol_space[self.current_symbol_table]), ] if self.config.get('pid', None) is not None: self.change_task(self.config['pid']) @@ -62,5 +62,21 @@ class Volshell(generic.Volshell): def display_symbols(self, symbol_table: str = None): """Prints an alphabetical list of symbols for a symbol table""" if symbol_table is None: - symbol_table = self.config['darwin'] + symbol_table = self.current_symbol_table return super().display_symbols(symbol_table) + + @property + def kernel(self): + if self.__kernel is None: + self.__kernel = self.context.modules[self.config['kernel']] + return self.__kernel + + @property + def current_symbol_table(self): + return self.kernel.symbol_table_name + + @property + def current_layer(self): + if self.__current_layer is None: + self.__current_layer = self.kernel.layer_name + return self.__current_layer diff --git a/volatility3/cli/volshell/windows.py b/volatility3/cli/volshell/windows.py index 6c191ad28..c35a8dfc7 100644 --- a/volatility3/cli/volshell/windows.py +++ b/volatility3/cli/volshell/windows.py @@ -5,7 +5,7 @@ from typing import Any, List, Tuple, Union from volatility3.cli.volshell import generic -from volatility3.framework import interfaces, constants +from volatility3.framework import constants, interfaces from volatility3.framework.configuration import requirements from volatility3.plugins.windows import pslist @@ -15,8 +15,8 @@ class Volshell(generic.Volshell): @classmethod def get_requirements(cls): - return (super().get_requirements() + [ - requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), + return ([ + requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel'), requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)), requirements.IntRequirement(name = 'pid', description = "Process ID", optional = True) ]) @@ -34,14 +34,14 @@ class Volshell(generic.Volshell): def list_processes(self): """Returns a list of EPROCESS objects from the primary layer""" # We always use the main kernel memory and associated symbols - return list(pslist.PsList.list_processes(self.context, self.config['primary'], self.config['nt_symbols'])) + return list(pslist.PsList.list_processes(self.context, self.current_layer, self.current_symbol_table)) def construct_locals(self) -> List[Tuple[List[str], Any]]: result = super().construct_locals() result += [ (['cp', 'change_process'], self.change_process), (['lp', 'list_processes', 'ps'], self.list_processes), - (['symbols'], self.context.symbol_space[self.config['nt_symbols']]), + (['symbols'], self.context.symbol_space[self.current_symbol_table]), ] if self.config.get('pid', None) is not None: self.change_process(self.config['pid']) @@ -53,11 +53,27 @@ class Volshell(generic.Volshell): """Display Type describes the members of a particular object in alphabetical order""" if isinstance(object, str): if constants.BANG not in object: - object = self.config['nt_symbols'] + constants.BANG + object + object = self.current_symbol_table + constants.BANG + object return super().display_type(object, offset) def display_symbols(self, symbol_table: str = None): """Prints an alphabetical list of symbols for a symbol table""" if symbol_table is None: - symbol_table = self.config['nt_symbols'] + symbol_table = self.current_symbol_table return super().display_symbols(symbol_table) + + @property + def kernel(self): + if self.__kernel is None: + self.__kernel = self.context.modules[self.config['kernel']] + return self.__kernel + + @property + def current_symbol_table(self): + return self.kernel.symbol_table_name + + @property + def current_layer(self): + if self.__current_layer is None: + self.__current_layer = self.kernel.layer_name + return self.__current_layer