From 85dcd04961f76c095bce069102d2d6ba4b4a9411 Mon Sep 17 00:00:00 2001 From: Andrew Case Date: Fri, 28 Oct 2022 19:59:53 +0000 Subject: [PATCH 1/3] Add drivermodule plugin and wrap common access to a Drver's names in a method --- .../framework/plugins/windows/drivermodule.py | 60 +++++++++++++++++++ .../framework/plugins/windows/driverscan.py | 45 +++++++++----- 2 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 volatility3/framework/plugins/windows/drivermodule.py diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py new file mode 100644 index 000000000..54a132adb --- /dev/null +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -0,0 +1,60 @@ +# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# + +from volatility3.framework import renderers, interfaces +from volatility3.framework.configuration import requirements +from volatility3.framework.renderers import format_hints +from volatility3.plugins.windows import ssdt, driverscan + +# built in Windows-components that trigger false positives +KNOWN_DRIVERS = ["ACPI_HAL", + "PnpManager", + "RAW", + "WMIxWDM", + "Win32k", + "Fs_Rec"] + +class DriverModule(interfaces.plugins.PluginInterface): + """Determines if any loaded drivers were hidden by a rootkit""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + return [ + requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', + architectures = ["Intel32", "Intel64"]), + requirements.PluginRequirement(name = 'ssdt', plugin = ssdt.SSDT, version = (1, 0, 0)), + requirements.PluginRequirement(name = 'driverscan', plugin = driverscan.DriverScan, version = (1, 0, 0)), + ] + + def _generator(self): + """ + Attempt to match each driver's start code address to a known kernel module + A common rootkit technique is to register drivers from modules that are hidden, + which allows us to detect the disconnect between a malicious driver and its hidden module. + """ + kernel = self.context.modules[self.config['kernel']] + + collection = ssdt.SSDT.build_module_collection(self.context, kernel.layer_name, kernel.symbol_table_name) + + for driver in driverscan.DriverScan.scan_drivers(self.context, kernel.layer_name, kernel.symbol_table_name): + # we do not care about actual symbol names, we just want to know if the driver points to a known module + module_symbols = list(collection.get_module_symbols_by_absolute_location(driver.DriverStart)) + if not module_symbols: + driver_name, service_key, name = driverscan.DriverScan.get_names_for_driver(driver) + + known_exception = driver_name in KNOWN_DRIVERS + + yield (0, (format_hints.Hex(driver.vol.offset), known_exception, driver_name, service_key, name)) + + def run(self): + + return renderers.TreeGrid([ + ("Offset", format_hints.Hex), + ("Known Exception", bool), + ("Driver Name", str), + ("Serivce Key", str), + ("Alternative Name", str), + ], self._generator()) diff --git a/volatility3/framework/plugins/windows/driverscan.py b/volatility3/framework/plugins/windows/driverscan.py index 2cf309014..60ac0d67a 100644 --- a/volatility3/framework/plugins/windows/driverscan.py +++ b/volatility3/framework/plugins/windows/driverscan.py @@ -48,25 +48,40 @@ class DriverScan(interfaces.plugins.PluginInterface): _constraint, mem_object, _header = result yield mem_object + @classmethod + def get_names_for_driver(cls, driver): + """ + Convenience method for getting the commonly used + names associated with a driver + + Args: + driver: A Eriver object + + Returns: + A tuple of strings of (driver name, service key, driver alt. name) + """ + try: + driver_name = driver.get_driver_name() + except (ValueError, exceptions.InvalidAddressException): + driver_name = renderers.NotApplicableValue() + + try: + service_key = driver.DriverExtension.ServiceKeyName.String + except exceptions.InvalidAddressException: + service_key = renderers.NotApplicableValue() + + try: + name = driver.DriverName.String + except exceptions.InvalidAddressException: + name = renderers.NotApplicableValue() + + return driver_name, service_key, name + def _generator(self): kernel = self.context.modules[self.config['kernel']] for driver in self.scan_drivers(self.context, kernel.layer_name, kernel.symbol_table_name): - - try: - driver_name = driver.get_driver_name() - except (ValueError, exceptions.InvalidAddressException): - driver_name = renderers.NotApplicableValue() - - try: - service_key = driver.DriverExtension.ServiceKeyName.String - except exceptions.InvalidAddressException: - service_key = renderers.NotApplicableValue() - - try: - name = driver.DriverName.String - except exceptions.InvalidAddressException: - name = renderers.NotApplicableValue() + driver_name, service_key, name = self.get_names_for_driver(driver) yield (0, (format_hints.Hex(driver.vol.offset), format_hints.Hex(driver.DriverStart), format_hints.Hex(driver.DriverSize), service_key, driver_name, name)) From 380b76a90d7012937749b206163668abdc13e53c Mon Sep 17 00:00:00 2001 From: ikelos Date: Thu, 3 Nov 2022 01:05:40 +0000 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Donghyun Kim --- volatility3/framework/plugins/windows/drivermodule.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py index 54a132adb..1a97edb80 100644 --- a/volatility3/framework/plugins/windows/drivermodule.py +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -19,9 +19,10 @@ class DriverModule(interfaces.plugins.PluginInterface): """Determines if any loaded drivers were hidden by a rootkit""" _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) @classmethod - def get_requirements(cls): + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: return [ requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel', architectures = ["Intel32", "Intel64"]), @@ -29,7 +30,7 @@ class DriverModule(interfaces.plugins.PluginInterface): requirements.PluginRequirement(name = 'driverscan', plugin = driverscan.DriverScan, version = (1, 0, 0)), ] - def _generator(self): + def _generator(self) -> Iterator[Tuple]: """ Attempt to match each driver's start code address to a known kernel module A common rootkit technique is to register drivers from modules that are hidden, @@ -49,12 +50,12 @@ class DriverModule(interfaces.plugins.PluginInterface): yield (0, (format_hints.Hex(driver.vol.offset), known_exception, driver_name, service_key, name)) - def run(self): + def run(self) -> renderers.TreeGrid: return renderers.TreeGrid([ ("Offset", format_hints.Hex), ("Known Exception", bool), ("Driver Name", str), - ("Serivce Key", str), + ("Service Key", str), ("Alternative Name", str), ], self._generator()) From c1dcfe8b570bcaad184721b9b244ccfe5a6e2c3f Mon Sep 17 00:00:00 2001 From: ikelos Date: Thu, 3 Nov 2022 01:11:18 +0000 Subject: [PATCH 3/3] Update volatility3/framework/plugins/windows/drivermodule.py --- volatility3/framework/plugins/windows/drivermodule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py index 1a97edb80..31f4711b1 100644 --- a/volatility3/framework/plugins/windows/drivermodule.py +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -1,7 +1,7 @@ # This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # - +from typing import Iterator, List, Tuple from volatility3.framework import renderers, interfaces from volatility3.framework.configuration import requirements from volatility3.framework.renderers import format_hints