From a06d59bc52d670ba3ba9ccef11c0fdd9d4e88d0c Mon Sep 17 00:00:00 2001 From: SolitudePy <47316655+SolitudePy@users.noreply.github.com> Date: Sat, 7 Jun 2025 22:58:46 +0300 Subject: [PATCH] Plugins: categorize linux.malfind as a malware plugin --- test/plugins/linux/linux.py | 7 +- .../framework/plugins/linux/malfind.py | 114 ++---------------- .../plugins/linux/malware/malfind.py | 114 ++++++++++++++++++ 3 files changed, 129 insertions(+), 106 deletions(-) create mode 100644 volatility3/framework/plugins/linux/malware/malfind.py diff --git a/test/plugins/linux/linux.py b/test/plugins/linux/linux.py index 9a11d9c95..69aeb8a6c 100644 --- a/test/plugins/linux/linux.py +++ b/test/plugins/linux/linux.py @@ -251,7 +251,7 @@ class TestLinuxKthreads: class TestLinuxMalfind: def test_linux_generic_malfind(self, image, volatility, python): rc, out, _err = test_volatility.runvol_plugin( - "linux.malfind.Malfind", image, volatility, python + "linux.malware.malfind.Malfind", image, volatility, python ) # linux-sample-1.bin has no process memory ranges with potential injected code. @@ -479,7 +479,10 @@ class TestLinuxIomem: class TestLinuxKeyboardNotifiers: def test_linux_generic_keyboard_notifiers(self, image, volatility, python): rc, out, _err = test_volatility.runvol_plugin( - "linux.malware.keyboard_notifiers.Keyboard_notifiers", image, volatility, python + "linux.malware.keyboard_notifiers.Keyboard_notifiers", + image, + volatility, + python, ) # linux-sample-1.bin has no suspicious results for this plugin. diff --git a/volatility3/framework/plugins/linux/malfind.py b/volatility3/framework/plugins/linux/malfind.py index c7e141c02..647e1531a 100644 --- a/volatility3/framework/plugins/linux/malfind.py +++ b/volatility3/framework/plugins/linux/malfind.py @@ -1,114 +1,20 @@ -# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0 +# 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 # - -from typing import List, Tuple, Optional import logging -from volatility3.framework import interfaces -from volatility3.framework import renderers, symbols -from volatility3.framework.configuration import requirements -from volatility3.framework.objects import utility -from volatility3.framework.renderers import format_hints -from volatility3.plugins.linux import pslist +from volatility3.framework import interfaces, deprecation +from volatility3.plugins.linux.malware import malfind vollog = logging.getLogger(__name__) -class Malfind(interfaces.plugins.PluginInterface): - """Lists process memory ranges that potentially contain injected code.""" +class Malfind( + interfaces.plugins.PluginInterface, + deprecation.PluginRenameClass, + replacement_class=malfind.Malfind, + removal_date="2026-06-07", +): + """Lists process memory ranges that potentially contain injected code (deprecated).""" _required_framework_version = (2, 0, 0) _version = (1, 0, 3) - - @classmethod - def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: - return [ - requirements.ModuleRequirement( - name="kernel", - description="Linux kernel", - architectures=["Intel32", "Intel64"], - ), - requirements.VersionRequirement( - name="pslist", component=pslist.PsList, version=(4, 0, 0) - ), - requirements.ListRequirement( - name="pid", - description="Filter on specific process IDs", - element_type=int, - optional=True, - ), - ] - - def _list_injections( - self, task - ) -> Tuple[interfaces.objects.ObjectInterface, Optional[str], bytes]: - """Generate memory regions for a process that may contain injected - code.""" - - proc_layer_name = task.add_process_layer() - if not proc_layer_name: - return None - - proc_layer = self.context.layers[proc_layer_name] - - for vma in task.mm.get_vma_iter(): - vma_name = vma.get_name(self.context, task) - vollog.debug( - f"Injections : processing PID {task.pid} : VMA {vma_name} : {hex(vma.vm_start)}-{hex(vma.vm_end)}" - ) - if vma.is_suspicious(proc_layer) and vma_name != "[vdso]": - data = proc_layer.read(vma.vm_start, 64, pad=True) - yield vma, vma_name, data - - def _generator(self, tasks): - # determine if we're on a 32 or 64 bit kernel - vmlinux = self.context.modules[self.config["kernel"]] - is_32bit_arch = not symbols.symbol_table_is_64bit( - context=self.context, symbol_table_name=vmlinux.symbol_table_name - ) - - for task in tasks: - process_name = utility.array_to_string(task.comm) - - for vma, vma_name, data in self._list_injections(task): - if is_32bit_arch: - architecture = "intel" - else: - architecture = "intel64" - - disasm = renderers.Disassembly(data, vma.vm_start, architecture) - - yield ( - 0, - ( - task.pid, - process_name, - format_hints.Hex(vma.vm_start), - format_hints.Hex(vma.vm_end), - vma_name or renderers.NotAvailableValue(), - vma.get_protection(), - format_hints.HexBytes(data), - disasm, - ), - ) - - def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - - return renderers.TreeGrid( - [ - ("PID", int), - ("Process", str), - ("Start", format_hints.Hex), - ("End", format_hints.Hex), - ("Path", str), - ("Protection", str), - ("Hexdump", format_hints.HexBytes), - ("Disasm", renderers.Disassembly), - ], - self._generator( - pslist.PsList.list_tasks( - self.context, self.config["kernel"], filter_func=filter_func - ) - ), - ) diff --git a/volatility3/framework/plugins/linux/malware/malfind.py b/volatility3/framework/plugins/linux/malware/malfind.py new file mode 100644 index 000000000..c7e141c02 --- /dev/null +++ b/volatility3/framework/plugins/linux/malware/malfind.py @@ -0,0 +1,114 @@ +# 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, Tuple, Optional +import logging +from volatility3.framework import interfaces +from volatility3.framework import renderers, symbols +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints +from volatility3.plugins.linux import pslist + +vollog = logging.getLogger(__name__) + + +class Malfind(interfaces.plugins.PluginInterface): + """Lists process memory ranges that potentially contain injected code.""" + + _required_framework_version = (2, 0, 0) + _version = (1, 0, 3) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="pslist", component=pslist.PsList, version=(4, 0, 0) + ), + requirements.ListRequirement( + name="pid", + description="Filter on specific process IDs", + element_type=int, + optional=True, + ), + ] + + def _list_injections( + self, task + ) -> Tuple[interfaces.objects.ObjectInterface, Optional[str], bytes]: + """Generate memory regions for a process that may contain injected + code.""" + + proc_layer_name = task.add_process_layer() + if not proc_layer_name: + return None + + proc_layer = self.context.layers[proc_layer_name] + + for vma in task.mm.get_vma_iter(): + vma_name = vma.get_name(self.context, task) + vollog.debug( + f"Injections : processing PID {task.pid} : VMA {vma_name} : {hex(vma.vm_start)}-{hex(vma.vm_end)}" + ) + if vma.is_suspicious(proc_layer) and vma_name != "[vdso]": + data = proc_layer.read(vma.vm_start, 64, pad=True) + yield vma, vma_name, data + + def _generator(self, tasks): + # determine if we're on a 32 or 64 bit kernel + vmlinux = self.context.modules[self.config["kernel"]] + is_32bit_arch = not symbols.symbol_table_is_64bit( + context=self.context, symbol_table_name=vmlinux.symbol_table_name + ) + + for task in tasks: + process_name = utility.array_to_string(task.comm) + + for vma, vma_name, data in self._list_injections(task): + if is_32bit_arch: + architecture = "intel" + else: + architecture = "intel64" + + disasm = renderers.Disassembly(data, vma.vm_start, architecture) + + yield ( + 0, + ( + task.pid, + process_name, + format_hints.Hex(vma.vm_start), + format_hints.Hex(vma.vm_end), + vma_name or renderers.NotAvailableValue(), + vma.get_protection(), + format_hints.HexBytes(data), + disasm, + ), + ) + + def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + + return renderers.TreeGrid( + [ + ("PID", int), + ("Process", str), + ("Start", format_hints.Hex), + ("End", format_hints.Hex), + ("Path", str), + ("Protection", str), + ("Hexdump", format_hints.HexBytes), + ("Disasm", renderers.Disassembly), + ], + self._generator( + pslist.PsList.list_tasks( + self.context, self.config["kernel"], filter_func=filter_func + ) + ), + )