mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-07 18:27:39 +02:00
add the windows callbacks plugin
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
# 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 List, Iterable, Tuple
|
||||
import logging
|
||||
import volatility.framework.interfaces.plugins as interfaces_plugins
|
||||
from volatility.framework import constants, exceptions, renderers, interfaces, symbols
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.plugins.windows import ssdt
|
||||
from volatility.plugins.windows import svcscan
|
||||
from volatility.framework.symbols import intermed
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Callbacks(interfaces_plugins.PluginInterface):
|
||||
"""Lists kernel callbacks and notification routines"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(
|
||||
name = 'primary', description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols")
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def create_callback_table(context: interfaces.context.ContextInterface, symbol_table: str, config_path: str) -> str:
|
||||
|
||||
native_types = context.symbol_space[symbol_table].natives
|
||||
is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
|
||||
table_mapping = {"nt_symbols": symbol_table}
|
||||
|
||||
if is_64bit:
|
||||
symbol_filename = "callbacks-x64"
|
||||
else:
|
||||
symbol_filename = "callbacks-x86"
|
||||
|
||||
return intermed.IntermediateSymbolTable.create(
|
||||
context,
|
||||
config_path,
|
||||
"windows",
|
||||
symbol_filename,
|
||||
native_types = native_types,
|
||||
table_mapping = table_mapping)
|
||||
|
||||
@classmethod
|
||||
def list_notify_routines(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
config_path: str, callback_table_name: str) -> Iterable[Tuple[str, int, str]]:
|
||||
"""Lists all kernel notification routines"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
|
||||
is_vista_or_later = svcscan.SvcScan.is_vista_or_later(context = context, symbol_table = symbol_table)
|
||||
full_type_name = callback_table_name + constants.BANG + "_GENERIC_CALLBACK"
|
||||
|
||||
symbol_names = [("PspLoadImageNotifyRoutine", False), ("PspCreateThreadNotifyRoutine", True),
|
||||
("PspCreateProcessNotifyRoutine", True)]
|
||||
|
||||
for symbol_name, extended_list in symbol_names:
|
||||
|
||||
try:
|
||||
symbol_offset = ntkrnlmp.get_symbol(symbol_name).address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Cannot find {}".format(symbol_name))
|
||||
continue
|
||||
|
||||
if is_vista_or_later and extended_list:
|
||||
count = 64
|
||||
else:
|
||||
count = 8
|
||||
|
||||
fast_refs = ntkrnlmp.object(object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=count)
|
||||
|
||||
for fast_ref in fast_refs:
|
||||
try:
|
||||
callback = fast_ref.dereference().cast(full_type_name)
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if callback.Callback != 0:
|
||||
yield symbol_name, callback.Callback, None
|
||||
|
||||
@classmethod
|
||||
def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
config_path: str, callback_table_name: str) -> Iterable[Tuple[str, int, str]]:
|
||||
"""Lists all registry callbacks"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
full_type_name = callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK"
|
||||
|
||||
try:
|
||||
symbol_offset = ntkrnlmp.get_symbol("CmpCallBackVector").address
|
||||
symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Cannot find CmpCallBackVector or CmpCallBackCount")
|
||||
return
|
||||
|
||||
callback_count = ntkrnlmp.object(object_type="unsigned int",
|
||||
offset=symbol_count_offset)
|
||||
|
||||
if callback_count == 0:
|
||||
return
|
||||
|
||||
fast_refs = ntkrnlmp.object(object_type="array",
|
||||
offset=symbol_offset,
|
||||
subtype=ntkrnlmp.get_type("_EX_FAST_REF"),
|
||||
count=callback_count)
|
||||
|
||||
for fast_ref in fast_refs:
|
||||
try:
|
||||
callback = fast_ref.dereference().cast(full_type_name)
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if callback.Function != 0:
|
||||
yield "CmRegisterCallback", callback.Function, None
|
||||
|
||||
@classmethod
|
||||
def list_bugcheck_reason_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str,
|
||||
symbol_table: str, config_path: str,
|
||||
callback_table_name: str) -> Iterable[Tuple[str, int, str]]:
|
||||
"""Lists all kernel bugcheck reason callbacks"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
|
||||
try:
|
||||
list_offset = ntkrnlmp.get_symbol("KeBugCheckReasonCallbackListHead").address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Cannot find KeBugCheckReasonCallbackListHead")
|
||||
return
|
||||
|
||||
full_type_name = callback_table_name + constants.BANG + "_KBUGCHECK_REASON_CALLBACK_RECORD"
|
||||
callback_record = context.object(
|
||||
object_type = full_type_name, offset = kvo + list_offset, layer_name = layer_name)
|
||||
|
||||
for callback in callback_record.Entry:
|
||||
|
||||
if not context.layers[layer_name].is_valid(callback.CallbackRoutine):
|
||||
continue
|
||||
|
||||
try:
|
||||
component = context.object(
|
||||
symbol_table + constants.BANG + "string",
|
||||
layer_name = layer_name,
|
||||
offset = callback.Component,
|
||||
max_length = 64,
|
||||
errors = "replace")
|
||||
except exceptions.InvalidAddressException:
|
||||
component = renderers.UnreadableValue()
|
||||
|
||||
yield "KeBugCheckReasonCallbackListHead", callback.CallbackRoutine, component
|
||||
|
||||
@classmethod
|
||||
def list_bugcheck_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
config_path: str, callback_table_name: str) -> Iterable[Tuple[str, int, str]]:
|
||||
"""Lists all kernel bugcheck callbacks"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
|
||||
try:
|
||||
list_offset = ntkrnlmp.get_symbol("KeBugCheckCallbackListHead").address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Cannot find KeBugCheckCallbackListHead")
|
||||
return
|
||||
|
||||
full_type_name = callback_table_name + constants.BANG + "_KBUGCHECK_CALLBACK_RECORD"
|
||||
callback_record = context.object(full_type_name, offset = kvo + list_offset, layer_name = layer_name)
|
||||
|
||||
for callback in callback_record.Entry:
|
||||
|
||||
if not context.layers[layer_name].is_valid(callback.CallbackRoutine):
|
||||
continue
|
||||
|
||||
try:
|
||||
component = context.object(
|
||||
symbol_table + constants.BANG + "string",
|
||||
layer_name = layer_name,
|
||||
offset = callback.Component,
|
||||
max_length = 64,
|
||||
errors = "replace")
|
||||
except exceptions.InvalidAddressException:
|
||||
component = renderers.UnreadableValue()
|
||||
|
||||
yield "KeBugCheckCallbackListHead", callback.CallbackRoutine, component
|
||||
|
||||
def _generator(self):
|
||||
|
||||
callback_table_name = self.create_callback_table(self.context, self.config["nt_symbols"], self.config_path)
|
||||
|
||||
collection = ssdt.SSDT.build_module_collection(self.context, self.config['primary'], self.config['nt_symbols'])
|
||||
|
||||
callback_methods = (self.list_notify_routines, self.list_bugcheck_callbacks,
|
||||
self.list_bugcheck_reason_callbacks, self.list_registry_callbacks)
|
||||
|
||||
for callback_method in callback_methods:
|
||||
for callback_type, callback_address, callback_detail in callback_method(
|
||||
self.context, self.config['primary'], self.config['nt_symbols'], self.config_path,
|
||||
callback_table_name):
|
||||
|
||||
if callback_detail is None:
|
||||
detail = renderers.NotApplicableValue()
|
||||
else:
|
||||
detail = callback_detail
|
||||
|
||||
module_symbols = list(collection.get_module_symbols_by_absolute_location(callback_address))
|
||||
|
||||
if module_symbols:
|
||||
for module_name, symbol_generator in module_symbols:
|
||||
symbols_found = False
|
||||
|
||||
# we might have multiple symbols pointing to the same location
|
||||
for symbol in symbol_generator:
|
||||
symbols_found = True
|
||||
yield (0, (callback_type, format_hints.Hex(callback_address), module_name,
|
||||
symbol.split(constants.BANG)[1], detail))
|
||||
|
||||
# no symbols, but we at least can report the module name
|
||||
if not symbols_found:
|
||||
yield (0, (callback_type, format_hints.Hex(callback_address), module_name,
|
||||
renderers.NotAvailableValue(), detail))
|
||||
else:
|
||||
# no module was found at the absolute location
|
||||
yield (0, (callback_type, format_hints.Hex(callback_address), renderers.NotAvailableValue(),
|
||||
renderers.NotAvailableValue(), detail))
|
||||
|
||||
def run(self):
|
||||
|
||||
return renderers.TreeGrid([("Type", str), ("Callback", format_hints.Hex), ("Module", str), ("Symbol", str),
|
||||
("Detail", str)], self._generator())
|
||||
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned char": {
|
||||
"kind": "char",
|
||||
"size": 1,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"pointer": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned int": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned short": {
|
||||
"kind": "int",
|
||||
"size": 2,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"_GENERIC_CALLBACK": {
|
||||
"fields": {
|
||||
"Callback": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 16
|
||||
},
|
||||
"_KBUGCHECK_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Component": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
},
|
||||
"_KBUGCHECK_REASON_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Component": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
},
|
||||
"_EX_CALLBACK_ROUTINE_BLOCK": {
|
||||
"fields": {
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "mhl by hand",
|
||||
"datetime": "2019-08-27T18:17:16.417006"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"symbols": {},
|
||||
"enums": {},
|
||||
"base_types": {
|
||||
"unsigned long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned char": {
|
||||
"kind": "char",
|
||||
"size": 1,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"pointer": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned int": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned short": {
|
||||
"kind": "int",
|
||||
"size": 2,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"long": {
|
||||
"kind": "int",
|
||||
"size": 4,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"_GENERIC_CALLBACK": {
|
||||
"fields": {
|
||||
"Callback": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 8
|
||||
},
|
||||
"_KBUGCHECK_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Component": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 20
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 32
|
||||
},
|
||||
"_KBUGCHECK_REASON_CALLBACK_RECORD": {
|
||||
"fields": {
|
||||
"Entry": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"CallbackRoutine": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 8
|
||||
},
|
||||
"Component": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "unsigned char"
|
||||
}
|
||||
},
|
||||
"offset": 12
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 28
|
||||
},
|
||||
"_EX_CALLBACK_ROUTINE_BLOCK": {
|
||||
"fields": {
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 4
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 28
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"producer": {
|
||||
"version": "0.0.1",
|
||||
"name": "mhl by hand",
|
||||
"datetime": "2019-08-27T18:17:16.417006"
|
||||
},
|
||||
"format": "4.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user