Exemple #1
0
    def __init__(self, widget, args):
        # Load configuration
        self.config_file = os.path.join(self.get_temp_dir(),
                                        "pe_tree_volatility.ini")
        super(VolatilityRuntime, self).__init__(widget, args)

        # Initialise Volatility3
        framework.require_interface_version(1, 0, 0)
        self.context = contexts.Context()

        # Input file in URI format
        single_location = "file:///{}".format(self.args.filename.name)
        self.context.config[
            "automagic.LayerStacker.single_location"] = single_location
        self.context.config["single_location"] = single_location

        # Find supported plugins
        plugins.__path__ = framework.constants.PLUGINS_PATH
        framework.import_files(plugins, True)
        self.plugin_list = framework.list_plugins()

        # Initialise required plugins configuration
        self._automagic("windows.pslist.PsList")
        self._automagic("windows.vadinfo.VadInfo")
        self._automagic("windows.modules.Modules")
Exemple #2
0
    def __call__(
        self,
        context: interfaces.context.ContextInterface,
        config_path: str,
        requirement: interfaces.configuration.RequirementInterface,
        progress_callback: constants.ProgressCallback = None
    ) -> Optional[List[str]]:
        """Runs the automagic over the configurable."""

        framework.import_files(sys.modules['volatility3.framework.layers'])

        # Quick exit if we're not needed
        if not requirement.unsatisfied(context, config_path):
            return None

        # Bow out quickly if the UI hasn't provided a single_location
        unsatisfied = self.unsatisfied(self.context, self.config_path)
        if unsatisfied:
            vollog.info(
                "Unable to run LayerStacker, unsatisfied requirement: {}".
                format(unsatisfied))
            return list(unsatisfied)
        if not self.config or not self.config.get('single_location', None):
            raise ValueError(
                "Unable to run LayerStacker, single_location parameter not provided"
            )

        # Search for suitable requirements
        self.stack(context, config_path, requirement, progress_callback)

        if progress_callback is not None:
            progress_callback(100, "Stacking attempts finished")
        return None
Exemple #3
0
    def __call__(self,
                 context: interfaces.context.ContextInterface,
                 config_path: str,
                 requirement: interfaces.configuration.RequirementInterface,
                 progress_callback=None,
                 optional=False) -> List[str]:

        # Make sure we import the layers, so they can reconstructed
        framework.import_files(sys.modules['volatility3.framework.layers'])

        result = []  # type: List[str]
        if requirement.unsatisfied(context, config_path):
            # Having called validate at the top level tells us both that we need to dig deeper
            # but also ensures that TranslationLayerRequirements have got the correct subrequirements if their class is populated

            subreq_config_path = interfaces.configuration.path_join(
                config_path, requirement.name)
            for subreq in requirement.requirements.values():
                try:
                    self(context,
                         subreq_config_path,
                         subreq,
                         optional=optional or subreq.optional)
                except Exception as e:
                    # We don't really care if this fails, it tends to mean the configuration isn't complete for that item
                    vollog.log(constants.LOGLEVEL_VVVV,
                               "Construction Exception occurred: {}".format(e))
                invalid = subreq.unsatisfied(context, subreq_config_path)
                # We want to traverse optional paths, so don't check until we've tried to validate
                # We also don't want to emit a debug message when a parent is optional, hence the optional parameter
                if invalid and not (optional or subreq.optional):
                    vollog.log(
                        constants.LOGLEVEL_V,
                        "Failed on requirement: {}".format(subreq_config_path))
                    result.append(
                        interfaces.configuration.path_join(
                            subreq_config_path, subreq.name))
            if result:
                return result
            elif isinstance(
                    requirement, interfaces.configuration.
                    ConstructableRequirementInterface):
                # We know all the subrequirements are filled, so let's populate
                requirement.construct(context, config_path)

        if progress_callback is not None:
            progress_callback(100, "Reconstruction finished")

        return []
Exemple #4
0
def refresh_banners(operating_system=None):
    """
    Refreshes banner cache
    """
    ctx = contexts.Context()
    _ = framework.import_files(volatility3.plugins, True)
    banner_automagics = automagic.available(ctx)
    banners = {}

    to_be_runned = []
    if not operating_system or operating_system == "Linux":
        to_be_runned.append((
            volatility3.framework.automagic.linux.LinuxBannerCache,
            "automagics.LinuxBannerCache",
        ))
    if not operating_system or operating_system == "Mac":
        to_be_runned.append((
            volatility3.framework.automagic.mac.MacBannerCache,
            "automagics.MacBannerCache",
        ))

    for cache_automagics, cache_name in to_be_runned:
        banner_automagic = next(
            iter([
                x for x in banner_automagics
                if isinstance(x, cache_automagics)
            ]),
            None,
        )
        if banner_automagic:
            banner_automagic(ctx, cache_name, None)
            banners.update(banner_automagic.load_banners())
    return banners
Exemple #5
0
    def run(self, plugin_class):
        """ Module which initialize all volatility 3 internals
        https://github.com/volatilityfoundation/volatility3/blob/stable/doc/source/using-as-a-library.rst
        @param plugin_class: plugin class. Ex. windows.pslist.PsList
        @param memdump: path to memdump. Ex. file:///home/vol3/memory.dmp
        @return: Volatility3 interface.

        """
        if not self.loaded:
            self.ctx = contexts.Context()
            constants.PARALLELISM = constants.Parallelism.Off
            failures = framework.import_files(volatility3.plugins, True)
            self.automagics = automagic.available(self.ctx)
            self.plugin_list = framework.list_plugins()
            seen_automagics = set()
            #volatility3.symbols.__path__ = [symbols_path] + constants.SYMBOL_BASEPATHS
            for amagic in self.automagics:
                if amagic in seen_automagics:
                    continue
                seen_automagics.add(amagic)

            base_config_path = "plugins"
            single_location = self.memdump  #"file:" + pathname2url(path)
            self.ctx.config[
                "automagic.LayerStacker.single_location"] = single_location
        plugin = self.plugin_list.get(plugin_class)
        automagics = automagic.choose_automagic(self.automagics, plugin)
        constructed = plugins.construct_plugin(self.ctx, automagics, plugin,
                                               "plugins", None, None)
        runned_plugin = constructed.run()
        json_data, error = ReturnJsonRenderer().render(runned_plugin)
        return json_data  #, error
Exemple #6
0
    def save(self, commit=True):
        plugin = self.cleaned_data["plugin"]

        bash_script = None
        reqs_script = False
        py_name = None

        plugin_folder = Path(settings.VOLATILITY_PLUGIN_PATH)
        tmp_folder = plugin_folder / str(uuid.uuid4())
        os.mkdir(tmp_folder)

        with zipfile.ZipFile(plugin.file.path, "r") as f:
            for name in f.namelist():
                if name.endswith(".sh"):
                    bash_script = f.read(name)
                elif name.lower() == "requirements.txt":
                    reqs_script = True
                    with open(tmp_folder / "requirements.txt", "wb") as reqs:
                        reqs.write(f.read(name))
                elif name.endswith(".py"):
                    with open(plugin_folder / name, "wb") as reqs:
                        reqs.write(f.read(name))
                    py_name = Path(name).stem

        if bash_script:
            os.system(shlex.quote("apt update"))
            os.system(shlex.quote(bash_script))
        if reqs_script:
            os.system(
                shlex.quote(
                    "pip install -r {}/requirements.txt".format(tmp_folder)))

        _ = contexts.Context()
        _ = framework.import_files(volatility3.plugins, True)
        available_plugins = framework.list_plugins()

        for plugin in available_plugins:
            if plugin.startswith("custom.{}".format(py_name)):
                self.cleaned_data["name"] = plugin

        def install(bash_script, reqs_script, tmp_folder):
            if bash_script:
                os.system(shlex.quote("apt update"))
                os.system(shlex.quote(bash_script))
            if reqs_script:
                os.system(
                    shlex.quote("pip install -r {}/requirements.txt".format(
                        tmp_folder)))
                os.system(shlex.quote("rm -rf {}".format(tmp_folder)))

        dask_client = get_client(address="tcp://scheduler:8786")
        dask_client.run(install, bash_script, reqs_script, tmp_folder)
        plugin = super(PluginCreateAdminForm, self).save(commit=commit)

        for available_plugin in available_plugins:
            if available_plugin.startswith("custom.{}".format(py_name)):
                plugin.name = available_plugin
                plugin.save()

        return plugin
Exemple #7
0
def choose_os_stackers(
        plugin: Type[interfaces.plugins.PluginInterface]) -> List[str]:
    """Identifies the stackers that should be run, based on the plugin (and thus os) provided"""
    plugin_first_level = plugin.__module__.split('.')[2]

    # Ensure all stackers are loaded
    framework.import_files(sys.modules['volatility3.framework.layers'])

    result = []
    for stacker in sorted(framework.class_subclasses(
            interfaces.automagic.StackerLayerInterface),
                          key=lambda x: x.stack_order):
        if plugin_first_level in stacker.exclusion_list:
            continue
        result.append(stacker.__name__)
    return result
Exemple #8
0
    def run(self, plugin_class, pids=None, round=1):
        """Module which initialize all volatility 3 internals
        https://github.com/volatilityfoundation/volatility3/blob/stable/doc/source/using-as-a-library.rst
        @param plugin_class: plugin class. Ex. windows.pslist.PsList
        @param plugin_class: plugin class. Ex. windows.pslist.PsList
        @param pids: pid list -> abstrats.py -> get_pids(), for custom scripts
        @param round: read -> https://github.com/volatilityfoundation/volatility3/pull/504
        @return: Volatility3 interface.

        """
        if not self.loaded:
            self.ctx = contexts.Context()
            constants.PARALLELISM = constants.Parallelism.Off
            framework.import_files(volatility3.plugins, True)
            self.automagics = automagic.available(self.ctx)
            self.plugin_list = framework.list_plugins()
            seen_automagics = set()
            # volatility3.symbols.__path__ = [symbols_path] + constants.SYMBOL_BASEPATHS
            for amagic in self.automagics:
                if amagic in seen_automagics:
                    continue
                seen_automagics.add(amagic)

            single_location = self.memdump
            self.ctx.config[
                "automagic.LayerStacker.single_location"] = single_location
            if os.path.exists(yara_rules_path):
                self.ctx.config[
                    "plugins.YaraScan.yara_compiled_file"] = f"file:///{yara_rules_path}"

        if pids is not None:
            self.ctx.config["sandbox_pids"] = pids
            self.ctx.config["sandbox_round"] = round

        plugin = self.plugin_list.get(plugin_class)
        try:
            automagics = automagic.choose_automagic(self.automagics, plugin)
            constructed = plugins.construct_plugin(self.ctx, automagics,
                                                   plugin, "plugins", None,
                                                   None)
            runned_plugin = constructed.run()
            json_data, error = ReturnJsonRenderer().render(runned_plugin)
            return json_data  # , error
        except AttributeError:
            log.error("Failing %s on %s", plugin_class, self.memdump)
            return {}
Exemple #9
0
def available(context: interfaces.context.ContextInterface) -> List[interfaces.automagic.AutomagicInterface]:
    """Returns an ordered list of all subclasses of
    :class:`~volatility3.framework.interfaces.automagic.AutomagicInterface`.

    The order is based on the priority attributes of the subclasses, in order to ensure the automagics are listed in
    an appropriate order.

    Args:
        context: The context that will contain any automagic configuration values.
    """
    import_files(sys.modules[__name__])
    config_path = constants.AUTOMAGIC_CONFIG_PATH
    return sorted([
        clazz(context, interfaces.configuration.path_join(config_path, clazz.__name__))
        for clazz in class_subclasses(interfaces.automagic.AutomagicInterface)
    ],
                  key = lambda x: x.priority)
Exemple #10
0
def get_parameters(plugin):
    """
    Obtains parameters list from volatility plugin
    """
    _ = contexts.Context()
    _ = framework.import_files(volatility3.plugins, True)
    plugin_list = framework.list_plugins()
    params = []
    if plugin in plugin_list:
        for requirement in plugin_list[plugin].get_requirements():
            additional = {
                "optional": requirement.optional,
                "name": requirement.name
            }
            if isinstance(requirement, requirements.URIRequirement):
                additional["mode"] = "single"
                additional["type"] = "file"
            elif isinstance(requirement,
                            interfaces.configuration.SimpleTypeRequirement):
                additional["mode"] = "single"
                additional["type"] = requirement.instance_type
            elif isinstance(
                    requirement,
                    volatility3.framework.configuration.requirements.
                    ListRequirement,
            ):
                additional["mode"] = "list"
                additional["type"] = requirement.element_type
            elif isinstance(
                    requirement,
                    volatility3.framework.configuration.requirements.
                    ChoiceRequirement,
            ):
                additional["type"] = str
                additional["mode"] = "single"
                additional["choices"] = requirement.choices
            else:
                continue
            params.append(additional)
    return params
Exemple #11
0
def run_plugin(dump_obj, plugin_obj, params=None, user_pk=None):
    """
    Execute a single plugin on a dump with optional params.
    If success data are sent to elastic.
    """
    logging.info("[dump {} - plugin {}] start".format(dump_obj.pk,
                                                      plugin_obj.pk))
    try:
        ctx = contexts.Context()
        constants.PARALLELISM = constants.Parallelism.Off
        _ = framework.import_files(volatility3.plugins, True)
        automagics = automagic.available(ctx)
        plugin_list = framework.list_plugins()
        json_renderer = ReturnJsonRenderer
        seen_automagics = set()
        for amagic in automagics:
            if amagic in seen_automagics:
                continue
            seen_automagics.add(amagic)
        plugin = plugin_list.get(plugin_obj.name)
        base_config_path = "plugins"
        file_name = os.path.abspath(dump_obj.upload.path)
        single_location = "file:" + pathname2url(file_name)
        ctx.config["automagic.LayerStacker.single_location"] = single_location
        automagics = automagic.choose_automagic(automagics, plugin)
        if ctx.config.get("automagic.LayerStacker.stackers", None) is None:
            ctx.config[
                "automagic.LayerStacker.stackers"] = stacker.choose_os_stackers(
                    plugin)
        # LOCAL DUMPS REQUIRES FILES
        local_dump = plugin_obj.local_dump

        # ADD PARAMETERS, AND IF LOCAL DUMP ENABLE ADD DUMP TRUE BY DEFAULT
        plugin_config_path = interfaces.configuration.path_join(
            base_config_path, plugin.__name__)
        if params:
            # ADD PARAMETERS TO PLUGIN CONF
            for k, v in params.items():
                if v != "":
                    extended_path = interfaces.configuration.path_join(
                        plugin_config_path, k)
                    ctx.config[extended_path] = v

                if k == "dump" and v:
                    # IF DUMP TRUE HAS BEEN PASS IT'LL DUMP LOCALLY
                    local_dump = True

        if not params and local_dump:
            # IF ADMIN SET LOCAL DUMP ADD DUMP TRUE AS PARAMETER
            extended_path = interfaces.configuration.path_join(
                plugin_config_path, "dump")
            ctx.config[extended_path] = True

        logging.debug("[dump {} - plugin {}] params: {}".format(
            dump_obj.pk, plugin_obj.pk, ctx.config))

        file_list = []
        if local_dump:
            # IF PARAM/ADMIN DUMP CREATE FILECONSUMER
            local_path = "{}/{}/{}".format(settings.MEDIA_ROOT, dump_obj.index,
                                           plugin_obj.name)
            if not os.path.exists(local_path):
                os.mkdir(local_path)
            file_handler = file_handler_class_factory(output_dir=local_path,
                                                      file_list=file_list)
        else:
            local_path = None
            file_handler = file_handler_class_factory(output_dir=None,
                                                      file_list=file_list)

        # #####################
        # ## YARA
        # if not file or rule selected and exists default use that
        if plugin_obj.name in [
                "yarascan.YaraScan", "windows.vadyarascan.VadYaraScan"
        ]:
            if not params:
                has_file = False
            else:
                has_file = False
                for k, v in params.items():
                    if k in ["yara_file", "yara_compiled_file", "yara_rules"]:
                        if v is not None and v != "":
                            has_file = True

            if not has_file:
                rule = CustomRule.objects.get(user__pk=user_pk, default=True)
                if rule:
                    extended_path = interfaces.configuration.path_join(
                        plugin_config_path, "yara_compiled_file")
                    ctx.config[extended_path] = "file:{}".format(rule.path)

            logging.error("[dump {} - plugin {}] params: {}".format(
                dump_obj.pk, plugin_obj.pk, ctx.config))

        try:
            # RUN PLUGIN
            constructed = plugins.construct_plugin(
                ctx,
                automagics,
                plugin,
                base_config_path,
                MuteProgress(),
                file_handler,
            )
        except exceptions.UnsatisfiedException as excp:
            # LOG UNSATISFIED ERROR
            result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
            result.result = 3
            result.description = "\n".join([
                excp.unsatisfied[config_path].description
                for config_path in excp.unsatisfied
            ])
            result.save()
            send_to_ws(dump_obj, result, plugin_obj.name)

            logging.error("[dump {} - plugin {}] unsatisfied".format(
                dump_obj.pk, plugin_obj.pk))

            return 0
        try:
            runned_plugin = constructed.run()
        except Exception as excp:
            # LOG GENERIC ERROR [VOLATILITY]
            fulltrace = traceback.TracebackException.from_exception(
                excp).format(chain=True)
            result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
            result.result = 4
            result.description = "\n".join(fulltrace)
            result.save()
            send_to_ws(dump_obj, result, plugin_obj.name)
            logging.error("[dump {} - plugin {}] generic error".format(
                dump_obj.pk, plugin_obj.pk))
            return 0

        # RENDER OUTPUT IN JSON AND PUT IT IN ELASTIC
        json_data, error = json_renderer().render(runned_plugin)

        logging.debug("DATA: {}".format(json_data))
        logging.debug("ERROR: {}".format(error))
        logging.debug("CONFIG: {}".format(ctx.config))

        if len(json_data) > 0:

            # IF DUMP STORE FILE ON DISK
            if local_dump and file_list:
                for file_id in file_list:
                    output_path = "{}/{}".format(local_path,
                                                 file_id.preferred_filename)
                    with open(output_path, "wb") as f:
                        f.write(file_id.getvalue())

                # RUN CLAMAV ON ALL FOLDER
                if plugin_obj.clamav_check:
                    cd = pyclamd.ClamdUnixSocket()
                    match = cd.multiscan_file(local_path)
                    match = {} if not match else match
                else:
                    match = {}

                result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)

                # BULK CREATE EXTRACTED DUMP FOR EACH DUMPED FILE
                ExtractedDump.objects.bulk_create([
                    ExtractedDump(
                        result=result,
                        path="{}/{}".format(local_path,
                                            file_id.preferred_filename),
                        sha256=hash_checksum("{}/{}".format(
                            local_path, file_id.preferred_filename))[0],
                        md5=hash_checksum("{}/{}".format(
                            local_path, file_id.preferred_filename))[1],
                        clamav=(match["{}/{}".format(
                            local_path,
                            file_id.preferred_filename,
                        )][1] if "{}/{}".format(local_path,
                                                file_id.preferred_filename)
                                in match.keys() else None),
                    ) for file_id in file_list
                ])

                # RUN VT AND REGIPY AS DASK SUBTASKS
                if plugin_obj.vt_check or plugin_obj.regipy_check:
                    dask_client = get_client()
                    secede()
                    tasks = []
                    for file_id in file_list:
                        if plugin_obj.vt_check:
                            task = dask_client.submit(
                                run_vt,
                                result.pk,
                                "{}/{}".format(local_path,
                                               file_id.preferred_filename),
                            )
                            tasks.append(task)
                        if plugin_obj.regipy_check:
                            task = dask_client.submit(
                                run_regipy,
                                result.pk,
                                "{}/{}".format(local_path,
                                               file_id.preferred_filename),
                            )
                            tasks.append(task)
                    _ = dask_client.gather(tasks)
                    rejoin()

            es = Elasticsearch(
                [settings.ELASTICSEARCH_URL],
                request_timeout=60,
                timeout=60,
                max_retries=10,
                retry_on_timeout=True,
            )
            helpers.bulk(
                es,
                gendata(
                    "{}_{}".format(dump_obj.index, plugin_obj.name.lower()),
                    json_data,
                    {
                        "orochi_dump":
                        dump_obj.name,
                        "orochi_plugin":
                        plugin_obj.name.lower(),
                        "orochi_os":
                        dump_obj.get_operating_system_display(),
                        "orochi_createdAt":
                        datetime.datetime.now().replace(
                            microsecond=0).isoformat(),
                    },
                ),
            )

            # set max_windows_size on new created index
            es.indices.put_settings(
                index="{}_{}".format(dump_obj.index, plugin_obj.name.lower()),
                body={
                    "index": {
                        "max_result_window": settings.MAX_ELASTIC_WINDOWS_SIZE
                    }
                },
            )

            # EVERYTHING OK
            result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
            result.result = 2
            result.description = error
            result.save()

            logging.debug("[dump {} - plugin {}] sent to elastic".format(
                dump_obj.pk, plugin_obj.pk))
        else:
            # OK BUT EMPTY
            result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
            result.result = 1
            result.description = error
            result.save()

            logging.debug("[dump {} - plugin {}] empty".format(
                dump_obj.pk, plugin_obj.pk))
        send_to_ws(dump_obj, result, plugin_obj.name)
        return 0

    except Exception as excp:
        # LOG GENERIC ERROR [ELASTIC]
        fulltrace = traceback.TracebackException.from_exception(excp).format(
            chain=True)
        result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
        result.result = 4
        result.description = "\n".join(fulltrace)
        result.save()
        send_to_ws(dump_obj, result, plugin_obj.name)
        logging.error("[dump {} - plugin {}] generic error".format(
            dump_obj.pk, plugin_obj.pk))
        return 0
Exemple #12
0
    def run(self):
        """Executes the command line module, taking the system arguments,
        determining the plugin to run and then running it."""
        sys.stdout.write(
            f"Volshell (Volatility 3 Framework) {constants.PACKAGE_VERSION}\n")

        framework.require_interface_version(2, 0, 0)

        parser = argparse.ArgumentParser(
            prog=self.CLI_NAME,
            description=
            "A tool for interactivate forensic analysis of memory images")
        parser.add_argument("-c",
                            "--config",
                            help="Load the configuration from a json file",
                            default=None,
                            type=str)
        parser.add_argument(
            "-e",
            "--extend",
            help="Extend the configuration with a new (or changed) setting",
            default=None,
            action='append')
        parser.add_argument(
            "-p",
            "--plugin-dirs",
            help="Semi-colon separated list of paths to find plugins",
            default="",
            type=str)
        parser.add_argument(
            "-s",
            "--symbol-dirs",
            help="Semi-colon separated list of paths to find symbols",
            default="",
            type=str)
        parser.add_argument("-v",
                            "--verbosity",
                            help="Increase output verbosity",
                            default=0,
                            action="count")
        parser.add_argument(
            "-o",
            "--output-dir",
            help="Directory in which to output any generated files",
            default=os.path.abspath(
                os.path.join(os.path.dirname(__file__), '..', '..')),
            type=str)
        parser.add_argument("-q",
                            "--quiet",
                            help="Remove progress feedback",
                            default=False,
                            action='store_true')
        parser.add_argument("--log",
                            help="Log output to a file as well as the console",
                            default=None,
                            type=str)
        parser.add_argument(
            "-f",
            "--file",
            metavar='FILE',
            default=None,
            type=str,
            help=
            "Shorthand for --single-location=file:// if single-location is not defined"
        )
        parser.add_argument(
            "--write-config",
            help="Write configuration JSON file out to config.json",
            default=False,
            action='store_true')
        parser.add_argument("--clear-cache",
                            help="Clears out all short-term cached items",
                            default=False,
                            action='store_true')
        parser.add_argument(
            "--cache-path",
            help=
            f"Change the default path ({constants.CACHE_PATH}) used to store the cache",
            default=constants.CACHE_PATH,
            type=str)

        # Volshell specific flags
        os_specific = parser.add_mutually_exclusive_group(required=False)
        os_specific.add_argument("-w",
                                 "--windows",
                                 default=False,
                                 action="store_true",
                                 help="Run a Windows volshell")
        os_specific.add_argument("-l",
                                 "--linux",
                                 default=False,
                                 action="store_true",
                                 help="Run a Linux volshell")
        os_specific.add_argument("-m",
                                 "--mac",
                                 default=False,
                                 action="store_true",
                                 help="Run a Mac volshell")

        # We have to filter out help, otherwise parse_known_args will trigger the help message before having
        # processed the plugin choice or had the plugin subparser added.
        known_args = [
            arg for arg in sys.argv if arg != '--help' and arg != '-h'
        ]
        partial_args, _ = parser.parse_known_args(known_args)
        if partial_args.plugin_dirs:
            volatility3.plugins.__path__ = [
                os.path.abspath(p) for p in partial_args.plugin_dirs.split(";")
            ] + constants.PLUGINS_PATH

        if partial_args.symbol_dirs:
            volatility3.symbols.__path__ = [
                os.path.abspath(p) for p in partial_args.symbol_dirs.split(";")
            ] + constants.SYMBOL_BASEPATHS

        if partial_args.cache_path:
            constants.CACHE_PATH = partial_args.cache_path

        vollog.info(f"Volatility plugins path: {volatility3.plugins.__path__}")
        vollog.info(f"Volatility symbols path: {volatility3.symbols.__path__}")

        if partial_args.log:
            file_logger = logging.FileHandler(partial_args.log)
            file_logger.setLevel(0)
            file_formatter = logging.Formatter(
                datefmt='%y-%m-%d %H:%M:%S',
                fmt='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
            file_logger.setFormatter(file_formatter)
            vollog.addHandler(file_logger)
            vollog.info("Logging started")

        if partial_args.verbosity < 3:
            console.setLevel(30 - (partial_args.verbosity * 10))
        else:
            console.setLevel(10 - (partial_args.verbosity - 2))

        if partial_args.clear_cache:
            framework.clear_cache()

        # Do the initialization
        ctx = contexts.Context()  # Construct a blank context
        failures = framework.import_files(
            volatility3.plugins,
            True)  # Will not log as console's default level is WARNING
        if failures:
            parser.epilog = "The following plugins could not be loaded (use -vv to see why): " + \
                            ", ".join(sorted(failures))
            vollog.info(parser.epilog)
        automagics = automagic.available(ctx)

        # Initialize the list of plugins in case volshell needs it
        framework.list_plugins()

        seen_automagics = set()
        configurables_list = {}
        for amagic in automagics:
            if amagic in seen_automagics:
                continue
            seen_automagics.add(amagic)
            if isinstance(amagic,
                          interfaces.configuration.ConfigurableInterface):
                self.populate_requirements_argparse(parser, amagic.__class__)
                configurables_list[amagic.__class__.__name__] = amagic

        # We don't list plugin arguments, because they can be provided within python
        volshell_plugin_list = {
            'generic': generic.Volshell,
            'windows': windows.Volshell
        }
        for plugin in volshell_plugin_list:
            subparser = parser.add_argument_group(
                title=plugin.capitalize(),
                description="Configuration options based on {} options".format(
                    plugin.capitalize()))
            self.populate_requirements_argparse(subparser,
                                                volshell_plugin_list[plugin])
            configurables_list[plugin] = volshell_plugin_list[plugin]

        ###
        # PASS TO UI
        ###
        # Hand the plugin requirements over to the CLI (us) and let it construct the config tree

        # Run the argparser
        args = parser.parse_args()

        vollog.log(constants.LOGLEVEL_VVV,
                   f"Cache directory used: {constants.CACHE_PATH}")

        plugin = generic.Volshell
        if args.windows:
            plugin = windows.Volshell
        if args.linux:
            plugin = linux.Volshell
        if args.mac:
            plugin = mac.Volshell

        base_config_path = "plugins"
        plugin_config_path = interfaces.configuration.path_join(
            base_config_path, plugin.__name__)

        # Special case the -f argument because people use is so frequently
        # It has to go here so it can be overridden by single-location if it's defined
        # NOTE: This will *BREAK* if LayerStacker, or the automagic configuration system, changes at all
        ###
        if args.file:
            try:
                single_location = self.location_from_file(args.file)
                ctx.config[
                    'automagic.LayerStacker.single_location'] = single_location
            except ValueError as excp:
                parser.error(str(excp))

        # UI fills in the config, here we load it from the config file and do it before we process the CL parameters
        if args.config:
            with open(args.config, "r") as f:
                json_val = json.load(f)
                ctx.config.splice(
                    plugin_config_path,
                    interfaces.configuration.HierarchicalDict(json_val))

        self.populate_config(ctx, configurables_list, args, plugin_config_path)

        if args.extend:
            for extension in args.extend:
                if '=' not in extension:
                    raise ValueError(
                        "Invalid extension (extensions must be of the format \"conf.path.value='value'\")"
                    )
                address, value = extension[:extension.find('=')], json.loads(
                    extension[extension.find('=') + 1:])
                ctx.config[address] = value

        # It should be up to the UI to determine which automagics to run, so this is before BACK TO THE FRAMEWORK
        automagics = automagic.choose_automagic(automagics, plugin)
        self.output_dir = args.output_dir

        ###
        # BACK TO THE FRAMEWORK
        ###
        try:
            progress_callback = cli.PrintedProgress()
            if args.quiet:
                progress_callback = cli.MuteProgress()

            constructed = plugins.construct_plugin(
                ctx, automagics, plugin, base_config_path, progress_callback,
                self.file_handler_class_factory())

            if args.write_config:
                vollog.debug("Writing out configuration data to config.json")
                with open("config.json", "w") as f:
                    json.dump(dict(constructed.build_configuration()),
                              f,
                              sort_keys=True,
                              indent=2)
        except exceptions.UnsatisfiedException as excp:
            self.process_unsatisfied_exceptions(excp)
            parser.exit(
                1,
                f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n"
            )

        try:
            # Construct and run the plugin
            if constructed:
                constructed.run()
        except exceptions.VolatilityException as excp:
            self.process_exceptions(excp)
            parser.exit(
                1,
                f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n"
            )
Exemple #13
0
    def handle(self, *args, **kwargs):

        plugins = Plugin.objects.all()
        installed_plugins = [x.name for x in plugins]
        if len(plugins) > 0:
            self.stdout.write(
                self.style.SUCCESS(
                    "Plugins in db: {}".format(", ".join(installed_plugins))
                )
            )
        else:
            self.stdout.write(self.style.SUCCESS("No plugins in db"))

        _ = contexts.Context()
        _ = framework.import_files(volatility3.plugins, True)
        available_plugins = framework.list_plugins()
        self.stdout.write("Available Plugins: {}".format(", ".join(available_plugins)))

        # If plugin doesn't exists anymore disable it
        for plugin in plugins:
            if plugin.name not in available_plugins:
                plugin.disabled = True
                plugin.save()
                self.stdout.write(
                    self.style.ERROR(
                        "Plugin {} disabled. It is not available anymore!".format(
                            plugin
                        )
                    )
                )

        # Create new plugin, take os from name
        for plugin in available_plugins:
            if plugin not in installed_plugins:
                if plugin.startswith("linux"):
                    plugin = Plugin(name=plugin, operating_system="Linux")
                elif plugin.startswith("windows"):
                    plugin = Plugin(name=plugin, operating_system="Windows")
                elif plugin.startswith("mac"):
                    plugin = Plugin(name=plugin, operating_system="Mac")
                else:
                    plugin = Plugin(name=plugin, operating_system="Other")
                plugin.save()
                self.stdout.write(self.style.SUCCESS("Plugin {} added!".format(plugin)))

                # Add new plugin in old dump
                for dump in Dump.objects.all():
                    if plugin.operating_system in [dump.operating_system, "Other"]:
                        up, created = Result.objects.get_or_create(
                            dump=dump, plugin=plugin
                        )
                        if created:
                            up.result = 5
                            up.save()
                self.stdout.write(
                    self.style.SUCCESS("Plugin {} added to old dumps!".format(plugin))
                )

            else:
                plugin = Plugin.objects.get(name=plugin)

            # Add new plugin to user
            for user in get_user_model().objects.all():
                up, created = UserPlugin.objects.get_or_create(user=user, plugin=plugin)
                if created:
                    self.stdout.write(
                        self.style.SUCCESS(
                            "Plugin {} added to {}!".format(plugin, user)
                        )
                    )
Exemple #14
0
    def run(self):
        """Executes the command line module, taking the system arguments,
        determining the plugin to run and then running it."""

        volatility3.framework.require_interface_version(1, 0, 0)

        renderers = dict([(x.name.lower(), x) for x in framework.class_subclasses(text_renderer.CLIRenderer)])

        parser = volargparse.HelpfulArgParser(add_help = False,
                                              prog = self.CLI_NAME,
                                              description = "An open-source memory forensics framework")
        parser.add_argument(
            "-h",
            "--help",
            action = "help",
            default = argparse.SUPPRESS,
            help = "Show this help message and exit, for specific plugin options use '{} <pluginname> --help'".format(
                parser.prog))
        parser.add_argument("-c",
                            "--config",
                            help = "Load the configuration from a json file",
                            default = None,
                            type = str)
        parser.add_argument("--parallelism",
                            help = "Enables parallelism (defaults to off if no argument given)",
                            nargs = '?',
                            choices = ['processes', 'threads', 'off'],
                            const = 'processes',
                            default = None,
                            type = str)
        parser.add_argument("-e",
                            "--extend",
                            help = "Extend the configuration with a new (or changed) setting",
                            default = None,
                            action = 'append')
        parser.add_argument("-p",
                            "--plugin-dirs",
                            help = "Semi-colon separated list of paths to find plugins",
                            default = "",
                            type = str)
        parser.add_argument("-s",
                            "--symbol-dirs",
                            help = "Semi-colon separated list of paths to find symbols",
                            default = "",
                            type = str)
        parser.add_argument("-v", "--verbosity", help = "Increase output verbosity", default = 0, action = "count")
        parser.add_argument("-l",
                            "--log",
                            help = "Log output to a file as well as the console",
                            default = None,
                            type = str)
        parser.add_argument("-o",
                            "--output-dir",
                            help = "Directory in which to output any generated files",
                            default = os.getcwd(),
                            type = str)
        parser.add_argument("-q", "--quiet", help = "Remove progress feedback", default = False, action = 'store_true')
        parser.add_argument("-r",
                            "--renderer",
                            metavar = 'RENDERER',
                            help = "Determines how to render the output ({})".format(", ".join(list(renderers))),
                            default = "quick",
                            choices = list(renderers))
        parser.add_argument("-f",
                            "--file",
                            metavar = 'FILE',
                            default = None,
                            type = str,
                            help = "Shorthand for --single-location=file:// if single-location is not defined")
        parser.add_argument("--write-config",
                            help = "Write configuration JSON file out to config.json",
                            default = False,
                            action = 'store_true')
        parser.add_argument("--clear-cache",
                            help = "Clears out all short-term cached items",
                            default = False,
                            action = 'store_true')
        parser.add_argument("--cache-path",
                            help = "Change the default path ({}) used to store the cache".format(constants.CACHE_PATH),
                            default = constants.CACHE_PATH,
                            type = str)

        # We have to filter out help, otherwise parse_known_args will trigger the help message before having
        # processed the plugin choice or had the plugin subparser added.
        known_args = [arg for arg in sys.argv if arg != '--help' and arg != '-h']
        partial_args, _ = parser.parse_known_args(known_args)

        banner_output = sys.stdout
        if renderers[partial_args.renderer].structured_output:
            banner_output = sys.stderr
        banner_output.write("Volatility 3 Framework {}\n".format(constants.PACKAGE_VERSION))

        if partial_args.plugin_dirs:
            volatility3.plugins.__path__ = [os.path.abspath(p)
                                            for p in partial_args.plugin_dirs.split(";")] + constants.PLUGINS_PATH

        if partial_args.symbol_dirs:
            volatility3.symbols.__path__ = [os.path.abspath(p)
                                            for p in partial_args.symbol_dirs.split(";")] + constants.SYMBOL_BASEPATHS

        if partial_args.cache_path:
            constants.CACHE_PATH = partial_args.cache_path

        if partial_args.log:
            file_logger = logging.FileHandler(partial_args.log)
            file_logger.setLevel(1)
            file_formatter = logging.Formatter(datefmt = '%y-%m-%d %H:%M:%S',
                                               fmt = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
            file_logger.setFormatter(file_formatter)
            vollog.addHandler(file_logger)
            vollog.info("Logging started")
        if partial_args.verbosity < 3:
            if partial_args.verbosity < 1:
                sys.tracebacklimit = None
            console.setLevel(30 - (partial_args.verbosity * 10))
        else:
            console.setLevel(10 - (partial_args.verbosity - 2))

        vollog.info("Volatility plugins path: {}".format(volatility3.plugins.__path__))
        vollog.info("Volatility symbols path: {}".format(volatility3.symbols.__path__))

        # Set the PARALLELISM
        if partial_args.parallelism == 'processes':
            constants.PARALLELISM = constants.Parallelism.Multiprocessing
        elif partial_args.parallelism == 'threads':
            constants.PARALLELISM = constants.Parallelism.Threading
        else:
            constants.PARALLELISM = constants.Parallelism.Off

        if partial_args.clear_cache:
            framework.clear_cache()

        # Do the initialization
        ctx = contexts.Context()  # Construct a blank context
        failures = framework.import_files(volatility3.plugins,
                                          True)  # Will not log as console's default level is WARNING
        if failures:
            parser.epilog = "The following plugins could not be loaded (use -vv to see why): " + \
                            ", ".join(sorted(failures))
            vollog.info(parser.epilog)
        automagics = automagic.available(ctx)

        plugin_list = framework.list_plugins()

        seen_automagics = set()
        chosen_configurables_list = {}
        for amagic in automagics:
            if amagic in seen_automagics:
                continue
            seen_automagics.add(amagic)
            if isinstance(amagic, interfaces.configuration.ConfigurableInterface):
                self.populate_requirements_argparse(parser, amagic.__class__)

        subparser = parser.add_subparsers(title = "Plugins",
                                          dest = "plugin",
                                          description = "For plugin specific options, run '{} <plugin> --help'".format(
                                              self.CLI_NAME),
                                          action = volargparse.HelpfulSubparserAction)
        for plugin in sorted(plugin_list):
            plugin_parser = subparser.add_parser(plugin, help = plugin_list[plugin].__doc__)
            self.populate_requirements_argparse(plugin_parser, plugin_list[plugin])

        ###
        # PASS TO UI
        ###
        # Hand the plugin requirements over to the CLI (us) and let it construct the config tree

        # Run the argparser
        args = parser.parse_args()
        if args.plugin is None:
            parser.error("Please select a plugin to run")

        vollog.log(constants.LOGLEVEL_VVV, "Cache directory used: {}".format(constants.CACHE_PATH))

        plugin = plugin_list[args.plugin]
        chosen_configurables_list[args.plugin] = plugin
        base_config_path = "plugins"
        plugin_config_path = interfaces.configuration.path_join(base_config_path, plugin.__name__)

        # Special case the -f argument because people use is so frequently
        # It has to go here so it can be overridden by single-location if it's defined
        # NOTE: This will *BREAK* if LayerStacker, or the automagic configuration system, changes at all
        ###
        if args.file:
            file_name = os.path.abspath(args.file)
            if not os.path.exists(file_name):
                vollog.log(logging.INFO, "File does not exist: {}".format(file_name))
            else:
                single_location = "file:" + request.pathname2url(file_name)
                ctx.config['automagic.LayerStacker.single_location'] = single_location

        # UI fills in the config, here we load it from the config file and do it before we process the CL parameters
        if args.config:
            with open(args.config, "r") as f:
                json_val = json.load(f)
                ctx.config.splice(plugin_config_path, interfaces.configuration.HierarchicalDict(json_val))

        # It should be up to the UI to determine which automagics to run, so this is before BACK TO THE FRAMEWORK
        automagics = automagic.choose_automagic(automagics, plugin)
        for amagic in automagics:
            chosen_configurables_list[amagic.__class__.__name__] = amagic

        if ctx.config.get('automagic.LayerStacker.stackers', None) is None:
            ctx.config['automagic.LayerStacker.stackers'] = stacker.choose_os_stackers(plugin)
        self.output_dir = args.output_dir
        if not os.path.exists(self.output_dir):
            parser.error("The output directory specified does not exist: {}".format(self.output_dir))

        self.populate_config(ctx, chosen_configurables_list, args, plugin_config_path)

        if args.extend:
            for extension in args.extend:
                if '=' not in extension:
                    raise ValueError("Invalid extension (extensions must be of the format \"conf.path.value='value'\")")
                address, value = extension[:extension.find('=')], json.loads(extension[extension.find('=') + 1:])
                ctx.config[address] = value

        ###
        # BACK TO THE FRAMEWORK
        ###
        constructed = None
        try:
            progress_callback = PrintedProgress()
            if args.quiet:
                progress_callback = MuteProgress()

            constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback,
                                                   self.file_handler_class_factory())

            if args.write_config:
                vollog.debug("Writing out configuration data to config.json")
                with open("config.json", "w") as f:
                    json.dump(dict(constructed.build_configuration()), f, sort_keys = True, indent = 2)
        except exceptions.UnsatisfiedException as excp:
            self.process_unsatisfied_exceptions(excp)
            parser.exit(1, "Unable to validate the plugin requirements: {}\n".format([x for x in excp.unsatisfied]))

        try:
            # Construct and run the plugin
            if constructed:
                renderers[args.renderer]().render(constructed.run())
        except (exceptions.VolatilityException) as excp:
            self.process_exceptions(excp)