mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-05 09:17:38 +02:00
Windows Handles: Convert to classmethods
A lot of functionality in this plugin class was using instance methods instead of classmethods; this refactors the plugin to use classmethods instead of instance methods for consistency with the rest of the framework, and bumps the plugin major version to 4.0.0.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, Iterator, List, Optional
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, renderers, symbols
|
||||
from volatility3.framework.configuration import requirements
|
||||
@@ -18,13 +18,9 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
"""Lists process open handles."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (3, 0, 0)
|
||||
_version = (4, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._type_map = None
|
||||
self._cookie = None
|
||||
self._level_mask = 7
|
||||
LEVEL_MASK = 7
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -54,18 +50,27 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
),
|
||||
]
|
||||
|
||||
def _get_item(self, handle_table_entry, handle_value):
|
||||
"""Given a handle table entry (_HANDLE_TABLE_ENTRY) structure from a
|
||||
@classmethod
|
||||
def _get_item(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
handle_table_entry: interfaces.objects.ObjectInterface,
|
||||
handle_value: int,
|
||||
) -> Optional[interfaces.objects.ObjectInterface]:
|
||||
"""
|
||||
Given a handle table entry (_HANDLE_TABLE_ENTRY) structure from a
|
||||
process' handle table, determine where the corresponding object's
|
||||
_OBJECT_HEADER can be found."""
|
||||
_OBJECT_HEADER can be found, and construct and return the _OBJECT_HEADER
|
||||
"""
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
virtual = kernel.layer_name
|
||||
|
||||
try:
|
||||
# before windows 7
|
||||
if not self.context.layers[virtual].is_valid(handle_table_entry.Object):
|
||||
if not context.layers[virtual].is_valid(handle_table_entry.Object):
|
||||
return None
|
||||
fast_ref = handle_table_entry.Object.cast("_EX_FAST_REF")
|
||||
|
||||
@@ -78,7 +83,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
except AttributeError:
|
||||
# starting with windows 8
|
||||
is_64bit = symbols.symbol_table_is_64bit(
|
||||
context=self.context, symbol_table_name=kernel.symbol_table_name
|
||||
context=context, symbol_table_name=kernel.symbol_table_name
|
||||
)
|
||||
|
||||
if is_64bit:
|
||||
@@ -104,7 +109,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
offset = info_table & ~7
|
||||
|
||||
# print("LowValue: {0:#x} Magic: {1:#x} Offset: {2:#x}".format(handle_table_entry.InfoTable, magic, offset))
|
||||
object_header = self.context.object(
|
||||
object_header = context.object(
|
||||
kernel.symbol_table_name + constants.BANG + "_OBJECT_HEADER",
|
||||
virtual,
|
||||
offset=offset,
|
||||
@@ -205,11 +210,23 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
offset=symbol_offset,
|
||||
)
|
||||
|
||||
def _make_handle_array(self, offset, level, depth=0):
|
||||
"""Parse a process' handle table and yield valid handle table entries,
|
||||
going as deep into the table "levels" as necessary."""
|
||||
@classmethod
|
||||
def _make_handle_array(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
offset: int,
|
||||
level: int,
|
||||
depth: int = 0,
|
||||
) -> Iterator[interfaces.objects.ObjectInterface]:
|
||||
"""
|
||||
Parses a process' handle table by constructing an array of
|
||||
`_HANDLE_TABLE_ENTRY` structures at the given offset, and yields valid
|
||||
handle table entries, going as deep into the table "levels" as
|
||||
necessary.
|
||||
"""
|
||||
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
if level > 0:
|
||||
subtype = kernel.get_type("pointer")
|
||||
@@ -218,7 +235,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
subtype = kernel.get_type("_HANDLE_TABLE_ENTRY")
|
||||
count = 0x1000 / subtype.size
|
||||
|
||||
if not self.context.layers[kernel.layer_name].is_valid(offset):
|
||||
if not context.layers[kernel.layer_name].is_valid(offset):
|
||||
return None
|
||||
|
||||
table = kernel.object(
|
||||
@@ -229,7 +246,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
absolute=True,
|
||||
)
|
||||
|
||||
layer_object = self.context.layers[kernel.layer_name]
|
||||
layer_object = context.layers[kernel.layer_name]
|
||||
masked_offset = offset & layer_object.maximum_address
|
||||
|
||||
for i in range(len(table)):
|
||||
@@ -243,11 +260,13 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
# The code above this calls `is_valid` on the `offset`
|
||||
# It is sent but then does not validate `entry` before
|
||||
# sending it to `_get_item`
|
||||
if not self.context.layers[kernel.layer_name].is_valid(entry.vol.offset):
|
||||
if not context.layers[kernel.layer_name].is_valid(entry.vol.offset):
|
||||
continue
|
||||
|
||||
if level > 0:
|
||||
yield from self._make_handle_array(entry, level - 1, depth)
|
||||
yield from cls._make_handle_array(
|
||||
context, kernel_module_name, entry, level - 1, depth
|
||||
)
|
||||
depth += 1
|
||||
else:
|
||||
handle_multiplier = 4
|
||||
@@ -258,7 +277,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
/ (subtype.size / handle_multiplier)
|
||||
) + handle_level_base
|
||||
|
||||
item = self._get_item(entry, handle_value)
|
||||
item = cls._get_item(context, kernel_module_name, entry, handle_value)
|
||||
|
||||
if item is None:
|
||||
continue
|
||||
@@ -272,10 +291,21 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
def handles(self, handle_table):
|
||||
@classmethod
|
||||
def handles(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_module_name: str,
|
||||
handle_table: interfaces.objects.ObjectInterface,
|
||||
) -> Iterator[interfaces.objects.ObjectInterface]:
|
||||
"""
|
||||
Takes a context, kernel module name, and handle table structure
|
||||
(_HANDLE_TABLE), and yields _HANDLE_TABLE_ENTRY structures from the
|
||||
handle table.
|
||||
"""
|
||||
try:
|
||||
TableCode = handle_table.TableCode & ~self._level_mask
|
||||
table_levels = handle_table.TableCode & self._level_mask
|
||||
TableCode = handle_table.TableCode & ~cls.LEVEL_MASK
|
||||
table_levels = handle_table.TableCode & cls.LEVEL_MASK
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVV,
|
||||
@@ -283,7 +313,9 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
)
|
||||
return None
|
||||
|
||||
yield from self._make_handle_array(TableCode, table_levels)
|
||||
yield from cls._make_handle_array(
|
||||
context, kernel_module_name, TableCode, table_levels
|
||||
)
|
||||
|
||||
def _generator(self, procs):
|
||||
type_map = self.get_type_map(
|
||||
@@ -306,7 +338,9 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
|
||||
process_name = utility.array_to_string(proc.ImageFileName)
|
||||
|
||||
for entry in self.handles(object_table):
|
||||
for entry in self.handles(
|
||||
self.context, self.config["kernel"], object_table
|
||||
):
|
||||
try:
|
||||
obj_type = entry.get_object_type(type_map, cookie)
|
||||
if obj_type is None:
|
||||
|
||||
Reference in New Issue
Block a user