Merge pull request #651 from volatilityfoundation/feature/volshell-kernel-requirement

Volshell: Update to use KernelRequirement
This commit is contained in:
ikelos
2022-02-20 22:59:28 +00:00
committed by GitHub
7 changed files with 135 additions and 67 deletions
+35 -32
View File
@@ -24,13 +24,15 @@ 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
Current Kernel Name : None
(primary) >>>
@@ -55,9 +57,9 @@ python environment, we can do the following:
::
(primary) >>> proc = ps()[0]
(primary) >>> proc
<EPROCESS nt_symbols1!_EPROCESS: primary @ 0x8c0bcac87040 #2624>
(layer_name) >>> proc = ps()[0]
(layer_name) >>> proc
<EPROCESS symbol_table_name1!_EPROCESS: layer_name @ 0xe08ff2459040 #1968>
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 +72,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 +104,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 +116,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()
[<TranslationLayerRequirement: primary>, <SymbolTableRequirement: nt_symbols>, <BooleanRequirement: physical>, <ListRequirement: pid>, <BooleanRequirement: dump>]
(layer_name) >>> pslist.PsList.get_requirements()
[<ModuleRequirement: kernel>, <BooleanRequirement: physical>, <ListRequirement: pid>, <BooleanRequirement: dump>]
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 +144,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 +154,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.
+2 -2
View File
@@ -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
+1 -2
View File
@@ -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
+60 -12
View File
@@ -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
@@ -31,6 +31,8 @@ class Volshell(interfaces.plugins.PluginInterface):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__current_layer: Optional[str] = None
self.__current_symbol_table: Optional[str] = None
self.__current_kernel_name: Optional[str] = None
self.__console = None
def random_string(self, length: int = 32) -> str:
@@ -57,8 +59,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 +79,11 @@ 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}
Current Kernel Name : {self.current_kernel_name}
"""
sys.ps1 = f"({self.current_layer}) >>> "
self.__console = code.InteractiveConsole(locals = self._construct_locals_dict())
@@ -121,7 +123,10 @@ class Volshell(interfaces.plugins.PluginInterface):
(['dw', 'display_words'], self.display_words), (['dd',
'display_doublewords'], self.display_doublewords),
(['dq', 'display_quadwords'], self.display_quadwords), (['dis', 'disassemble'], self.disassemble),
(['cl', 'change_layer'], self.change_layer), (['context'], self.context), (['self'], self),
(['cl', 'change_layer'], self.change_layer),
(['cs', 'change_symboltable'], self.change_symbol_table),
(['ck', 'change_kernel'], self.change_kernel),
(['context'], self.context), (['self'], self),
(['dpo', 'display_plugin_output'], self.display_plugin_output),
(['gt', 'generate_treegrid'], self.generate_treegrid), (['rt',
'render_treegrid'], self.render_treegrid),
@@ -174,15 +179,58 @@ 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
def change_layer(self, layer_name = None):
@property
def current_symbol_table(self):
if self.__current_symbol_table is None and self.kernel:
self.__current_symbol_table = self.kernel.symbol_table_name
return self.__current_symbol_table
@property
def current_kernel_name(self):
if self.__current_kernel_name is None:
self.__current_kernel_name = self.config.get('kernel', None)
return self.__current_kernel_name
@property
def kernel(self):
"""Returns the current kernel object"""
if self.current_kernel_name not in self.context.modules:
return None
return self.context.modules[self.current_kernel_name]
def change_layer(self, layer_name: str = None):
"""Changes the current default layer"""
if not layer_name:
layer_name = self.config['primary']
self.__current_layer = layer_name
layer_name = self.current_layer
if layer_name not in self.context.layers:
print(f"Layer {layer_name} not present in context")
else:
self.__current_layer = layer_name
sys.ps1 = f"({self.current_layer}) >>> "
def change_symbol_table(self, symbol_table_name: str = None):
"""Changes the current_symbol_table"""
if not symbol_table_name:
print("No symbol table provided, not changing current symbol table")
if symbol_table_name not in self.context.symbol_space:
print(f"Symbol table {symbol_table_name} not present in context symbol_space")
else:
self.__current_symbol_table = symbol_table_name
print(f"Current Symbol Table: {self.current_symbol_table}")
def change_kernel(self, kernel_name: str = None):
if not kernel_name:
print("No kernel module name provided, not changing current kernel")
if kernel_name not in self.context.modules:
print(f"Kernel module {kernel_name} not found in the context module list")
else:
self.__current_kernel_name = kernel_name
print(f"Current kernel : {self.current_kernel_name}")
def display_bytes(self, offset, count = 128, layer_name = None):
"""Displays byte values and ASCII characters"""
remaining_data = self._read_data(offset, count = count, layer_name = layer_name)
+11 -5
View File
@@ -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_kernel_name))
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,9 @@ class Volshell(generic.Volshell):
if symbol_table is None:
symbol_table = self.config['vmlinux']
return super().display_symbols(symbol_table)
@property
def current_layer(self):
if self.__current_layer is None:
self.__current_layer = self.kernel.layer_name
return self.__current_layer
+13 -7
View File
@@ -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)
])
@@ -34,17 +34,17 @@ class Volshell(generic.Volshell):
return
print(f"No task with task ID {pid} found")
def list_tasks(self):
def list_tasks(self, method = None):
"""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.get_list_tasks(method)(self.context, self.current_kernel_name))
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,11 @@ 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 current_layer(self):
if self.__current_layer is None:
self.__current_layer = self.kernel.layer_name
return self.__current_layer
+13 -7
View File
@@ -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,17 @@ 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 current_layer(self):
if self.__current_layer is None:
self.__current_layer = self.kernel.layer_name
return self.__current_layer