Skip to content

Commit

Permalink
Changes to improve AMCache.hve format support #2790 (#3701)
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz authored Jun 6, 2021
1 parent 2fc4e0a commit 71e0a8a
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 26 deletions.
1 change: 1 addition & 0 deletions ACKNOWLEDGEMENTS
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Copied with permission granted by Jerome Marty.

Copied with permission granted by Antoine Brodin.
* PartitionsEx-WebCacheV01.dat
* win10-Amcache.hve

Copied with permission granted by Rob Lee.
Copyright SANS Institute - Digital Forensics and Incident Response.
Expand Down
1 change: 1 addition & 0 deletions plaso/lib/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
TIME_DESCRIPTION_LAST_SHUTDOWN = 'Last Shutdown Time'
TIME_DESCRIPTION_LAST_USED = 'Last Used Time'
TIME_DESCRIPTION_LAST_VISITED = 'Last Visited Time'
TIME_DESCRIPTION_LINK_TIME = 'Link Time'
TIME_DESCRIPTION_MODIFICATION = 'Content Modification Time'
TIME_DESCRIPTION_PURCHASED = 'Purchased'
TIME_DESCRIPTION_RECORDED = 'Event Recorded'
Expand Down
185 changes: 160 additions & 25 deletions plaso/parsers/winreg_plugins/amcache.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
"""Windows Registry plugin to parse the AMCache.hve Root key."""

import re

from dfdatetime import filetime as dfdatetime_filetime
from dfdatetime import posix_time as dfdatetime_posix_time
from dfdatetime import time_elements as dfdatetime_time_elements

from dfwinreg import errors as dfwinreg_errors

Expand Down Expand Up @@ -95,6 +98,14 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
interface.WindowsRegistryKeyPathFilter('\\Root')])

# Contains: {value name: attribute name}
_APPLICATION_SUB_KEY_VALUES = {
'LowerCaseLongPath': 'full_path',
'ProductName': 'product_name',
'ProductVersion': 'file_version',
'ProgramId': 'program_identifier',
'Publisher': 'company_name',
'Size': 'file_size'}

_FILE_REFERENCE_KEY_VALUES = {
'0': 'product_name',
'1': 'company_name',
Expand All @@ -106,7 +117,7 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
'100': 'program_identifier',
'101': 'sha1'}

_AMCACHE_COMPILATION_TIME = 'f'
_AMCACHE_LINK_TIME = 'f'
_AMCACHE_FILE_MODIFICATION_TIME = '11'
_AMCACHE_FILE_CREATION_TIME = '12'
_AMCACHE_ENTRY_WRITE_TIME = '17'
Expand All @@ -115,6 +126,13 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):

_AMCACHE_P_FILES = 'Files'

# Date and time string formatted as: "MM/DD/YYYY hh:mm:ss"
# for example "04/07/2014 15:18:49"
# TODO: determine if this is true for other locales.
_LINK_DATE_TIME_RE = re.compile(
r'([0-9][0-9])/([0-9][0-9])/([0-9][0-9][0-9][0-9]) '
r'([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')

_PRODUCT_KEY_VALUES = {
'0': 'name',
'1': 'version',
Expand All @@ -126,8 +144,7 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
'f': 'product_code',
'10': 'package_code',
'11': 'msi_product_code',
'12': 'msi_package_code',
}
'12': 'msi_package_code'}

def _GetValueDataAsObject(
self, parser_mediator, key_path, value_name, registry_value):
Expand Down Expand Up @@ -167,6 +184,112 @@ def _GetValueDataAsObject(

return value_object

def _ParseApplicationSubKey(self, parser_mediator, application_sub_key):
"""Parses a Root\\InventoryApplicationFile\\%NAME%|%IDENTIFIER% key.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
application_sub_key (dfwinreg.WinRegistryKey): application sub key of the
InventoryApplicationFile Windows Registry key.
"""
event_data = AMCacheFileEventData()

for value_name, attribute_name in self._APPLICATION_SUB_KEY_VALUES.items():
value = application_sub_key.GetValueByName(value_name)
if value:
value_data = self._GetValueDataAsObject(
parser_mediator, application_sub_key.path, value_name, value)

setattr(event_data, attribute_name, value_data)

install_date_value = application_sub_key.GetValueByName('InstallDate')
if install_date_value:
date_time = self._ParseDateStringValue(
parser_mediator, application_sub_key.path, install_date_value)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
parser_mediator.ProduceEventWithEventData(event, event_data)

install_date_msi_value = application_sub_key.GetValueByName(
'InstallDateMsi')
if install_date_msi_value:
date_time = self._ParseDateStringValue(
parser_mediator, application_sub_key.path, install_date_msi_value)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
parser_mediator.ProduceEventWithEventData(event, event_data)

link_date_value = application_sub_key.GetValueByName('LinkDate')
if link_date_value:
date_time = self._ParseDateStringValue(
parser_mediator, application_sub_key.path, link_date_value)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
parser_mediator.ProduceEventWithEventData(event, event_data)

def _ParseDateStringValue(self, parser_mediator, key_path, registry_value):
"""Parses a date and time string value.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfvfs.
key_path (str): key path.
registry_value (dfwinreg.WinRegistryValue): Windows Registry value.
Returns:
dfdatetime_time_elements.TimeElements: date and time value or None
if not available.
"""
if not registry_value.DataIsString():
parser_mediator.ProduceExtractionWarning((
'unsupported {0:s} with value data type: {1:s} in key: '
'{2:s}').format(
registry_value.name, registry_value.data_type_string, key_path))
return None

date_time_string = registry_value.GetDataAsObject()
if not date_time_string:
parser_mediator.ProduceExtractionWarning(
'missing {0:s} value data in key: {1:s}'.format(
registry_value.name, key_path))
return None

re_match = self._LINK_DATE_TIME_RE.match(date_time_string)
if not re_match:
parser_mediator.ProduceExtractionWarning(
'unsupported {0:s} value data: {1!s} in key: {2:s}'.format(
registry_value.name, date_time_string, key_path))
return None

month, day_of_month, year, hours, minutes, seconds= re_match.groups()

try:
year = int(year, 10)
month = int(month, 10)
day_of_month = int(day_of_month, 10)
hours = int(hours, 10)
minutes = int(minutes, 10)
seconds = int(seconds, 10)
except (TypeError, ValueError):
parser_mediator.ProduceExtractionWarning(
'invalid {0:s} date time value: {1!s} in key: {2:s}'.format(
registry_value.name, date_time_string, key_path))
return None

time_elements_tuple = (year, month, day_of_month, hours, minutes, seconds)

try:
date_time = dfdatetime_time_elements.TimeElements(
time_elements_tuple=time_elements_tuple)
except ValueError:
parser_mediator.ProduceExtractionWarning(
'invalid {0:s} date time value: {1!s} in key: {2:s}'.format(
registry_value.name, time_elements_tuple, key_path))
return None

return date_time

def _ParseFileKey(self, parser_mediator, file_key):
"""Parses a Root\\File key.
Expand Down Expand Up @@ -220,42 +343,54 @@ def _ParseFileReferenceKey(self, parser_mediator, file_reference_key):

setattr(event_data, attribute_name, value_data)

amcache_time_value = file_reference_key.GetValueByName(
write_time_value = file_reference_key.GetValueByName(
self._AMCACHE_ENTRY_WRITE_TIME)
if amcache_time_value:
timestamp = amcache_time_value.GetDataAsObject()
amcache_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
if write_time_value:
timestamp = write_time_value.GetDataAsObject()
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
amcache_time, definitions.TIME_DESCRIPTION_MODIFICATION)
date_time, definitions.TIME_DESCRIPTION_MODIFICATION)
parser_mediator.ProduceEventWithEventData(event, event_data)

creation_time_value = file_reference_key.GetValueByName(
self._AMCACHE_FILE_CREATION_TIME)
if creation_time_value:
timestamp = creation_time_value.GetDataAsObject()
creation_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
creation_time, definitions.TIME_DESCRIPTION_CREATION)
date_time, definitions.TIME_DESCRIPTION_CREATION)
parser_mediator.ProduceEventWithEventData(event, event_data)

modification_time_value = file_reference_key.GetValueByName(
self._AMCACHE_FILE_MODIFICATION_TIME)
if modification_time_value:
timestamp = modification_time_value.GetDataAsObject()
modification_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
modification_time, definitions.TIME_DESCRIPTION_MODIFICATION)
date_time, definitions.TIME_DESCRIPTION_MODIFICATION)
parser_mediator.ProduceEventWithEventData(event, event_data)

compilation_time_value = file_reference_key.GetValueByName(
self._AMCACHE_COMPILATION_TIME)
if compilation_time_value:
timestamp = compilation_time_value.GetDataAsObject()
link_time = dfdatetime_posix_time.PosixTime(timestamp=timestamp)
link_time_value = file_reference_key.GetValueByName(self._AMCACHE_LINK_TIME)
if link_time_value:
timestamp = link_time_value.GetDataAsObject()
date_time = dfdatetime_posix_time.PosixTime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
link_time, definitions.TIME_DESCRIPTION_CHANGE)
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
parser_mediator.ProduceEventWithEventData(event, event_data)

def _ParseInventoryApplicationFileKey(
self, parser_mediator, inventory_application_file_key):
"""Parses a Root\\InventoryApplicationFile key.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
inventory_application_file_key (dfwinreg.WinRegistryKey): the
InventoryApplicationFile Windows Registry key.
"""
for application_sub_key in inventory_application_file_key.GetSubkeys():
self._ParseApplicationSubKey(parser_mediator, application_sub_key)

def _ParseProgramKey(self, parser_mediator, program_key):
"""Parses a program key (a sub key of Root\\Programs) for events.
Expand All @@ -268,13 +403,11 @@ def _ParseProgramKey(self, parser_mediator, program_key):

for value_name, attribute_name in self._PRODUCT_KEY_VALUES.items():
value = program_key.GetValueByName(value_name)
if not value:
continue

value_data = self._GetValueDataAsObject(
parser_mediator, program_key.path, value_name, value)
if value:
value_data = self._GetValueDataAsObject(
parser_mediator, program_key.path, value_name, value)

setattr(event_data, attribute_name, value_data)
setattr(event_data, attribute_name, value_data)

installation_time_value = program_key.GetValueByName(
self._AMCACHE_P_INSTALLATION_TIME)
Expand Down Expand Up @@ -312,6 +445,9 @@ def _ParseRootKey(self, parser_mediator, root_key):
if sub_key.name == 'File':
self._ParseFileKey(parser_mediator, sub_key)

elif sub_key.name == 'InventoryApplicationFile':
self._ParseInventoryApplicationFileKey(parser_mediator, sub_key)

elif sub_key.name == 'Programs':
self._ParseProgramsKey(parser_mediator, sub_key)

Expand All @@ -328,7 +464,6 @@ def _ParseSubKey(self, parser_mediator, registry_key):
for sub_key in registry_key.GetSubkeys():
self._ParseSubKey(parser_mediator, sub_key)


def ExtractEvents(self, parser_mediator, registry_key, **kwargs):
"""Extracts events from a Windows Registry key.
Expand Down
Binary file added test_data/win10-Amcache.hve
Binary file not shown.
35 changes: 34 additions & 1 deletion tests/parsers/winreg_plugins/amcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from dfwinreg import regf as dfwinreg_regf

from plaso.lib import definitions
from plaso.parsers.winreg_plugins import amcache

from tests.parsers.winreg_plugins import test_lib
Expand Down Expand Up @@ -54,7 +55,8 @@ def testProcess(self):
'data_type': 'windows:registry:amcache',
'date_time': '1992-06-19 22:22:17',
'full_path': expected_full_path,
'sha1': '82274eef0911a948f91425f5e5b0e730517fe75e'}
'sha1': '82274eef0911a948f91425f5e5b0e730517fe75e',
'timestamp_desc': definitions.TIME_DESCRIPTION_LINK_TIME}

self.CheckEventValues(storage_writer, events[0], expected_event_values)

Expand All @@ -68,12 +70,43 @@ def testProcess(self):
'c:\\program files (x86)\\fileinsight'],
'name': 'FileInsight - File analysis tool',
'publisher': 'McAfee Inc.',
'timestamp_desc': definitions.TIME_DESCRIPTION_INSTALLATION,
'uninstall_key': [
'HKEY_LOCAL_MACHINE\\Software\\Wow6432Node\\Microsoft\\Windows\\'
'CurrentVersion\\Uninstall\\FileInsight']}

self.CheckEventValues(storage_writer, events[1285], expected_event_values)

def testProcessWindows101(self):
"""Tests the Process function on a Windows 10 1807 AMCache.hve file."""
test_file_entry = self._GetTestFileEntry(['win10-Amcache.hve'])

file_object = test_file_entry.GetFileObject()

registry_file = dfwinreg_regf.REGFWinRegistryFile(
ascii_codepage='cp1252', emulate_virtual_keys=False)
registry_file.Open(file_object)

registry_key = registry_file.GetKeyByPath('\\Root')

plugin = amcache.AMCachePlugin()
storage_writer = self._ParseKeyWithPlugin(
registry_key, plugin, file_entry=test_file_entry)

self.assertEqual(storage_writer.number_of_events, 236)
self.assertEqual(storage_writer.number_of_extraction_warnings, 0)
self.assertEqual(storage_writer.number_of_recovery_warnings, 0)

events = list(storage_writer.GetSortedEvents())

expected_event_values = {
'data_type': 'windows:registry:amcache',
'date_time': '1997-01-10 22:26:24',
'full_path': 'c:\\windows\\system32\\svchost.exe',
'timestamp_desc': definitions.TIME_DESCRIPTION_LINK_TIME}

self.CheckEventValues(storage_writer, events[0], expected_event_values)


if __name__ == '__main__':
unittest.main()

0 comments on commit 71e0a8a

Please sign in to comment.