diff --git a/plaso/cli/pinfo_tool.py b/plaso/cli/pinfo_tool.py index d1fbf42c04..ae9ea39c9f 100644 --- a/plaso/cli/pinfo_tool.py +++ b/plaso/cli/pinfo_tool.py @@ -547,11 +547,13 @@ def _GenerateWinEvtProvidersReport(self, storage_reader): storage_reader (StorageReader): storage reader. """ column_titles = [ - 'Identifier', 'Log source(s)', 'Log type(s)', 'Event message file(s)'] + 'Identifier', 'Log source(s)', 'Log type(s)', 'Event message file(s)', + 'Parameter message file(s)'] self._GenerateReportHeader('winevt_providers', column_titles) attribute_names = [ - 'identifier', 'log_sources', 'log_types', 'event_message_files'] + 'identifier', 'log_sources', 'log_types', 'event_message_files', + 'parameter_message_files'] entry_format_string = self._GenerateReportEntryFormatString(attribute_names) if storage_reader.HasAttributeContainers('windows_eventlog_provider'): @@ -567,7 +569,8 @@ def _GenerateWinEvtProvidersReport(self, storage_reader): 'identifier': artifact.identifier or '', 'event_message_files': artifact.event_message_files or [], 'log_sources': artifact.log_sources or [], - 'log_types': artifact.log_types or []} + 'log_types': artifact.log_types or [], + 'parameter_message_files': artifact.parameter_message_files or []} self._output_writer.Write(entry_format_string.format( **attribute_values)) diff --git a/plaso/engine/artifact_filters.py b/plaso/engine/artifact_filters.py index 9c838466ed..ba0c6d3f21 100644 --- a/plaso/engine/artifact_filters.py +++ b/plaso/engine/artifact_filters.py @@ -59,8 +59,8 @@ def _BuildFindSpecsFromArtifact( Args: definition (artifacts.ArtifactDefinition): artifact definition. - environment_variables (list[EnvironmentVariableArtifact]): - environment variables. + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. user_accounts (list[UserAccountArtifact]): user accounts. Returns: @@ -95,8 +95,8 @@ def _BuildFindSpecsFromArtifact( key_paths_string = ', '.join(key_paths) logger.warning(( - 'Windows Registry values are not supported, extracting keys: ' - '"{0!s}"').format(key_paths_string)) + f'Windows Registry values are not supported, extracting keys: ' + f'"{key_paths_string!s}"')) for key_path in key_paths: if ArtifactDefinitionsFiltersHelper.CheckKeyCompatibility(key_path): @@ -112,9 +112,9 @@ def _BuildFindSpecsFromArtifact( find_specs.extend(specifications) else: - logger.warning( - 'Unsupported artifact definition source type: "{0:s}"'.format( - source.type_indicator)) + logger.warning(( + f'Unsupported artifact definition source type: ' + f'"{source.type_indicator:s}"')) return find_specs @@ -124,8 +124,8 @@ def _BuildFindSpecsFromGroupName( Args: group_name (str): artifact group name. - environment_variables (list[EnvironmentVariableArtifact]): - environment variables. + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. user_accounts (list[UserAccountArtifact]): user accounts. Returns: @@ -153,18 +153,17 @@ def _BuildFindSpecsFromRegistrySourceKey(self, key_path): """ find_specs = [] for key_path_glob in path_helper.PathHelper.ExpandGlobStars(key_path, '\\'): - logger.debug('building find spec from key path glob: {0:s}'.format( - key_path_glob)) + logger.debug(f'building find spec from key path glob: {key_path_glob:s}') key_path_glob_upper = key_path_glob.upper() if key_path_glob_upper.startswith( 'HKEY_LOCAL_MACHINE\\SYSTEM\\CURRENTCONTROLSET'): # Rewrite CurrentControlSet to ControlSet* for Windows NT. - key_path_glob = 'HKEY_LOCAL_MACHINE\\System\\ControlSet*{0:s}'.format( - key_path_glob[43:]) + key_path_glob = ''.join([ + 'HKEY_LOCAL_MACHINE\\System\\ControlSet*', key_path_glob[43:]]) elif key_path_glob_upper.startswith('HKEY_USERS\\%%USERS.SID%%'): - key_path_glob = 'HKEY_CURRENT_USER{0:s}'.format(key_path_glob[26:]) + key_path_glob = ''.join(['HKEY_CURRENT_USER', key_path_glob[26:]]) find_spec = dfwinreg_registry_searcher.FindSpec( key_path_glob=key_path_glob) @@ -179,8 +178,8 @@ def _BuildFindSpecsFromFileSourcePath( Args: source_path (str): file system path defined by the source. path_separator (str): file system path segment separator. - environment_variables (list[EnvironmentVariableArtifact]): - environment variables. + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. user_accounts (list[UserAccountArtifact]): user accounts. Returns: @@ -189,23 +188,21 @@ def _BuildFindSpecsFromFileSourcePath( find_specs = [] for path_glob in path_helper.PathHelper.ExpandGlobStars( source_path, path_separator): - logger.debug('building find spec from path glob: {0:s}'.format( - path_glob)) + logger.debug(f'building find spec from path glob: {path_glob:s}') for path in path_helper.PathHelper.ExpandUsersVariablePath( path_glob, path_separator, user_accounts): - logger.debug('building find spec from path: {0:s}'.format(path)) + logger.debug(f'building find spec from path: {path:s}') if '%' in path: path = path_helper.PathHelper.ExpandWindowsPath( path, environment_variables) - logger.debug('building find spec from expanded path: {0:s}'.format( - path)) + logger.debug(f'building find spec from expanded path: {path:s}') if not path.startswith(path_separator): logger.warning(( - 'The path filter must be defined as an absolute path: ' - '"{0:s}"').format(path)) + f'The path filter must be defined as an absolute path: ' + f'"{path:s}"')) continue try: @@ -214,8 +211,8 @@ def _BuildFindSpecsFromFileSourcePath( location_separator=path_separator) except ValueError as exception: logger.error(( - 'Unable to build find specification for path: "{0:s}" with ' - 'error: {1!s}').format(path, exception)) + f'Unable to build find specification for path: "{path:s}" with ' + f'error: {exception!s}')) continue find_specs.append(find_spec) @@ -230,8 +227,8 @@ def BuildFindSpecs( Args: artifact_filter_names (list[str]): names of artifact definitions that are used for filtering file system and Windows Registry key paths. - environment_variables (Optional[list[EnvironmentVariableArtifact]]): - environment variables. + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. user_accounts (Optional[list[UserAccountArtifact]]): user accounts. """ find_specs = [] @@ -240,11 +237,10 @@ def BuildFindSpecs( if not definition: definition = self._artifacts_registry.GetDefinitionByAlias(name) if not definition: - logger.debug('undefined artifact definition: {0:s}'.format(name)) + logger.debug(f'undefined artifact definition: {name:s}') continue - logger.debug('building find spec from artifact definition: {0:s}'.format( - name)) + logger.debug(f'building find spec from artifact definition: {name:s}') artifact_find_specs = self._BuildFindSpecsFromArtifact( definition, environment_variables, user_accounts) find_specs.extend(artifact_find_specs) @@ -257,8 +253,8 @@ def BuildFindSpecs( self.registry_find_specs.append(find_spec) else: - logger.warning('Unsupported find specification type: {0!s}'.format( - type(find_spec))) + type_string = type(find_spec) + logger.warning(f'Unsupported find specification type: {type_string!s}') @classmethod def CheckKeyCompatibility(cls, key_path): @@ -275,6 +271,5 @@ def CheckKeyCompatibility(cls, key_path): if key_path_upper.startswith(key_path_prefix): return True - logger.warning('Key path: "{0:s}" is currently not supported'.format( - key_path)) + logger.warning(f'Key path: "{key_path:s}" is currently not supported') return False diff --git a/plaso/formatters/winevt.py b/plaso/formatters/winevt.py index af296dfc3e..4d0cf18a14 100644 --- a/plaso/formatters/winevt.py +++ b/plaso/formatters/winevt.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """Windows EventLog custom event formatter helpers.""" +import re + from plaso.formatters import interface from plaso.formatters import logger from plaso.formatters import manager @@ -12,6 +14,8 @@ class WindowsEventLogMessageFormatterHelper( IDENTIFIER = 'windows_eventlog_message' + _PARAMETER_REGEX = re.compile(r'^%%[1-9][0-9]*$') + def __init__(self): """Initialized a indows EventLog message formatter helper.""" super(WindowsEventLogMessageFormatterHelper, self).__init__() @@ -36,8 +40,24 @@ def FormatEventValues(self, output_mediator, event_values): message_string_template = self._winevt_resources_helper.GetMessageString( provider_identifier, source_name, message_identifier, event_version) if message_string_template: - string_values = [ - string or '' for string in event_values.get('strings', [])] + string_values = [] + for string_value in event_values.get('strings', []): + if string_value is None: + string_value = '' + + elif self._PARAMETER_REGEX.match(string_value): + try: + parameter_identifier = int(string_value[2:], 10) + parameter_string = ( + self._winevt_resources_helper.GetParameterString( + provider_identifier, source_name, parameter_identifier)) + if parameter_string: + string_value = parameter_string + + except ValueError: + pass + + string_values.append(string_value) try: message_string = message_string_template.format(*string_values) diff --git a/plaso/output/winevt_rc.py b/plaso/output/winevt_rc.py index 12f0ff9feb..a3e1307e3c 100644 --- a/plaso/output/winevt_rc.py +++ b/plaso/output/winevt_rc.py @@ -342,7 +342,7 @@ class WinevtResourcesHelper(object): DEFAULT_LCID = 0x0409 # The maximum number of cached message strings - _MAXIMUM_CACHED_MESSAGE_STRINGS = 32 * 1024 + _MAXIMUM_CACHED_MESSAGE_STRINGS = 64 * 1024 _WINEVT_RC_DATABASE = 'winevt-rc.db' @@ -430,6 +430,126 @@ def _GetCachedMessageString( return message_string + def _GetEventMessageFileIdentifiers(self, message_files): + """Retrieves event message file identifiers. + + Args: + message_files (list[str]): Windows EventLog message files. + + Returns: + list[str]: message file identifiers. + """ + message_file_identifiers = [] + for windows_path in message_files or []: + path, filename = path_helper.PathHelper.GetWindowsSystemPath( + windows_path, self._environment_variables) + + lookup_path = '\\'.join([path, filename]).lower() + message_file_identifier = self._windows_eventlog_message_files.get( + lookup_path, None) + if message_file_identifier: + message_file_identifier = message_file_identifier.CopyToString() + message_file_identifiers.append(message_file_identifier) + + mui_filename = f'{filename:s}.mui' + lookup_path = '\\'.join([path, self._language_tag, mui_filename]).lower() + message_file_identifier = self._windows_eventlog_message_files.get( + lookup_path, None) + if message_file_identifier: + message_file_identifier = message_file_identifier.CopyToString() + message_file_identifiers.append(message_file_identifier) + + return message_file_identifiers + + def _GetMappedMessageIdentifier( + self, storage_reader, provider_identifier, message_identifier, + event_version): + """Retrieves a WEVT_TEMPLATE mapped message identifier if available. + + Args: + storage_reader (StorageReader): storage reader. + provider_identifier (str): EventLog provider identifier. + message_identifier (int): message identifier. + event_version (int): event version or None if not set. + + Returns: + int: message identifier. + """ + # Map the event identifier to a message identifier as defined by the + # WEVT_TEMPLATE event definition. + if provider_identifier and storage_reader.HasAttributeContainers( + 'windows_wevt_template_event'): + # TODO: add message_file_identifiers to filter_expression + filter_expression = ( + f'provider_identifier == "{provider_identifier:s}" and ' + f'identifier == {message_identifier:d}') + if event_version is not None: + filter_expression = ( + f'{filter_expression:s} and version == {event_version:d}') + + for event_definition in storage_reader.GetAttributeContainers( + 'windows_wevt_template_event', filter_expression=filter_expression): + logger.debug(( + f'Message: 0x{message_identifier:08x} of provider: ' + f'{provider_identifier:s} maps to: ' + f'0x{event_definition.message_identifier:08x}')) + + return event_definition.message_identifier + + return message_identifier + + def _GetMessageStrings( + self, storage_reader, message_file_identifiers, message_identifier): + """Retrieves message strings. + + Args: + storage_reader (StorageReader): storage reader. + message_file_identifiers (list[str]): message file identifiers. + message_identifier (int): message identifier. + + Returns: + list[str]: message strings. + """ + message_strings = [] + + # TODO: add message_file_identifiers to filter_expression + filter_expression = ( + f'language_identifier == {self._lcid:d} and ' + f'message_identifier == {message_identifier:d}') + + for message_string in storage_reader.GetAttributeContainers( + 'windows_eventlog_message_string', + filter_expression=filter_expression): + identifier = message_string.GetMessageFileIdentifier() + identifier = identifier.CopyToString() + if identifier in message_file_identifiers: + message_strings.append(message_string) + + return message_strings + + def _GetWindowsEventLogProvider(self, provider_identifier, log_source): + """Retrieves a Windows EventLog provider. + + Args: + provider_identifier (str): EventLog provider identifier. + log_source (str): EventLog source, such as "Application Error". + + Returns: + tuple[WindowsEventLogProviderArtifact, str]: Windows EventLog provider + or None if not available, and provider lookup key. + """ + provider = None + + if provider_identifier: + lookup_key = provider_identifier.lower() + provider = self._windows_eventlog_providers.get(lookup_key, None) + + if not provider: + lookup_key = log_source.lower() + provider = self._windows_eventlog_providers.get(lookup_key, None) + + return provider, lookup_key + def _GetWinevtRcDatabaseReader(self): """Opens the Windows EventLog resource database reader. @@ -506,10 +626,10 @@ def _ReadWindowsEventLogMessageFiles(self, storage_reader): self._windows_eventlog_message_files[lookup_path] = ( message_file_identifier) - def _ReadWindowsEventLogMessageString( + def _ReadEventMessageString( self, storage_reader, provider_identifier, log_source, message_identifier, event_version): - """Reads an Windows EventLog message string. + """Reads an event message string. Args: storage_reader (StorageReader): storage reader. @@ -530,17 +650,8 @@ def _ReadWindowsEventLogMessageString( if self._windows_eventlog_message_files is None: self._ReadWindowsEventLogMessageFiles(storage_reader) - provider = None - - if provider_identifier: - lookup_key = provider_identifier.lower() - provider = self._windows_eventlog_providers.get(lookup_key, None) - - if not provider: - lookup_key = log_source.lower() - provider = self._windows_eventlog_providers.get(lookup_key, None) - - provider = self._windows_eventlog_providers.get(lookup_key, None) + provider, provider_lookup_key = self._GetWindowsEventLogProvider( + provider_identifier, log_source) if not provider: return None @@ -550,73 +661,81 @@ def _ReadWindowsEventLogMessageString( original_message_identifier = message_identifier - # Map the event identifier to a message identifier as defined by the - # WEVT_TEMPLATE event definition. - if provider_identifier and storage_reader.HasAttributeContainers( - 'windows_wevt_template_event'): - # TODO: add message_file_identifiers to filter_expression - filter_expression = ( - f'provider_identifier == "{provider_identifier:s}" and ' - f'identifier == {message_identifier:d}') - if event_version is not None: - filter_expression = ( - f'{filter_expression:s} and version == {event_version:d}') + # TODO: pass message_file_identifiers + message_identifier = self._GetMappedMessageIdentifier( + storage_reader, provider_identifier, message_identifier, event_version) - for event_definition in storage_reader.GetAttributeContainers( - 'windows_wevt_template_event', filter_expression=filter_expression): - logger.debug(( - f'Message: 0x{message_identifier:08x} of provider: ' - f'{provider_identifier:s} maps to: ' - f'0x{event_definition.message_identifier:08x}')) - message_identifier = event_definition.message_identifier - break + message_file_identifiers = self._GetEventMessageFileIdentifiers( + provider.event_message_files) + if not message_file_identifiers: + logger.warning(( + f'No event message file for identifier: 0x{message_identifier:08x} ' + f'(original: 0x{original_message_identifier:08x}) ' + f'of provider: {provider_lookup_key:s}')) + return None - message_file_identifiers = [] - for windows_path in provider.event_message_files or []: - path, filename = path_helper.PathHelper.GetWindowsSystemPath( - windows_path, self._environment_variables) + message_strings = self._GetMessageStrings( + storage_reader, message_file_identifiers, message_identifier) + if not message_strings: + logger.warning(( + f'No message string for identifier: 0x{message_identifier:08x} ' + f'(original: 0x{original_message_identifier:08x}) ' + f'of provider: {provider_lookup_key:s}')) + return None - lookup_path = '\\'.join([path, filename]).lower() - message_file_identifier = self._windows_eventlog_message_files.get( - lookup_path, None) - if message_file_identifier: - message_file_identifier = message_file_identifier.CopyToString() - message_file_identifiers.append(message_file_identifier) + return message_strings[0].string - mui_filename = f'{filename:s}.mui' - lookup_path = '\\'.join([path, self._language_tag, mui_filename]).lower() - message_file_identifier = self._windows_eventlog_message_files.get( - lookup_path, None) - if message_file_identifier: - message_file_identifier = message_file_identifier.CopyToString() - message_file_identifiers.append(message_file_identifier) + def _ReadParameterMessageString( + self, storage_reader, provider_identifier, log_source, + message_identifier): + """Reads a parameter message string. - if not message_file_identifiers: - logger.warning(( - f'No message file for message: 0x{message_identifier:08x} ' - f'(original: 0x{original_message_identifier:08x}) ' - f'of provider: {lookup_key:s}')) + Args: + storage_reader (StorageReader): storage reader. + provider_identifier (str): EventLog provider identifier. + log_source (str): EventLog source, such as "Application Error". + message_identifier (int): message identifier. + + Returns: + str: parameter string or None if not available. + """ + if self._environment_variables is None: + self._ReadEnvironmentVariables(storage_reader) + + if self._windows_eventlog_providers is None: + self._ReadWindowsEventLogProviders(storage_reader) + + if self._windows_eventlog_message_files is None: + self._ReadWindowsEventLogMessageFiles(storage_reader) + + provider, provider_lookup_key = self._GetWindowsEventLogProvider( + provider_identifier, log_source) + if not provider: return None - message_strings = [] - # TODO: add message_file_identifiers to filter_expression - filter_expression = ( - f'language_identifier == {self._lcid:d} and ' - f'message_identifier == {message_identifier:d}') + if not storage_reader.HasAttributeContainers( + 'windows_eventlog_message_string'): + return None - for message_string in storage_reader.GetAttributeContainers( - 'windows_eventlog_message_string', - filter_expression=filter_expression): - identifier = message_string.GetMessageFileIdentifier() - identifier = identifier.CopyToString() - if identifier in message_file_identifiers: - message_strings.append(message_string) + message_file_identifiers = self._GetEventMessageFileIdentifiers( + provider.parameter_message_files) + if not message_file_identifiers: + message_file_identifiers = self._GetEventMessageFileIdentifiers( + provider.event_message_files) + + if not message_file_identifiers: + logger.warning(( + f'No parameter message file for identifier: ' + f'0x{message_identifier:08x} of provider: {provider_lookup_key:s}')) + return None + + message_strings = self._GetMessageStrings( + storage_reader, message_file_identifiers, message_identifier) if not message_strings: logger.warning(( - f'No message string for message: 0x{message_identifier:08x} ' - f'(original: 0x{original_message_identifier:08x}) ' - f'of provider: {lookup_key:s}')) + f'No parameter string for identifier: 0x{message_identifier:08x} ' + f'of provider: {provider_lookup_key:s}')) return None return message_strings[0].string @@ -659,7 +778,7 @@ def GetMessageString( # TODO: change this logic. if self._storage_reader and self._storage_reader.HasAttributeContainers( 'windows_eventlog_provider'): - message_string = self._ReadWindowsEventLogMessageString( + message_string = self._ReadEventMessageString( self._storage_reader, provider_identifier, log_source, message_identifier, event_version) else: @@ -672,3 +791,29 @@ def GetMessageString( message_string) return message_string + + def GetParameterString( + self, provider_identifier, log_source, message_identifier): + """Retrieves a specific Windows EventLog parameter string. + + Args: + provider_identifier (str): EventLog provider identifier. + log_source (str): EventLog source, such as "Application Error". + message_identifier (int): parameter identifier. + + Returns: + str: parameter string or None if not available. + """ + message_string = self._GetCachedMessageString( + provider_identifier, log_source, message_identifier, None) + if not message_string: + message_string = self._ReadParameterMessageString( + self._storage_reader, provider_identifier, log_source, + message_identifier) + + if message_string: + self._CacheMessageString( + provider_identifier, log_source, message_identifier, + None, message_string) + + return message_string diff --git a/plaso/parsers/firefox_cache.py b/plaso/parsers/firefox_cache.py index 1ccf53dffe..4bf78646f0 100644 --- a/plaso/parsers/firefox_cache.py +++ b/plaso/parsers/firefox_cache.py @@ -92,7 +92,7 @@ def _ParseHTTPHeaders(self, http_headers_data, offset, display_name): try: http_header_start = header_string.index('request-method') except ValueError: - logger.debug('No request method in header: "{0:s}"'.format(header_string)) + logger.debug(f'No request method in header: "{header_string:s}"') return None, None # HTTP request and response headers. @@ -105,14 +105,13 @@ def _ParseHTTPHeaders(self, http_headers_data, offset, display_name): if request_method not in self._REQUEST_METHODS: logger.debug(( - '[{0:s}] {1:s}:{2:d}: Unknown HTTP method \'{3:s}\'. Response ' - 'headers: \'{4:s}\'').format( - self.NAME, display_name, offset, request_method, header_string)) + f'[{self.NAME:s}] {display_name:s}:{offset:d}: Unknown HTTP method ' + f'"{request_method,:s}". Response headers: "{header_string:s}"')) try: response_head_start = http_headers.index('response-head') except ValueError: - logger.debug('No response head in header: "{0:s}"'.format(header_string)) + logger.debug(f'No response head in header: "{header_string:s}"') return request_method, None # HTTP response headers. @@ -132,9 +131,8 @@ def _ParseHTTPHeaders(self, http_headers_data, offset, display_name): if not response_code.startswith('HTTP'): logger.debug(( - '[{0:s}] {1:s}:{2:d}: Could not determine HTTP response code. ' - 'Response headers: \'{3:s}\'.').format( - self.NAME, display_name, offset, header_string)) + f'[{self.NAME:s}] {display_name:s}:{offset:d}: Could not determine ' + f'HTTP response code. Response headers: "{header_string:s}".')) return request_method, response_code @@ -156,7 +154,7 @@ class FirefoxCacheParser( _MINIMUM_BLOCK_SIZE = 256 # Name of a cache data file that contains metadata. - _CACHE_FILENAME_RE = re.compile(r'^[0-9A-Fa-f]{5}m[0-9]{2}$') + _CACHE_FILENAME_REGEX = re.compile(r'^[0-9A-Fa-f]{5}m[0-9]{2}$') FIREFOX_CACHE_CONFIG = collections.namedtuple( 'firefox_cache_config', @@ -206,8 +204,8 @@ def _GetFirefoxConfig(self, file_object, display_name): return self.FIREFOX_CACHE_CONFIG(block_size, offset) except IOError: - logger.debug('[{0:s}] {1:s}:{2:d}: Invalid record.'.format( - self.NAME, display_name, offset)) + logger.debug( + f'[{self.NAME:s}] {display_name:s}:{offset:d}: Invalid record.') raise errors.WrongParser( 'Could not find a valid cache record. Not a Firefox cache file.') @@ -241,9 +239,9 @@ def _ParseCacheEntry( cache_entry_header, header_data_size = self._ReadStructureFromFileObject( file_object, file_offset, cache_entry_header_map) except (ValueError, errors.ParseError) as exception: - raise errors.ParseError( - 'Unable to parse Firefox cache entry header with error: {0!s}'.format( - exception)) + raise errors.ParseError(( + f'Unable to parse Firefox cache entry header with error: ' + f'{exception!s}')) if not self._ValidateCacheEntryHeader(cache_entry_header): # Skip to the next block potentially containing a cache entry. @@ -269,8 +267,8 @@ def _ParseCacheEntry( context=context) except (ValueError, errors.ParseError) as exception: raise errors.ParseError(( - 'Unable to map cache entry body data at offset: 0x{0:08x} with ' - 'error: {1!s}').format(file_offset, exception)) + f'Unable to map cache entry body data at offset: ' + f'0x{file_offset:08x} with error: {exception!s}')) file_offset += cache_entry_header.request_size @@ -295,9 +293,9 @@ def _ParseCacheEntry( event_data.request_size = cache_entry_header.request_size event_data.response_code = response_code event_data.url = cache_entry_body.request - event_data.version = '{0:d}.{1:d}'.format( - cache_entry_header.major_format_version, - cache_entry_header.minor_format_version) + event_data.version = '.'.join([ + f'{cache_entry_header.major_format_version:d}', + f'{cache_entry_header.minor_format_version:d}']) if cache_entry_header.last_modified_time: event_data.last_modified_time = dfdatetime_posix_time.PosixTime( @@ -339,7 +337,7 @@ def ParseFileObject(self, parser_mediator, file_object): """ filename = parser_mediator.GetFilename() - if (not self._CACHE_FILENAME_RE.match(filename) and + if (not self._CACHE_FILENAME_REGEX.match(filename) and not filename.startswith('_CACHE_00')): raise errors.WrongParser('Not a Firefox cache1 file.') @@ -357,8 +355,8 @@ def ParseFileObject(self, parser_mediator, file_object): except IOError: file_offset = file_object.get_offset() - self._MINIMUM_BLOCK_SIZE logger.debug(( - '[{0:s}] Invalid cache record in file: {1:s} at offset: ' - '{2:d}.').format(self.NAME, display_name, file_offset)) + f'[{self.NAME:s}] Invalid cache record in file: {display_name:s} ' + f'at offset: 0x{file_offset:08x}.')) class FirefoxCache2Parser( @@ -372,7 +370,7 @@ class FirefoxCache2Parser( os.path.dirname(__file__), 'firefox_cache.yaml') # Cache version 2 filenames are SHA-1 hex digests. - _CACHE_FILENAME_RE = re.compile(r'^[0-9A-Fa-f]{40}$') + _CACHE_FILENAME_REGEX = re.compile(r'^[0-9A-Fa-f]{40}$') _CHUNK_SIZE = 512 * 1024 @@ -410,8 +408,7 @@ def _GetCacheFileMetadataHeaderOffset(self, file_object): file_object, file_offset, metadata_size_map) except (ValueError, errors.ParseError) as exception: raise errors.WrongParser( - 'Unable to parse cache file metadata size with error: {0!s}'.format( - exception)) + f'Unable to parse cache file metadata size with error: {exception!s}') # Firefox splits the content into chunks. number_of_chunks, remainder = divmod(metadata_size, self._CHUNK_SIZE) @@ -450,7 +447,7 @@ def ParseFileObject(self, parser_mediator, file_object): WrongParser: when the file cannot be parsed. """ filename = parser_mediator.GetFilename() - if not self._CACHE_FILENAME_RE.match(filename): + if not self._CACHE_FILENAME_REGEX.match(filename): raise errors.WrongParser('Not a Firefox cache2 file.') file_offset = self._GetCacheFileMetadataHeaderOffset(file_object) @@ -462,8 +459,8 @@ def ParseFileObject(self, parser_mediator, file_object): file_object, file_offset, file_metadata_header_map) except (ValueError, errors.ParseError) as exception: raise errors.WrongParser(( - 'Unable to parse Firefox cache2 file metadata header with error: ' - '{0!s}').format(exception)) + f'Unable to parse Firefox cache2 file metadata header with error: ' + f'{exception!s}')) if not self._ValidateCacheFileMetadataHeader(file_metadata_header): raise errors.WrongParser('Not a valid Firefox cache2 record.') diff --git a/plaso/parsers/mediator.py b/plaso/parsers/mediator.py index e733671e07..ae586f1a85 100644 --- a/plaso/parsers/mediator.py +++ b/plaso/parsers/mediator.py @@ -141,6 +141,51 @@ def _CreateEnvironmentVariablesPerPathSpec(self, system_configurations): self._environment_variables_per_path_spec[path_spec.parent] = ( system_configuration.environment_variables) + def _CreateEventLogMessageFileLookupTables(self, environment_variables): + """Creates the Windows EventLog message file lookup tables. + + Args: + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. + """ + self._windows_event_log_providers_per_filename = {} + self._windows_event_log_providers_per_path = {} + + for provider in self._windows_event_log_providers: + self._FillEventLogMessageFileLookupTablesWithMessageFiles( + environment_variables, provider, provider.event_message_files) + + self._FillEventLogMessageFileLookupTablesWithMessageFiles( + environment_variables, provider, provider.parameter_message_files) + + def _FillEventLogMessageFileLookupTablesWithMessageFiles( + self, environment_variables, event_log_provider, message_files): + """Fills the Windows EventLog message file lookup tables with message files. + + Args: + environment_variables (list[EnvironmentVariableArtifact]): environment + variables. + event_log_provider (WindowsEventLogProviderArtifact): Windows EventLog + provider. + message_files (list[str]): message file paths. + """ + for windows_path in message_files or []: + path, filename = path_helper.PathHelper.GetWindowsSystemPath( + windows_path, environment_variables) + path = path.lower() + filename = filename.lower() + + # Use the path prefix as the key to handle language specific EventLog + # message files. + if path not in self._windows_event_log_providers_per_path: + self._windows_event_log_providers_per_path[path] = {} + + # Note that multiple providers can share EventLog message files. + self._windows_event_log_providers_per_filename[filename] = ( + event_log_provider) + self._windows_event_log_providers_per_path[path][filename] = ( + event_log_provider) + def _GetEnvironmentVariablesByPathSpec(self, path_spec): """Retrieves the environment variables for a specific path specification. @@ -365,28 +410,12 @@ def GetWindowsEventLogMessageFile(self): if (self._windows_event_log_providers_per_filename is None and self._windows_event_log_providers_per_path is None): + # TODO: improve this by building lookup tables per volume path spec environment_variables = self._GetEnvironmentVariablesByPathSpec(path_spec) if not environment_variables: return None - self._windows_event_log_providers_per_filename = {} - self._windows_event_log_providers_per_path = {} - - for provider in self._windows_event_log_providers: - for windows_path in provider.event_message_files or []: - path, filename = path_helper.PathHelper.GetWindowsSystemPath( - windows_path, environment_variables) - path = path.lower() - filename = filename.lower() - - # Use the path prefix as the key to handle language specific EventLog - # message files. - if path not in self._windows_event_log_providers_per_path: - self._windows_event_log_providers_per_path[path] = {} - - # Note that multiple providers can share EventLog message files. - self._windows_event_log_providers_per_filename[filename] = provider - self._windows_event_log_providers_per_path[path][filename] = provider + self._CreateEventLogMessageFileLookupTables(environment_variables) relative_path = path_helper.PathHelper.GetRelativePathForPathSpec(path_spec) lookup_path = relative_path.lower() diff --git a/tests/cli/pinfo_tool.py b/tests/cli/pinfo_tool.py index a492fe4b5c..2ef2f09a1b 100644 --- a/tests/cli/pinfo_tool.py +++ b/tests/cli/pinfo_tool.py @@ -419,8 +419,9 @@ def testGenerateWinEvtProvidersReportAsMarkdown(self): storage_reader.Close() expected_output = [ - 'Identifier | Log source(s) | Log type(s) | Event message file(s)', - '--- | --- | --- | ---', + ('Identifier | Log source(s) | Log type(s) | Event message file(s) | ' + 'Parameter message file(s)'), + '--- | --- | --- | --- | ---', ''] output = output_writer.ReadOutput() @@ -446,7 +447,8 @@ def testGenerateWinEvtProvidersReportAsText(self): storage_reader.Close() expected_output = [ - 'Identifier\tLog source(s)\tLog type(s)\tEvent message file(s)', + ('Identifier\tLog source(s)\tLog type(s)\tEvent message file(s)\t' + 'Parameter message file(s)'), ''] output = output_writer.ReadOutput()