diff --git a/config/dpkg/changelog b/config/dpkg/changelog index edc9342b49..e856ff7b76 100644 --- a/config/dpkg/changelog +++ b/config/dpkg/changelog @@ -2,4 +2,4 @@ python-plaso (1.3.1-1) unstable; urgency=low * Auto-generated - -- Log2Timeline Thu, 17 Sep 2015 19:44:25 +0200 + -- Log2Timeline Sat, 19 Sep 2015 07:45:06 +0200 diff --git a/docs/plaso.parsers.winreg_plugins.rst b/docs/plaso.parsers.winreg_plugins.rst index b4a4a8af07..4117f3e046 100644 --- a/docs/plaso.parsers.winreg_plugins.rst +++ b/docs/plaso.parsers.winreg_plugins.rst @@ -100,6 +100,14 @@ plaso.parsers.winreg_plugins.outlook module :undoc-members: :show-inheritance: +plaso.parsers.winreg_plugins.programscache module +------------------------------------------------- + +.. automodule:: plaso.parsers.winreg_plugins.programscache + :members: + :undoc-members: + :show-inheritance: + plaso.parsers.winreg_plugins.run module --------------------------------------- diff --git a/plaso/__init__.py b/plaso/__init__.py index 1cd0d5d4bf..8e2c79f110 100644 --- a/plaso/__init__.py +++ b/plaso/__init__.py @@ -3,7 +3,7 @@ __version__ = '1.3.1' VERSION_DEV = True -VERSION_DATE = '20150917' +VERSION_DATE = '20150919' def GetVersion(): diff --git a/plaso/events/windows_events.py b/plaso/events/windows_events.py index be9d19069a..ce2daba658 100644 --- a/plaso/events/windows_events.py +++ b/plaso/events/windows_events.py @@ -6,7 +6,13 @@ class WindowsDistributedLinkTrackingCreationEvent(time_events.UUIDTimeEvent): - """Convenience class for a Windows distributed link creation event.""" + """Convenience class for a Windows distributed link creation event. + + Attributes: + origin: a string containing the origin of the event (event source). + E.g. the path of the corresponding LNK file or file reference + MFT entry with the corresponding NTFS $OBJECT_ID attribute. + """ DATA_TYPE = 'windows:distributed_link_tracking:creation' @@ -14,8 +20,10 @@ def __init__(self, uuid, origin): """Initializes an event object. Args: - uuid: A uuid object (instance of uuid.UUID). - origin: A string containing the origin of the event (event source). + uuid: an uuid object (instance of uuid.UUID). + origin: a string containing the origin of the event (event source). + E.g. the path of the corresponding LNK file or file reference + MFT entry with the corresponding NTFS $OBJECT_ID attribute. """ super(WindowsDistributedLinkTrackingCreationEvent, self).__init__( uuid, eventdata.EventTimestamp.CREATION_TIME) @@ -36,16 +44,13 @@ def __init__( Args: filetime: the FILETIME timestamp value. key_path: the Windows Registry key path. - values_dict: Dictionary object containing values of the key. - usage: Optional description of the usage of the time value. - The default is None. - offset: Optional (data) offset of the Registry key or value. - The default is None. - registry_file_type: Optional string containing the Windows Registry file - type, e.g. NTUSER, SOFTWARE. The default is None. - source_append: Optional string to append to the source_long of the event. - The default is None. - urls: Optional list of URLs. The default is None. + values_dict: dictionary object containing values of the key. + usage: optional description of the usage of the time value. + offset: optional (data) offset of the Registry key or value. + registry_file_type: optional string containing the Windows Registry file + type, e.g. NTUSER, SOFTWARE. + source_append: optional string to append to the source_long of the event. + urls: optional list of URLs. """ if usage is None: usage = eventdata.EventTimestamp.WRITTEN_TIME @@ -81,7 +86,6 @@ class WindowsRegistryInstallationEvent(time_events.PosixTimeEvent): service_pack: string containing service pack. version: string containing the version. """ - DATA_TYPE = 'windows:registry:installation' def __init__( @@ -106,24 +110,67 @@ def __init__( self.version = version +class WindowsRegistryListEvent(time_events.FiletimeEvent): + """Convenience class for a list retrieved from the Registry e.g. MRU. + + Attributes: + key_path: string containing the Windows Registry key path. + list_name: string containing the name of the list. + list_values: string containing the list values. + value_name: string containing the Windows Registry value name. + """ + DATA_TYPE = 'windows:registry:list' + + def __init__( + self, filetime, key_path, list_name, list_values, + timestamp_description=None, value_name=None): + """Initializes a Windows registry event. + + Args: + filetime: the FILETIME timestamp value. + key_path: string containing the Windows Registry key path. + list_name: string containing the name of the list. + list_values: string containing the list values. + timestamp_description: optional usage string for the timestamp value. + value_name: optional string containing the Windows Registry value name. + """ + if timestamp_description is None: + timestamp_description = eventdata.EventTimestamp.WRITTEN_TIME + + super(WindowsRegistryListEvent, self).__init__( + filetime, timestamp_description) + + self.key_path = key_path + self.list_name = list_name + self.list_values = list_values + self.value_name = value_name + + class WindowsRegistryServiceEvent(WindowsRegistryEvent): - """Convenience class for service entries retrieved from the registry.""" + """Convenience class for service information retrieved from the Registry.""" DATA_TYPE = 'windows:registry:service' class WindowsVolumeCreationEvent(time_events.FiletimeEvent): - """Convenience class for a Windows volume creation event.""" + """Convenience class for a Windows volume creation event. + Attributes: + device_path: a string containing the volume device path. + serial_number: a string containing the volume serial number. + origin: a string containing the origin of the event (event source). + E.g. corresponding Prefetch file name. + """ DATA_TYPE = 'windows:volume:creation' def __init__(self, filetime, device_path, serial_number, origin): """Initializes an event object. Args: - filetime: The FILETIME timestamp value. - device_path: A string containing the volume device path. - serial_number: A string containing the volume serial number. - origin: A string containing the origin of the event (event source). + filetime: the FILETIME timestamp value. + device_path: a string containing the volume device path. + serial_number: a string containing the volume serial number. + origin: a string containing the origin of the event (event source). + E.g. corresponding Prefetch file name. """ super(WindowsVolumeCreationEvent, self).__init__( filetime, eventdata.EventTimestamp.CREATION_TIME) diff --git a/plaso/formatters/windows.py b/plaso/formatters/windows.py index 62fe044de3..e5c180f3f2 100644 --- a/plaso/formatters/windows.py +++ b/plaso/formatters/windows.py @@ -47,6 +47,21 @@ class WindowsRegistryInstallationEventFormatter( SOURCE_SHORT = u'LOG' +class WindowsRegistryListEventFormatter(interface.ConditionalEventFormatter): + """Formatter for a Windows list event e.g. MRU or Jump list.""" + + DATA_TYPE = u'windows:registry:list' + + FORMAT_STRING_PIECES = [ + u'Key: {key_path}', + u'Value: {value_name}', + u'List: {list_name}', + u'[{list_values}]'] + + SOURCE_LONG = u'System' + SOURCE_SHORT = u'LOG' + + class WindowsVolumeCreationEventFormatter(interface.ConditionalEventFormatter): """Formatter for a Windows volume creation event.""" @@ -67,5 +82,6 @@ class WindowsVolumeCreationEventFormatter(interface.ConditionalEventFormatter): manager.FormattersManager.RegisterFormatters([ WindowsDistributedLinkTrackingCreationEventFormatter, + WindowsRegistryListEventFormatter, WindowsRegistryInstallationEventFormatter, WindowsVolumeCreationEventFormatter]) diff --git a/plaso/parsers/asl.py b/plaso/parsers/asl.py index c0260b11b9..9abe76d6fe 100644 --- a/plaso/parsers/asl.py +++ b/plaso/parsers/asl.py @@ -274,10 +274,11 @@ def ReadAslEvent(self, file_object, parser_mediator, offset): while tam_fields > 0: try: raw_field = file_object.read(8) - except (IOError, construct.FieldError) as exception: + except IOError as exception: logging.warning( - u'Unable to parse ASL event with error: {0:d}'.format(exception)) + u'Unable to read ASL event with error: {0:d}'.format(exception)) return None, None + try: # Try to read as a String. field = self.ASL_STRING.parse(raw_field) diff --git a/plaso/parsers/mediator.py b/plaso/parsers/mediator.py index a32555f127..e1550f36c2 100644 --- a/plaso/parsers/mediator.py +++ b/plaso/parsers/mediator.py @@ -250,6 +250,7 @@ def ProcessEvent( file entry set in the mediator. query: Optional query string. The default is None. """ + # TODO: rename this to event_object.parser_chain or equivalent. if not getattr(event_object, u'parser', None) and parser_chain: event_object.parser = parser_chain diff --git a/plaso/parsers/winreg_plugins/__init__.py b/plaso/parsers/winreg_plugins/__init__.py index 25ebd03136..d615d8b9e2 100644 --- a/plaso/parsers/winreg_plugins/__init__.py +++ b/plaso/parsers/winreg_plugins/__init__.py @@ -12,6 +12,7 @@ from plaso.parsers.winreg_plugins import msie_zones from plaso.parsers.winreg_plugins import officemru from plaso.parsers.winreg_plugins import outlook +from plaso.parsers.winreg_plugins import programscache from plaso.parsers.winreg_plugins import run from plaso.parsers.winreg_plugins import sam_users from plaso.parsers.winreg_plugins import services diff --git a/plaso/parsers/winreg_plugins/programscache.py b/plaso/parsers/winreg_plugins/programscache.py new file mode 100644 index 0000000000..80465518f8 --- /dev/null +++ b/plaso/parsers/winreg_plugins/programscache.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +"""Windows Registry plugin to parse the Explorer ProgramsCache key.""" + +import construct + +from plaso.events import windows_events +from plaso.parsers import winreg +from plaso.parsers.shared import shell_items +from plaso.parsers.winreg_plugins import interface + + +class ExplorerProgramsCachePlugin(interface.WindowsRegistryPlugin): + """Class that parses the Explorer ProgramsCache Registry data.""" + + NAME = u'explorer_programscache' + DESCRIPTION = u'Parser for Explorer ProgramsCache Registry data.' + + REG_TYPE = u'NTUSER' + + REG_KEYS = [ + (u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage'), + (u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage2')] + + URLS = [ + (u'https://github.com/libyal/assorted/blob/master/documentation/' + u'Jump%20lists%20format.asciidoc#4-explorer-programscache-registry-' + u'values')] + + _HEADER_STRUCT = construct.Struct( + u'programscache_header', + construct.ULInt32(u'format_version')) + + _ENTRY_HEADER_STRUCT = construct.Struct( + u'programscache_entry_header', + construct.ULInt32(u'data_size')) + + _ENTRY_FOOTER_STRUCT = construct.Struct( + u'programscache_entry_footer', + construct.Byte(u'sentinel')) + + def _ParseValueData(self, parser_mediator, registry_key, registry_value): + """Extracts event objects from a Explorer ProgramsCache value data. + + Args: + parser_mediator: A parser mediator object (instance of ParserMediator). + registry_key: A Windows Registry key (instance of + dfwinreg.WinRegistryKey). + registry_value: A Windows Registry value (instance of + dfwinreg.WinRegistryValue). + """ + value_data = registry_value.data + if len(value_data) < 4: + return + + try: + header_struct = self._HEADER_STRUCT.parse(value_data) + except construct.FieldError as exception: + parser_mediator.ProduceParseError( + u'unable to parse header with error: {0:s}'.format( + exception)) + return + + format_version = header_struct.get(u'format_version') + if format_version not in [0x01, 0x09, 0x0c, 0x13]: + parser_mediator.ProduceParseError( + u'unsupported format version: 0x{0:08x}'.format(format_version)) + return + + if format_version == 0x01: + value_data_offset = 8 + + elif format_version == 0x09: + value_data_offset = 6 + + else: + # TODO: get known folder identifier? + value_data_offset = 20 + + if format_version == 0x09: + sentinel = 0 + else: + try: + entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( + value_data[value_data_offset:]) + except construct.FieldError as exception: + parser_mediator.ProduceParseError(( + u'unable to parse sentinel at offset: 0x{0:08x} ' + u'with error: {1:s}').format(value_data_offset, exception)) + return + + value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() + + sentinel = entry_footer_struct.get(u'sentinel') + + link_targets = [] + while sentinel in [0x00, 0x01]: + try: + entry_header_struct = self._ENTRY_HEADER_STRUCT.parse( + value_data[value_data_offset:]) + except construct.FieldError as exception: + parser_mediator.ProduceParseError(( + u'unable to parse entry header at offset: 0x{0:08x} ' + u'with error: {1:s}').format(value_data_offset, exception)) + break + + value_data_offset += self._ENTRY_HEADER_STRUCT.sizeof() + + entry_data_size = entry_header_struct.get(u'data_size') + + display_name = u'{0:s} {1:s}'.format( + registry_key.path, registry_value.name) + + shell_items_parser = shell_items.ShellItemsParser(display_name) + shell_items_parser.UpdateChainAndParse( + parser_mediator, value_data[value_data_offset:], None, + codepage=parser_mediator.codepage) + + link_target = shell_items_parser.CopyToPath() + link_targets.append(link_target) + + value_data_offset += entry_data_size + + try: + entry_footer_struct = self._ENTRY_FOOTER_STRUCT.parse( + value_data[value_data_offset:]) + except construct.FieldError as exception: + parser_mediator.ProduceParseError(( + u'unable to parse entry footer at offset: 0x{0:08x} ' + u'with error: {1:s}').format(value_data_offset, exception)) + break + + value_data_offset += self._ENTRY_FOOTER_STRUCT.sizeof() + + sentinel = entry_footer_struct.get(u'sentinel') + + # TODO: recover remaining items. + + list_name = registry_value.name + list_values = u' '.join([ + u'{0:d}: {1:s}'.format(index, link_target) + for index, link_target in enumerate(link_targets)]) + + event_object = windows_events.WindowsRegistryListEvent( + registry_key.last_written_time, registry_key.path, + list_name, list_values, value_name=registry_value.name) + + parser_mediator.ProduceEvent(event_object) + + def GetEntries(self, parser_mediator, registry_key, **kwargs): + """Extracts event objects from a Explorer ProgramsCache Registry key. + + Args: + parser_mediator: A parser mediator object (instance of ParserMediator). + registry_key: A Windows Registry key (instance of + dfwinreg.WinRegistryKey). + """ + registry_value = registry_key.GetValueByName(u'ProgramsCache') + if registry_value: + self._ParseValueData(parser_mediator, registry_key, registry_value) + + registry_value = registry_key.GetValueByName(u'ProgramsCacheSMP') + if registry_value: + self._ParseValueData(parser_mediator, registry_key, registry_value) + + registry_value = registry_key.GetValueByName(u'ProgramsCacheTBP') + if registry_value: + self._ParseValueData(parser_mediator, registry_key, registry_value) + + values_dict = {} + for registry_value in registry_key.GetValues(): + # Ignore the default value. + if not registry_value.name or registry_value.name in [ + u'ProgramsCache', u'ProgramsCacheSMP', u'ProgramsCacheTBP']: + continue + + # Ignore any value that is empty or that does not contain a string. + if not registry_value.data or not registry_value.DataIsString(): + continue + + values_dict[registry_value.name] = registry_value.data + + event_object = windows_events.WindowsRegistryEvent( + registry_key.last_written_time, registry_key.path, values_dict, + offset=registry_key.offset) + + parser_mediator.ProduceEvent(event_object) + + +winreg.WinRegistryParser.RegisterPlugin(ExplorerProgramsCachePlugin) diff --git a/tests/parsers/winreg_plugins/programscache.py b/tests/parsers/winreg_plugins/programscache.py new file mode 100644 index 0000000000..8c9ad73046 --- /dev/null +++ b/tests/parsers/winreg_plugins/programscache.py @@ -0,0 +1,154 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Tests for the Explorer ProgramsCache Windows Registry plugin.""" + +import unittest + +from plaso.formatters import winreg as _ # pylint: disable=unused-import +from plaso.lib import eventdata +from plaso.lib import timelib +from plaso.parsers.winreg_plugins import programscache + +from tests.parsers.winreg_plugins import test_lib + + +class ExplorerProgramCachePluginTest(test_lib.RegistryPluginTestCase): + """Tests for the Explorer ProgramsCache Windows Registry plugin.""" + + def setUp(self): + """Sets up the needed objects used throughout the test.""" + self._plugin = programscache.ExplorerProgramsCachePlugin() + + def testProcessStartPage(self): + """Tests the Process function on a StartPage key.""" + test_file_entry = self._GetTestFileEntryFromPath([u'NTUSER.DAT']) + key_path = ( + u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage') + registry_key = self._GetKeyFromFileEntry(test_file_entry, key_path) + event_queue_consumer = self._ParseKeyWithPlugin( + self._plugin, registry_key, file_entry=test_file_entry) + event_objects = self._GetEventObjectsFromQueue(event_queue_consumer) + + self.assertEqual(len(event_objects), 77) + + # The ProgramsCache entry shell item event. + event_object = event_objects[0] + + expected_parser = u'explorer_programscache/shell_items' + self.assertEqual(event_object.parser, expected_parser) + + self.assertEqual( + event_object.timestamp_desc, eventdata.EventTimestamp.CREATION_TIME) + expected_timestamp = timelib.Timestamp.CopyFromString( + u'2009-08-04 15:12:24') + self.assertEqual(event_object.timestamp, expected_timestamp) + + expected_data_type = 'windows:shell_item:file_entry' + self.assertEqual(event_object.data_type, expected_data_type) + + expected_message = ( + u'Name: Programs ' + u'Long name: Programs ' + u'Localized name: @shell32.dll,-21782 ' + u'Shell item path: Programs ' + u'Origin: \\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage ProgramsCache') + expected_short_message = ( + u'Name: Programs ' + u'Origin: \\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'St...') + + self._TestGetMessageStrings( + event_object, expected_message, expected_short_message) + + # The ProgramsCache list event. + event_object = event_objects[75] + + expected_parser = u'explorer_programscache' + self.assertEqual(event_object.parser, expected_parser) + + self.assertEqual( + event_object.timestamp_desc, eventdata.EventTimestamp.WRITTEN_TIME) + expected_timestamp = timelib.Timestamp.CopyFromString( + u'2009-08-04 15:22:18.419625') + self.assertEqual(event_object.timestamp, expected_timestamp) + + expected_data_type = 'windows:registry:list' + self.assertEqual(event_object.data_type, expected_data_type) + + expected_message = ( + u'Key: \\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage ' + u'Value: ProgramsCache ' + u'List: ProgramsCache [' + u'0: Programs ' + u'1: Internet Explorer.lnk ' + u'2: Outlook Express.lnk ' + u'3: Remote Assistance.lnk ' + u'4: Windows Media Player.lnk ' + u'5: Programs Accessories ' + u'6: Address Book.lnk ' + u'7: Command Prompt.lnk ' + u'8: Notepad.lnk ' + u'9: Program Compatibility Wizard.lnk ' + u'10: Synchronize.lnk ' + u'11: Tour Windows XP.lnk ' + u'12: Windows Explorer.lnk ' + u'13: Programs Accessories\\Accessibility ' + u'14: Magnifier.lnk ' + u'15: Narrator.lnk ' + u'16: On-Screen Keyboard.lnk ' + u'17: Utility Manager.lnk ' + u'18: Programs Accessories\\System Tools ' + u'19: Internet Explorer (No Add-ons).lnk]') + expected_short_message = u'{0:s}...'.format(expected_message[0:77]) + + self._TestGetMessageStrings( + event_object, expected_message, expected_short_message) + + # The Windows Registry key event. + event_object = event_objects[76] + + expected_parser = u'explorer_programscache' + self.assertEqual(event_object.parser, expected_parser) + + self.assertEqual( + event_object.timestamp_desc, eventdata.EventTimestamp.WRITTEN_TIME) + expected_timestamp = timelib.Timestamp.CopyFromString( + u'2009-08-04 15:22:18.419625') + self.assertEqual(event_object.timestamp, expected_timestamp) + + expected_data_type = 'windows:registry:key_value' + self.assertEqual(event_object.data_type, expected_data_type) + + def testProcessStartPage2(self): + """Tests the Process function on a StartPage2 key.""" + test_file_entry = self._GetTestFileEntryFromPath([u'NTUSER-WIN7.DAT']) + key_path = ( + u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\' + u'StartPage2') + registry_key = self._GetKeyFromFileEntry(test_file_entry, key_path) + event_queue_consumer = self._ParseKeyWithPlugin( + self._plugin, registry_key, file_entry=test_file_entry) + event_objects = self._GetEventObjectsFromQueue(event_queue_consumer) + + self.assertEqual(len(event_objects), 118) + + event_object = event_objects[0] + + expected_parser = u'explorer_programscache/shell_items' + self.assertEqual(event_object.parser, expected_parser) + + self.assertEqual( + event_object.timestamp_desc, eventdata.EventTimestamp.CREATION_TIME) + expected_timestamp = timelib.Timestamp.CopyFromString( + u'2010-11-10 07:50:38') + self.assertEqual(event_object.timestamp, expected_timestamp) + + expected_data_type = 'windows:shell_item:file_entry' + self.assertEqual(event_object.data_type, expected_data_type) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/parsers/winreg_plugins/winver.py b/tests/parsers/winreg_plugins/winver.py index af2fe5a192..443cb29e0a 100644 --- a/tests/parsers/winreg_plugins/winver.py +++ b/tests/parsers/winreg_plugins/winver.py @@ -88,7 +88,9 @@ def testProcess(self): expected_timestamp = timelib.Timestamp.CopyFromString(time_string) self.assertEqual(event_object.timestamp, expected_timestamp) - self.assertEqual(event_object.data_type, 'windows:registry:key_value') + + expected_data_type = 'windows:registry:key_value' + self.assertEqual(event_object.data_type, expected_data_type) expected_message = ( u'[{0:s}] ' @@ -107,7 +109,9 @@ def testProcess(self): expected_timestamp = timelib.Timestamp.CopyFromString( u'2012-08-31 20:09:55') self.assertEqual(event_object.timestamp, expected_timestamp) - self.assertEqual(event_object.data_type, 'windows:registry:installation') + + expected_data_type = 'windows:registry:installation' + self.assertEqual(event_object.data_type, expected_data_type) expected_message = ( u'MyTestOS 5.1 Service Pack 1 '