Add error checking when accessing the KVO

Also bumps hivescan to take a module name rather than a
layer_name/symbol_table combo.
This commit is contained in:
Mike Auty
2025-02-02 16:07:32 +00:00
parent df310c0d92
commit 144a7cc3f5
16 changed files with 143 additions and 65 deletions
@@ -638,13 +638,10 @@ class ModuleRequirement(
self.add_requirement(
TranslationLayerRequirement(name="layer_name", architectures=architectures)
)
self.add_requirement(SymbolTableRequirement(name="symbol_table_name"))
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
IntRequirement(name="offset"),
]
return interfaces.context.ModuleInterface.get_requirements()
def unsatisfied(
self, context: "interfaces.context.ContextInterface", config_path: str
@@ -158,6 +158,14 @@ class ModuleInterface(interfaces.configuration.ConfigurableInterface):
super().__init__(context, config_path)
self._module_name = name
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
# Can't include the translation layer without knowing the architectures
return [
SymbolTableRequirement(name="symbol_table_name"),
IntRequirement(name="offset"),
]
@property
def _layer_name(self) -> str:
return self.config["layer_name"]
@@ -21,7 +21,7 @@ class BigPools(interfaces.plugins.PluginInterface):
"""List big page pools."""
_required_framework_version = (2, 0, 0)
_version = (1, 1, 0)
_version = (1, 1, 1)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -66,7 +66,11 @@ class BigPools(interfaces.plugins.PluginInterface):
Yields:
A big page pool object
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
big_page_table_offset = ntkrnlmp.get_symbol("PoolBigPageTable").address
@@ -28,7 +28,7 @@ class Callbacks(interfaces.plugins.PluginInterface):
"""Lists kernel callbacks and notification routines."""
_required_framework_version = (2, 0, 0)
_version = (2, 0, 0)
_version = (2, 0, 1)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -361,7 +361,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
A name, location and optional detail string
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
is_vista_or_later = versions.is_vista_or_later(
@@ -418,7 +422,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
Lists all registry callbacks from the old format via the CmpCallBackVector.
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
full_type_name = (
callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK"
@@ -465,7 +473,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
Lists all registry callbacks via the CallbackListHead.
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
full_type_name = callback_table_name + constants.BANG + "_CM_CALLBACK_ENTRY"
@@ -506,7 +518,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
A name, location and optional detail string
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
if ntkrnlmp.has_symbol("CmpCallBackVector") and ntkrnlmp.has_symbol(
@@ -562,7 +578,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
A name, location and optional detail string
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
try:
@@ -626,7 +646,11 @@ class Callbacks(interfaces.plugins.PluginInterface):
A name, location and optional detail string
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
try:
@@ -18,7 +18,7 @@ class Handles(interfaces.plugins.PluginInterface):
"""Lists process open handles."""
_required_framework_version = (2, 0, 0)
_version = (2, 0, 0)
_version = (2, 0, 1)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -144,7 +144,11 @@ class Handles(interfaces.plugins.PluginInterface):
type_map: Dict[int, str] = {}
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
try:
@@ -202,7 +206,11 @@ class Handles(interfaces.plugins.PluginInterface):
except exceptions.SymbolError:
return None
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
return context.object(
symbol_table + constants.BANG + "unsigned int",
layer_name,
@@ -216,7 +224,7 @@ class Handles(interfaces.plugins.PluginInterface):
kernel = self.context.modules[self.config["kernel"]]
virtual = kernel.layer_name
kvo = self.context.layers[virtual].config["kernel_virtual_offset"]
kvo = kernel.offset
ntkrnlmp = self.context.module(
kernel.symbol_table_name, layer_name=virtual, offset=kvo
@@ -17,7 +17,7 @@ class Info(plugins.PluginInterface):
"""Show OS & kernel details of the memory sample being analyzed."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 0)
_version = (1, 0, 1)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -68,7 +68,9 @@ class Info(plugins.PluginInterface):
if not isinstance(virtual_layer, layers.intel.Intel):
raise TypeError("Virtual Layer is not an intel layer")
kvo = virtual_layer.config["kernel_virtual_offset"]
kvo = virtual_layer.config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError("Intel layer has no kernel virtual offset defined")
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
return ntkrnlmp
@@ -166,7 +168,9 @@ class Info(plugins.PluginInterface):
if not isinstance(virtual_layer, layers.intel.Intel):
raise TypeError("Virtual Layer is not an intel layer")
kvo = virtual_layer.config["kernel_virtual_offset"]
kvo = virtual_layer.config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError("Intel layer has no kernel virtual offset defined")
pe_table_name = intermed.IntermediateSymbolTable.create(
context,
@@ -18,7 +18,7 @@ class Modules(interfaces.plugins.PluginInterface):
"""Lists the loaded kernel modules."""
_required_framework_version = (2, 0, 0)
_version = (2, 0, 0)
_version = (2, 0, 1)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -247,7 +247,11 @@ class Modules(interfaces.plugins.PluginInterface):
A list of Modules as retrieved from PsLoadedModuleList
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
try:
@@ -22,7 +22,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
"""Lists the processes present in a particular windows memory image."""
_required_framework_version = (2, 0, 0)
_version = (2, 0, 0)
_version = (2, 0, 1)
PHYSICAL_DEFAULT = False
@classmethod
@@ -226,7 +226,11 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
"""
# We only use the object factory to demonstrate how to use one
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
ps_aph_offset = ntkrnlmp.get_symbol("PsActiveProcessHead").address
@@ -23,7 +23,7 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
"""Scans for processes present in a particular windows memory image."""
_required_framework_version = (2, 3, 1)
_version = (1, 1, 0)
_version = (1, 1, 1)
@classmethod
def get_requirements(cls):
@@ -194,9 +194,12 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
# If it's WinXP->8.1 we have now a physical process address.
# We'll use the first thread to bounce back to the virtual process
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
tleoffset = ntkrnlmp.get_type("_ETHREAD").relative_child_offset(
"ThreadListEntry"
)
@@ -41,7 +41,7 @@ class HiveGenerator:
class HiveList(interfaces.plugins.PluginInterface):
"""Lists the registry hives present in a particular memory image."""
_version = (1, 0, 0)
_version = (1, 0, 1)
_required_framework_version = (2, 0, 0)
@classmethod
@@ -59,7 +59,7 @@ class HiveList(interfaces.plugins.PluginInterface):
default=None,
),
requirements.PluginRequirement(
name="hivescan", plugin=hivescan.HiveScan, version=(1, 0, 0)
name="hivescan", plugin=hivescan.HiveScan, version=(2, 0, 0)
),
requirements.BooleanRequirement(
name="dump",
@@ -215,7 +215,11 @@ class HiveList(interfaces.plugins.PluginInterface):
"""
# We only use the object factory to demonstrate how to use one
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
list_head = ntkrnlmp.get_symbol("CmpHiveListHead").address
@@ -278,9 +282,7 @@ class HiveList(interfaces.plugins.PluginInterface):
f"Hivelist failed traversing backwards at {hex(backward_invalid)}, a different "
"location from forwards, revert to scanning"
)
for hive in hivescan.HiveScan.scan_hives(
context, layer_name, symbol_table
):
for hive in hivescan.HiveScan.scan_hives(context, ntkrnlmp.name):
try:
if hive.HiveList.Flink:
start_hive_offset = hive.HiveList.Flink - reloff
@@ -15,7 +15,7 @@ class HiveScan(interfaces.plugins.PluginInterface):
"""Scans for registry hives present in a particular windows memory image."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 0)
_version = (2, 0, 0)
@classmethod
def get_requirements(cls):
@@ -35,10 +35,7 @@ class HiveScan(interfaces.plugins.PluginInterface):
@classmethod
def scan_hives(
cls,
context: interfaces.context.ContextInterface,
layer_name: str,
symbol_table: str,
cls, context: interfaces.context.ContextInterface, kernel_name: str
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Scans for hives using the poolscanner module and constraints or bigpools module with tag.
@@ -51,17 +48,21 @@ class HiveScan(interfaces.plugins.PluginInterface):
A list of Hive objects as found from the `layer_name` layer based on Hive pool signatures
"""
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
kernel = context.modules[kernel_name]
is_64bit = symbols.symbol_table_is_64bit(context, kernel.symbol_table_name)
is_windows_8_1_or_later = versions.is_windows_8_1_or_later(
context=context, symbol_table=symbol_table
context=context, symbol_table=kernel.symbol_table_name
)
if is_windows_8_1_or_later and is_64bit:
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
ntkrnlmp = kernel
for pool in bigpools.BigPools.list_big_pools(
context, layer_name=layer_name, symbol_table=symbol_table, tags=["CM10"]
context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
tags=["CM10"],
):
cmhive = ntkrnlmp.object(
object_type="_CMHIVE", offset=pool.Va, absolute=True
@@ -70,21 +71,17 @@ class HiveScan(interfaces.plugins.PluginInterface):
else:
constraints = poolscanner.PoolScanner.builtin_constraints(
symbol_table, [b"CM10"]
kernel.symbol_table_name, [b"CM10"]
)
for result in poolscanner.PoolScanner.generate_pool_scan(
context, layer_name, symbol_table, constraints
context, kernel.layer_name, kernel.symbol_table_name, constraints
):
_constraint, mem_object, _header = result
yield mem_object
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
for hive in self.scan_hives(
self.context, kernel.layer_name, kernel.symbol_table_name
):
for hive in self.scan_hives(self.context, self.config["kernel"]):
yield (0, (format_hints.Hex(hive.vol.offset),))
def run(self):
@@ -19,7 +19,7 @@ class SSDT(plugins.PluginInterface):
"""Lists the system call table."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 0)
_version = (1, 0, 1)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -89,9 +89,8 @@ class SSDT(plugins.PluginInterface):
self.context, layer_name, kernel.symbol_table_name
)
kvo = self.context.layers[layer_name].config["kernel_virtual_offset"]
ntkrnlmp = self.context.module(
kernel.symbol_table_name, layer_name=layer_name, offset=kvo
kernel.symbol_table_name, layer_name=kernel.offset, offset=kvo
)
# this is just one way to enumerate the native (NT) service table.
@@ -22,7 +22,7 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
"""Lists the unloaded kernel modules."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 1)
_version = (1, 0, 2)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -88,7 +88,11 @@ class UnloadedModules(interfaces.plugins.PluginInterface, timeliner.TimeLinerInt
A list of Unloaded Modules as retrieved from MmUnloadedDrivers
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
unloadedmodules_offset = ntkrnlmp.get_symbol("MmUnloadedDrivers").address
unloadedmodules = ntkrnlmp.object(
@@ -34,7 +34,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
"""Lists process memory ranges."""
_required_framework_version = (2, 4, 0)
_version = (2, 0, 0)
_version = (2, 0, 1)
MAXSIZE_DEFAULT = 1024 * 1024 * 1024 # 1 Gb
def __init__(self, *args, **kwargs):
@@ -99,7 +99,11 @@ class VadInfo(interfaces.plugins.PluginInterface):
symbol_table: The name of the table containing the kernel symbols
"""
kvo = context.layers[layer_name].config["kernel_virtual_offset"]
kvo = context.layers[layer_name].config.get("kernel_virtual_offset", None)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo)
addr = ntkrnlmp.get_symbol("MmProtectToValue").address
values = ntkrnlmp.object(
@@ -17,6 +17,7 @@ class VirtMap(interfaces.plugins.PluginInterface):
"""Lists virtual mapped sections."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 1)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -147,7 +148,7 @@ class VirtMap(interfaces.plugins.PluginInterface):
module = self.context.module(
kernel.symbol_table_name,
layer_name=layer.name,
offset=layer.config["kernel_virtual_offset"],
offset=kernel.offset,
)
return renderers.TreeGrid(
@@ -838,9 +838,14 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
return renderers.NotApplicableValue()
symbol_table_name = self.get_symbol_table_name()
kvo = self._context.layers[self.vol.native_layer_name].config[
"kernel_virtual_offset"
]
kvo = self._context.layers[self.vol.native_layer_name].config.get(
"kernel_virtual_offset", None
)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = self._context.module(
symbol_table_name,
layer_name=self.vol.native_layer_name,
@@ -1030,7 +1035,13 @@ class TOKEN(objects.StructType):
if self.UserAndGroupCount < 0xFFFF:
layer_name = self.vol.layer_name
kvo = self._context.layers[layer_name].config["kernel_virtual_offset"]
kvo = self._context.layers[layer_name].config.get(
"kernel_virtual_offset", None
)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
symbol_table = self.get_symbol_table_name()
ntkrnlmp = self._context.module(
symbol_table, layer_name=layer_name, offset=kvo
@@ -1132,9 +1143,13 @@ class KTIMER(objects.StructType):
def get_dpc(self):
"""Return Dpc, and if Windows 7 or later, decode it"""
symbol_table_name = self.get_symbol_table_name()
kvo = self._context.layers[self.vol.native_layer_name].config[
"kernel_virtual_offset"
]
kvo = self._context.layers[self.vol.native_layer_name].config.get(
"kernel_virtual_offset", None
)
if not kvo:
raise ValueError(
"Intel layer does not have an associatd kernel virtual offset, failing"
)
ntkrnlmp = self._context.module(
symbol_table_name,
layer_name=self.vol.native_layer_name,