diff --git a/volatility3/framework/plugins/windows/poolscanner.py b/volatility3/framework/plugins/windows/poolscanner.py index 5cbb2ffc8..7446768c7 100644 --- a/volatility3/framework/plugins/windows/poolscanner.py +++ b/volatility3/framework/plugins/windows/poolscanner.py @@ -79,6 +79,7 @@ class PoolHeaderScanner(interfaces.layers.ScannerInterface): offset=offset - self._header_offset, absolute=True, ) + constraint = self._constraint_lookup[pattern] try: # Size check @@ -128,7 +129,7 @@ class PoolScanner(plugins.PluginInterface): """A generic pool scanner plugin.""" _required_framework_version = (2, 0, 0) - _version = (1, 0, 1) + _version = (1, 1, 1) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -331,11 +332,12 @@ class PoolScanner(plugins.PluginInterface): return [constraint for constraint in builtins if constraint.tag in tags_filter] @classmethod - def generate_pool_scan( + def generate_pool_scan_extended( cls, context: interfaces.context.ContextInterface, - layer_name: str, - symbol_table: str, + kernel_layer_name: str, + kernel_symbol_table: str, + object_symbol_table: str, constraints: List[PoolConstraint], ) -> Generator[ Tuple[ @@ -347,49 +349,60 @@ class PoolScanner(plugins.PluginInterface): None, ]: """ + The extended version of `generate_pool_scan` to support pool scanning for objects outside of the kernel (ntoskrnl). + This requires the symbol table of the object being scanned for. Args: context: The context to retrieve required elements (layers, symbol tables) from - layer_name: The name of the layer on which to operate - symbol_table: The name of the table containing the kernel symbols + kernel_layer_name: The name of the base kernel layer + kernel_symbol_table_name: The name of the table containing the kernel symbols + object_symbol_table_name: The name of the symbol table for the object being scanned for constraints: List of pool constraints used to limit the scan results - Returns: Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object """ # get the object type map type_map = handles.Handles.get_type_map( - context=context, layer_name=layer_name, symbol_table=symbol_table + context=context, + layer_name=kernel_layer_name, + symbol_table=kernel_symbol_table, ) cookie = handles.Handles.find_cookie( - context=context, layer_name=layer_name, symbol_table=symbol_table + context=context, + layer_name=kernel_layer_name, + symbol_table=kernel_symbol_table, ) - is_windows_10 = versions.is_windows_10(context, symbol_table) - is_windows_8_or_later = versions.is_windows_8_or_later(context, symbol_table) + is_windows_10 = versions.is_windows_10(context, kernel_symbol_table) + is_windows_8_or_later = versions.is_windows_8_or_later( + context, kernel_symbol_table + ) # start off with the primary virtual layer - scan_layer = layer_name + scan_layer = kernel_layer_name # switch to a non-virtual layer if necessary if not is_windows_10: scan_layer = context.layers[scan_layer].config["memory_layer"] - if symbols.symbol_table_is_64bit(context, symbol_table): + if symbols.symbol_table_is_64bit(context, kernel_symbol_table): alignment = 0x10 else: alignment = 8 + # scan in the main kernel layer for the object(s) for constraint, header in cls.pool_scan( - context, scan_layer, symbol_table, constraints, alignment=alignment + context, scan_layer, object_symbol_table, constraints, alignment=alignment ): + + # construct the object in its own layer, using its own types mem_objects = header.get_object( constraint=constraint, use_top_down=is_windows_8_or_later, - native_layer_name=layer_name, - kernel_symbol_table=symbol_table, + native_layer_name=kernel_layer_name, + kernel_symbol_table=kernel_symbol_table, ) for mem_object in mem_objects: @@ -398,6 +411,7 @@ class PoolScanner(plugins.PluginInterface): constants.LOGLEVEL_VVV, f"Cannot create an instance of {constraint.type_name}", ) + continue if constraint.object_type is not None and not constraint.skip_type_test: @@ -418,6 +432,40 @@ class PoolScanner(plugins.PluginInterface): yield constraint, mem_object, header + @classmethod + def generate_pool_scan( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + constraints: List[PoolConstraint], + ) -> Generator[ + Tuple[ + PoolConstraint, + interfaces.objects.ObjectInterface, + interfaces.objects.ObjectInterface, + ], + None, + None, + ]: + """ + The original version of `generate_pool_scan` which is sufficient for objects in the kernel (ntoskrnl), + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + layer_name: The name of the layer on which to operate + symbol_table: The name of the table containing the kernel symbols + constraints: List of pool constraints used to limit the scan results + + Returns: + Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object + """ + + # repeat the symbol table to match the original `generate_pool_scan` behaviour + yield from cls.generate_pool_scan_extended( + context, layer_name, symbol_table, symbol_table, constraints + ) + @classmethod def pool_scan( cls,