issue #186 - add bigpools plugin

This commit is contained in:
superponible
2020-05-27 23:16:18 +01:00
committed by ikelos
parent 054d144f9b
commit a21ab89019
9 changed files with 725 additions and 2 deletions
@@ -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())
@@ -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:
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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."""