Add in the initial scanning framework.

This commit is contained in:
Mike Auty
2016-07-02 15:49:00 +01:00
parent c626bebb59
commit 90fa5574b4
2 changed files with 103 additions and 1 deletions
+88 -1
View File
@@ -3,13 +3,49 @@ Created on 4 May 2013
@author: mike
"""
import collections
from volatility.framework import exceptions, validity
# We can't just import interfaces because we'd have a cycle going
from volatility.framework.interfaces import configuration
from volatility.framework.interfaces import configuration, context
from abc import ABCMeta, abstractmethod, abstractproperty
class ScannerInterface(validity.ValidityRoutines, metaclass = ABCMeta):
def __init__(self):
self.chunk_size = 0x1000000 # Default to 16Mb chunks
self.overlap = 0x1000 # A page of overlap by default
self._context = None
self._layer_name = None
@property
def context(self):
return self._context
@context.setter
def context(self, ctx):
"""Stores the context locally in case the scanner needs to access the layer"""
self._context = self._check_type(ctx, context.ContextInterface)
@property
def layer_name(self):
return self._layer_name
@layer_name.setter
def layer_name(self, layer_name):
"""Stores the layer_name being scanned locally in case the scanner needs to access the layer"""
self._layer_name = self._check_type(layer_name, str)
@abstractmethod
def __call__(self, data, data_offset):
"""Searches through a chunk of data for a particular value/pattern/etc
Returns the ABSOLUTE offset (ie, offset_within_data + data_offset)
data is the chunk of data to search through
data_offset is the offset within the layer that the data being searched starts at
"""
class DataLayerInterface(configuration.ProviderInterface, validity.ValidityRoutines, metaclass = ABCMeta):
"""A Layer that directly holds data (and does not translate it"""
@@ -88,6 +124,38 @@ class DataLayerInterface(configuration.ProviderInterface, validity.ValidityRouti
context.add_layer(layer)
context.config[config_path] = layer_name
def _pre_scan(self, context, min_address, max_address, progress_callback, scanner):
"""Prepares the scanner based on standard procedures shared between TranslationLayers and DataLayers"""
if progress_callback is not None:
self._check_type(progress_callback, collections.Callable)
scanner = self._check_type(scanner, ScannerInterface)
scanner.context = context
scanner.layer_name = self.name
if min_address is None:
min_address = self.minimum_address
if max_address is None:
max_address = self.maximum_address
min_address = min(self.minimum_address, min_address)
max_address = max(self.maximum_address, max_address)
total_size = (max_address - min_address)
return min_address, max_address, scanner, total_size
def scan(self, context, scanner, progress_callback = None, min_address = None, max_address = None):
"""Scans the layer using scanner, between min_address and max_address calling progress_callback at internvals to report percentage progress"""
min_address, max_address, scanner, total_size = self._pre_scan(context, min_address, max_address,
progress_callback, scanner)
for offset in range(min_address, max_address, scanner.chunk_size):
length = min(scanner.chunk_size + scanner.overlap, max_address - offset)
chunk = self.read(offset, length)
if progress_callback:
progress_callback((offset * 100) / total_size)
for x in scanner(chunk, offset):
yield x
class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta):
# Unfortunately class attributes can't easily be inheritted from parent classes
@@ -142,3 +210,22 @@ class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta):
raise exceptions.LayerException("Mapping returned an overlapping element")
self._context.memory.write(layer, mapped_offset, length)
current_offset += length
def scan(self, context, scanner, progress_callback = None, min_address = None, max_address = None):
"""Scans a Translation layer by chunk
Note: this will skip missing/unmappable chunks of memory
"""
min_address, max_address, scanner, total_size = self._pre_scan(context, min_address, max_address,
progress_callback, scanner)
progress = 0
for block in range(min_address, total_size, scanner.chunk_size):
for map in self.mapping(block, scanner.chunk_size + scanner.overlap, ignore_errors = True):
offset, mapped_offset, length, layer = map
chunk = self._context.memory.read(layer, mapped_offset, length)
if progress_callback:
progress += length
progress_callback(progress / total_size)
for x in scanner(chunk, offset):
yield x
+15
View File
@@ -0,0 +1,15 @@
from volatility.framework.interfaces import layers
class BytesScanner(layers.ScannerInterface):
def __init__(self, needle):
layers.ScannerInterface.__init__(self)
self.needle = self._check_type(needle, bytes)
def __call__(self, data, data_offset):
"""Runs through the data looking for the needle, and yields all offsets where the needle is found
"""
find_pos = data.find(self.needle)
while find_pos >= 0:
yield find_pos + data_offset
find_pos = data.find(self.needle, find_pos + 1)