diff --git a/volatility/framework/constants.py b/volatility/framework/constants.py index a0fc04b42..ebe9b7b3c 100644 --- a/volatility/framework/constants.py +++ b/volatility/framework/constants.py @@ -2,9 +2,16 @@ Stores all the constant values that are generally fixed throughout volatility This includes default scanning block sizes, etc.""" - import os.path +import sys + PLUGINS_PATH = [os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "plugins"))] BANG = "!" PACKAGE_VERSION = "3.0.0_alpha1" + +if sys.platform == 'windows': + CACHE_PATH = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "volatility3") +else: + CACHE_PATH = os.path.join(os.path.expanduser("~"), ".cache", "volatility3") +os.makedirs(CACHE_PATH, exist_ok = True) diff --git a/volatility/framework/symbols/intermed.py b/volatility/framework/symbols/intermed.py index 0273110a8..abc577158 100644 --- a/volatility/framework/symbols/intermed.py +++ b/volatility/framework/symbols/intermed.py @@ -5,6 +5,7 @@ import urllib.parse from volatility import schemas from volatility.framework import class_subclasses, constants, exceptions, interfaces, objects +from volatility.framework.exceptions import SymbolSpaceError vollog = logging.getLogger(__name__) @@ -21,7 +22,7 @@ def _construct_delegate_function(name, is_property = False): class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): - def __init__(self, name, idd_filepath, native_types = None, validate = False): + def __init__(self, name, idd_filepath, native_types = None): # Check there are no obvious errors url = urllib.parse.urlparse(idd_filepath) if url.scheme != 'file': @@ -35,10 +36,9 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface): with open(url.path, "r") as fp: json_object = json.load(fp) - # Validation is expensive, we should either explicitly demand it, or build a caching mechanism - # to store the hashes of successfully validated files - if validate: - schemas.validate(json_object) + # Validation is expensive, but we cache to store the hashes of successfully validated json objects + if not schemas.validate(json_object): + raise SymbolSpaceError("File does not pass version validation: {}".format(url.geturl())) metadata = json_object.get('metadata', None) diff --git a/volatility/schemas/__init__.py b/volatility/schemas/__init__.py index 26c06f282..1f7c8b7a3 100644 --- a/volatility/schemas/__init__.py +++ b/volatility/schemas/__init__.py @@ -1,9 +1,32 @@ +import hashlib import json import logging import os +from volatility.framework import constants + vollog = logging.getLogger(__name__) +cached_validation_filepath = os.path.join(constants.CACHE_PATH, "valid_idf.cache") + + +def load_cached_validations(): + """Loads up the list of successfully cached json objects, so we don't need to revalidate them""" + validhashes = set() + if os.path.exists(cached_validation_filepath): + with open(cached_validation_filepath, "r") as f: + validhashes.update(json.load(f)) + return validhashes + + +def record_cached_validations(validations): + """Record the cached validations, so we don't need to revalidate them in future""" + with open(cached_validation_filepath, "w") as f: + json.dump(list(validations), f) + + +cached_validations = load_cached_validations() + def validate(input): """Validates an input JSON file based upon """ @@ -23,6 +46,9 @@ def validate(input): def valid(input, schema): """Validates a json schema""" + input_hash = hashlib.sha1(bytes(json.dumps(input, sort_keys = True), 'utf-8')).hexdigest() + if input_hash in cached_validations: + return True try: import jsonschema jsonschema.validate(input, schema) @@ -32,4 +58,6 @@ def valid(input, schema): return True except: return False + cached_validations.add(input_hash) + record_cached_validations(cached_validations) return True