def run(self): # we aren't really doing logging, but you can change these numbers to get more details vollog = logging.getLogger(__name__) vollog = logging.getLogger() # vollog.setLevel(1000) console = logging.StreamHandler() #console.setLevel(logging.WARNING) formatter = logging.Formatter( '%(levelname)-8s %(name)-12s: %(message)s') console.setFormatter(formatter) vollog.addHandler(console) volatility.framework.require_interface_version(1, 0, 0) # also change here for log level #console.setLevel(1000) constants.PARALLELISM = constants.Parallelism.Off ctx = contexts.Context() # Construct a blank context failures = framework.import_files( volatility.plugins, True) # Will not log as console's default level is WARNING automagics = automagic.available(ctx) plugin_list = 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): configurables_list[amagic.__class__.__name__] = amagic plugin_name = "linux.pstree.PsTree" # we're just "kinda" running a plugin plugin = plugin_list[plugin_name] base_config_path = "plugins" plugin_config_path = interfaces.configuration.path_join( base_config_path, plugin.__name__) # 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) # this is our fake file that represents QEMU memory single_location = "file:" + pathname2url("/panda.panda") ctx.config['automagic.LayerStacker.single_location'] = single_location constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, MuteProgress(), self) return constructed
def run(self, argstring): # Make sure we log everything vollog = logging.getLogger() #vollog.setLevel(1) # Trim the console down by default console = logging.StreamHandler() #console.setLevel(logging.FATAL) formatter = logging.Formatter( '%(levelname)-8s %(name)-12s: %(message)s') console.setFormatter(formatter) vollog.addHandler(console) # Make sure we log everything vollog = logging.getLogger() #vollog.setLevel(1) # Trim the console down by default console = logging.StreamHandler() #console.setLevel(logging.WARNING) formatter = logging.Formatter( '%(levelname)-8s %(name)-12s: %(message)s') console.setFormatter(formatter) vollog.addHandler(console) arg_arr = shlex.split(argstring) """Executes the command line module, taking the system arguments, determining the plugin to run and then running it.""" sys.stdout.write("Volatility 3 Framework {}\n".format( constants.PACKAGE_VERSION)) volatility.framework.require_interface_version(1, 0, 0) renderers = dict([ (x.name.lower(), x) for x in framework.class_subclasses(text_renderer.CLIRenderer) ]) parser = argparse.ArgumentParser( prog='volatility', description="An open-source memory forensics framework") 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 processes 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.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( "-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') # 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 arg_arr if arg != '--help' and arg != '-h' ] partial_args, _ = parser.parse_known_args(known_args) if partial_args.plugin_dirs: volatility.plugins.__path__ = [ os.path.abspath(p) for p in partial_args.plugin_dirs.split(";") ] + constants.PLUGINS_PATH if partial_args.symbol_dirs: volatility.symbols.__path__ = [ os.path.abspath(p) for p in partial_args.symbol_dirs.split(";") ] + constants.SYMBOL_BASEPATHS 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: console.setLevel(30 - (partial_args.verbosity * 10)) else: console.setLevel(10 - (partial_args.verbosity - 2)) #console.setLevel(0) vollog.info("Volatility plugins path: {}".format( volatility.plugins.__path__)) vollog.info("Volatility symbols path: {}".format( volatility.symbols.__path__)) # Set the PARALLELISM if partial_args.parallelism == 'processes': constants.PARALLELISM = constants.Parallelism.Multiprocessing elif partial_args.parallelism == 'threading': constants.PARALLELISM = constants.Parallelism.Threading else: constants.PARALLELISM = constants.Parallelism.Off # Do the initialization ctx = contexts.Context() # Construct a blank context failures = framework.import_files( volatility.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() 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 subparser = parser.add_subparsers(title="Plugins", dest="plugin", action=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]) configurables_list[plugin] = 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(arg_arr) print(partial_args.verbosity) print(args.plugin, type(args.plugin)) 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] 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) and "panda.panda" not in file_name: print("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)) 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 = PrintedProgress() if args.quiet: progress_callback = MuteProgress() constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self) # return (ctx, automagics, plugin, base_config_path, progress_callback, self) 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) # return StringTextRenderer().render(constructed.run()) return constructed except exceptions.UnsatisfiedException as excp: self.process_exceptions(excp) parser.exit( 1, "Unable to validate the plugin requirements: {}\n".format( [x for x in excp.unsatisfied]))