diff --git a/volatility/framework/plugins/windows/mutantscan.py b/volatility/framework/plugins/windows/mutantscan.py new file mode 100644 index 000000000..1528e3509 --- /dev/null +++ b/volatility/framework/plugins/windows/mutantscan.py @@ -0,0 +1,74 @@ +# This file was contributed to the Volatility Framework Version 3. +# Copyright (C) 2018 Volatility Foundation. +# +# THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE Volatility Contributors +# Public License V1.0("LICENSE") AS FIRST COMPLETED BY: Volatility Foundation, +# Inc. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION +# OF, OR PREPARATION OF SUBSEQUENT WORKS, DERIVATIVE WORKS OR DERIVED WORKS BASED +# ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS +# TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED +# WORK,” “RECIPIENT" AND “DISTRIBUTOR" ARE DEFINED IN THE LICENSE. A COPY OF THE +# LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.txt" ACCOMPANYING THE +# CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A +# COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE: +# https://www.volatilityfoundation.org/license/vcpl_v1.0 +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +# specific language governing rights and limitations under the License. +# + +from typing import Iterable + +import volatility.framework.interfaces.plugins as plugins +from volatility.framework import renderers, interfaces, exceptions +from volatility.framework.configuration import requirements +from volatility.framework.renderers import format_hints +import volatility.plugins.windows.poolscanner as poolscanner + +class MutantScan(plugins.PluginInterface): + """Scans for mutexes present in a particular windows memory image""" + + @classmethod + def get_requirements(cls): + return [ + requirements.TranslationLayerRequirement( + name = 'primary', description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), + requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), + ] + + @classmethod + def scan_mutants(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str) -> \ + Iterable[interfaces.objects.ObjectInterface]: + """Scans for mutants using the poolscanner module and constraints""" + + constraints = poolscanner.PoolScanner.builtin_constraints(symbol_table, + [b'Mut\xe1', b'Muta']) + + for result in poolscanner.PoolScanner.generate_pool_scan(context, + layer_name, + symbol_table, + constraints): + + constraint, mem_object, _header = result + if constraint.object_type == "Mutant": + yield mem_object + + def _generator(self): + for mutant in self.scan_mutants(self.context, + self.config['primary'], + self.config['nt_symbols']): + + try: + name = mutant.get_name() + except exceptions.InvalidAddressException: + name = renderers.NotApplicableValue() + + yield (0, (format_hints.Hex(mutant.vol.offset), name)) + + def run(self): + return renderers.TreeGrid([("Offset", format_hints.Hex), ("Name", str),], + self._generator()) diff --git a/volatility/framework/plugins/windows/poolscanner.py b/volatility/framework/plugins/windows/poolscanner.py index 9810b98a6..d302fc17d 100644 --- a/volatility/framework/plugins/windows/poolscanner.py +++ b/volatility/framework/plugins/windows/poolscanner.py @@ -204,6 +204,20 @@ class PoolScanner(plugins.PluginInterface): object_type = "File", size = (150, None), page_type = PoolType.PAGED | PoolType.NONPAGED | PoolType.FREE), + # mutants on windows before windows 8 + PoolConstraint( + b'Mut\xe1', + type_name = symbol_table + constants.BANG + "_KMUTANT", + object_type = "Mutant", + size = (64, None), + page_type = PoolType.PAGED | PoolType.NONPAGED | PoolType.FREE), + # mutants on windows starting with windows 8 + PoolConstraint( + b'Muta', + type_name = symbol_table + constants.BANG + "_KMUTANT", + object_type = "Mutant", + size = (64, None), + page_type = PoolType.PAGED | PoolType.NONPAGED | PoolType.FREE), ] if not tags_filter: diff --git a/volatility/framework/symbols/windows/__init__.py b/volatility/framework/symbols/windows/__init__.py index d7c8e0862..846ab62bc 100644 --- a/volatility/framework/symbols/windows/__init__.py +++ b/volatility/framework/symbols/windows/__init__.py @@ -46,6 +46,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('_MMVAD_SHORT', extensions._MMVAD_SHORT) self.set_type_class('_MMVAD', extensions._MMVAD) self.set_type_class('_KSYSTEM_TIME', extensions._KSYSTEM_TIME) + self.set_type_class('_KMUTANT', extensions._KMUTANT) # This doesn't exist in very specific versions of windows try: diff --git a/volatility/framework/symbols/windows/extensions/__init__.py b/volatility/framework/symbols/windows/extensions/__init__.py index cec117deb..39db102eb 100644 --- a/volatility/framework/symbols/windows/extensions/__init__.py +++ b/volatility/framework/symbols/windows/extensions/__init__.py @@ -457,6 +457,17 @@ class _FILE_OBJECT(objects.Struct, ExecutiveObject): return name +class _KMUTANT(objects.Struct, ExecutiveObject): + """A class for windows mutant objects""" + + def is_valid(self) -> bool: + """Determine if the object is valid""" + return True + + def get_name(self) -> str: + """Get the object's name from the object header""" + header = self.object_header() + return header.NameInfo.Name.String # type: ignore class _OBJECT_HEADER(objects.Struct): """A class for the headers for executive kernel objects, which contains