plaso-rubanetra/plaso/parsers/winreg_plugins/appcompatcache.py
2020-04-06 18:48:34 +02:00

625 lines
23 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright 2014 The Plaso Project Authors.
# Please see the AUTHORS file for details on individual authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Windows Registry plugin to parse the Application Compatibility Cache key."""
import construct
import logging
from plaso.events import time_events
from plaso.lib import binary
from plaso.lib import eventdata
from plaso.parsers import winreg
from plaso.parsers.winreg_plugins import interface
class AppCompatCacheEvent(time_events.FiletimeEvent):
"""Class that contains the event object for AppCompatCache entries."""
DATA_TYPE = 'windows:registry:appcompatcache'
def __init__(
self, filetime, usage, key, entry_index, path, offset):
"""Initializes a Windows Registry event.
Args:
filetime: The FILETIME timestamp value.
usage: The description of the usage of the time value.
key: Name of the Registry key being parsed.
entry_index: The cache entry index number for the record.
path: The full path to the executable.
offset: The (data) offset of the Registry key or value.
"""
super(AppCompatCacheEvent, self).__init__(filetime, usage)
self.keyname = key
self.offset = offset
self.entry_index = entry_index
self.path = path
class AppCompatCacheHeader(object):
"""Class that contains the Application Compatibility Cache header."""
def __init__(self):
"""Initializes the header object."""
super(AppCompatCacheHeader, self).__init__()
self.number_of_cached_entries = 0
self.header_size = 0
class AppCompatCacheCachedEntry(object):
"""Class that contains the Application Compatibility Cache cached entry."""
def __init__(self):
"""Initializes the cached entry object."""
super(AppCompatCacheCachedEntry, self).__init__()
self.cached_entry_size = 0
self.data = None
self.file_size = None
self.insertion_flags = None
self.last_modification_time = None
self.last_update_time = None
self.shim_flags = None
self.path = None
class AppCompatCachePlugin(interface.KeyPlugin):
"""Class that parses the Application Compatibility Cache Registry data."""
NAME = 'winreg_appcompatcache'
DESCRIPTION = u'Parser for Application Compatibility Cache Registry data.'
REG_KEYS = [
u'\\{current_control_set}\\Control\\Session Manager\\AppCompatibility',
u'\\{current_control_set}\\Control\\Session Manager\\AppCompatCache']
REG_TYPE = 'SYSTEM'
URL = [
(u'https://code.google.com/p/winreg-kb/wiki/'
u'ApplicationCompatibilityCacheKey')]
_FORMAT_TYPE_2000 = 1
_FORMAT_TYPE_XP = 2
_FORMAT_TYPE_2003 = 3
_FORMAT_TYPE_VISTA = 4
_FORMAT_TYPE_7 = 5
_FORMAT_TYPE_8 = 6
# AppCompatCache format signature used in Windows XP.
_HEADER_SIGNATURE_XP = 0xdeadbeef
# AppCompatCache format used in Windows XP.
_HEADER_XP_32BIT_STRUCT = construct.Struct(
'appcompatcache_header_xp',
construct.ULInt32('signature'),
construct.ULInt32('number_of_cached_entries'),
construct.ULInt32('unknown1'),
construct.ULInt32('unknown2'),
construct.Padding(384))
_CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_xp_32bit',
construct.Array(528, construct.Byte('path')),
construct.ULInt64('last_modification_time'),
construct.ULInt64('file_size'),
construct.ULInt64('last_update_time'))
# AppCompatCache format signature used in Windows 2003, Vista and 2008.
_HEADER_SIGNATURE_2003 = 0xbadc0ffe
# AppCompatCache format used in Windows 2003.
_HEADER_2003_STRUCT = construct.Struct(
'appcompatcache_header_2003',
construct.ULInt32('signature'),
construct.ULInt32('number_of_cached_entries'))
_CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_2003_32bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt64('file_size'))
_CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_2003_64bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('unknown1'),
construct.ULInt64('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt64('file_size'))
# AppCompatCache format used in Windows Vista and 2008.
_CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_vista_32bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt32('insertion_flags'),
construct.ULInt32('shim_flags'))
_CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_vista_64bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('unknown1'),
construct.ULInt64('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt32('insertion_flags'),
construct.ULInt32('shim_flags'))
# AppCompatCache format signature used in Windows 7 and 2008 R2.
_HEADER_SIGNATURE_7 = 0xbadc0fee
# AppCompatCache format used in Windows 7 and 2008 R2.
_HEADER_7_STRUCT = construct.Struct(
'appcompatcache_header_7',
construct.ULInt32('signature'),
construct.ULInt32('number_of_cached_entries'),
construct.Padding(120))
_CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_7_32bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt32('insertion_flags'),
construct.ULInt32('shim_flags'),
construct.ULInt32('data_size'),
construct.ULInt32('data_offset'))
_CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct(
'appcompatcache_cached_entry_7_64bit',
construct.ULInt16('path_size'),
construct.ULInt16('maximum_path_size'),
construct.ULInt32('unknown1'),
construct.ULInt64('path_offset'),
construct.ULInt64('last_modification_time'),
construct.ULInt32('insertion_flags'),
construct.ULInt32('shim_flags'),
construct.ULInt64('data_size'),
construct.ULInt64('data_offset'))
# AppCompatCache format used in Windows 8.0 and 8.1.
_HEADER_SIGNATURE_8 = 0x00000080
_HEADER_8_STRUCT = construct.Struct(
'appcompatcache_header_8',
construct.ULInt32('signature'),
construct.Padding(124))
_CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct(
'appcompatcache_cached_entry_header_8',
construct.ULInt32('signature'),
construct.ULInt32('unknown1'),
construct.ULInt32('cached_entry_data_size'),
construct.ULInt16('path_size'))
# AppCompatCache format used in Windows 8.0.
_CACHED_ENTRY_SIGNATURE_8_0 = '00ts'
# AppCompatCache format used in Windows 8.1.
_CACHED_ENTRY_SIGNATURE_8_1 = '10ts'
def _CheckSignature(self, value_data):
"""Parses and validates the signature.
Args:
value_data: a binary string containing the value data.
Returns:
The format type if successful or None otherwise.
"""
signature = construct.ULInt32('signature').parse(value_data)
if signature == self._HEADER_SIGNATURE_XP:
return self._FORMAT_TYPE_XP
elif signature == self._HEADER_SIGNATURE_2003:
# TODO: determine which format version is used (2003 or Vista).
return self._FORMAT_TYPE_2003
elif signature == self._HEADER_SIGNATURE_7:
return self._FORMAT_TYPE_7
elif signature == self._HEADER_SIGNATURE_8:
if value_data[signature:signature + 4] in [
self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1]:
return self._FORMAT_TYPE_8
def _DetermineCacheEntrySize(
self, format_type, value_data, cached_entry_offset):
"""Determines the size of a cached entry.
Args:
format_type: integer value that contains the format type.
value_data: a binary string containing the value data.
cached_entry_offset: integer value that contains the offset of
the first cached entry data relative to the start of
the value data.
Returns:
The cached entry size if successful or None otherwise.
Raises:
RuntimeError: if the format type is not supported.
"""
if format_type not in [
self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA,
self._FORMAT_TYPE_7, self._FORMAT_TYPE_8]:
raise RuntimeError(
u'[{0:s}] Unsupported format type: {1:d}'.format(
self.NAME, format_type))
cached_entry_data = value_data[cached_entry_offset:]
cached_entry_size = 0
if format_type == self._FORMAT_TYPE_XP:
cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof()
elif format_type in [
self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7]:
path_size = construct.ULInt16('path_size').parse(cached_entry_data[0:2])
maximum_path_size = construct.ULInt16('maximum_path_size').parse(
cached_entry_data[2:4])
path_offset_32bit = construct.ULInt32('path_offset').parse(
cached_entry_data[4:8])
path_offset_64bit = construct.ULInt32('path_offset').parse(
cached_entry_data[8:16])
if maximum_path_size < path_size:
logging.error(
u'[{0:s}] Path size value out of bounds.'.format(self.NAME))
return
path_end_of_string_size = maximum_path_size - path_size
if path_size == 0 or path_end_of_string_size != 2:
logging.error(
u'[{0:s}] Unsupported path size values.'.format(self.NAME))
return
# Assume the entry is 64-bit if the 32-bit path offset is 0 and
# the 64-bit path offset is set.
if path_offset_32bit == 0 and path_offset_64bit != 0:
if format_type == self._FORMAT_TYPE_2003:
cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof()
elif format_type == self._FORMAT_TYPE_VISTA:
cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof()
elif format_type == self._FORMAT_TYPE_7:
cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof()
else:
if format_type == self._FORMAT_TYPE_2003:
cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof()
elif format_type == self._FORMAT_TYPE_VISTA:
cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof()
elif format_type == self._FORMAT_TYPE_7:
cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof()
elif format_type == self._FORMAT_TYPE_8:
cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof()
return cached_entry_size
def _ParseHeader(self, format_type, value_data):
"""Parses the header.
Args:
format_type: integer value that contains the format type.
value_data: a binary string containing the value data.
Returns:
A header object (instance of AppCompatCacheHeader).
Raises:
RuntimeError: if the format type is not supported.
"""
if format_type not in [
self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA,
self._FORMAT_TYPE_7, self._FORMAT_TYPE_8]:
raise RuntimeError(
u'[{0:s}] Unsupported format type: {1:d}'.format(
self.NAME, format_type))
# TODO: change to collections.namedtuple or use __slots__ if the overhead
# of a regular object becomes a problem.
header_object = AppCompatCacheHeader()
if format_type == self._FORMAT_TYPE_XP:
header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof()
header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data)
elif format_type == self._FORMAT_TYPE_2003:
header_object.header_size = self._HEADER_2003_STRUCT.sizeof()
header_struct = self._HEADER_2003_STRUCT.parse(value_data)
elif format_type == self._FORMAT_TYPE_VISTA:
header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof()
header_struct = self._HEADER_VISTA_STRUCT.parse(value_data)
elif format_type == self._FORMAT_TYPE_7:
header_object.header_size = self._HEADER_7_STRUCT.sizeof()
header_struct = self._HEADER_7_STRUCT.parse(value_data)
elif format_type == self._FORMAT_TYPE_8:
header_object.header_size = self._HEADER_8_STRUCT.sizeof()
header_struct = self._HEADER_8_STRUCT.parse(value_data)
if format_type in [
self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA,
self._FORMAT_TYPE_7]:
header_object.number_of_cached_entries = header_struct.get(
'number_of_cached_entries')
return header_object
def _ParseCachedEntry(
self, format_type, value_data, cached_entry_offset, cached_entry_size):
"""Parses a cached entry.
Args:
format_type: integer value that contains the format type.
value_data: a binary string containing the value data.
cached_entry_offset: integer value that contains the offset of
the cached entry data relative to the start of
the value data.
cached_entry_size: integer value that contains the cached entry data size.
Returns:
A cached entry object (instance of AppCompatCacheCachedEntry).
Raises:
RuntimeError: if the format type is not supported.
"""
if format_type not in [
self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA,
self._FORMAT_TYPE_7, self._FORMAT_TYPE_8]:
raise RuntimeError(
u'[{0:s}] Unsupported format type: {1:d}'.format(
self.NAME, format_type))
cached_entry_data = value_data[
cached_entry_offset:cached_entry_offset + cached_entry_size]
cached_entry_struct = None
if format_type == self._FORMAT_TYPE_XP:
if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse(
cached_entry_data)
elif format_type == self._FORMAT_TYPE_2003:
if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse(
cached_entry_data)
elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse(
cached_entry_data)
elif format_type == self._FORMAT_TYPE_VISTA:
if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse(
cached_entry_data)
elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse(
cached_entry_data)
elif format_type == self._FORMAT_TYPE_7:
if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse(
cached_entry_data)
elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse(
cached_entry_data)
elif format_type == self._FORMAT_TYPE_8:
if cached_entry_data[0:4] not in [
self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1]:
raise RuntimeError(
u'[{0:s}] Unsupported cache entry signature'.format(self.NAME))
if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof():
cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse(
cached_entry_data)
cached_entry_data_size = cached_entry_struct.get(
'cached_entry_data_size')
cached_entry_size = 12 + cached_entry_data_size
cached_entry_data = value_data[
cached_entry_offset:cached_entry_offset + cached_entry_size]
if not cached_entry_struct:
raise RuntimeError(
u'[{0:s}] Unsupported cache entry size: {1:d}'.format(
self.NAME, cached_entry_size))
cached_entry_object = AppCompatCacheCachedEntry()
cached_entry_object.cached_entry_size = cached_entry_size
path_offset = 0
data_size = 0
if format_type == self._FORMAT_TYPE_XP:
string_size = 0
for string_index in xrange(0, 528, 2):
if (ord(cached_entry_data[string_index]) == 0 and
ord(cached_entry_data[string_index + 1]) == 0):
break
string_size += 2
cached_entry_object.path = binary.Ut16StreamCopyToString(
cached_entry_data[0:string_size])
elif format_type in [
self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7]:
path_size = cached_entry_struct.get('path_size')
path_offset = cached_entry_struct.get('path_offset')
elif format_type == self._FORMAT_TYPE_8:
path_size = cached_entry_struct.get('path_size')
cached_entry_data_offset = 14 + path_size
cached_entry_object.path = binary.Ut16StreamCopyToString(
cached_entry_data[14:cached_entry_data_offset])
remaining_data = cached_entry_data[cached_entry_data_offset:]
cached_entry_object.insertion_flags = construct.ULInt32(
'insertion_flags').parse(remaining_data[0:4])
cached_entry_object.shim_flags = construct.ULInt32(
'shim_flags').parse(remaining_data[4:8])
if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0:
cached_entry_data_offset += 8
elif cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_1:
cached_entry_data_offset += 10
remaining_data = cached_entry_data[cached_entry_data_offset:]
if format_type in [
self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003, self._FORMAT_TYPE_VISTA,
self._FORMAT_TYPE_7]:
cached_entry_object.last_modification_time = cached_entry_struct.get(
'last_modification_time')
elif format_type == self._FORMAT_TYPE_8:
cached_entry_object.last_modification_time = construct.ULInt64(
'last_modification_time').parse(remaining_data[0:8])
if format_type in [self._FORMAT_TYPE_XP, self._FORMAT_TYPE_2003]:
cached_entry_object.file_size = cached_entry_struct.get('file_size')
elif format_type in [self._FORMAT_TYPE_VISTA, self._FORMAT_TYPE_7]:
cached_entry_object.insertion_flags = cached_entry_struct.get(
'insertion_flags')
cached_entry_object.shim_flags = cached_entry_struct.get('shim_flags')
if format_type == self._FORMAT_TYPE_XP:
cached_entry_object.last_update_time = cached_entry_struct.get(
'last_update_time')
if format_type == self._FORMAT_TYPE_7:
data_offset = cached_entry_struct.get('data_offset')
data_size = cached_entry_struct.get('data_size')
elif format_type == self._FORMAT_TYPE_8:
data_offset = cached_entry_offset + cached_entry_data_offset + 12
data_size = construct.ULInt32('data_size').parse(remaining_data[8:12])
if path_offset > 0 and path_size > 0:
path_size += path_offset
cached_entry_object.path = binary.Ut16StreamCopyToString(
value_data[path_offset:path_size])
if data_size > 0:
data_size += data_offset
cached_entry_object.data = value_data[data_offset:data_size]
return cached_entry_object
def GetEntries(self, parser_context, key=None, file_entry=None,
parser_chain=None, **unused_kwargs):
"""Extracts event objects from a Application Compatibility Cache key.
Args:
parser_context: A parser context object (instance of ParserContext).
key: Optional Registry key (instance of winreg.WinRegKey).
The default is None.
parser_chain: Optional string containing the parsing chain up to this
point. The default is None.
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
The default is None.
"""
value = key.GetValue('AppCompatCache')
if not value:
return
value_data = value.data
value_data_size = len(value.data)
format_type = self._CheckSignature(value_data)
if not format_type:
# TODO: Instead of logging emit a parser error object that once that
# mechanism is implemented.
logging.error(
u'AppCompatCache format error: [{0:s}] Unsupported signature'.format(
key.path))
return
header_object = self._ParseHeader(format_type, value_data)
# On Windows Vista and 2008 when the cache is empty it will
# only consist of the header.
if value_data_size <= header_object.header_size:
return
cached_entry_offset = header_object.header_size
cached_entry_size = self._DetermineCacheEntrySize(
format_type, value_data, cached_entry_offset)
if not cached_entry_size:
# TODO: Instead of logging emit a parser error object that once that
# mechanism is implemented.
logging.error(
u'AppCompatCache format error: [{0:s}] Unsupported cached entry '
u'size.'.format(key.path))
return
cached_entry_index = 0
while cached_entry_offset < value_data_size:
cached_entry_object = self._ParseCachedEntry(
format_type, value_data, cached_entry_offset, cached_entry_size)
if cached_entry_object.last_modification_time is not None:
# TODO: refactor to file modification event.
event_object = AppCompatCacheEvent(
cached_entry_object.last_modification_time,
u'File Last Modification Time', key.path,
cached_entry_index + 1, cached_entry_object.path,
cached_entry_offset)
parser_context.ProduceEvent(
event_object, parser_chain=parser_chain, file_entry=file_entry)
if cached_entry_object.last_update_time is not None:
# TODO: refactor to process run event.
event_object = AppCompatCacheEvent(
cached_entry_object.last_update_time,
eventdata.EventTimestamp.LAST_RUNTIME, key.path,
cached_entry_index + 1, cached_entry_object.path,
cached_entry_offset)
parser_context.ProduceEvent(
event_object, parser_chain=parser_chain, file_entry=file_entry)
cached_entry_offset += cached_entry_object.cached_entry_size
cached_entry_index += 1
if (header_object.number_of_cached_entries != 0 and
cached_entry_index >= header_object.number_of_cached_entries):
break
winreg.WinRegistryParser.RegisterPlugin(AppCompatCachePlugin)