From c172f16e680ebfb8ddea3eec07dc4b6c4041a5f2 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 20 Jun 2024 18:13:32 -0500 Subject: [PATCH] placeholder --- .../framework/plugins/windows/pedump.py | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 volatility3/framework/plugins/windows/pedump.py diff --git a/volatility3/framework/plugins/windows/pedump.py b/volatility3/framework/plugins/windows/pedump.py new file mode 100644 index 000000000..ba8168736 --- /dev/null +++ b/volatility3/framework/plugins/windows/pedump.py @@ -0,0 +1,178 @@ +# This file is Copyright 2024 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 + +from volatility3.framework import constants, exceptions, interfaces, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.symbols import intermed +from volatility3.framework.symbols.windows.extensions import pe +from volatility3.plugins.windows import pslist, modules + +vollog = logging.getLogger(__name__) + + +class PEDump(interfaces.plugins.PluginInterface): + """Allows extracting PE Files from a specific address in a specific address space""" + + _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) + + @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.ModuleRequirement( + name="kernel", + description="Windows kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="pslist", component=pslist.PsList, version=(2, 0, 0) + ), + requirements.ListRequirement( + name="pid", + element_type=int, + description="Process IDs to include (all other processes are excluded)", + optional=True, + ), + requirements.IntRequirement( + name="base", + description="Base address to reconstruct a PE file", + optional=False, + ), + requirements.BooleanRequirement( + name="kernel_module", + description="Extract from kernel address space.", + default=False, + optional=True, + ), + ] + + def _write_pe(self, pe_table_name, layer_name, proc_offset, pid): + try: + file_handle = self.open( + "PE.{:#x}.{:d}.{:#x}.dmp".format( + proc_offset, + pid, + self.config["base"], + ) + ) + + dos_header = self.context.object( + pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", + offset=self.config["base"], + layer_name=layer_name, + ) + + for offset, data in dos_header.reconstruct(): + file_handle.seek(offset) + file_handle.write(data) + except ( + IOError, + exceptions.VolatilityException, + OverflowError, + ValueError, + ) as excp: + vollog.debug(f"Unable to dump dll at offset {self.config['base']}: {excp}") + return None + + file_handle.close() + return file_handle.preferred_filename + + def _dump_kernel(self, _, pe_table_name, session_layers): + session_layer_name = modules.Modules.find_session_layer( + self.context, session_layers, self.config["base"] + ) + + if session_layer_name: + system_pid = 4 + + file_output = self._write_pe( + pe_table_name, + session_layer_name, + 0, + system_pid, + ) + + if file_output: + yield system_pid, "Kernel", file_output + else: + vollog.warning( + "Unable to find a session layer with the provided base address mapped in the kernel." + ) + + def _dump_processes(self, kernel, pe_table_name, _): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + kernel = self.context.modules[self.config["kernel"]] + + for proc in pslist.PsList.list_processes( + context=self.context, + layer_name=kernel.layer_name, + symbol_table=kernel.symbol_table_name, + filter_func=filter_func, + ): + pid = proc.UniqueProcessId + proc_name = proc.ImageFileName.cast( + "string", + max_length=proc.ImageFileName.vol.count, + errors="replace", + ) + proc_layer_name = proc.add_process_layer() + + file_output = self._write_pe( + pe_table_name, + proc_layer_name, + proc.vol.offset, + pid, + ) + + if file_output: + yield pid, proc_name, file_output + + def _generator(self): + kernel = self.context.modules[self.config["kernel"]] + + pe_table_name = intermed.IntermediateSymbolTable.create( + self.context, self.config_path, "windows", "pe", class_types=pe.class_types + ) + + if self.config["kernel_module"] and self.config["pid"]: + vollog.error("Only --kernel_module or --pid should be set. Not both") + return + + if not self.config["kernel_module"] and not self.config["pid"]: + vollog.error("--kernel_module or --pid must be set") + return + + if self.config["kernel_module"]: + session_layers = modules.Modules.get_session_layers( + self.context, kernel.layer_name, kernel.symbol_table_name + ) + method = self._dump_kernel + else: + session_layers = None + method = self._dump_processes + + for pid, proc_name, file_output in method( + kernel, pe_table_name, session_layers + ): + yield ( + 0, + ( + pid, + proc_name, + file_output, + ), + ) + + def run(self): + return renderers.TreeGrid( + [ + ("PID", int), + ("Process", str), + ("File output", str), + ], + self._generator(), + )