mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-22 14:32:21 +02:00
303 lines
11 KiB
Python
303 lines
11 KiB
Python
"""Renderers
|
|
|
|
Renderers display the unified output format in some manner (be it text or file or graphical output"""
|
|
|
|
import collections
|
|
import re
|
|
|
|
from volatility.framework import validity
|
|
|
|
|
|
class TreeRow(validity.ValidityRoutines):
|
|
"""Class providing the interface for an individual Row of the TreeGrid"""
|
|
|
|
def __init__(self, treegrid, values):
|
|
self._type_check(treegrid, TreeGrid)
|
|
if not isinstance(self, TreeGrid):
|
|
self._type_check(values, list)
|
|
treegrid.validate_values(values)
|
|
self._treegrid = treegrid
|
|
self._children = []
|
|
self._values = values
|
|
|
|
def add_child(self, child):
|
|
"""Appends a child to the current Row"""
|
|
self._type_check(child, TreeRow)
|
|
self._children += [child]
|
|
|
|
def insert_child(self, child, position):
|
|
"""Inserts a child at a specific position in the current Row"""
|
|
self._type_check(child, TreeRow)
|
|
self._children = self._children[:position] + [child] + self._children[:position]
|
|
|
|
def clear(self):
|
|
"""Removes all children from this row
|
|
|
|
:rtype : None
|
|
"""
|
|
self._children = []
|
|
|
|
@property
|
|
def values(self):
|
|
"""The individual cell values of the row"""
|
|
return self._values
|
|
|
|
@property
|
|
def children(self):
|
|
"""Returns an iterator of the children of the current row
|
|
|
|
:rtype : iterator of TreeRows
|
|
"""
|
|
for child in self._children:
|
|
yield child
|
|
|
|
def iterator(self, level = 0):
|
|
"""Returns an iterator of all rows with their depths
|
|
|
|
:type level: int
|
|
:param level: Indicates the depth of the current iterator
|
|
"""
|
|
yield (level, self)
|
|
for child in self.children:
|
|
for grandchild in child.iterator(level + 1):
|
|
yield grandchild
|
|
|
|
|
|
Column = collections.namedtuple('Column', ['index', 'name', 'type', 'format'])
|
|
|
|
|
|
class TreeGrid(TreeRow):
|
|
"""Class providing the interface for a TreeGrid (which contains TreeRows)"""
|
|
|
|
simple_types = set((int, str, float, bytes))
|
|
|
|
def __init__(self, columns):
|
|
"""Constructs a TreeGrid object using a specific set of columns
|
|
|
|
The TreeGrid itself is a root element, that can have children but no values.
|
|
The format_hint is a suggestion to the renderer as to how the field should be portrayed as a string,
|
|
but it should be noted that the renderer is not under obligation to use it.
|
|
|
|
:param columns: A list of column tuples made up of (name, type and format_hint).
|
|
"""
|
|
self._type_check(columns, list)
|
|
converted_columns = []
|
|
for (name, column_type, column_format) in columns:
|
|
is_simple_type = False
|
|
for stype in self.simple_types:
|
|
is_simple_type = is_simple_type or issubclass(column_type, stype)
|
|
if not is_simple_type:
|
|
raise TypeError("Column " + name + "'s type " + column_type.__class__.__name__ +
|
|
" is not a simple type")
|
|
if isinstance(column_format, str):
|
|
column_format = FormatSpecification.from_specification(column_format)
|
|
if not (column_format is None or isinstance(column_format, FormatSpecification)):
|
|
raise TypeError(
|
|
"Column " + name + "'s format " + repr(column_format) + " is not an accepted formatter.")
|
|
converted_columns.append(Column(len(converted_columns), name, column_type, column_format))
|
|
self._columns = converted_columns
|
|
|
|
# We can use the special type None because we're the top level node without values
|
|
TreeRow.__init__(self, self, None)
|
|
|
|
@property
|
|
def columns(self):
|
|
"""Returns list of tuples of (name, type and format_hint)"""
|
|
for column in self._columns:
|
|
yield column
|
|
|
|
def validate_values(self, values):
|
|
"""Takes a list of values and verified them against the column types"""
|
|
if len(values) != len(self._columns):
|
|
raise ValueError("The length of the values provided does not match the number of columns.")
|
|
for column in self._columns:
|
|
if not isinstance(values[column.index], column.type):
|
|
raise TypeError("Column type " + str(column.index) + " is incorrect.")
|
|
|
|
def iterator(self, level = 0):
|
|
"""Returns an iterator of all rows with their depths
|
|
|
|
:type level: int
|
|
:param level: Indicates the depth of the current iterator
|
|
"""
|
|
for child in self.children:
|
|
for grandchild in child.iterator(level + 1):
|
|
yield grandchild
|
|
|
|
|
|
class FormatSpecification(object):
|
|
valid_types = "bcdeEfFgGnosxX%"
|
|
pattern = re.compile("^((?P<fill>.)?(?P<align>[<>=^]))?" +
|
|
"(?P<sign>[ +-])?" +
|
|
"(?P<alt>#)?" +
|
|
"?(?P<zero>0)?" +
|
|
"(?P<width>[0-9]+)?" +
|
|
"(?P<precision>[.][0-9]+)?" +
|
|
"(?P<type>[" + valid_types + "])?$")
|
|
|
|
# noinspection PyShadowingBuiltins
|
|
def __init__(self, fill = None, align = None, sign = None, alt = None, zero = None, width = None,
|
|
precision = None, type = None): # pylint: disable=W0622
|
|
self._fill = fill
|
|
self._align = align
|
|
self._sign = sign
|
|
self._alt = alt
|
|
self._zero = zero
|
|
self._width = width
|
|
self._precision = precision
|
|
self._type = type
|
|
|
|
@classmethod
|
|
def from_specification(cls, format_spec):
|
|
"""Converts a format_specification string into a FormatSpecification object
|
|
|
|
:param format_spec: Format specification string
|
|
:type format_spec: str
|
|
|
|
:rtype: FormatSpecification
|
|
:return: A FormatSpecification object with the various parameters parsed
|
|
"""
|
|
result = cls.pattern.match(format_spec)
|
|
if not result:
|
|
raise ValueError("Invalid format specification identified.")
|
|
return cls(result.group('fill'),
|
|
result.group('align'),
|
|
result.group('sign'),
|
|
bool(result.group('alt')),
|
|
bool(result.group('zero')),
|
|
int(result.group('width')) if result.group('width') else None,
|
|
int(result.group('precision')[1:]) if result.group('precision') else None,
|
|
result.group('type'))
|
|
|
|
@property
|
|
def fill(self):
|
|
"""Selects a character to fill out the empty space after a specific alignment has been chosen."""
|
|
return self._fill
|
|
|
|
@property
|
|
def align(self):
|
|
"""Determines how to align the text
|
|
|
|
'>' will align to the right (default for numbers)
|
|
'<' will align to the left (default for strings)
|
|
'^' will align to the center
|
|
'=' is only valid for numeric types and will force the padding to go after the sign but before digits
|
|
|
|
The alignment option has no meaning if the width is not specified, as it will default to the same width as the
|
|
data.
|
|
"""
|
|
return self._align
|
|
|
|
@property
|
|
def sign(self):
|
|
"""Determines whether to insert a sign
|
|
|
|
'+' indicates a sign should be present on both positive and negative numbers
|
|
'-' indicates that a sign should be present only on negative numbers (the default)
|
|
' ' indicates that a leading space should be used on positive numbers, and a minus sign on negative ones
|
|
"""
|
|
return self._sign
|
|
|
|
@property
|
|
def alt(self):
|
|
"""Specifies whether an alternate form should be used.
|
|
|
|
This option is only valid for integer, float, complex and Decimal types. For integers, when binary, octal, or
|
|
hexadecimal output is used, this option adds the prefix respective '0b', '0o', or '0x' to the output value. For
|
|
floats, complex and Decimal the alternate form causes the result of the conversion to always contain a
|
|
decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result
|
|
of these conversions only if a digit follows it. In addition, for 'g' and 'G' conversions, trailing zeros are
|
|
not removed from the result.
|
|
"""
|
|
return self._alt
|
|
|
|
@property
|
|
def zero(self):
|
|
"""Specifies whether to enable sign-aware zero-padding for numeric types.
|
|
|
|
This is equivalent to a fill character of '0' with an alignment type of '='.
|
|
"""
|
|
return self._zero
|
|
|
|
@property
|
|
def width(self):
|
|
"""Specifies a decimal integer defining the minimum field width.
|
|
|
|
If not specified, then the field width will be determined by the content.
|
|
"""
|
|
return self._width
|
|
|
|
@property
|
|
def precision(self):
|
|
"""Specifies a decimal number indicating how many digits after the decimal point should be displayed.
|
|
|
|
This specifies a decimal number indicating how many digits should be displayed after the decimal point for a
|
|
floating point value formatted with 'f' and 'F', or before and after the decimal point for a floating point
|
|
value formatted with 'g' or 'G'. For non-number types the field indicates the maximum field size - in other
|
|
words, how many characters will be used from the field content. The precision is not allowed for integer values.
|
|
"""
|
|
return self._precision
|
|
|
|
@property
|
|
def type(self):
|
|
"""Specifies the type conversion
|
|
|
|
For complete documentation, see the Format Specification Mini-Language in the python string documentation.
|
|
"""
|
|
return self._type
|
|
|
|
@fill.setter
|
|
def fill(self, value):
|
|
if value and not (isinstance(value, str) and len(value) == 1):
|
|
raise ValueError("Fill value must be a single character string.")
|
|
self._fill = value or None
|
|
|
|
@align.setter
|
|
def align(self, value):
|
|
if value and not (value in "<>^=" and len(value) == 1):
|
|
raise ValueError("Alignment value must be one of '<', '>', '=' or '^', not '" + value + "'.")
|
|
self._align = value or None
|
|
|
|
@sign.setter
|
|
def sign(self, value):
|
|
if value and not (value in "" and len(value) == 1):
|
|
raise ValueError("Sign value must be one of '-', '+' or ' ', not '" + value + "'.")
|
|
self._sign = value or None
|
|
|
|
@alt.setter
|
|
def alt(self, value):
|
|
self._alt = bool(value)
|
|
|
|
@zero.setter
|
|
def zero(self, value):
|
|
self._zero = bool(value)
|
|
|
|
@width.setter
|
|
def width(self, value):
|
|
self._width = int(value)
|
|
|
|
@precision.setter
|
|
def precision(self, value):
|
|
self._precision = int(value)
|
|
|
|
@type.setter
|
|
def type(self, value):
|
|
if not (value in self.valid_types and len(value) == 1):
|
|
raise ValueError("Invalid type value provided.")
|
|
self._type = value
|
|
|
|
def to_string(self):
|
|
return str(self)
|
|
|
|
def __str__(self):
|
|
""""""
|
|
spec = ((self.fill if self.fill and self.align else '') +
|
|
(self.align or '') +
|
|
(self.sign or '') +
|
|
('#' if self.alt else '') +
|
|
('0' if self.zero else '') +
|
|
(str(self.width) if self.width else '') +
|
|
(("." + str(self.precision)) if self.precision or self.precision == 0 else '') +
|
|
(self.type or ''))
|
|
return spec
|