From c172f16e680ebfb8ddea3eec07dc4b6c4041a5f2 Mon Sep 17 00:00:00 2001 From: atcuno Date: Thu, 20 Jun 2024 18:13:32 -0500 Subject: [PATCH 1/2] 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(), + ) From 938635d5593cb9babc3c0f6ea3b74f6a0ed4abe5 Mon Sep 17 00:00:00 2001 From: atcuno Date: Wed, 3 Jul 2024 10:30:05 -0500 Subject: [PATCH 2/2] Address feedback from ikelos --- .../framework/plugins/windows/pedump.py | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/volatility3/framework/plugins/windows/pedump.py b/volatility3/framework/plugins/windows/pedump.py index ba8168736..03896dcf9 100644 --- a/volatility3/framework/plugins/windows/pedump.py +++ b/volatility3/framework/plugins/windows/pedump.py @@ -50,13 +50,13 @@ class PEDump(interfaces.plugins.PluginInterface): ), ] - def _write_pe(self, pe_table_name, layer_name, proc_offset, pid): + def _dump_pe(self, pe_table_name, layer_name, proc_offset, pid, base): try: file_handle = self.open( "PE.{:#x}.{:d}.{:#x}.dmp".format( proc_offset, pid, - self.config["base"], + base, ) ) @@ -77,23 +77,25 @@ class PEDump(interfaces.plugins.PluginInterface): ) as excp: vollog.debug(f"Unable to dump dll at offset {self.config['base']}: {excp}") return None + finally: + file_handle.close() - file_handle.close() return file_handle.preferred_filename - def _dump_kernel(self, _, pe_table_name, session_layers): + def _dump_kernel(self, kernel, pe_table_name, base): + session_layers = modules.Modules.get_session_layers( + self.context, kernel.layer_name, kernel.symbol_table_name + ) + session_layer_name = modules.Modules.find_session_layer( - self.context, session_layers, self.config["base"] + self.context, session_layers, base ) if session_layer_name: system_pid = 4 - file_output = self._write_pe( - pe_table_name, - session_layer_name, - 0, - system_pid, + file_output = self._dump_pe( + pe_table_name, session_layer_name, 0, system_pid, base ) if file_output: @@ -103,9 +105,8 @@ class PEDump(interfaces.plugins.PluginInterface): "Unable to find a session layer with the provided base address mapped in the kernel." ) - def _dump_processes(self, kernel, pe_table_name, _): + def _dump_processes(self, kernel, pe_table_name, base): 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, @@ -121,11 +122,8 @@ class PEDump(interfaces.plugins.PluginInterface): ) proc_layer_name = proc.add_process_layer() - file_output = self._write_pe( - pe_table_name, - proc_layer_name, - proc.vol.offset, - pid, + file_output = self._dump_pe( + pe_table_name, proc_layer_name, proc.vol.offset, pid, base ) if file_output: @@ -147,17 +145,11 @@ class PEDump(interfaces.plugins.PluginInterface): 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 + pe_files = self._dump_kernel(kernel, pe_table_name, self.config["base"]) else: - session_layers = None - method = self._dump_processes + pe_files = self._dump_processes(kernel, pe_table_name, self.config["base"]) - for pid, proc_name, file_output in method( - kernel, pe_table_name, session_layers - ): + for pid, proc_name, file_output in pe_files: yield ( 0, (