From a5f0c7f1b95390bae67d8bcf107a30b5f0a95c32 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 25 Aug 2019 13:39:27 +0100 Subject: [PATCH] Don't forget the linear layer. --- volatility/framework/layers/linear.py | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 volatility/framework/layers/linear.py diff --git a/volatility/framework/layers/linear.py b/volatility/framework/layers/linear.py new file mode 100644 index 000000000..be9323c22 --- /dev/null +++ b/volatility/framework/layers/linear.py @@ -0,0 +1,62 @@ +import functools +from typing import List, Optional, Tuple + +from volatility.framework import exceptions, interfaces + + +class LinearlyMappedLayer(interfaces.layers.TranslationLayerInterface): + """Class to differentiate Linearly Mapped layers (where a => b implies that a + c => b + c)""" + + ### Translation layer convenience function + + def translate(self, offset: int, ignore_errors: bool = False) -> Tuple[Optional[int], Optional[str]]: + mapping = list(self.mapping(offset, 0, ignore_errors)) + if len(mapping) == 1: + original_offset, mapped_offset, _, layer = mapping[0] + if original_offset != offset: + raise exceptions.LayerException(self.name, + "Layer {} claims to map linearly but does not".format(self.name)) + else: + if ignore_errors: + # We should only hit this if we ignored errors, but check anyway + return None, None + raise exceptions.InvalidAddressException(self.name, offset, + "Cannot translate {} in layer {}".format(offset, self.name)) + return mapped_offset, layer + + # ## Read/Write functions for mapped pages + # Redefine read here for speed reasons (so we don't call a processing method + + @functools.lru_cache(maxsize = 512) + def read(self, offset: int, length: int, pad: bool = False) -> bytes: + """Reads an offset for length bytes and returns 'bytes' (not 'str') of length size""" + current_offset = offset + output = [] # type: List[bytes] + for (offset, mapped_offset, mapped_length, layer) in self.mapping(offset, length, ignore_errors = pad): + if not pad and offset > current_offset: + raise exceptions.InvalidAddressException( + self.name, current_offset, "Layer {} cannot map offset: {}".format(self.name, current_offset)) + elif offset > current_offset: + output += [b"\x00" * (offset - current_offset)] + current_offset = offset + elif offset < current_offset: + raise exceptions.LayerException(self.name, "Mapping returned an overlapping element") + if mapped_length > 0: + output += [self._context.layers.read(layer, mapped_offset, mapped_length, pad)] + current_offset += mapped_length + recovered_data = b"".join(output) + return recovered_data + b"\x00" * (length - len(recovered_data)) + + def write(self, offset: int, value: bytes) -> None: + """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( + self.name, current_offset, "Layer {} cannot map offset: {}".format(self.name, current_offset)) + elif offset < current_offset: + raise exceptions.LayerException(self.name, "Mapping returned an overlapping element") + self._context.layers.write(layer, mapped_offset, value[:length]) + value = value[length:] + current_offset += length