""" Created on 7 May 2013 @author: mike """ import math import struct from volatility.framework import exceptions, interfaces from volatility.framework.configuration import requirements class classproperty(object): """Class property decorator""" def __init__(self, func): self._func = func def __get__(self, owner_self, owner_cls): return self._func(owner_cls) class Intel(interfaces.layers.TranslationLayerInterface): """Translation Layer for the Intel IA32 memory mapping""" priority = 40 _architecture = "Intel32" _entry_format = "> (position + 1) # Grab the base address of the table we'll be getting the next entry from base_address = self._mask(entry, self._maxphyaddr - 1, size + self._index_shift) # Create the offset for the next entry table_offset = base_address | (index << self._index_shift) # Read out the new entry from memory entry, = struct.unpack(self._entry_format, self._context.memory.read(self._base_layer, table_offset, struct.calcsize(self._entry_format))) # Now we're done if not self._page_is_valid(entry): raise exceptions.PagedInvalidAddressException(self.name, offset, position + 1, "Page Fault at entry {} in page entry".format(hex(entry))) page = self._mask(entry, self._maxphyaddr - 1, position + 1) | self._mask(offset, position, 0) return page, 1 << (position + 1), self._base_layer def is_valid(self, offset, length = 1): """Returns whether the address offset can be translated to a valid address""" try: # TODO: Consider reimplementing this, since calls to mapping can call is_valid return all([self._context.memory[layer].is_valid(mapped_offset) for _, mapped_offset, _, layer in self.mapping(offset, length)]) except exceptions.InvalidAddressException: return False def mapping(self, offset, length, ignore_errors = False): """Returns a sorted iterable of (offset, mapped_offset, length, layer) mappings This allows translation layers to provide maps of contiguous regions in one layer """ result = [] if length == 0: if ignore_errors and not self.is_valid(offset): raise StopIteration mapped_offset, _, layer_name = self._translate(offset) yield (offset, mapped_offset, length, layer_name) raise StopIteration while length > 0: if ignore_errors: while not self.is_valid(offset) and length > 0: length -= 1 << self._page_size_in_bits offset += 1 << self._page_size_in_bits if length <= 0: raise StopIteration chunk_offset, page_size, layer_name = self._translate(offset) chunk_size = min(page_size - (chunk_offset % page_size), length) yield (offset, chunk_offset, chunk_size, layer_name) length -= chunk_size offset += chunk_size @property def dependencies(self): """Returns a list of the lower layer names that this layer is dependent upon""" # TODO: Add in the whole buffalo return [self._base_layer] @classmethod def get_requirements(cls): return [requirements.TranslationLayerRequirement(name = 'memory_layer', optional = False), requirements.TranslationLayerRequirement(name = 'swap_layer', optional = True), requirements.IntRequirement(name = 'page_map_offset', optional = False), requirements.IntRequirement(name = 'kernel_virtual_offset', optional = True)] def _scan_iterator(self, scanner, min_address, max_address): previous = None data_to_scan = [] scanned_pairs = set() chunk_end = min_address while chunk_end <= max_address: try: address, page_size, layer_name = self._translate(chunk_end) chunk_size = page_size - (address & (page_size - 1)) except exceptions.PagedInvalidAddressException as e: address, chunk_size, layer_name = None, 1 << self._page_size_in_bits, '' # We've come to a break, so scan what we've seen so far if address is None or (previous, address) in scanned_pairs: yield data_to_scan, chunk_end data_to_scan = [] else: # TODO: We've already done the translation, so don't bother doing it again data_to_scan += [(layer_name, address, chunk_size)] previous = address chunk_end += chunk_size def _scan_chunk(self, scanner, min_address, max_address, progress, iterator_value): data_to_scan, chunk_end = iterator_value data = b'' for layer_name, address, chunk_size in data_to_scan: data += self.context.memory[layer_name].read(address, chunk_size) progress.value = chunk_end return list(scanner(data, chunk_end - len(data_to_scan))) def _scan_metric(self, _scanner, min_address, max_address, value): return max(0, ((value - min_address) * 100) / (max_address - min_address)) class IntelPAE(Intel): """Class for handling Physical Address Extensions for Intel architectures""" priority = 35 _architecture = "Intel32" _entry_format = "