diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py new file mode 100644 index 000000000..31f4711b1 --- /dev/null +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -0,0 +1,61 @@ +# 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 +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) + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + 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) -> 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, + 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) -> renderers.TreeGrid: + + return renderers.TreeGrid([ + ("Offset", format_hints.Hex), + ("Known Exception", bool), + ("Driver Name", str), + ("Service 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))