diff --git a/volatility/plugins/windows/ssdt.py b/volatility/plugins/windows/ssdt.py new file mode 100644 index 000000000..5f6d5f0f5 --- /dev/null +++ b/volatility/plugins/windows/ssdt.py @@ -0,0 +1,101 @@ +import os +import volatility.framework.interfaces.plugins as plugins +import volatility.plugins.windows.modules as modules +import volatility.framework.exceptions as exceptions +import volatility.framework.contexts as contexts +import volatility.framework.constants as constants +import volatility.framework.constants.windows as windows_constants +from volatility.framework import renderers +from volatility.framework.renderers import format_hints + +class SSDT(plugins.PluginInterface): + """Lists the system call table""" + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return modules.Modules.get_requirements() + [] + + def _generator(self, modules): + + layer_name = self.config['primary'] + context_modules = [] + + for mod in modules: + + try: + module_name_with_ext = mod.BaseDllName.get_string() + except exceptions.InvalidAddressException: + # there's no use for a module with no name? + continue + + module_name = os.path.splitext(module_name_with_ext)[0] + + if module_name in windows_constants.KERNEL_MODULE_NAMES: + symbol_table_name = self.config["nt_symbols"] + else: + symbol_table_name = None + + context_module = contexts.SizedModule(self._context, + module_name, + layer_name, + mod.DllBase, + mod.SizeOfImage, + symbol_table_name) + + context_modules.append(context_module) + + collection = contexts.ModuleCollection(context_modules) + + kvo = self.context.memory[layer_name].config['kernel_virtual_offset'] + ntkrnlmp = self.context.module(self.config["nt_symbols"], layer_name=layer_name, offset=kvo) + + # this is just one way to enumerate the native (NT) service table. + # to do the same thing for the Win32K service table, we would need Win32K.sys symbol support + ## we could also find nt!KeServiceDescriptorTable (NT) and KeServiceDescriptorTableShadow (NT, Win32K) + service_table_address = ntkrnlmp.get_symbol("KiServiceTable").address + service_limit_address = ntkrnlmp.get_symbol("KiServiceLimit").address + service_limit = ntkrnlmp.object(type_name="int", offset=kvo + service_limit_address) + + # on 32-bit systems the table indexes are 32-bits and contain pointers (unsigned) + # on 64-bit systems the indexes are also 32-bits but they're offsets from the + # base address of the table and can be negative, so we need a signed data type + is_kernel_64 = ntkrnlmp.get_type("pointer").size == 8 + if is_kernel_64: + array_subtype = "long" + else: + array_subtype = "unsigned long" + + functions = ntkrnlmp.object(type_name="array", offset=kvo + service_table_address, + subtype=ntkrnlmp.get_type(array_subtype), + count=service_limit) + + for idx, function in enumerate(functions): + + if is_kernel_64: + function = kvo + service_table_address + (function >> 4) + + module_symbols = collection.get_module_symbols_by_absolute_location(function) + + for module_name, symbol_generator in module_symbols: + symbols = list(symbol_generator) + if len(symbols) > 0: + for symbol in symbols: + yield (0, (idx, + format_hints.Hex(function), + module_name, + symbol.split(constants.BANG)[1])) + else: + yield (0, (idx, + format_hints.Hex(function), + module_name, + renderers.NotAvailableValue())) + + def run(self): + return renderers.TreeGrid([("Index", int), + ("Address", format_hints.Hex), + ("Module", str), + ("Symbol", str)], + self._generator(modules.Modules.list_modules(self.context, + self.config['primary'], + self.config['nt_symbols'])))