mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-22 01:24:51 +02:00
Rework templates so that they're created referentially, and that attributes are generated by the underlying class and arguments.
This commit is contained in:
+24
-12
@@ -4,22 +4,34 @@ Created on 10 Mar 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
from volatility.framework import xp_sp2_x86_vtypes, context
|
||||
import pdb
|
||||
from volatility.framework import xp_sp2_x86_vtypes, context, symbols
|
||||
from volatility.framework.symbols import vtypes, native
|
||||
|
||||
if __name__ == '__main__':
|
||||
def main():
|
||||
nativelst = native.x86NativeTable
|
||||
|
||||
virtual_types = xp_sp2_x86_vtypes.ntkrnlmp_types
|
||||
|
||||
ntkrnlmp = vtypes.VTypeSymbolList('ntkrnlmp', virtual_types)
|
||||
native = native.x86NativeSymbolList
|
||||
ntkrnlmp = vtypes.VTypeSymbolTable('ntkrnlmp', virtual_types, nativelst)
|
||||
|
||||
ctx = context.Context()
|
||||
ctx.add_symbol_list(native)
|
||||
ctx.add_symbol_list(ntkrnlmp)
|
||||
print("Symbols,", native.symbols)
|
||||
ctx = context.Context(symbols.SymbolSpace(nativelst))
|
||||
ctx.symbol_space.append(nativelst)
|
||||
ctx.symbol_space.append(ntkrnlmp)
|
||||
print("Symbols,", nativelst.symbols)
|
||||
|
||||
for i in ntkrnlmp.symbols:
|
||||
symbol = ctx.resolve('ntkrnlmp!' + i)
|
||||
print(symbol)
|
||||
# objthing = symbol(context, layer_name = '', offset = 0)
|
||||
for _ in [1, 2]:
|
||||
for i in list(ntkrnlmp.symbols):
|
||||
symbol = ctx.symbol_space.resolve('ntkrnlmp!' + i)
|
||||
print(symbol.symbol_name, symbol, symbol.size)
|
||||
# objthing = symbol(context, layer_name = '', offset = 0)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# import timeit
|
||||
# print(timeit.Timer(main).timeit(10))
|
||||
try:
|
||||
main()
|
||||
except:
|
||||
pdb.post_mortem()
|
||||
|
||||
@@ -4,28 +4,21 @@ Created on 12 Feb 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
import volatility.framework.symbols as symbols
|
||||
import volatility.framework.interfaces as interfaces
|
||||
|
||||
class Context(object):
|
||||
class Context(interfaces.ContextInterface):
|
||||
"""Maintains the context within which to construct objects"""
|
||||
|
||||
def __init__(self):
|
||||
self._symbol_space = symbols.SymbolSpace()
|
||||
def __init__(self, symbol_space):
|
||||
super(Context, self).__init__()
|
||||
self._symbol_space = symbol_space
|
||||
self._layers = {}
|
||||
|
||||
### Symbol Space Functions
|
||||
|
||||
def add_symbol_list(self, symbol_list):
|
||||
"""Adds a symbol list to the symbol space used by the context"""
|
||||
self._symbol_space.append(symbol_list)
|
||||
|
||||
def remove_symbol_list(self, symbol_list_name):
|
||||
"""Removes a symbol list from the symbol space used by the context"""
|
||||
self._symbol_space.remove(symbol_list_name)
|
||||
|
||||
def resolve(self, symbol_name):
|
||||
"""Resolves a symbol name from the various symbol lists in the symbol space"""
|
||||
return self._symbol_space.resolve(symbol_name)
|
||||
@property
|
||||
def symbol_space(self):
|
||||
return self._symbol_space
|
||||
|
||||
### Address Space Functions
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ Created on 12 Apr 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
import copy
|
||||
|
||||
class ObjectInterface(object):
|
||||
""" A base object required to be the ancestor of every object used in volatility """
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size, **kwargs):
|
||||
@@ -14,5 +16,132 @@ class ObjectInterface(object):
|
||||
self._size = size
|
||||
|
||||
def cast(self, new_symbol_name):
|
||||
object_template = self._context.resolve()
|
||||
object_template = self._context.resolve(new_symbol_name)
|
||||
return object_template(context = self._context, layer_name = self._layer_name, offset = self._offset)
|
||||
|
||||
class ContextInterface(object):
|
||||
"""Class for providing the interface for the Context object"""
|
||||
|
||||
def __init__(self):
|
||||
"""Intializes the context with a symbol_space"""
|
||||
|
||||
### Symbol Space Functions
|
||||
|
||||
@property
|
||||
def symbol_space(self):
|
||||
"""Returns the symbol_space for the context"""
|
||||
|
||||
### Address Space Functions
|
||||
|
||||
def add_translation_layer(self, layer, name = None):
|
||||
"""Adds a named translation layer to the context"""
|
||||
|
||||
### Object Factory Functions
|
||||
|
||||
def object(self, symbol, layer_name, offset):
|
||||
"""Object factory, takes a context, symbol, offset and optional layername
|
||||
|
||||
Looks up the layername in the context, finds the object template based on the symbol,
|
||||
and constructs an object using the object template on the layer at the offset.
|
||||
|
||||
Returns a fully constructed object
|
||||
"""
|
||||
|
||||
class Template(object):
|
||||
"""Class for all Factories that take offsets, and data layers and produce objects
|
||||
|
||||
This is effectively a class for currying object calls
|
||||
"""
|
||||
def __init__(self, symbol_name = None, **kwargs):
|
||||
"""Stores the keyword arguments for later use"""
|
||||
self._kwargs = kwargs
|
||||
self._symbol_name = symbol_name
|
||||
|
||||
@property
|
||||
def symbol_name(self):
|
||||
"""Returns the name of the particular symbol"""
|
||||
return self._symbol_name
|
||||
|
||||
@property
|
||||
def arguments(self):
|
||||
"""Returns the keyword arguments stored earlier"""
|
||||
return copy.deepcopy(self._kwargs)
|
||||
|
||||
def update_arguments(self, **newargs):
|
||||
"""Updates the keyword arguments"""
|
||||
self._kwargs.update(newargs)
|
||||
|
||||
def __call__(self, context, layer_name, offset, parent = None):
|
||||
"""Constructs the object
|
||||
|
||||
Returns: an object adhereing to the Object interface
|
||||
"""
|
||||
|
||||
class SymbolTableInterface(object):
|
||||
"""Handles a table of symbols"""
|
||||
|
||||
def __init__(self, name, native_symbols = None, *args, **kwargs):
|
||||
super(SymbolTableInterface, self).__init__(*args, **kwargs)
|
||||
if not isinstance(name, str) or not name:
|
||||
raise TypeError("Symbol Table name must be a string")
|
||||
if not isinstance(native_symbols, NativeTableInterface):
|
||||
raise TypeError("Symbol Table native_symbols must be a NativeTable, not " + str(type(native_symbols)))
|
||||
self.name = name
|
||||
self._native_symbols = native_symbols
|
||||
|
||||
### Required Symbol List functions
|
||||
|
||||
def resolve(self, symbol):
|
||||
"""Resolves a symbol name into an object template
|
||||
|
||||
If the symbol isn't found it raises a SymbolNotFound exception
|
||||
"""
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
"""Returns an iterator of the symbol names"""
|
||||
|
||||
### Native Type Handler
|
||||
|
||||
@property
|
||||
def natives(self):
|
||||
"""Returns None or a symbol_space for handling space specific native types"""
|
||||
return self._native_symbols
|
||||
|
||||
### Functions for overriding classes
|
||||
|
||||
def set_symbol_class(self, symbol, clazz):
|
||||
"""Overrides the object class for a specific symbol
|
||||
|
||||
Symbol *must* be present in self.symbols
|
||||
"""
|
||||
|
||||
def get_symbol_class(self, symbol):
|
||||
"""Returns the class associated with a symbol"""
|
||||
|
||||
def del_symbol_class(self, symbol):
|
||||
"""Removes the associated class override for a specific symbol"""
|
||||
|
||||
### Helper functions that can be overridden
|
||||
|
||||
def __len__(self):
|
||||
"""Returns the number of items in the symbol list"""
|
||||
return len(self.symbols)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Resolves a symbol name into an object template
|
||||
|
||||
Note, this method cannot sub-resolve throughout a whole symbol space
|
||||
"""
|
||||
return self.resolve(key)
|
||||
|
||||
def __iter__(self):
|
||||
"""Returns an iterator of the available keys"""
|
||||
return self.symbols
|
||||
|
||||
def __contains__(self, symbol):
|
||||
"""Determines whether a symbol exists in the list or not"""
|
||||
return symbol in self.symbols
|
||||
|
||||
class NativeTableInterface(SymbolTableInterface):
|
||||
"""Class to distinguish NativeSymbolLists from other symbol lists"""
|
||||
|
||||
+126
-48
@@ -9,39 +9,55 @@ import collections
|
||||
import volatility.framework.interfaces as interfaces
|
||||
import volatility.framework.templates as templates
|
||||
|
||||
class Void(interfaces.ObjectInterface):
|
||||
"""Returns an object to represent void/unknown types"""
|
||||
pass
|
||||
|
||||
class PrimitiveObject(interfaces.ObjectInterface):
|
||||
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
|
||||
|
||||
def __init__(self, context, layer_name, offset, symbol_name, struct_format = '<I', **kwargs):
|
||||
super(PrimitiveObject, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
|
||||
self._struct_format = struct_format
|
||||
|
||||
@classmethod
|
||||
def _struct_value(cls, struct_format, context, layer_name, offset, symbol_name, size):
|
||||
def _struct_value(cls, struct_format, context, layer_name, offset, symbol_name):
|
||||
aspace = context.get_address_space(layer_name)
|
||||
length = struct.calcsize(struct_format)
|
||||
data = aspace.read(offset, length)
|
||||
(value,) = struct.unpack(struct_format, data)
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def template_size(cls, arguments):
|
||||
"""Returns the size of the templated object"""
|
||||
return struct.calcsize(arguments.get('struct_format', '<I'))
|
||||
|
||||
@classmethod
|
||||
def template_children(cls, arguments):
|
||||
"""Since primitives have no children, this returns an empty list"""
|
||||
return []
|
||||
|
||||
def template_replace_child(self, old_child, new_child, arguments):
|
||||
"""Since this template can't ever have children, this method can be empty"""
|
||||
|
||||
class Integer(PrimitiveObject, int):
|
||||
"""Primitive Object that handles standard numeric types"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
|
||||
struct_format = context.get_primitive_struct_type("int", symbol_name)
|
||||
struct_size = struct.calcsize(struct_format)
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name, size = struct_size)
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
|
||||
|
||||
class Float(PrimitiveObject, float):
|
||||
"""Primitive Object that handles double or floating point numbers"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, size, **kwargs):
|
||||
struct_format = context.get_primitive_struct_type("float", symbol_name)
|
||||
struct_size = struct.calcsize(struct_format)
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name, size = struct_size)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, struct_format, **kwargs):
|
||||
return cls._struct_value(struct_format, context, layer_name, offset, symbol_name)
|
||||
|
||||
class Bytes(PrimitiveObject, bytes):
|
||||
"""Primitive Object that handles specific series of bytes
|
||||
"""
|
||||
"""Primitive Object that handles specific series of bytes"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, size, **kwargs):
|
||||
return cls._struct_value(str(size) + "s", context, layer_name, offset, symbol_name, size)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
|
||||
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
|
||||
|
||||
class String(PrimitiveObject, str):
|
||||
"""Primitive Object that handles string values
|
||||
@@ -49,15 +65,19 @@ class String(PrimitiveObject, str):
|
||||
length: specifies the maximum possible length that the string could hold in memory
|
||||
"""
|
||||
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, size = None, length = 1):
|
||||
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name, size)
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, length = 1):
|
||||
return cls._struct_value(str(length) + "s", context, layer_name, offset, symbol_name)
|
||||
|
||||
class Pointer(Integer):
|
||||
"""Pointer which points to another object"""
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, target = None):
|
||||
if not isinstance(target, interfaces.ObjectInterface):
|
||||
raise TypeError("Pointer targets must be an ObjectInterface")
|
||||
super(Pointer, self).__init__(context, layer_name, offset, symbol_name, size)
|
||||
def __init__(self, context, layer_name, offset, symbol_name, struct_format = None, target = None):
|
||||
if not isinstance(target, templates.ObjectTemplate):
|
||||
raise TypeError("Pointer targets must be an ObjectTemplate")
|
||||
super(Pointer, self).__init__(context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
struct_format = struct_format)
|
||||
self._target = target
|
||||
|
||||
def derefenence(self):
|
||||
@@ -71,46 +91,104 @@ class Pointer(Integer):
|
||||
"""Convenience function to access unknown attributes by getting them from the target object"""
|
||||
return getattr(self.dereference(), attr)
|
||||
|
||||
class BitField(Integer):
|
||||
""""""
|
||||
def __new__(self, context, layer_name, offset, symbol_name, size = None, start_bit = 0, end_bit = 0):
|
||||
# TODO: Determine endianness of the bitfield
|
||||
struct_size = (end_bit + 7) // 8
|
||||
if struct_size == 1:
|
||||
struct_format = "c"
|
||||
elif struct_size <= 2:
|
||||
struct_format = "H"
|
||||
elif struct_size <= 4:
|
||||
struct_format = "I"
|
||||
else:
|
||||
struct_format = "Q"
|
||||
return super(BitField, self).__new__(context, layer_name, offset, symbol_name, size = None, struct_format = struct_format)
|
||||
class BitField(PrimitiveObject):
|
||||
"""Object containing a field which is made up of bits rather than whole bytes"""
|
||||
def __new__(cls, context, layer_name, offset, symbol_name, target = None, start_bit = 0, end_bit = 0):
|
||||
return target(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name)
|
||||
|
||||
class Enumeration(interfaces.ObjectInterface):
|
||||
"""Returns an object made up of choices"""
|
||||
# FIXME: Add in body for the enumeration object
|
||||
pass
|
||||
|
||||
class Array(interfaces.ObjectInterface, collections.Sequence):
|
||||
"""Object which can contain a fixed number of an object type"""
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, count = 0, target = None):
|
||||
if not isinstance(target, templates.ObjectTemplate):
|
||||
raise TypeError("Array target must be an ObjectTemplate")
|
||||
super(Array, self).__init__(context = context, layer_name = layer_name, offset = offset, symbol_name = symbol_name, size = size)
|
||||
self._count = count
|
||||
self._target = target
|
||||
|
||||
@classmethod
|
||||
def template_size(cls, arguments):
|
||||
"""Returns the size of the array, based on the count and the target"""
|
||||
if 'target' not in arguments and 'count' not in arguments:
|
||||
raise TypeError("Array ObjectTemplate must be provided a count and target")
|
||||
return arguments.get('target', None).size * arguments.get('count', 0)
|
||||
|
||||
@classmethod
|
||||
def template_children(cls, arguments):
|
||||
"""Returns the children of the template"""
|
||||
if 'target' in arguments:
|
||||
return [arguments['target']]
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def template_replace_child(cls, old_child, new_child, arguments):
|
||||
"""Substitutes the old_child for the new_child"""
|
||||
if 'target' in arguments:
|
||||
if arguments['target'] == old_child:
|
||||
arguments['target'] = new_child
|
||||
|
||||
def __getitem__(self, i):
|
||||
"""Returns the i-th item from the array"""
|
||||
return self._target(context = self._context, layer_name = self._layer_name, offset = self._offset + (self._target.size * i), parent = self)
|
||||
|
||||
def __len__(self):
|
||||
"""Returns the length of the array"""
|
||||
return self._count
|
||||
|
||||
class Struct(interfaces.ObjectInterface):
|
||||
"""Object which can contain members that are other objects"""
|
||||
|
||||
def __init__(self, context, layer_name, offset, symbol_name, size = None, members = None):
|
||||
super(Struct, self).__init__(context, layer_name, offset, symbol_name, size)
|
||||
# Members should be an iterable mapping of symbol names to callable Object Templates
|
||||
super(Struct, self).__init__(context = context,
|
||||
layer_name = layer_name,
|
||||
offset = offset,
|
||||
symbol_name = symbol_name,
|
||||
size = size)
|
||||
self.check_members(members)
|
||||
self._members = members
|
||||
self._concrete_members = {}
|
||||
|
||||
@classmethod
|
||||
def template_children(cls, arguments):
|
||||
"""Method to list children of a template"""
|
||||
cls.check_members(arguments.get('members', None))
|
||||
return arguments['members'].values()
|
||||
|
||||
@classmethod
|
||||
def template_size(cls, arguments):
|
||||
"""Method to return the size of this structure"""
|
||||
if arguments.get('size', None) is None:
|
||||
raise TypeError("Struct ObjectTemplate not provided with a size")
|
||||
return arguments['size']
|
||||
|
||||
@classmethod
|
||||
def template_replace_child(cls, old_child, new_child, arguments):
|
||||
"""Replace a child elements within the arguments handed to the template"""
|
||||
for member in arguments.get('members', {}):
|
||||
relative_offset, member_template = arguments['members'][member]
|
||||
if member_template == old_child:
|
||||
arguments['members'][member] = (relative_offset, new_child)
|
||||
|
||||
@classmethod
|
||||
def check_members(cls, members):
|
||||
# Members should be an iterable mapping of symbol names to tuples of (relative_offset, ObjectTemplate)
|
||||
# An object template is a callable that when called with a context, offset, layer_name and symbol_name
|
||||
if not isinstance(members, collections.Iterable):
|
||||
raise TypeError("Struct members parameter must be iterable not " + type(members))
|
||||
if not all([isinstance(members, templates.MemberTemplate)]):
|
||||
raise TypeError("Struct members must be derived from MemberTemplate objects")
|
||||
if size is None:
|
||||
# Attempt to determine the maximum size by asking
|
||||
for i in members:
|
||||
pass
|
||||
|
||||
self._members = members
|
||||
if not all([(isinstance(member, tuple) and len(member) == 2) for member in members.values()]):
|
||||
raise TypeError("Struct members must be a tuple of relative_offsets and templates")
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""Method for accessing members of the structure"""
|
||||
if attr in self._members:
|
||||
member = self._members[attr]
|
||||
# Cache the constructed object
|
||||
if isinstance(member, templates.ObjectTemplate):
|
||||
member = member(context = self.context, layer_name = self.layer_name, offset = self.offset + member.relative_offset, parent = self)
|
||||
self._members[attr] = member
|
||||
if attr in self._concrete_members:
|
||||
return self._concrete_members[attr]
|
||||
elif attr in self._members:
|
||||
relative_offset, member = self._members[attr]
|
||||
member = member(context = self._context, layer_name = self._layer_name, offset = self._offset + relative_offset, parent = self)
|
||||
self._concrete_members[attr] = member
|
||||
return member
|
||||
raise AttributeError("'" + self._symbol_name + "' Struct has no attribute '" + attr + "'")
|
||||
|
||||
@@ -5,19 +5,27 @@ Created on 7 Feb 2013
|
||||
'''
|
||||
|
||||
import collections
|
||||
import volatility.framework.exceptions as exceptions
|
||||
from volatility.framework import obj, templates, interfaces
|
||||
from volatility.framework.exceptions import SymbolNotFoundException
|
||||
import volatility.framework.exceptions as exceptions
|
||||
|
||||
class SymbolSpace(collections.Mapping):
|
||||
"""Handles an ordered collection of SymbolLists
|
||||
"""Handles an ordered collection of SymbolTables
|
||||
|
||||
This collection is ordered so that resolution of symbols can
|
||||
proceed down through the ranks if a namespace isn't specified.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, native_symbols):
|
||||
if not isinstance(native_symbols, interfaces.NativeTableInterface):
|
||||
raise TypeError("SymbolSpace native_symbols must be NativeSymbolInterface")
|
||||
self._dict = collections.OrderedDict()
|
||||
self._native_symbols = native_symbols
|
||||
|
||||
@property
|
||||
def natives(self):
|
||||
"""Returns the native_types for this symbol space"""
|
||||
return self._native_symbols
|
||||
|
||||
def __len__(self):
|
||||
return len(self._dict)
|
||||
@@ -30,7 +38,7 @@ class SymbolSpace(collections.Mapping):
|
||||
|
||||
def append(self, value):
|
||||
"""Adds a symbol_list to the end of the space"""
|
||||
if not isinstance(value, SymbolListInterface):
|
||||
if not isinstance(value, interfaces.SymbolTableInterface):
|
||||
raise TypeError(value)
|
||||
if value.name in self._dict:
|
||||
del self._dict[value.name]
|
||||
@@ -40,102 +48,54 @@ class SymbolSpace(collections.Mapping):
|
||||
"""Removes a named symbol_list from the space"""
|
||||
del self._dict[key]
|
||||
|
||||
def resolve(self, symbol, start_from = None):
|
||||
"""Takes a symbol name and resolves it"""
|
||||
def _weak_resolve(self, symbol):
|
||||
"""Takes a symbol name and resolves it with ReferentialTemplates"""
|
||||
symarr = symbol.split("!")
|
||||
if len(symarr) == 2:
|
||||
listname = symarr[0]
|
||||
symname = symarr[1]
|
||||
for symlistname in reversed(self._dict):
|
||||
if symlistname == listname:
|
||||
if symname in self[symlistname]:
|
||||
return self[symlistname].resolve(symname, self)
|
||||
else:
|
||||
raise exceptions.SymbolNotFoundException("Symbol \"" + symname + "\" could not be found in the \"" + listname + "\" list")
|
||||
else:
|
||||
raise exceptions.SymbolNotFoundException("Symbol list \"" + listname + "\" was not present in the symbol space")
|
||||
elif len(symarr) == 1:
|
||||
# Establish skipping all elements before the symbol list to start from
|
||||
skip = (start_from is not None)
|
||||
for slist in reversed(self._dict):
|
||||
if skip:
|
||||
skip = (slist.name != start_from)
|
||||
else:
|
||||
if symbol in self[slist]:
|
||||
return self[slist].resolve(symbol, self)
|
||||
else:
|
||||
if skip:
|
||||
raise exceptions.SymbolSpaceError("Symbol search for \"" + symbol + "\" failed because symbol list \"" + start_from.name + "\" is not in the search space")
|
||||
raise exceptions.SymbolNotFoundException("Symbol \"" + symbol + "\" could not be found in any symbol list")
|
||||
table_name = symarr[0]
|
||||
symbol_name = symarr[1]
|
||||
return self._dict[table_name].resolve(symbol_name)
|
||||
else:
|
||||
raise exceptions.SymbolNotFoundException("Malformed symbol name")
|
||||
raise RuntimeError("Symbol Space Resolution hit an unexpected branch!")
|
||||
|
||||
class SymbolListInterface(object):
|
||||
"""Handles a list of symbols"""
|
||||
|
||||
def __init__(self, name, symbol_space = None, *args, **kwargs):
|
||||
super(SymbolListInterface, self).__init__(*args, **kwargs)
|
||||
if not isinstance(name, str) or not name:
|
||||
raise exceptions.SymbolSpaceError("Symbol lists cannot be nameless")
|
||||
self.name = name
|
||||
self._overrides = {}
|
||||
|
||||
### Required Symbol List functions
|
||||
|
||||
def resolve(self, symbol, symbol_space = None):
|
||||
"""Resolves a symbol name into an object template
|
||||
def resolve(self, symbol):
|
||||
"""Takes a symbol name and resolves it
|
||||
|
||||
symbol_space is used to resolve any target symbols if they don't exist in this list
|
||||
This method ensures that all referenced templatess (inlcuding self-referential templates)
|
||||
are satifsfied as ObjectTemplates
|
||||
"""
|
||||
resolved = {symbol: self._weak_resolve(symbol)}
|
||||
weakref_list = [symbol]
|
||||
while weakref_list:
|
||||
weakref, weakref_list = resolved[weakref_list[0]], weakref_list[1:]
|
||||
for child in weakref.children:
|
||||
if isinstance(child, templates.ReferenceTemplate):
|
||||
child_resolved = self._weak_resolve(child.symbol_name)
|
||||
resolved[child.symbol_name] = child_resolved
|
||||
weakref_list.append(child.symbol_name)
|
||||
weakref.replace_child(child, child_resolved)
|
||||
return resolved[symbol]
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
"""Returns an iterator of the symbol names"""
|
||||
|
||||
### Functions for overriding classes
|
||||
|
||||
def set_symbol_class(self, symbol, clazz):
|
||||
"""Overrides the object class for a specific symbol
|
||||
|
||||
We can only override existing classes, otherwise determining the complete symbol list
|
||||
will become complicated for each subclass.
|
||||
"""
|
||||
if not symbol in self.symbols:
|
||||
raise exceptions.SymbolNotFoundException("Cannot override \"" + symbol + "\" in \"" + self.name + "\", symbol not already present")
|
||||
if not issubclass(clazz, interfaces.ObjectInterface):
|
||||
raise exceptions.SymbolSpaceError("Attempting to add an object that does not inherit from ObjectInterface as a symbol class override")
|
||||
self._overrides[symbol] = clazz
|
||||
|
||||
def has_symbol_class(self, symbol):
|
||||
"""Returns whether the symbol's class has bee overridden or not"""
|
||||
return symbol in self._overrides
|
||||
|
||||
def get_symbol_class(self, symbol):
|
||||
"""Returns the class associated with a symbol or None if there is no associated symbol
|
||||
"""
|
||||
return self._overrides[symbol]
|
||||
|
||||
def del_symbol_class(self, symbol):
|
||||
"""Removes the associated class override for a specific symbol"""
|
||||
del self._overrides[symbol]
|
||||
|
||||
### Helper functions that can be overridden
|
||||
|
||||
def __len__(self):
|
||||
"""Returns the number of items in the symbol list"""
|
||||
return len(self.symbols)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Resolves a symbol name into an object template"""
|
||||
return self.resolve(key)
|
||||
|
||||
def __iter__(self):
|
||||
"""Returns an iterator of the available keys"""
|
||||
return self.symbols
|
||||
|
||||
def __contains__(self, symbol):
|
||||
"""Determines whether a symbol exists in the list or not"""
|
||||
return symbol in self.symbols
|
||||
|
||||
|
||||
# symarr = symbol.split("!")
|
||||
# if len(symarr) == 2:
|
||||
# tablename = symarr[0]
|
||||
# symname = symarr[1]
|
||||
# untied = set()
|
||||
# symbol = self._dict[tablename].resolve(symname)
|
||||
# else:
|
||||
# raise exceptions.SymbolNotFoundException("Malformed symbol name")
|
||||
#
|
||||
#
|
||||
# elif len(symarr) == 1:
|
||||
# # Establish skipping all elements before the symbol list to start from
|
||||
# skip = (start_from is not None)
|
||||
# for slist in reversed(self._dict):
|
||||
# if skip:
|
||||
# skip = (slist.name != start_from)
|
||||
# else:
|
||||
# if symbol in self[slist]:
|
||||
# return self[slist].resolve(symbol, self)
|
||||
# else:
|
||||
# if skip:
|
||||
# raise exceptions.SymbolSpaceError("Symbol search for \"" + symbol + "\" failed because symbol list \"" + start_from.name + "\" is not in the search space")
|
||||
# raise exceptions.SymbolNotFoundException("Symbol \"" + symbol + "\" could not be found in any symbol list")
|
||||
|
||||
@@ -6,53 +6,65 @@ Created on 10 Apr 2013
|
||||
import copy
|
||||
import struct
|
||||
import volatility.framework.obj as obj
|
||||
import volatility.framework.symbols as symbols
|
||||
import volatility.framework.templates as templates
|
||||
import volatility.framework.exceptions as exceptions
|
||||
import volatility.framework.interfaces as interfaces
|
||||
|
||||
class NativeSymbolList(symbols.SymbolListInterface):
|
||||
class NativeTable(interfaces.NativeTableInterface):
|
||||
"""Symbol List that handles Native types"""
|
||||
|
||||
def __init__(self, name, native_dictionary):
|
||||
super(NativeSymbolList, self).__init__(name)
|
||||
self._native_dictionary = native_dictionary
|
||||
for item in self._native_dictionary:
|
||||
self._overrides[item] = obj.Integer
|
||||
super(NativeTable, self).__init__(name, self)
|
||||
self._native_dictionary = copy.deepcopy(native_dictionary)
|
||||
self._overrides = {}
|
||||
for native_type in self._native_dictionary.keys():
|
||||
native_class, _native_struct = self._native_dictionary[native_type]
|
||||
self._overrides[native_type] = native_class
|
||||
|
||||
def get_symbol_class(self, symbol):
|
||||
ntype, fmt = native_types.get(symbol, (obj.Integer, ''))
|
||||
return ntype
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return self._native_dictionary.keys()
|
||||
"""Returns an iterator of the symbol names"""
|
||||
return set(self._native_dictionary.keys()).union(set(['Enumeration', 'array', 'BitField', 'void', 'pointer']))
|
||||
|
||||
def resolve(self, symbol, symbol_space = None):
|
||||
"""Resolve the native symbols by looking up their struct types in a dictionary"""
|
||||
if symbol in self._native_dictionary:
|
||||
fmt = self._native_dictionary[symbol]
|
||||
return templates.ObjectTemplate(self.get_symbol_class(symbol), symbol_name = symbol, size = struct.calcsize(fmt), format_string = fmt)
|
||||
if isinstance(symbol_space, symbols.SymbolSpace):
|
||||
return symbol_space.resolve(symbol, self.name)
|
||||
raise exceptions.SymbolNotFoundException()
|
||||
def resolve(self, symbol_name, **kwargs):
|
||||
"""Resolves a symbol name into an object template
|
||||
|
||||
symbol_space is used to resolve any target symbols if they don't exist in this list
|
||||
"""
|
||||
if symbol_name == 'void':
|
||||
return templates.ObjectTemplate(obj.Void, size = 0)
|
||||
elif symbol_name == 'array':
|
||||
return templates.ObjectTemplate(obj.Array, count = 0, target = self.resolve('void'), size = 0)
|
||||
elif symbol_name == 'Enumeration':
|
||||
return templates.ObjectTemplate(obj.Enumeration, target = self.resolve('void'), choices = {}, size = 0)
|
||||
elif symbol_name == 'BitField':
|
||||
return templates.ObjectTemplate(obj.BitField, start_bit = 0, end_bit = 0, size = 0)
|
||||
|
||||
x86_native_types = {'int' : '<i',
|
||||
'long': '<i',
|
||||
'unsigned long' : '<I',
|
||||
'unsigned int' : '<I',
|
||||
'address' : '<I',
|
||||
'char' : '<c',
|
||||
'unsigned char' : '<B',
|
||||
'unsigned short int' : '<H',
|
||||
'unsigned short' : '<H',
|
||||
'unsigned be short' : '>H',
|
||||
'short' : '<h',
|
||||
'long long' : '<q',
|
||||
'unsigned long long' : '<Q',
|
||||
'void': "<I",
|
||||
'float': "",
|
||||
'double': "d"}
|
||||
x64_native_types = copy.deepcopy(x86_native_types)
|
||||
x64_native_types['address'] = '<Q'
|
||||
_native_type, native_format = self._native_dictionary[symbol_name]
|
||||
native_size = struct.calcsize(native_format)
|
||||
if symbol_name == 'pointer':
|
||||
return templates.ObjectTemplate(obj.Pointer, target = self.resolve('void'), size = native_size)
|
||||
return templates.ObjectTemplate(self.get_symbol_class(symbol_name), symbol_name = symbol_name, struct_format = native_format, size = native_size)
|
||||
|
||||
x86NativeSymbolList = NativeSymbolList("native", x86_native_types)
|
||||
x64NativeSymbolList = NativeSymbolList("native", x64_native_types)
|
||||
for ftype in ['double', 'float']:
|
||||
x86NativeSymbolList.set_symbol_class(ftype, obj.Float)
|
||||
x64NativeSymbolList.set_symbol_class(ftype, obj.Float)
|
||||
native_types = {'int' : (obj.Integer, '<i'),
|
||||
'long': (obj.Integer, '<i'),
|
||||
'unsigned long' : (obj.Integer, '<I'),
|
||||
'unsigned int' : (obj.Integer, '<I'),
|
||||
'pointer' : (obj.Pointer, '<I'),
|
||||
'char' : (obj.Integer, '<b'),
|
||||
'byte' : (obj.Bytes, '<c'),
|
||||
'unsigned char' : (obj.Integer, '<B'),
|
||||
'unsigned short int' : (obj.Integer, '<H'),
|
||||
'unsigned short' : (obj.Integer, '<H'),
|
||||
'unsigned be short' : (obj.Integer, '>H'),
|
||||
'short' : (obj.Integer, '<h'),
|
||||
'long long' : (obj.Integer, '<q'),
|
||||
'unsigned long long' : (obj.Integer, '<Q'),
|
||||
'float': (obj.Float, "<d"),
|
||||
'double': (obj.Float, "<d")}
|
||||
x86NativeTable = NativeTable("native", native_types)
|
||||
native_types['pointer'] = '<Q'
|
||||
x64NativeTable = NativeTable("native", native_types)
|
||||
|
||||
@@ -4,8 +4,9 @@ Created on 10 Apr 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
import copy
|
||||
import volatility.framework.exceptions as exceptions
|
||||
import volatility.framework.symbols as symbols
|
||||
import volatility.framework.interfaces as interfaces
|
||||
import volatility.framework.templates as templates
|
||||
import volatility.framework.obj as obj
|
||||
|
||||
@@ -20,69 +21,81 @@ import volatility.framework.obj as obj
|
||||
# Symbol list could be a dict with knowledge of its parent?
|
||||
# Class split is arbitrary, it's an extension for developers
|
||||
# Object template should contain both class and initial parameters
|
||||
#
|
||||
#
|
||||
# *** Resolution should not happen in the resolve function
|
||||
# It should only happen on access of contained types ***
|
||||
#
|
||||
# Recursive objects can be fixed by having caching the objects
|
||||
# (however, they have to be built first!)
|
||||
#
|
||||
# Single hop resolution is probably the solution
|
||||
# Could probably deal iwth it by having a property that caches
|
||||
# for container types
|
||||
#
|
||||
# Need to figure out how to tell the difference for vtypes between
|
||||
# vtype list and a struct dictionary
|
||||
|
||||
class VTypeSymbolTable(interfaces.SymbolTableInterface):
|
||||
"""Symbol Table that handles vtype datastructures"""
|
||||
|
||||
class VTypeSymbolList(symbols.SymbolListInterface):
|
||||
"""Symbol List that handles vtype datastructures"""
|
||||
|
||||
def __init__(self, name, vtype_dictionary):
|
||||
super(VTypeSymbolList, self).__init__(name)
|
||||
def __init__(self, name, vtype_dictionary, native_symbols = None):
|
||||
super(VTypeSymbolTable, self).__init__(name, native_symbols)
|
||||
self._vtypedict = vtype_dictionary
|
||||
self._default_object_class = obj.Struct
|
||||
self._overrides = {}
|
||||
|
||||
def _vtypedict_to_template(self, dictionary, symbol_space = None):
|
||||
def get_symbol_class(self, symbol):
|
||||
return self._overrides.get(symbol, obj.Struct)
|
||||
|
||||
def set_symbol_class(self, symbol, clazz):
|
||||
if symbol not in self.symbols:
|
||||
raise ValueError("Symbol " + symbol + " not in " + self.name + " SymbolTable")
|
||||
self._overrides[symbol] = clazz
|
||||
|
||||
def del_symbol_class(self, symbol):
|
||||
if symbol in self._overrides:
|
||||
del self._overrides[symbol]
|
||||
|
||||
def _vtypedict_to_template(self, dictionary):
|
||||
"""Converts a vtypedict into an object template"""
|
||||
if not dictionary:
|
||||
raise exceptions.SymbolSpaceError("Invalid vtype dictionary: " + repr(dictionary))
|
||||
# print(repr(dictionary))
|
||||
|
||||
symbol_name = dictionary[0]
|
||||
# Establish the defaults
|
||||
result = {"object_class": self._default_object_class, "symbol_name": symbol_name, "size" : 0}
|
||||
|
||||
# Can we handle the next layer ourselves?
|
||||
if symbol_name in self.symbols:
|
||||
# Check if the class has been overridden
|
||||
if self.has_symbol_class(symbol_name):
|
||||
result["object_class"] = self.get_symbol_class(symbol_name)
|
||||
if symbol_name not in self.symbols:
|
||||
# Handle specific "well known" symbols
|
||||
if symbol_name in self.natives:
|
||||
# The symbol is a native type
|
||||
native_template = self.natives.resolve(symbol_name)
|
||||
|
||||
# Add specific additional parameters, etc
|
||||
update = {}
|
||||
if symbol_name == 'array':
|
||||
result["object_class"] = obj.Integer
|
||||
result["count"] = dictionary[1]
|
||||
result["target"] = self._vtypedict_to_template(dictionary[2], symbol_space = symbol_space)
|
||||
result["size"] = dictionary[1] * result["target"].size
|
||||
update['count'] = dictionary[1],
|
||||
update['target'] = self._vtypedict_to_template(dictionary[2])
|
||||
elif symbol_name == 'pointer':
|
||||
result["object_class"] = obj.Pointer
|
||||
result["target"] = self._vtypedict_to_template(dictionary[1], symbol_space = symbol_space)
|
||||
update["target"] = self._vtypedict_to_template(dictionary[1])
|
||||
elif symbol_name == 'Enumeration':
|
||||
result["object_class"] = obj.Integer
|
||||
result.update(dictionary[1])
|
||||
result["target"] = self._vtypedict_to_template([result["target"]], symbol_space = symbol_space)
|
||||
update = copy.deepcopy(dictionary[1])
|
||||
update["target"] = self._vtypedict_to_template([update['target']])
|
||||
elif symbol_name == 'BitField':
|
||||
result["object_class"] = obj.Pointer
|
||||
result["fields"] = dictionary[1]
|
||||
elif symbol_name == 'void':
|
||||
result["object_class"] = obj.Integer
|
||||
elif len(dictionary) > 1:
|
||||
raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary))
|
||||
elif isinstance(symbol_space, symbols.SymbolSpace):
|
||||
# Resolve the object class for this
|
||||
return symbol_space.resolve(symbol_name)
|
||||
else:
|
||||
raise exceptions.SymbolNotFoundException("Unable to resolve \"" + symbol_name + "\" in \"" + self.name + "\", no symbol space to rescurse through")
|
||||
elif self.has_symbol_class(symbol_name):
|
||||
result["object_class"] = self.get_symbol_class(symbol_name)
|
||||
update = dictionary[1]
|
||||
update['target'] = self._vtypedict_to_template([update['native_type']])
|
||||
native_template.update_arguments(**update) #pylint: disable-msg=W0142
|
||||
return native_template
|
||||
|
||||
print(result)
|
||||
return templates.ObjectTemplate(**result) #pylint: disable-msg=W0142
|
||||
# Otherwise
|
||||
if len(dictionary) > 1:
|
||||
print("Symbol name", symbol_name)
|
||||
raise exceptions.SymbolSpaceError("Unknown vtype format: " + repr(dictionary))
|
||||
|
||||
return templates.ReferenceTemplate(symbol_name = symbol_name)
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
"""Returns an iterator of the symbol names"""
|
||||
return self._vtypedict.keys()
|
||||
|
||||
def resolve(self, symbol_name, symbol_space = None):
|
||||
def resolve(self, symbol_name):
|
||||
"""Resolves an individual symbol"""
|
||||
if symbol_name not in self._vtypedict:
|
||||
raise exceptions.SymbolNotFoundException
|
||||
@@ -90,9 +103,7 @@ class VTypeSymbolList(symbols.SymbolListInterface):
|
||||
members = {}
|
||||
for member_name in curdict:
|
||||
relative_offset, vtypedict = curdict[member_name]
|
||||
member = templates.member_from_object_template(relative_offset = relative_offset, object_template = self._vtypedict_to_template(vtypedict, symbol_space))
|
||||
member = (relative_offset, self._vtypedict_to_template(vtypedict))
|
||||
members[member_name] = member
|
||||
object_class = self._default_object_class
|
||||
if self.has_symbol_class(symbol_name):
|
||||
object_class = self.get_symbol_class(symbol_name)
|
||||
object_class = self.get_symbol_class(symbol_name)
|
||||
return templates.ObjectTemplate(object_class = object_class, symbol_name = symbol_name, size = size, members = members)
|
||||
|
||||
@@ -4,10 +4,9 @@ Created on 1 Mar 2013
|
||||
@author: mike
|
||||
'''
|
||||
|
||||
import copy
|
||||
import volatility.framework.interfaces as interfaces
|
||||
|
||||
class ObjectTemplate(object):
|
||||
class ObjectTemplate(interfaces.Template):
|
||||
"""Factory class that produces objects that adhere to the Object interface on demand
|
||||
|
||||
This is effectively a method of currying, but adds more structure to avoid abuse.
|
||||
@@ -16,62 +15,44 @@ class ObjectTemplate(object):
|
||||
* Members, etc
|
||||
etc.
|
||||
"""
|
||||
def __init__(self, object_class = None, symbol_name = None, size = None, **kwargs):
|
||||
def __init__(self, object_class = None, symbol_name = None, **kwargs):
|
||||
super(ObjectTemplate, self).__init__(symbol_name = symbol_name, **kwargs)
|
||||
if not issubclass(object_class, interfaces.ObjectInterface):
|
||||
raise TypeError("ObjectTemplate object_class must be a class, not " + str(type(object_class)))
|
||||
if not isinstance(size, int):
|
||||
raise TypeError("ObjectTemplate size must be numeric, not " + str(type(size)))
|
||||
self._size = size
|
||||
self._symbol_name = symbol_name
|
||||
self._kwargs = kwargs
|
||||
self._object_class = object_class
|
||||
|
||||
@property
|
||||
def object_class(self):
|
||||
return self._object_class
|
||||
raise TypeError("ObjectTemplate object_class must inherit from ObjectInterface")
|
||||
self.object_class = object_class
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self._size
|
||||
"""Returns the size of the template"""
|
||||
return self.object_class.template_size(self._kwargs)
|
||||
|
||||
@property
|
||||
def symbol_name(self):
|
||||
"""Returns the name of the symbol if one was provided"""
|
||||
return self._symbol_name
|
||||
def children(self):
|
||||
"""A function that returns a list of child templates of a template
|
||||
|
||||
This is used to traverse the template tree
|
||||
"""
|
||||
return self.object_class.template_children(self._kwargs)
|
||||
|
||||
@property
|
||||
def kwargs(self):
|
||||
return copy.deepcopy(self._kwargs)
|
||||
def replace_child(self, old_child, new_child):
|
||||
"""A function for replacing one child with another
|
||||
|
||||
We pass in the kwargs directly so they can be changed
|
||||
"""
|
||||
self.object_class.replace_child(old_child, new_child, self._kwargs)
|
||||
|
||||
def __call__(self, context, layer_name, offset, parent = None):
|
||||
"""Constructs the object
|
||||
|
||||
Returns: an object adhereing to the Object interface
|
||||
"""
|
||||
return self._object_class(context = context, layer_name = layer_name, offset = offset, symbol_name = self.symbol_name, size = self.size, parent = parent, **self._kwargs)
|
||||
return self.object_class(context = context, layer_name = layer_name, offset = offset, symbol_name = self.symbol_name, size = self.size, parent = parent, **self._kwargs)
|
||||
|
||||
def member_from_object_template(relative_offset, object_template):
|
||||
"""Returns a MemberTemplate based upon an existing ObjectTemplate"""
|
||||
if not isinstance(object_template, ObjectTemplate):
|
||||
raise TypeError("object_template must be an ObjectTemplate, not " + str(type(object_template)))
|
||||
return MemberTemplate(relative_offset = relative_offset,
|
||||
object_class = object_template.object_class,
|
||||
symbol_name = object_template.symbol_name,
|
||||
size = object_template.size,
|
||||
**object_template.kwargs)
|
||||
|
||||
class MemberTemplate(ObjectTemplate):
|
||||
"""Factory class that produces members of Structs
|
||||
class ReferenceTemplate(interfaces.Template):
|
||||
"""Factory class that produces objects based on a delayed reference type
|
||||
|
||||
This is just like a normal ObjectTemplate, but contains the relative offset
|
||||
fron the parent object.
|
||||
It should not return any attributes
|
||||
"""
|
||||
def __init__(self, relative_offset, **kwargs):
|
||||
super(MemberTemplate, self).__init__(**kwargs)
|
||||
if not isinstance(relative_offset, int):
|
||||
raise TypeError("MemberTemplate relative_offset must be numeric, not " + str(type(relative_offset)))
|
||||
self._relative_offset = relative_offset
|
||||
|
||||
@property
|
||||
def relative_offset(self):
|
||||
return self._relative_offset
|
||||
def __call__(self, context, *args, **kwargs):
|
||||
template = context.symbol_space.resolve(self._symbol_name)
|
||||
return template(context = context, *args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user