def ParseOptions(self, options): """Parses the options. Args: options: the command line arguments (instance of argparse.Namespace). Raises: BadConfigOption: if the options are invalid. """ # Check the list options first otherwise required options will raise. self._ParseExtractionOptions(options) self._ParseOutputOptions(options) # TODO: refactor usage of self._old_preprocess. self._front_end.SetUseOldPreprocess(self._old_preprocess) self._ParseTimezoneOption(options) self._ParseExperimentalOptions(options) self.show_info = getattr(options, u'show_info', False) if getattr(options, u'use_markdown', False): self._views_format_type = cli_views.ViewsFactory.FORMAT_TYPE_MARKDOWN self.dependencies_check = getattr(options, u'dependencies_check', True) if (self.list_hashers or self.list_output_modules or self.list_parsers_and_plugins or self.list_timezones or self.show_info): return super(Log2TimelineTool, self).ParseOptions(options) self._ParseOutputOptions(options) self._ParseProcessingOptions(options) format_string = ( u'%(asctime)s [%(levelname)s] (%(processName)-10s) PID:%(process)d ' u'<%(module)s> %(message)s') if self._debug_mode: logging_level = logging.DEBUG elif self._quiet_mode: logging_level = logging.WARNING else: logging_level = logging.INFO self.ParseLogFileOptions(options) self._ConfigureLogging(filename=self._log_file, format_string=format_string, log_level=logging_level) if self._debug_mode: logging_filter = log2timeline.LoggingFilter() root_logger = logging.getLogger() root_logger.addFilter(logging_filter) self._output = self.ParseStringOption(options, u'output') if not self._output: raise errors.BadConfigOption(u'No output defined.') # TODO: where is this defined? self._operating_system = getattr(options, u'os', None) if self._operating_system: self._mount_path = getattr(options, u'filename', None) self._filter_expression = self.ParseStringOption(options, u'filter') if self._filter_expression: # TODO: refactor self._filter_object out the tool into the frontend. self._filter_object = self._GetMatcher(self._filter_expression) if not self._filter_object: raise errors.BadConfigOption( u'Invalid filter expression: {0:s}'.format( self._filter_expression)) self._status_view_mode = getattr(options, u'status_view_mode', u'linear') self._enable_sigsegv_handler = getattr(options, u'sigsegv_handler', False)
def ParseOptions(self, options): """Parses the options. Args: options: the command line arguments (instance of argparse.Namespace). Raises: BadConfigOption: if the options are invalid. """ super(Log2TimelineTool, self).ParseOptions(options) self._ParseOutputOptions(options) self._ParseProcessingOptions(options) format_string = ( u'%(asctime)s [%(levelname)s] (%(processName)-10s) PID:%(process)d ' u'<%(module)s> %(message)s') debug = getattr(options, u'debug', False) logfile = getattr(options, u'logfile', None) if debug: logging_level = logging.DEBUG else: logging_level = logging.INFO if logfile: logging.basicConfig(level=logging_level, format=format_string, filename=logfile) else: logging.basicConfig(level=logging_level, format=format_string) if self._debug_mode: logging_filter = log2timeline.LoggingFilter() root_logger = logging.getLogger() root_logger.addFilter(logging_filter) self._output = getattr(options, u'output', None) if not self._output: raise errors.BadConfigOption(u'No output defined.') # TODO: consider moving this into the extraction frontend. timezone_string = getattr(options, u'timezone', None) if timezone_string and timezone_string == u'list': self.list_timezones = True self.list_parsers_and_plugins = getattr(options, u'show_info', False) # TODO: where is this defined? self._operating_system = getattr(options, u'os', None) if self._operating_system: self._mount_path = getattr(options, u'filename', None) self._filter_expression = getattr(options, u'filter', None) if self._filter_expression: # TODO: refactor self._filter_object out the tool into the frontend. self._filter_object = pfilter.GetMatcher(self._filter_expression) if not self._filter_object: raise errors.BadConfigOption( u'Invalid filter expression: {0:s}'.format( self._filter_expression))
def Main(): """Start the tool.""" multiprocessing.freeze_support() front_end = log2timeline.Log2TimelineFrontend() epilog = u'\n'.join([ u'', u'Example usage:', u'', u'Run the tool against an image (full kitchen sink)', u' log2timeline.py /cases/mycase/plaso.dump ímynd.dd', u'', u'Instead of answering questions, indicate some of the options on the', u'command line (including data from particular VSS stores).', (u' log2timeline.py -o 63 --vss_stores 1,2 /cases/plaso_vss.dump ' u'image.E01'), u'', u'And that\'s how you build a timeline using log2timeline...', u'' ]) description = u'\n'.join([ u'', u'log2timeline is the main front-end to the plaso back-end, used to', u'collect and correlate events extracted from a filesystem.', u'', u'More information can be gathered from here:', u' http://plaso.kiddaland.net/usage/log2timeline', u'' ]) arg_parser = argparse.ArgumentParser( description=textwrap.dedent(description), formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent(epilog), add_help=False) # Create few argument groups to make formatting help messages clearer. info_group = arg_parser.add_argument_group('Informational Arguments') function_group = arg_parser.add_argument_group('Functional Arguments') deep_group = arg_parser.add_argument_group('Deep Analysis Arguments') performance_group = arg_parser.add_argument_group('Performance Arguments') function_group.add_argument( '-z', '--zone', '--timezone', dest='timezone', action='store', type=str, default='UTC', help= (u'Define the timezone of the IMAGE (not the output). This is usually ' u'discovered automatically by preprocessing but might need to be ' u'specifically set if preprocessing does not properly detect or to ' u'overwrite the detected time zone.')) function_group.add_argument( '-t', '--text', dest='text_prepend', action='store', type=unicode, default=u'', metavar='TEXT', help=(u'Define a free form text string that is prepended to each path ' u'to make it easier to distinguish one record from another in a ' u'timeline (like c:\\, or host_w_c:\\)')) function_group.add_argument( '--parsers', dest='parsers', type=str, action='store', default='', metavar='PARSER_LIST', help= (u'Define a list of parsers to use by the tool. This is a comma ' u'separated list where each entry can be either a name of a parser ' u'or a parser list. Each entry can be prepended with a minus sign ' u'to negate the selection (exclude it). The list match is an ' u'exact match while an individual parser matching is a case ' u'insensitive substring match, with support for glob patterns. ' u'Examples would be: "reg" that matches the substring "reg" in ' u'all parser names or the glob pattern "sky[pd]" that would match ' u'all parsers that have the string "skyp" or "skyd" in it\'s name. ' u'All matching is case insensitive.')) function_group.add_argument( '--hashers', dest='hashers', type=str, action='store', default='', metavar='HASHER_LIST', help=( u'Define a list of hashers to use by the tool. This is a comma ' u'separated list where each entry is the name of a hasher. eg. md5,' u'sha256.')) info_group.add_argument('-h', '--help', action='help', help=u'Show this help message and exit.') info_group.add_argument( '--logfile', action='store', metavar='FILENAME', dest='logfile', type=unicode, default=u'', help=(u'If defined all log messages will be redirected to this file ' u'instead the default STDERR.')) function_group.add_argument( '-p', '--preprocess', dest='preprocess', action='store_true', default=False, help=(u'Turn on preprocessing. Preprocessing is turned on by default ' u'when parsing image files, however if a mount point is being ' u'parsed then this parameter needs to be set manually.')) front_end.AddPerformanceOptions(performance_group) performance_group.add_argument( '--workers', dest='workers', action='store', type=int, default=0, help=(u'The number of worker threads [defaults to available system ' u'CPU\'s minus three].')) # TODO: seems to be no longer used, remove. # function_group.add_argument( # '-i', '--image', dest='image', action='store_true', default=False, # help=( # 'Indicates that this is an image instead of a regular file. It is ' # 'not necessary to include this option if -o (offset) is used, then ' # 'this option is assumed. Use this when parsing an image with an ' # 'offset of zero.')) front_end.AddVssProcessingOptions(deep_group) performance_group.add_argument( '--single_thread', '--single-thread', '--single_process', '--single-process', dest='single_process', action='store_true', default=False, help=(u'Indicate that the tool should run in a single process.')) function_group.add_argument( '-f', '--file_filter', '--file-filter', dest='file_filter', action='store', type=unicode, default=None, help=( u'List of files to include for targeted collection of files to ' u'parse, one line per file path, setup is /path|file - where each ' u'element can contain either a variable set in the preprocessing ' u'stage or a regular expression.')) deep_group.add_argument('--scan_archives', dest='scan_archives', action='store_true', default=False, help=argparse.SUPPRESS) # This option is "hidden" for the time being, still left in there for testing # purposes, but hidden from the tool usage and help messages. # help=('Indicate that the tool should try to open files to extract embedd' # 'ed files within them, for instance to extract files from compress' # 'ed containers, etc. Be AWARE THAT THIS IS EXTREMELY SLOW.')) front_end.AddImageOptions(function_group) function_group.add_argument( '--partition', dest='partition_number', action='store', type=int, default=None, help=(u'Choose a partition number from a disk image. This partition ' u'number should correspond to the partion number on the disk ' u'image, starting from partition 1.')) # Build the version information. version_string = u'log2timeline - plaso back-end {0:s}'.format( plaso.GetVersion()) info_group.add_argument('-v', '--version', action='version', version=version_string, help=u'Show the current version of the back-end.') info_group.add_argument( '--info', dest='show_info', action='store_true', default=False, help=u'Print out information about supported plugins and parsers.') info_group.add_argument( '--show_memory_usage', '--show-memory-usage', action='store_true', default=False, dest='foreman_verbose', help=(u'Indicates that basic memory usage should be included in the ' u'output of the process monitor. If this option is not set the ' u'tool only displays basic status and counter information.')) info_group.add_argument( '--disable_worker_monitor', '--disable-worker-monitor', action='store_false', default=True, dest='foreman_enabled', help= (u'Turn off the foreman. The foreman monitors all worker processes ' u'and periodically prints out information about all running workers.' u'By default the foreman is run, but it can be turned off using this ' u'parameter.')) front_end.AddExtractionOptions(function_group) function_group.add_argument( '--output', dest='output_module', action='store', type=unicode, default='', help=( u'Bypass the storage module directly storing events according to ' u'the output module. This means that the output will not be in the ' u'pstorage format but in the format chosen by the output module. ' u'[Please not this feature is EXPERIMENTAL at this time, use at ' u'own risk (eg. sqlite output does not yet work)]')) function_group.add_argument( '--serializer-format', '--serializer_format', dest='serializer_format', action='store', default='proto', metavar='FORMAT', help=(u'By default the storage uses protobufs for serializing event ' u'objects. This parameter can be used to change that behavior. ' u'The choices are "proto" and "json".')) front_end.AddInformationalOptions(info_group) arg_parser.add_argument( 'output', action='store', metavar='STORAGE_FILE', nargs='?', type=unicode, help=(u'The path to the output file, if the file exists it will get ' u'appended to.')) arg_parser.add_argument( 'source', action='store', metavar='SOURCE', nargs='?', type=unicode, help= (u'The path to the source device, file or directory. If the source is ' u'a supported storage media device or image file, archive file or ' u'a directory, the files within are processed recursively.')) arg_parser.add_argument( 'filter', action='store', metavar='FILTER', nargs='?', default=None, type=unicode, help=(u'A filter that can be used to filter the dataset before it ' u'is written into storage. More information about the filters ' u'and it\'s usage can be found here: http://plaso.kiddaland.' u'net/usage/filters')) # Properly prepare the attributes according to local encoding. if front_end.preferred_encoding == 'ascii': logging.warning( u'The preferred encoding of your system is ASCII, which is not optimal ' u'for the typically non-ASCII characters that need to be parsed and ' u'processed. The tool will most likely crash and die, perhaps in a way ' u'that may not be recoverable. A five second delay is introduced to ' u'give you time to cancel the runtime and reconfigure your preferred ' u'encoding, otherwise continue at own risk.') time.sleep(5) u_argv = [x.decode(front_end.preferred_encoding) for x in sys.argv] sys.argv = u_argv try: options = arg_parser.parse_args() except UnicodeEncodeError: # If we get here we are attempting to print help in a "dumb" terminal. print arg_parser.format_help().encode(front_end.preferred_encoding) return False if options.timezone == 'list': front_end.ListTimeZones() return True if options.show_info: front_end.ListPluginInformation() return True format_str = ( u'%(asctime)s [%(levelname)s] (%(processName)-10s) PID:%(process)d ' u'<%(module)s> %(message)s') if options.debug: if options.logfile: logging.basicConfig(level=logging.DEBUG, format=format_str, filename=options.logfile) else: logging.basicConfig(level=logging.DEBUG, format=format_str) logging_filter = log2timeline.LoggingFilter() root_logger = logging.getLogger() root_logger.addFilter(logging_filter) elif options.logfile: logging.basicConfig(level=logging.INFO, format=format_str, filename=options.logfile) else: logging.basicConfig(level=logging.INFO, format=format_str) if not options.output: arg_parser.print_help() print u'' arg_parser.print_usage() print u'' logging.error(u'Wrong usage: need to define an output.') return False try: front_end.ParseOptions(options) front_end.SetStorageFile(options.output) except errors.BadConfigOption as exception: arg_parser.print_help() print u'' logging.error(u'{0:s}'.format(exception)) return False # Configure the foreman (monitors workers). front_end.SetShowMemoryInformation(show_memory=options.foreman_verbose) front_end.SetRunForeman(run_foreman=options.foreman_enabled) try: front_end.ProcessSource(options) logging.info(u'Processing completed.') except (KeyboardInterrupt, errors.UserAbort): logging.warning(u'Aborted by user.') return False except errors.SourceScannerError as exception: logging.warning( (u'Unable to scan for a supported filesystem with error: {0:s}\n' u'Most likely the image format is not supported by the ' u'tool.').format(exception)) return False return True