From 97135a992a4eb01c07f3bd0eae50a172ef96389b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 1 Feb 2025 16:42:18 +1100 Subject: [PATCH] linux: add Kallsyms plugin --- .../framework/plugins/linux/kallsyms.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 volatility3/framework/plugins/linux/kallsyms.py diff --git a/volatility3/framework/plugins/linux/kallsyms.py b/volatility3/framework/plugins/linux/kallsyms.py new file mode 100644 index 000000000..9c48ed8b8 --- /dev/null +++ b/volatility3/framework/plugins/linux/kallsyms.py @@ -0,0 +1,89 @@ +# This file is Copyright 2025 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, Union + +from volatility3.framework import interfaces, renderers +from volatility3.framework.interfaces import plugins +from volatility3.framework.configuration import requirements +from volatility3.framework.renderers import format_hints +from volatility3.framework.constants import architectures +from volatility3.framework.symbols.linux import kallsyms + + +vollog = logging.getLogger(__name__) + + +class Kallsyms(plugins.PluginInterface): + """Kallsyms symbols enumeration plugin""" + + _required_framework_version = (2, 19, 0) + + _version = (1, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=architectures.LINUX_ARCHS, + ), + requirements.VersionRequirement( + name="Kallsyms", component=kallsyms.Kallsyms, version=(1, 0, 0) + ), + ] + + def _get_symbol_size( + self, kassymbol: kallsyms.KASSymbol + ) -> Union[int, interfaces.renderers.BaseAbsentValue]: + # Symbol sizes are calculated using the address of the next non-aliased + # symbol or the end of the kernel text area _end/_etext. However, some kernel + # symbols live beyond that area. For these symbols, the size will be negative, + # resulting in incorrect values. Unfortunately, there isn't much that can be done + # in such cases. + # See comments on .init.scratch in arch/x86/kernel/vmlinux.lds.S for details + return kassymbol.size if kassymbol.size >= 0 else renderers.NotAvailableValue() + + def _generator(self): + module_name = self.config["kernel"] + vmlinux = self.context.modules[module_name] + + kas = kallsyms.Kallsyms( + context=self.context, + layer_name=vmlinux.layer_name, + module_name=self.config["kernel"], + ) + + for kassymbol in kas.get_all_symbols(): + # Symbol sizes are calculated using the address of the next non-aliased + # symbol or the end of the kernel text area _end/_etext. However, some kernel + # symbols are located beyond that area, which causes this method to fail for + # the last symbol, resulting in a negative size. + # See comments on .init.scratch in arch/x86/kernel/vmlinux.lds.S for details + symbol_size = self._get_symbol_size(kassymbol) + fields = ( + format_hints.Hex(kassymbol.address), + kassymbol.type, + symbol_size, + kassymbol.exported, + kassymbol.subsystem, + kassymbol.module_name, + kassymbol.name, + kassymbol.type_description or renderers.NotAvailableValue(), + ) + yield 0, fields + + def run(self): + headers = [ + ("Addr", format_hints.Hex), + ("Type", str), + ("Size", int), + ("Exported", bool), + ("SubSystem", str), + ("ModuleName", str), + ("SymbolName", str), + ("Description", str), + ] + return renderers.TreeGrid(headers, self._generator())