mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-13 21:27:39 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user