Skip to content

Commit 71e0a8a

Browse files
authored
Changes to improve AMCache.hve format support #2790 (#3701)
1 parent 2fc4e0a commit 71e0a8a

File tree

5 files changed

+196
-26
lines changed

5 files changed

+196
-26
lines changed

ACKNOWLEDGEMENTS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Copied with permission granted by Jerome Marty.
135135

136136
Copied with permission granted by Antoine Brodin.
137137
* PartitionsEx-WebCacheV01.dat
138+
* win10-Amcache.hve
138139

139140
Copied with permission granted by Rob Lee.
140141
Copyright SANS Institute - Digital Forensics and Incident Response.

plaso/lib/definitions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
TIME_DESCRIPTION_LAST_SHUTDOWN = 'Last Shutdown Time'
138138
TIME_DESCRIPTION_LAST_USED = 'Last Used Time'
139139
TIME_DESCRIPTION_LAST_VISITED = 'Last Visited Time'
140+
TIME_DESCRIPTION_LINK_TIME = 'Link Time'
140141
TIME_DESCRIPTION_MODIFICATION = 'Content Modification Time'
141142
TIME_DESCRIPTION_PURCHASED = 'Purchased'
142143
TIME_DESCRIPTION_RECORDED = 'Event Recorded'

plaso/parsers/winreg_plugins/amcache.py

Lines changed: 160 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# -*- coding: utf-8 -*-
22
"""Windows Registry plugin to parse the AMCache.hve Root key."""
33

4+
import re
5+
46
from dfdatetime import filetime as dfdatetime_filetime
57
from dfdatetime import posix_time as dfdatetime_posix_time
8+
from dfdatetime import time_elements as dfdatetime_time_elements
69

710
from dfwinreg import errors as dfwinreg_errors
811

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

97100
# Contains: {value name: attribute name}
101+
_APPLICATION_SUB_KEY_VALUES = {
102+
'LowerCaseLongPath': 'full_path',
103+
'ProductName': 'product_name',
104+
'ProductVersion': 'file_version',
105+
'ProgramId': 'program_identifier',
106+
'Publisher': 'company_name',
107+
'Size': 'file_size'}
108+
98109
_FILE_REFERENCE_KEY_VALUES = {
99110
'0': 'product_name',
100111
'1': 'company_name',
@@ -106,7 +117,7 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
106117
'100': 'program_identifier',
107118
'101': 'sha1'}
108119

109-
_AMCACHE_COMPILATION_TIME = 'f'
120+
_AMCACHE_LINK_TIME = 'f'
110121
_AMCACHE_FILE_MODIFICATION_TIME = '11'
111122
_AMCACHE_FILE_CREATION_TIME = '12'
112123
_AMCACHE_ENTRY_WRITE_TIME = '17'
@@ -115,6 +126,13 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
115126

116127
_AMCACHE_P_FILES = 'Files'
117128

129+
# Date and time string formatted as: "MM/DD/YYYY hh:mm:ss"
130+
# for example "04/07/2014 15:18:49"
131+
# TODO: determine if this is true for other locales.
132+
_LINK_DATE_TIME_RE = re.compile(
133+
r'([0-9][0-9])/([0-9][0-9])/([0-9][0-9][0-9][0-9]) '
134+
r'([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')
135+
118136
_PRODUCT_KEY_VALUES = {
119137
'0': 'name',
120138
'1': 'version',
@@ -126,8 +144,7 @@ class AMCachePlugin(interface.WindowsRegistryPlugin):
126144
'f': 'product_code',
127145
'10': 'package_code',
128146
'11': 'msi_product_code',
129-
'12': 'msi_package_code',
130-
}
147+
'12': 'msi_package_code'}
131148

132149
def _GetValueDataAsObject(
133150
self, parser_mediator, key_path, value_name, registry_value):
@@ -167,6 +184,112 @@ def _GetValueDataAsObject(
167184

168185
return value_object
169186

187+
def _ParseApplicationSubKey(self, parser_mediator, application_sub_key):
188+
"""Parses a Root\\InventoryApplicationFile\\%NAME%|%IDENTIFIER% key.
189+
190+
Args:
191+
parser_mediator (ParserMediator): mediates interactions between parsers
192+
and other components, such as storage and dfVFS.
193+
application_sub_key (dfwinreg.WinRegistryKey): application sub key of the
194+
InventoryApplicationFile Windows Registry key.
195+
"""
196+
event_data = AMCacheFileEventData()
197+
198+
for value_name, attribute_name in self._APPLICATION_SUB_KEY_VALUES.items():
199+
value = application_sub_key.GetValueByName(value_name)
200+
if value:
201+
value_data = self._GetValueDataAsObject(
202+
parser_mediator, application_sub_key.path, value_name, value)
203+
204+
setattr(event_data, attribute_name, value_data)
205+
206+
install_date_value = application_sub_key.GetValueByName('InstallDate')
207+
if install_date_value:
208+
date_time = self._ParseDateStringValue(
209+
parser_mediator, application_sub_key.path, install_date_value)
210+
event = time_events.DateTimeValuesEvent(
211+
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
212+
parser_mediator.ProduceEventWithEventData(event, event_data)
213+
214+
install_date_msi_value = application_sub_key.GetValueByName(
215+
'InstallDateMsi')
216+
if install_date_msi_value:
217+
date_time = self._ParseDateStringValue(
218+
parser_mediator, application_sub_key.path, install_date_msi_value)
219+
event = time_events.DateTimeValuesEvent(
220+
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
221+
parser_mediator.ProduceEventWithEventData(event, event_data)
222+
223+
link_date_value = application_sub_key.GetValueByName('LinkDate')
224+
if link_date_value:
225+
date_time = self._ParseDateStringValue(
226+
parser_mediator, application_sub_key.path, link_date_value)
227+
event = time_events.DateTimeValuesEvent(
228+
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
229+
parser_mediator.ProduceEventWithEventData(event, event_data)
230+
231+
def _ParseDateStringValue(self, parser_mediator, key_path, registry_value):
232+
"""Parses a date and time string value.
233+
234+
Args:
235+
parser_mediator (ParserMediator): mediates interactions between parsers
236+
and other components, such as storage and dfvfs.
237+
key_path (str): key path.
238+
registry_value (dfwinreg.WinRegistryValue): Windows Registry value.
239+
240+
Returns:
241+
dfdatetime_time_elements.TimeElements: date and time value or None
242+
if not available.
243+
"""
244+
if not registry_value.DataIsString():
245+
parser_mediator.ProduceExtractionWarning((
246+
'unsupported {0:s} with value data type: {1:s} in key: '
247+
'{2:s}').format(
248+
registry_value.name, registry_value.data_type_string, key_path))
249+
return None
250+
251+
date_time_string = registry_value.GetDataAsObject()
252+
if not date_time_string:
253+
parser_mediator.ProduceExtractionWarning(
254+
'missing {0:s} value data in key: {1:s}'.format(
255+
registry_value.name, key_path))
256+
return None
257+
258+
re_match = self._LINK_DATE_TIME_RE.match(date_time_string)
259+
if not re_match:
260+
parser_mediator.ProduceExtractionWarning(
261+
'unsupported {0:s} value data: {1!s} in key: {2:s}'.format(
262+
registry_value.name, date_time_string, key_path))
263+
return None
264+
265+
month, day_of_month, year, hours, minutes, seconds= re_match.groups()
266+
267+
try:
268+
year = int(year, 10)
269+
month = int(month, 10)
270+
day_of_month = int(day_of_month, 10)
271+
hours = int(hours, 10)
272+
minutes = int(minutes, 10)
273+
seconds = int(seconds, 10)
274+
except (TypeError, ValueError):
275+
parser_mediator.ProduceExtractionWarning(
276+
'invalid {0:s} date time value: {1!s} in key: {2:s}'.format(
277+
registry_value.name, date_time_string, key_path))
278+
return None
279+
280+
time_elements_tuple = (year, month, day_of_month, hours, minutes, seconds)
281+
282+
try:
283+
date_time = dfdatetime_time_elements.TimeElements(
284+
time_elements_tuple=time_elements_tuple)
285+
except ValueError:
286+
parser_mediator.ProduceExtractionWarning(
287+
'invalid {0:s} date time value: {1!s} in key: {2:s}'.format(
288+
registry_value.name, time_elements_tuple, key_path))
289+
return None
290+
291+
return date_time
292+
170293
def _ParseFileKey(self, parser_mediator, file_key):
171294
"""Parses a Root\\File key.
172295
@@ -220,42 +343,54 @@ def _ParseFileReferenceKey(self, parser_mediator, file_reference_key):
220343

221344
setattr(event_data, attribute_name, value_data)
222345

223-
amcache_time_value = file_reference_key.GetValueByName(
346+
write_time_value = file_reference_key.GetValueByName(
224347
self._AMCACHE_ENTRY_WRITE_TIME)
225-
if amcache_time_value:
226-
timestamp = amcache_time_value.GetDataAsObject()
227-
amcache_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
348+
if write_time_value:
349+
timestamp = write_time_value.GetDataAsObject()
350+
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
228351
event = time_events.DateTimeValuesEvent(
229-
amcache_time, definitions.TIME_DESCRIPTION_MODIFICATION)
352+
date_time, definitions.TIME_DESCRIPTION_MODIFICATION)
230353
parser_mediator.ProduceEventWithEventData(event, event_data)
231354

232355
creation_time_value = file_reference_key.GetValueByName(
233356
self._AMCACHE_FILE_CREATION_TIME)
234357
if creation_time_value:
235358
timestamp = creation_time_value.GetDataAsObject()
236-
creation_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
359+
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
237360
event = time_events.DateTimeValuesEvent(
238-
creation_time, definitions.TIME_DESCRIPTION_CREATION)
361+
date_time, definitions.TIME_DESCRIPTION_CREATION)
239362
parser_mediator.ProduceEventWithEventData(event, event_data)
240363

241364
modification_time_value = file_reference_key.GetValueByName(
242365
self._AMCACHE_FILE_MODIFICATION_TIME)
243366
if modification_time_value:
244367
timestamp = modification_time_value.GetDataAsObject()
245-
modification_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
368+
date_time = dfdatetime_filetime.Filetime(timestamp=timestamp)
246369
event = time_events.DateTimeValuesEvent(
247-
modification_time, definitions.TIME_DESCRIPTION_MODIFICATION)
370+
date_time, definitions.TIME_DESCRIPTION_MODIFICATION)
248371
parser_mediator.ProduceEventWithEventData(event, event_data)
249372

250-
compilation_time_value = file_reference_key.GetValueByName(
251-
self._AMCACHE_COMPILATION_TIME)
252-
if compilation_time_value:
253-
timestamp = compilation_time_value.GetDataAsObject()
254-
link_time = dfdatetime_posix_time.PosixTime(timestamp=timestamp)
373+
link_time_value = file_reference_key.GetValueByName(self._AMCACHE_LINK_TIME)
374+
if link_time_value:
375+
timestamp = link_time_value.GetDataAsObject()
376+
date_time = dfdatetime_posix_time.PosixTime(timestamp=timestamp)
255377
event = time_events.DateTimeValuesEvent(
256-
link_time, definitions.TIME_DESCRIPTION_CHANGE)
378+
date_time, definitions.TIME_DESCRIPTION_LINK_TIME)
257379
parser_mediator.ProduceEventWithEventData(event, event_data)
258380

381+
def _ParseInventoryApplicationFileKey(
382+
self, parser_mediator, inventory_application_file_key):
383+
"""Parses a Root\\InventoryApplicationFile key.
384+
385+
Args:
386+
parser_mediator (ParserMediator): mediates interactions between parsers
387+
and other components, such as storage and dfVFS.
388+
inventory_application_file_key (dfwinreg.WinRegistryKey): the
389+
InventoryApplicationFile Windows Registry key.
390+
"""
391+
for application_sub_key in inventory_application_file_key.GetSubkeys():
392+
self._ParseApplicationSubKey(parser_mediator, application_sub_key)
393+
259394
def _ParseProgramKey(self, parser_mediator, program_key):
260395
"""Parses a program key (a sub key of Root\\Programs) for events.
261396
@@ -268,13 +403,11 @@ def _ParseProgramKey(self, parser_mediator, program_key):
268403

269404
for value_name, attribute_name in self._PRODUCT_KEY_VALUES.items():
270405
value = program_key.GetValueByName(value_name)
271-
if not value:
272-
continue
273-
274-
value_data = self._GetValueDataAsObject(
275-
parser_mediator, program_key.path, value_name, value)
406+
if value:
407+
value_data = self._GetValueDataAsObject(
408+
parser_mediator, program_key.path, value_name, value)
276409

277-
setattr(event_data, attribute_name, value_data)
410+
setattr(event_data, attribute_name, value_data)
278411

279412
installation_time_value = program_key.GetValueByName(
280413
self._AMCACHE_P_INSTALLATION_TIME)
@@ -312,6 +445,9 @@ def _ParseRootKey(self, parser_mediator, root_key):
312445
if sub_key.name == 'File':
313446
self._ParseFileKey(parser_mediator, sub_key)
314447

448+
elif sub_key.name == 'InventoryApplicationFile':
449+
self._ParseInventoryApplicationFileKey(parser_mediator, sub_key)
450+
315451
elif sub_key.name == 'Programs':
316452
self._ParseProgramsKey(parser_mediator, sub_key)
317453

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

331-
332467
def ExtractEvents(self, parser_mediator, registry_key, **kwargs):
333468
"""Extracts events from a Windows Registry key.
334469

test_data/win10-Amcache.hve

512 KB
Binary file not shown.

tests/parsers/winreg_plugins/amcache.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from dfwinreg import regf as dfwinreg_regf
88

9+
from plaso.lib import definitions
910
from plaso.parsers.winreg_plugins import amcache
1011

1112
from tests.parsers.winreg_plugins import test_lib
@@ -54,7 +55,8 @@ def testProcess(self):
5455
'data_type': 'windows:registry:amcache',
5556
'date_time': '1992-06-19 22:22:17',
5657
'full_path': expected_full_path,
57-
'sha1': '82274eef0911a948f91425f5e5b0e730517fe75e'}
58+
'sha1': '82274eef0911a948f91425f5e5b0e730517fe75e',
59+
'timestamp_desc': definitions.TIME_DESCRIPTION_LINK_TIME}
5860

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

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

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

80+
def testProcessWindows101(self):
81+
"""Tests the Process function on a Windows 10 1807 AMCache.hve file."""
82+
test_file_entry = self._GetTestFileEntry(['win10-Amcache.hve'])
83+
84+
file_object = test_file_entry.GetFileObject()
85+
86+
registry_file = dfwinreg_regf.REGFWinRegistryFile(
87+
ascii_codepage='cp1252', emulate_virtual_keys=False)
88+
registry_file.Open(file_object)
89+
90+
registry_key = registry_file.GetKeyByPath('\\Root')
91+
92+
plugin = amcache.AMCachePlugin()
93+
storage_writer = self._ParseKeyWithPlugin(
94+
registry_key, plugin, file_entry=test_file_entry)
95+
96+
self.assertEqual(storage_writer.number_of_events, 236)
97+
self.assertEqual(storage_writer.number_of_extraction_warnings, 0)
98+
self.assertEqual(storage_writer.number_of_recovery_warnings, 0)
99+
100+
events = list(storage_writer.GetSortedEvents())
101+
102+
expected_event_values = {
103+
'data_type': 'windows:registry:amcache',
104+
'date_time': '1997-01-10 22:26:24',
105+
'full_path': 'c:\\windows\\system32\\svchost.exe',
106+
'timestamp_desc': definitions.TIME_DESCRIPTION_LINK_TIME}
107+
108+
self.CheckEventValues(storage_writer, events[0], expected_event_values)
109+
77110

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

0 commit comments

Comments
 (0)