mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-03 03:08:43 +02:00
280 lines
13 KiB
Python
280 lines
13 KiB
Python
import codecs
|
|
import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
import typing
|
|
|
|
import volatility.framework.interfaces.plugins as interfaces_plugins
|
|
from volatility.framework import exceptions, renderers, constants
|
|
from volatility.framework.configuration import requirements
|
|
from volatility.framework.layers.physical import BufferDataLayer
|
|
from volatility.framework.layers.registry import RegistryHive
|
|
from volatility.framework.objects import utility
|
|
from volatility.framework.renderers import format_hints
|
|
from volatility.framework.symbols import intermed
|
|
|
|
vollog = logging.getLogger(__name__)
|
|
|
|
|
|
class UserAssist(interfaces_plugins.PluginInterface):
|
|
""""Print userassist registry keys and information"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._userassist_size = 0
|
|
self._userassist_type_name = "_VOL_USERASSIST_TYPES_7"
|
|
self._reg_table_name = None
|
|
self._win7 = None
|
|
# taken from http://msdn.microsoft.com/en-us/library/dd378457%28v=vs.85%29.aspx
|
|
self._folder_guids = json.load(open(os.path.join(os.path.dirname(__file__), "userassist.json"), "rb"))
|
|
|
|
@classmethod
|
|
def get_requirements(cls):
|
|
return [requirements.TranslationLayerRequirement(name = 'primary',
|
|
description = 'Kernel Address Space',
|
|
architectures = ["Intel32", "Intel64"]),
|
|
requirements.SymbolRequirement(name = "nt_symbols",
|
|
description = "Windows OS"),
|
|
requirements.IntRequirement(name = 'offset',
|
|
description = "Hive Offset",
|
|
default = None,
|
|
optional = True)]
|
|
|
|
def parse_userassist_data(self, reg_val):
|
|
"""Reads the raw data of a _CM_KEY_VALUE and returns a dict of userassist fields"""
|
|
|
|
item = {
|
|
"id": renderers.UnparsableValue(),
|
|
"count": renderers.UnparsableValue(),
|
|
"focus": renderers.UnparsableValue(),
|
|
"time": renderers.UnparsableValue(),
|
|
"lastupdated": renderers.UnparsableValue(),
|
|
"rawdata": renderers.UnparsableValue(),
|
|
}
|
|
|
|
userassist_data = reg_val.decode_data()
|
|
|
|
if userassist_data is None:
|
|
return item
|
|
|
|
item["rawdata"] = userassist_data
|
|
|
|
if self._win7 is None:
|
|
# if OS is still unknown at this point, return the default item which just has the rawdata
|
|
return item
|
|
|
|
if len(userassist_data) < self._userassist_size:
|
|
return item
|
|
|
|
userassist_layer_name = self.context.memory.free_layer_name("userassist_buffer")
|
|
buffer = BufferDataLayer(self.context, self._config_path, userassist_layer_name, userassist_data)
|
|
self.context.add_layer(buffer)
|
|
userassist_obj = self.context.object(
|
|
symbol = self._reg_table_name + constants.BANG + self._userassist_type_name,
|
|
layer_name = userassist_layer_name,
|
|
offset = 0)
|
|
|
|
if self._win7:
|
|
item["id"] = renderers.NotApplicableValue()
|
|
item["count"] = int(userassist_obj.Count)
|
|
|
|
seconds = (userassist_obj.FocusTime + 500) / 1000.0
|
|
time = datetime.timedelta(seconds = seconds) if seconds > 0 else userassist_obj.FocusTime
|
|
item["focus"] = int(userassist_obj.FocusCount)
|
|
item["time"] = str(time)
|
|
|
|
else:
|
|
item["id"] = int(userassist_obj.ID)
|
|
item["count"] = int(userassist_obj.CountStartingAtFive
|
|
if userassist_obj.CountStartingAtFive < 5
|
|
else userassist_obj.CountStartingAtFive - 5)
|
|
item["focus"] = renderers.NotApplicableValue()
|
|
item["time"] = renderers.NotApplicableValue()
|
|
|
|
item["lastupdated"] = utility.wintime_to_datetime(userassist_obj.LastUpdated.QuadPart)
|
|
|
|
return item
|
|
|
|
def _determine_userassist_type(self) -> None:
|
|
"""Determine the userassist type and size depending on the OS version"""
|
|
|
|
if self._win7 is True:
|
|
self._userassist_type_name = "_VOL_USERASSIST_TYPES_7"
|
|
elif self._win7 is False:
|
|
self._userassist_type_name = "_VOL_USERASSIST_TYPES_XP"
|
|
|
|
self._userassist_size = self.context.symbol_space.get_type(
|
|
self._reg_table_name + constants.BANG + self._userassist_type_name).size
|
|
|
|
def _win7_or_later(self) -> bool:
|
|
# TODO: change this if there is a better way of determining the OS version
|
|
# _KUSER_SHARED_DATA.CookiePad is in Windows 6.1 (Win7) and later
|
|
return self.context.symbol_space.get_type(
|
|
self.config['nt_symbols'] + constants.BANG + "_KUSER_SHARED_DATA").has_member('CookiePad')
|
|
|
|
def list_userassist(self, hive: RegistryHive) -> typing.Generator:
|
|
"""Generate userassist data for a registry hive."""
|
|
|
|
hive_name = hive.hive.cast(self.config["nt_symbols"] + constants.BANG + "_CMHIVE").get_name()
|
|
|
|
if self._win7 is None:
|
|
try:
|
|
self._win7 = self._win7_or_later()
|
|
except exceptions.SymbolError:
|
|
# self._win7 will be None and only registry value rawdata will be output
|
|
pass
|
|
|
|
self._determine_userassist_type()
|
|
|
|
userassist_node_path = hive.get_key("software\\microsoft\\windows\\currentversion\\explorer\\userassist",
|
|
return_list = True)
|
|
|
|
if not userassist_node_path:
|
|
vollog.warning("list_userassist did not find a valid node_path (or None)")
|
|
raise StopIteration
|
|
|
|
userassist_node = userassist_node_path[-1]
|
|
# iterate through the GUIDs under the userassist key
|
|
for guidkey in userassist_node.get_subkeys():
|
|
# each guid key should have a Count key in it
|
|
for countkey in guidkey.get_subkeys():
|
|
countkey_path = countkey.get_key_path()
|
|
countkey_last_write_time = utility.wintime_to_datetime(countkey.LastWriteTime.QuadPart)
|
|
|
|
# output the parent Count key
|
|
result = (0,
|
|
(renderers.format_hints.Hex(hive.hive_offset),
|
|
hive_name,
|
|
countkey_path,
|
|
countkey_last_write_time,
|
|
"Key",
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue()))
|
|
yield result
|
|
|
|
# output any subkeys under Count
|
|
for subkey in countkey.get_subkeys():
|
|
|
|
subkey_name = subkey.get_name()
|
|
result = (1, (renderers.format_hints.Hex(hive.hive_offset),
|
|
hive_name,
|
|
countkey_path,
|
|
countkey_last_write_time,
|
|
"Subkey",
|
|
subkey_name,
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),
|
|
renderers.NotApplicableValue(),))
|
|
yield result
|
|
|
|
# output any values under Count
|
|
for value in countkey.get_values():
|
|
|
|
value_name = value.get_name()
|
|
try:
|
|
value_name = codecs.encode(value_name, "rot_13")
|
|
except UnicodeDecodeError:
|
|
pass
|
|
|
|
if self._win7:
|
|
guid = value_name.split("\\")[0]
|
|
if guid in self._folder_guids:
|
|
value_name = value_name.replace(guid, self._folder_guids[guid])
|
|
|
|
userassist_data_dict = self.parse_userassist_data(value)
|
|
result = (1, (renderers.format_hints.Hex(hive.hive_offset),
|
|
hive_name,
|
|
countkey_path,
|
|
countkey_last_write_time,
|
|
"Value",
|
|
value_name,
|
|
userassist_data_dict["id"],
|
|
userassist_data_dict["count"],
|
|
userassist_data_dict["focus"],
|
|
userassist_data_dict["time"],
|
|
userassist_data_dict["lastupdated"],
|
|
format_hints.HexBytes(userassist_data_dict["rawdata"]),))
|
|
yield result
|
|
|
|
def _generator(self):
|
|
|
|
# get all the user hive offsets or use the one specified
|
|
if self.config.get('offset', None) is None:
|
|
try:
|
|
import volatility.plugins.windows.hivelist as hivelist
|
|
hive_offsets = [hive.vol.offset for hive in
|
|
hivelist.HiveList.list_hives(context = self.context,
|
|
layer_name = self.config['primary'],
|
|
symbol_table = self.config['nt_symbols'],
|
|
filter_string = "ntuser.dat")]
|
|
except ImportError:
|
|
vollog.warning("Unable to import windows.hivelist plugin, please provide a hive offset")
|
|
raise ValueError("Unable to import windows.hivelist plugin, please provide a hive offset")
|
|
else:
|
|
hive_offsets = [self.config['offset']]
|
|
|
|
self._reg_table_name = intermed.IntermediateSymbolTable.create(self.context,
|
|
self._config_path,
|
|
'windows',
|
|
'registry')
|
|
|
|
for hive_offset in hive_offsets:
|
|
# Construct the hive
|
|
reg_config_path = self.make_subconfig(hive_offset = hive_offset,
|
|
base_layer = self.config['primary'],
|
|
nt_symbols = self.config['nt_symbols'])
|
|
|
|
hive_name = None
|
|
try:
|
|
hive = RegistryHive(self.context, reg_config_path, name = 'hive' + hex(hive_offset))
|
|
hive_name = hive.hive.cast(self.config["nt_symbols"] + constants.BANG + "_CMHIVE").get_name()
|
|
self.context.memory.add_layer(hive)
|
|
yield from self.list_userassist(hive)
|
|
continue
|
|
except exceptions.PagedInvalidAddressException as excp:
|
|
vollog.debug("Invalid address identified in Hive: {}".format(hex(excp.invalid_address)))
|
|
except KeyError:
|
|
vollog.debug("Key '{}' not found in Hive at offset {}.".format(
|
|
"software\\microsoft\\windows\\currentversion\\explorer\\userassist", hex(hive_offset)))
|
|
|
|
# yield UnreadableValues when an exception occurs for a given hive_offset
|
|
result = (0,
|
|
(renderers.format_hints.Hex(hive_offset),
|
|
hive_name if hive_name else renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue(),
|
|
renderers.UnreadableValue()))
|
|
yield result
|
|
|
|
def run(self):
|
|
|
|
return renderers.TreeGrid([("Hive Offset", renderers.format_hints.Hex),
|
|
("Hive Name", str),
|
|
("Path", str),
|
|
("Last Write Time", datetime.datetime),
|
|
("Type", str),
|
|
("Name", str),
|
|
("ID", int),
|
|
("Count", int),
|
|
("Focus Count", int),
|
|
("Time Focused", str),
|
|
("Last Updated", datetime.datetime),
|
|
("Raw Data", format_hints.HexBytes)],
|
|
self._generator())
|