Refactor the ResourceAccessor because it isn't an interface.

It's not strictly limited to layers either, but I don't really want to
create a whole extra generic file to put this in, so layers will do.
This commit is contained in:
Mike Auty
2017-11-05 21:27:56 +00:00
parent f6ac9c8367
commit b6d932f4a5
4 changed files with 81 additions and 74 deletions
-70
View File
@@ -1,19 +1,11 @@
"""Defines layers for containing data. One layer may combine other layers, map data based on the data itself,
or map a procedure (such as decryption) across another layer of data."""
import bz2
import collections
import collections.abc
import contextlib
import functools
import gzip
import hashlib
import logging
import lzma
import math
import multiprocessing
import os
import urllib.parse
import urllib.request
from abc import ABCMeta, abstractmethod
from volatility.framework import constants, exceptions, validity
@@ -408,65 +400,3 @@ class DummyProgress(object):
self.value = 0
class ResourceAccessor(object):
"""Object for openning URLs as files (downloading locally first if necessary)"""
def __init__(self, progress_callback = None, context = None):
self._progress_callback = progress_callback
self._context = context
def open(self, url, mode = "rb"):
"""Returns a file-like object for a particular URL opened in mode"""
with contextlib.closing(urllib.request.urlopen(url, context = self._context)) as fp:
# Cache the file locally
url_type, path = urllib.parse.splittype(url)
if url_type == 'file':
curfile = urllib.request.urlopen(url, context = self._context)
else:
# TODO: find a way to check if we already have this file (look at http headers?)
block_size = 1028 * 8
temp_filename = os.path.join(constants.CACHE_PATH,
"data_" + hashlib.sha512(bytes(url, 'latin-1')).hexdigest())
cache_file = open(temp_filename, "wb")
while True:
block = fp.read(block_size)
if not block:
break
cache_file.write(block)
if self._progress_callback:
# TODO: Figure out the size and therefore percentage complete
self._progress_callback(0, "Reading file {}".format(url))
cache_file.close()
# Re-open the cache with a different mode
curfile = open(temp_filename)
# Determine whether the file is a particular type of file, and if so, open it as such
if IMPORTED_MAGIC:
while True:
try:
# Detect the content
detected = magic.detect_from_fobj(curfile)
except:
break
if detected:
if detected.mime_type == 'application/x-xz':
curfile = lzma.LZMAFile(curfile, mode)
elif detected.mime_type == 'application/x-bzip2':
curfile = bz2.BZ2File(curfile, mode)
elif detected.mime_type == 'application/x-gzip':
curfile = gzip.GzipFile(fileobj = curfile, mode = mode)
else:
break
else:
break
# Read and rewind to ensure we're inside any compressed file layers
curfile.read(1)
curfile.seek(0)
# Fallback in case the file doesn't exist
if curfile is None:
raise ValueError("URL does not reference an openable file")
return curfile
+77
View File
@@ -1 +1,78 @@
import bz2
import contextlib
import gzip
import hashlib
import lzma
import os
import urllib.parse
import urllib.request
import magic
from volatility.framework import constants
from volatility.framework.interfaces.layers import IMPORTED_MAGIC
from volatility.framework.layers import intel, lime, physical, segmented, vmware
class ResourceAccessor(object):
"""Object for openning URLs as files (downloading locally first if necessary)"""
def __init__(self, progress_callback = None, context = None):
self._progress_callback = progress_callback
self._context = context
def open(self, url, mode = "rb"):
"""Returns a file-like object for a particular URL opened in mode"""
with contextlib.closing(urllib.request.urlopen(url, context = self._context)) as fp:
# Cache the file locally
url_type, path = urllib.parse.splittype(url)
if url_type == 'file':
curfile = urllib.request.urlopen(url, context = self._context)
else:
# TODO: find a way to check if we already have this file (look at http headers?)
block_size = 1028 * 8
temp_filename = os.path.join(constants.CACHE_PATH,
"data_" + hashlib.sha512(bytes(url, 'latin-1')).hexdigest())
cache_file = open(temp_filename, "wb")
while True:
block = fp.read(block_size)
if not block:
break
cache_file.write(block)
if self._progress_callback:
# TODO: Figure out the size and therefore percentage complete
self._progress_callback(0, "Reading file {}".format(url))
cache_file.close()
# Re-open the cache with a different mode
curfile = open(temp_filename)
# Determine whether the file is a particular type of file, and if so, open it as such
if IMPORTED_MAGIC:
while True:
try:
# Detect the content
detected = magic.detect_from_fobj(curfile)
except:
break
if detected:
if detected.mime_type == 'application/x-xz':
curfile = lzma.LZMAFile(curfile, mode)
elif detected.mime_type == 'application/x-bzip2':
curfile = bz2.BZ2File(curfile, mode)
elif detected.mime_type == 'application/x-gzip':
curfile = gzip.GzipFile(fileobj = curfile, mode = mode)
else:
break
else:
break
# Read and rewind to ensure we're inside any compressed file layers
curfile.read(1)
curfile.seek(0)
# Fallback in case the file doesn't exist
if curfile is None:
raise ValueError("URL does not reference an openable file")
return curfile
+2 -2
View File
@@ -1,4 +1,4 @@
from volatility.framework import exceptions, interfaces
from volatility.framework import exceptions, interfaces, layers
from volatility.framework.configuration import requirements
@@ -75,7 +75,7 @@ class FileLayer(interfaces.layers.DataLayerInterface):
# FIXME: Add "+" to the mode once we've determined whether write mode is enabled
mode = "rb"
if not self._file_:
self._file_ = interfaces.layers.ResourceAccessor().open(self._location, mode)
self._file_ = layers.ResourceAccessor().open(self._location, mode)
return self._file_
@property
+2 -2
View File
@@ -6,7 +6,7 @@ import os
import pathlib
from volatility import schemas
from volatility.framework import class_subclasses, constants, exceptions, interfaces, objects
from volatility.framework import class_subclasses, constants, exceptions, interfaces, objects, layers
from volatility.framework.symbols import native
vollog = logging.getLogger(__name__)
@@ -69,7 +69,7 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface):
# Check there are no obvious errors
# Open the file and test the version
self._versions = dict([(x.version, x) for x in class_subclasses(ISFormatTable)])
fp = interfaces.layers.ResourceAccessor().open(isf_url)
fp = layers.ResourceAccessor().open(isf_url)
json_object = json.load(fp)
fp.close()