diff --git a/volatility/framework/plugins/windows/bigpools.py b/volatility/framework/plugins/windows/bigpools.py new file mode 100644 index 000000000..70351e5ce --- /dev/null +++ b/volatility/framework/plugins/windows/bigpools.py @@ -0,0 +1,138 @@ +# 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 +# + +import logging +from typing import List, Optional, Tuple, Iterator + +from volatility.framework import interfaces, renderers, exceptions, symbols +from volatility.framework.configuration import requirements +from volatility.framework.renderers import format_hints +from volatility.framework.interfaces import configuration +from volatility.framework.symbols import intermed +from volatility.framework.symbols.windows import extensions +from volatility.plugins.windows import poolscanner + +vollog = logging.getLogger(__name__) + + +class BigPools(interfaces.plugins.PluginInterface): + """List big page pools.""" + + _version = (1, 0, 0) + + is_vista_or_later = poolscanner.os_distinguisher(version_check = lambda x: x >= (6, 0), + fallback_checks = [("KdCopyDataBlock", None, True)]) + + is_win10 = poolscanner.os_distinguisher(version_check=lambda x: (10, 0) <= x, + fallback_checks=[("ObHeaderCookie", None, True), + ("_HANDLE_TABLE", "HandleCount", False)]) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.TranslationLayerRequirement(name = 'primary', + description = 'Memory layer for the kernel', + architectures = ["Intel32", "Intel64"]), + requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), + requirements.StringRequirement(name='tags', + description="Comma separated list of pool tags to filter pools returned", + optional=True, + default=None) + ] + + @classmethod + def list_big_pools(cls, + context: interfaces.context.ContextInterface, + layer_name: str, + symbol_table: str, + tags: Optional[list] = None): + """Returns the big page pool objects from the kernel PoolBigPageTable array. + + 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 + tags: An optional list of pool tags to filter big page pool tags by + + Yields: + A big page pool object + """ + kvo = context.layers[layer_name].config['kernel_virtual_offset'] + ntkrnlmp = context.module(symbol_table, layer_name=layer_name, offset=kvo) + + big_page_table_offset = ntkrnlmp.get_symbol("PoolBigPageTable").address + big_page_table = ntkrnlmp.object(object_type="unsigned long long", + offset=big_page_table_offset) + + big_page_table_size_offset = ntkrnlmp.get_symbol("PoolBigPageTableSize").address + big_page_table_size = ntkrnlmp.object(object_type="unsigned long", + offset=big_page_table_size_offset) + + try: + big_page_table_type = ntkrnlmp.get_type("_POOL_TRACKER_BIG_PAGED") + except exceptions.SymbolError: + # We have to manually load a symbol table + is_vista_or_later = cls.is_vista_or_later(context, symbol_table) + is_win10 = cls.is_win10(context, symbol_table) + if is_win10: + big_pools_json_filename = "bigpools-win10" + elif is_vista_or_later: + big_pools_json_filename = "bigpools-vista" + else: + big_pools_json_filename = "bigpools" + + if symbols.symbol_table_is_64bit(context, symbol_table): + big_pools_json_filename += "-x64" + else: + big_pools_json_filename += "-x86" + + new_table_name = intermed.IntermediateSymbolTable.create( + context=context, + config_path=configuration.path_join(context.symbol_space[symbol_table].config_path, "bigpools"), + sub_path="windows", + filename=big_pools_json_filename, + table_mapping={'nt_symbols': symbol_table}, + class_types={'_POOL_TRACKER_BIG_PAGES': extensions.pool.POOL_TRACKER_BIG_PAGES}) + module = context.module(new_table_name, layer_name, offset=0) + big_page_table_type = module.get_type("_POOL_TRACKER_BIG_PAGES") + + big_pools = ntkrnlmp.object(object_type="array", + offset=big_page_table, + subtype=big_page_table_type, + count=big_page_table_size, + absolute=True) + + for big_pool in big_pools: + if big_pool.is_valid(): + if tags is None or big_pool.get_key() in tags: + yield big_pool + + def _generator(self) -> Iterator[Tuple[int, Tuple[int, str]]]: #, str, int]]]: + if self.config.get("tags"): + tags = [tag for tag in self.config["tags"].split(',')] + else: + tags = None + + for big_pool in self.list_big_pools(context = self.context, + layer_name = self.config["primary"], + symbol_table = self.config["nt_symbols"], + tags = tags): + + num_bytes = big_pool.get_number_of_bytes() + if not isinstance(num_bytes, interfaces.renderers.BaseAbsentValue): + num_bytes = format_hints.Hex(num_bytes) + + yield (0, (format_hints.Hex(big_pool.Va), + big_pool.get_key(), + big_pool.get_pool_type(), + num_bytes)) + + def run(self): + return renderers.TreeGrid([ + ('Allocation', format_hints.Hex), + ('Tag', str), + ('PoolType', str), + ('NumberOfBytes', format_hints.Hex), + ], self._generator()) diff --git a/volatility/framework/symbols/windows/__init__.py b/volatility/framework/symbols/windows/__init__.py index edf1793b1..a1af09f4c 100644 --- a/volatility/framework/symbols/windows/__init__.py +++ b/volatility/framework/symbols/windows/__init__.py @@ -33,6 +33,7 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('_KMUTANT', extensions.KMUTANT) self.set_type_class('_DRIVER_OBJECT', extensions.DRIVER_OBJECT) self.set_type_class('_OBJECT_SYMBOLIC_LINK', extensions.OBJECT_SYMBOLIC_LINK) + self.set_type_class('_POOL_TRACKER_BIG_PAGES', pool.POOL_TRACKER_BIG_PAGES) # This doesn't exist in very specific versions of windows try: diff --git a/volatility/framework/symbols/windows/bigpools-vista-x64.json b/volatility/framework/symbols/windows/bigpools-vista-x64.json new file mode 100644 index 000000000..da018a8b3 --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-vista-x64.json @@ -0,0 +1,88 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Key": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "PoolType": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "NumberOfBytes": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 24 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPoolBase": 0, + "PagedPool": 1, + "NonPagedPoolBaseMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolBaseCacheAligned": 4, + "PagedPoolCacheAligned": 5, + "NonPagedPoolBaseCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "NonPagedPoolNx": 512, + "NonPagedPoolSessionNx": 544, + "NonPagedPoolNxCacheAligned": 516, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 8 + }, + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + } + } +} diff --git a/volatility/framework/symbols/windows/bigpools-vista-x86.json b/volatility/framework/symbols/windows/bigpools-vista-x86.json new file mode 100644 index 000000000..c7fdbb419 --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-vista-x86.json @@ -0,0 +1,82 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Key": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "PoolType": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "NumberOfBytes": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 16 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPoolBase": 0, + "PagedPool": 1, + "NonPagedPoolBaseMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolBaseCacheAligned": 4, + "PagedPoolCacheAligned": 5, + "NonPagedPoolBaseCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "NonPagedPoolNx": 512, + "NonPagedPoolSessionNx": 544, + "NonPagedPoolNxCacheAligned": 516, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + } + } +} diff --git a/volatility/framework/symbols/windows/bigpools-win10-x64.json b/volatility/framework/symbols/windows/bigpools-win10-x64.json new file mode 100644 index 000000000..2556daf09 --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-win10-x64.json @@ -0,0 +1,123 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Key": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Pattern": { + "offset": 12, + "type": { + "bit_length": 8, + "bit_position": 0, + "kind": "bitfield", + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "PoolType": { + "offset": 12, + "type": { + "bit_length": 8, + "bit_position": 8, + "kind": "bitfield", + "type": { + "kind": "enum", + "name": "_POOL_TYPE" + } + } + }, + "SlushSize": { + "offset": 12, + "type": { + "bit_length": 12, + "bit_position": 20, + "kind": "bitfield", + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "NumberOfBytes": { + "offset": 16, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 24 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPoolBase": 0, + "PagedPool": 1, + "NonPagedPoolBaseMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolBaseCacheAligned": 4, + "PagedPoolCacheAligned": 5, + "NonPagedPoolBaseCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "NonPagedPoolNx": 512, + "NonPagedPoolSessionNx": 544, + "NonPagedPoolNxCacheAligned": 516, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 8 + }, + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + }, + "unsigned char": { + "endian": "little", + "kind": "char", + "signed": false, + "size": 1 + } + } +} diff --git a/volatility/framework/symbols/windows/bigpools-win10-x86.json b/volatility/framework/symbols/windows/bigpools-win10-x86.json new file mode 100644 index 000000000..1988e2b5c --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-win10-x86.json @@ -0,0 +1,111 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Key": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Pattern": { + "offset": 8, + "type": { + "bit_length": 8, + "bit_position": 0, + "kind": "bitfield", + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "PoolType": { + "offset": 8, + "type": { + "bit_length": 8, + "bit_position": 8, + "kind": "bitfield", + "type": { + "kind": "enum", + "name": "_POOL_TYPE" + } + } + }, + "SlushSize": { + "offset": 8, + "type": { + "bit_length": 12, + "bit_position": 20, + "kind": "bitfield", + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "NumberOfBytes": { + "offset": 12, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 16 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPoolBase": 0, + "PagedPool": 1, + "NonPagedPoolBaseMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolBaseCacheAligned": 4, + "PagedPoolCacheAligned": 5, + "NonPagedPoolBaseCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "NonPagedPoolNx": 512, + "NonPagedPoolSessionNx": 544, + "NonPagedPoolNxCacheAligned": 516, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + } + } +} diff --git a/volatility/framework/symbols/windows/bigpools-x64.json b/volatility/framework/symbols/windows/bigpools-x64.json new file mode 100644 index 000000000..f2be0d43b --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-x64.json @@ -0,0 +1,71 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "Key": { + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 12 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPool": 0, + "PagedPool": 1, + "NonPagedPoolMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolCacheAligned": 4, + "PagedPoolAligned": 5, + "NonPagedPoolCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 8 + }, + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + } + } +} diff --git a/volatility/framework/symbols/windows/bigpools-x86.json b/volatility/framework/symbols/windows/bigpools-x86.json new file mode 100644 index 000000000..87d46bf00 --- /dev/null +++ b/volatility/framework/symbols/windows/bigpools-x86.json @@ -0,0 +1,65 @@ +{ + "metadata": { + "producer": { + "version": "0.0.1", + "name": "dlassalle-by-hand", + "datetime": "2020-04-30T14:30:00.000000" + }, + "format": "6.2.0" + }, + "user_types": { + "_POOL_TRACKER_BIG_PAGES": { + "fields": { + "Va": { + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long" + } + }, + "Key": { + "offset": 4, + "type": { + "kind": "base", + "name": "unsigned long" + } + } + }, + "kind": "struct", + "size": 8 + } + }, + "symbols": { + }, + "enums": { + "_POOL_TYPE": { + "base": "unsigned long", + "constants": { + "NonPagedPool": 0, + "PagedPool": 1, + "NonPagedPoolMustSucceed": 2, + "DontUseThisType": 3, + "NonPagedPoolCacheAligned": 4, + "PagedPoolAligned": 5, + "NonPagedPoolCacheAlignedMustS": 6, + "MaxPoolType": 7, + "NonPagedPoolMustSucceedSession": 34, + "DontUseThisTypeSession": 35, + "NonPagedPoolSession": 32, + "PagedPoolSession": 33, + "NonPagedPoolCacheAlignedMustSSession": 38, + "PagedPoolCacheAlignedSession": 37, + "NonPagedPoolCacheAlignedSession": 36 + }, + "size": 4 + } + }, + "base_types": { + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + } + } +} diff --git a/volatility/framework/symbols/windows/extensions/pool.py b/volatility/framework/symbols/windows/extensions/pool.py index b5b83fb1f..464e361bf 100644 --- a/volatility/framework/symbols/windows/extensions/pool.py +++ b/volatility/framework/symbols/windows/extensions/pool.py @@ -1,8 +1,8 @@ import functools import struct -from typing import Optional, Tuple, List, Dict +from typing import Optional, Tuple, List, Dict, Union -from volatility.framework import objects, interfaces, constants, symbols, exceptions +from volatility.framework import objects, interfaces, constants, symbols, exceptions, renderers from volatility.framework.renderers import conversion @@ -166,6 +166,50 @@ class POOL_HEADER(objects.StructType): return headers, sizes +class POOL_TRACKER_BIG_PAGES(objects.StructType): + """A kernel big page pool tracker.""" + + pool_type_lookup = {} + + def _generate_pool_type_lookup(self): + # Enumeration._generate_inverse_choices() raises ValueError because multiple enum names map to the same + # value in the kernel _POOL_TYPE so create a custom mapping here and take the first match + symbol_table_name = self.vol.type_name.split(constants.BANG)[0] + pool_type_enum = self._context.symbol_space.get_enumeration(symbol_table_name + constants.BANG + "_POOL_TYPE") + for k, v in pool_type_enum.choices.items(): + if v not in self.pool_type_lookup: + self.pool_type_lookup[v] = k + + def is_valid(self) -> bool: + return self.Key > 0 + # return self.Va > 0x1 + + def get_key(self) -> str: + """Returns the Key value as a 4 character string""" + tag_bytes = objects.convert_value_to_data(self.Key, + int, + objects.DataFormatInfo(4, "little", False)) + return "".join([chr(x) if 32 < x < 127 else '' for x in tag_bytes]) + + def get_pool_type(self) -> Union[str, interfaces.renderers.BaseAbsentValue]: + """Returns the enum name for the PoolType value on applicable systems""" + # Not applicable until Vista + if hasattr(self, 'PoolType'): + if not self.pool_type_lookup: + self._generate_pool_type_lookup() + return self.pool_type_lookup.get(self.PoolType, "Unknown choice {}".format(self.PoolType)) + else: + return renderers.NotApplicableValue() + + def get_number_of_bytes(self) -> Union[int, interfaces.renderers.BaseAbsentValue]: + """Returns the NumberOfBytes value on applicable systems""" + # Not applicable until Vista + try: + return self.NumberOfBytes + except AttributeError: + return renderers.NotApplicableValue() + + class ExecutiveObject(interfaces.objects.ObjectInterface): """This is used as a "mixin" that provides all kernel executive objects with a means of finding their own object header."""