""" Created on 4 May 2013 @author: mike """ from volatility.framework import validity, exceptions # We can't just import interfaces because we'd have a cycle going from volatility.framework.interfaces import context as context_module from abc import ABCMeta, abstractmethod, abstractproperty class DataLayerInterface(validity.ValidityRoutines, metaclass = ABCMeta): """A Layer that directly holds data (and does not translate it""" def __init__(self, context, name): self._type_check(name, str) self._type_check(context, context_module.ContextInterface) self._name = name self._context = context @property def name(self): """Returns the layer name""" return self._name @abstractproperty def maximum_address(self): """Returns the maximum valid address of the space""" @abstractproperty def minimum_address(self): """Returns the minimum valid address of the space""" @abstractmethod def is_valid(self, offset, length = 1): """Returns a boolean based on whether the offset is valid or not""" @abstractmethod def read(self, offset, length, pad = False): """Reads an offset for length bytes and returns 'bytes' (not 'str') of length size If there is a fault of any kind (such as a page fault), an exception will be thrown unless pad is set, in which case the read errors will be replaced by null characters. """ @abstractmethod def write(self, offset, data): """Writes a chunk of data at offset. Any unavailable sections in the underlying bases will cause an exception to be thrown. Note: Writes are not atomic, therefore some data can be written, even if an exception is thrown. """ def destroy(self): """Allows DataLayers to close any open handles, etc. Systems that make use of Data Layers should called destroy when they are done with them. This will close all handles, and make the object unreadable (exceptions will be thrown using a DataLayer after destruction)""" pass class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta): @abstractmethod def translate(self, offset): """Returns a tuple of (offset, layer) indicating the translation of input domain to the output range""" @abstractmethod def mapping(self, offset, length): """Returns a sorted list of (offset, mapped_offset, length, layer) mappings This allows translation layers to provide maps of contiguous regions in one layer """ return [] @property @abstractmethod def dependencies(self): """Returns a list of layer names that this layer translates onto""" return [] # ## Read/Write functions for mapped pages def read(self, offset, length, pad = False): """Reads an offset for length bytes and returns 'bytes' (not 'str') of length size""" current_offset = offset output = [] for (offset, mapped_offset, length, layer) in self.mapping(offset, length): if not pad and offset > current_offset: raise exceptions.InvalidAddressException("Layer " + self.name + " cannot map offset " + str(current_offset)) elif offset > current_offset: output += [b"\x00" * (current_offset - offset)] current_offset = offset elif offset < current_offset: raise exceptions.LayerException("Mapping returned an overlapping element") output += [self._context.memory.read(layer, mapped_offset, length, pad)] current_offset += length return b"".join(output) def write(self, offset, value): """Writes a value at offset, distributing the writing across any underlying mapping""" current_offset = offset length = len(value) for (offset, mapped_offset, length, layer) in self.mapping(offset, length): if offset > current_offset: raise exceptions.InvalidAddressException("Layer " + self.name + " cannot map offset " + current_offset) elif offset < current_offset: raise exceptions.LayerException("Mapping returned an overlapping element") self._context.memory.write(layer, mapped_offset, length) current_offset += length