Windows Shimcachemem: Updates req's, new major version

This updates the ShimcacheMem plugins with the version bumps on the
modules and pslist requirements, updates the corresponding method calls,
and does a major version bump of its own due to a required breaking
method signature change.

Co-authored-by: Andrew Case <andrew@dfir.org>
This commit is contained in:
David McDonald
2025-03-05 17:59:38 -06:00
co-authored by Andrew Case
parent ae6ced5888
commit 886faec474
@@ -65,13 +65,13 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
name="pslist", plugin=pslist.PsList, version=(3, 0, 0)
),
requirements.VersionRequirement(
name="vadinfo", component=vadinfo.VadInfo, version=(2, 0, 0)
),
requirements.VersionRequirement(
name="modules", component=modules.Modules, version=(2, 0, 0)
name="modules", component=modules.Modules, version=(3, 0, 0)
),
]
@@ -79,7 +79,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
def create_shimcache_table(
cls,
context: interfaces.context.ContextInterface,
symbol_table: str,
symbol_table_name: str,
config_path: str,
) -> str:
"""Creates a shimcache symbol table
@@ -92,16 +92,16 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
Returns:
The name of the constructed shimcache table
"""
native_types = context.symbol_space[symbol_table].natives
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
table_mapping = {"nt_symbols": symbol_table}
native_types = context.symbol_space[symbol_table_name].natives
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table_name)
table_mapping = {"nt_symbols": symbol_table_name}
try:
symbol_filename = next(
filename
for version_check, for_64bit, filename in ShimcacheMem._win_version_file_map
if is_64bit == for_64bit
and version_check(context=context, symbol_table=symbol_table)
and version_check(context=context, symbol_table=symbol_table_name)
)
except StopIteration:
raise NotImplementedError("This version of Windows is not supported!")
@@ -122,8 +122,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
def find_shimcache_win_xp(
cls,
context: interfaces.context.ContextInterface,
layer_name: str,
kernel_symbol_table: str,
kernel_module_name: str,
shimcache_symbol_table: str,
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
"""Attempts to find the shimcache in a Windows XP memory image
@@ -142,9 +141,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
seen = set()
for process in pslist.PsList.list_processes(
context, layer_name, kernel_symbol_table
):
for process in pslist.PsList.list_processes(context, kernel_module_name):
pid = process.UniqueProcessId
vollog.debug("checking process %d", pid)
for vad in vadinfo.VadInfo.list_vads(
@@ -219,8 +216,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
cls,
context: interfaces.context.ContextInterface,
config_path: str,
kernel_layer_name: str,
nt_symbol_table: str,
kernel_module_name: str,
shimcache_symbol_table: str,
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
"""Implements the algorithm to search for the shim cache on Windows 2000
@@ -239,31 +235,33 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
"""
kernel = context.modules[kernel_module_name]
data_sec = cls.get_module_section_range(
context,
config_path,
kernel_layer_name,
nt_symbol_table,
kernel_module_name,
cls.NT_KRNL_MODS,
".data",
)
mod_page = cls.get_module_section_range(
context,
config_path,
kernel_layer_name,
nt_symbol_table,
kernel_module_name,
cls.NT_KRNL_MODS,
"PAGE",
)
# We require both in order to accurately handle AVL table
if not (data_sec and mod_page):
return None
return
data_sec_offset, data_sec_size = data_sec
mod_page_offset, mod_page_size = mod_page
addr_size = 8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4
addr_size = (
8 if symbols.symbol_table_is_64bit(context, kernel.symbol_table_name) else 4
)
shim_head = None
for offset in range(
@@ -272,8 +270,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
shim_head = cls.try_get_shim_head_at_offset(
context,
shimcache_symbol_table,
nt_symbol_table,
kernel_layer_name,
kernel_module_name,
mod_page_offset,
mod_page_offset + mod_page_size,
offset,
@@ -293,9 +290,8 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
def try_get_shim_head_at_offset(
cls,
context: interfaces.context.ContextInterface,
symbol_table: str,
kernel_symbol_table: str,
layer_name: str,
shimcache_symbol_table: str,
kernel_module_name: str,
mod_page_start: int,
mod_page_end: int,
offset: int,
@@ -307,9 +303,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
If a number of validity checks are passed, this method will return the `SHIM_CACHE_HEAD`
object. Otherwise, `None` is returned.
"""
kernel = context.modules[kernel_module_name]
# Check RTL_AVL_TABLE at offset
rtl_avl_table = context.object(
symbol_table + constants.BANG + "_RTL_AVL_TABLE", layer_name, offset
shimcache_symbol_table + constants.BANG + "_RTL_AVL_TABLE",
kernel.layer_name,
offset,
)
if not rtl_avl_table.is_valid(mod_page_start, mod_page_end):
return None
@@ -317,11 +318,11 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
vollog.debug(f"Candidate RTL_AVL_TABLE found at offset {offset:#x}")
ersrc_size = context.symbol_space.get_type(
kernel_symbol_table + constants.BANG + "_ERESOURCE"
kernel.symbol_table_name + constants.BANG + "_ERESOURCE"
).size
ersrc_alignment = (
0x20
if symbols.symbol_table_is_64bit(context, kernel_symbol_table)
if symbols.symbol_table_is_64bit(context, kernel.symbol_table_name)
else 0x10
# 0x20 if context.symbol_space.get_type("pointer").size == 8 else 0x10
)
@@ -334,8 +335,8 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
vollog.debug(f"Constructing ERESOURCE at {eresource_offset:#x}")
eresource = context.object(
kernel_symbol_table + constants.BANG + "_ERESOURCE",
layer_name,
kernel.symbol_table_name + constants.BANG + "_ERESOURCE",
kernel.layer_name,
eresource_offset,
)
if not eresource.is_valid():
@@ -344,12 +345,12 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
shim_head_offset = offset + rtl_avl_table.vol.size
if not context.layers[layer_name].is_valid(shim_head_offset):
if not context.layers[kernel.layer_name].is_valid(shim_head_offset):
return None
shim_head = context.object(
symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
layer_name,
shimcache_symbol_table + constants.BANG + "SHIM_CACHE_ENTRY",
kernel.layer_name,
shim_head_offset,
)
@@ -365,8 +366,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
cls,
context: interfaces.context.ContextInterface,
config_path: str,
kernel_layer_name: str,
nt_symbol_table: str,
kernel_module_name: str,
shimcache_symbol_table: str,
) -> Iterator[shimcache.SHIM_CACHE_ENTRY]:
"""Attempts to locate and yield shimcache entries from a Windows 8 or later memory image.
@@ -376,10 +376,11 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
:param kernel_symbol_table: The name of an existing symbol table containing the kernel symbols
:param shimcache_symbol_table: The name of a symbol table containing the hand-crafted shimcache symbols
"""
kernel = context.modules[kernel_module_name]
is_8_1_or_later = versions.is_windows_8_1_or_later(
context, nt_symbol_table
) or versions.is_win10(context, nt_symbol_table)
context, kernel.symbol_table_name
) or versions.is_win10(context, kernel.symbol_table_name)
module_names = ["ahcache.sys"] if is_8_1_or_later else cls.NT_KRNL_MODS
vollog.debug(f"Searching for modules {module_names}")
@@ -387,16 +388,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
data_sec = cls.get_module_section_range(
context,
config_path,
kernel_layer_name,
nt_symbol_table,
kernel_module_name,
module_names,
".data",
)
mod_page = cls.get_module_section_range(
context,
config_path,
kernel_layer_name,
nt_symbol_table,
kernel_module_name,
module_names,
"PAGE",
)
@@ -419,12 +418,16 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
for offset in range(
data_sec_offset,
data_sec_offset + data_sec_size,
8 if symbols.symbol_table_is_64bit(context, nt_symbol_table) else 4,
(
8
if symbols.symbol_table_is_64bit(context, kernel.symbol_table_name)
else 4
),
):
vollog.debug(f"Building shim handle pointer at {offset:#x}")
shim_handle = context.object(
object_type=shimcache_symbol_table + constants.BANG + "pointer",
layer_name=kernel_layer_name,
layer_name=kernel.layer_name,
subtype=handle_type,
offset=offset,
)
@@ -445,7 +448,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
# On Windows 8 x64, the first cache contains the shim cache.
# On Windows 8 x86, 8.1 x86/x64, and 10, the second cache contains the shim cache.
if (
not symbols.symbol_table_is_64bit(context, nt_symbol_table)
not symbols.symbol_table_is_64bit(context, kernel.symbol_table_name)
and not is_8_1_or_later
):
valid_head = shim_heads[1]
@@ -474,8 +477,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
entries = self.find_shimcache_win_8_or_later(
self.context,
self.config_path,
kernel.layer_name,
kernel.symbol_table_name,
self.config["kernel"],
shimcache_table_name,
)
@@ -488,8 +490,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
entries = self.find_shimcache_win_2k3_to_7(
self.context,
self.config_path,
kernel.layer_name,
kernel.symbol_table_name,
self.config["kernel"],
shimcache_table_name,
)
@@ -499,8 +500,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
vollog.info("Finding shimcache entries for WinXP")
entries = self.find_shimcache_win_xp(
self._context,
kernel.layer_name,
kernel.symbol_table_name,
self.config["kernel"],
shimcache_table_name,
)
else:
@@ -547,8 +547,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
cls,
context: interfaces.context.ContextInterface,
config_path: str,
layer_name: str,
symbol_table: str,
kernel_module_name: str,
module_list: List[str],
section_name: str,
) -> Optional[Tuple[int, int]]:
@@ -566,14 +565,14 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
try:
krnl_mod = next(
module
for module in modules.Modules.list_modules(
context, layer_name, symbol_table
)
for module in modules.Modules.list_modules(context, kernel_module_name)
if module.BaseDllName.String in module_list
)
except StopIteration:
return None
kernel = context.modules[kernel_module_name]
pe_table_name = intermed.IntermediateSymbolTable.create(
context,
interfaces.configuration.path_join(config_path, "pe"),
@@ -585,7 +584,7 @@ class ShimcacheMem(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterf
# code taken from Win32KBase._section_chunks (win32_core.py)
dos_header = context.object(
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
layer_name,
kernel.layer_name,
offset=krnl_mod.DllBase,
)