Source code for volatility.plugins.windows.dlldump

# 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
#

import logging
import ntpath
from typing import List

from volatility.framework import interfaces, constants
from volatility.framework import renderers
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
from volatility.framework.symbols import intermed
from volatility.framework.symbols.windows import extensions
from volatility.plugins.windows import pslist, vadinfo

vollog = logging.getLogger(__name__)


[docs]class DllDump(interfaces.plugins.PluginInterface): """Dumps process memory ranges as DLLs."""
[docs] @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.TranslationLayerRequirement(name = 'primary', description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), # TODO: Convert this to a ListRequirement so that people can filter on sets of ranges requirements.IntRequirement(name = 'address', description = "Process virtual memory address to include " \ "(all other address ranges are excluded). This must be " \ "a base address, not an address within the desired range.", optional = True), requirements.IntRequirement( name = 'pid', description = "Process ID to include (all other processes are excluded)", optional = True), requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)), requirements.PluginRequirement(name = 'vadinfo', plugin = vadinfo.VadInfo, version = (1, 0, 0)), ]
def _generator(self, procs): pe_table_name = intermed.IntermediateSymbolTable.create(self.context, self.config_path, "windows", "pe", class_types = extensions.pe.class_types) filter_func = lambda _: False if self.config.get('address', None) is not None: filter_func = lambda x: x.get_start() not in [self.config['address']] for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) # TODO: what kind of exceptions could this raise and what should we do? proc_layer_name = proc.add_process_layer() for vad in vadinfo.VadInfo.list_vads(proc, filter_func = filter_func): # this parameter is inherited from the VadInfo plugin. if a user specifies # an address, then it bypasses the DLL identification heuristics if self.config.get("address", None) is None: # rather than relying on the PEB for DLLs, which can be swapped, # it requires special handling on wow64 processes, and its # unreliable from an integrity standpoint, let's use the VADs instead protection_string = vad.get_protection( vadinfo.VadInfo.protect_values(self.context, self.config['primary'], self.config['nt_symbols']), vadinfo.winnt_protections) # DLLs are write copy... if protection_string != "PAGE_EXECUTE_WRITECOPY": continue # DLLs have mapped files... if isinstance(vad.get_file_name(), interfaces.renderers.BaseAbsentValue): continue try: filedata = interfaces.plugins.FileInterface("pid.{0}.{1}.{2:#x}.dmp".format( proc.UniqueProcessId, ntpath.basename(vad.get_file_name()), vad.get_start())) dos_header = self.context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", offset = vad.get_start(), layer_name = proc_layer_name) for offset, data in dos_header.reconstruct(): filedata.data.seek(offset) filedata.data.write(data) self.produce_file(filedata) result_text = "Stored {}".format(filedata.preferred_filename) except Exception: result_text = "Unable to dump PE at {0:#x}".format(vad.get_start()) yield (0, (proc.UniqueProcessId, process_name, result_text))
[docs] def run(self): filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) return renderers.TreeGrid([("PID", int), ("Process", str), ("Result", str)], self._generator( pslist.PsList.list_processes(context = self.context, layer_name = self.config['primary'], symbol_table = self.config['nt_symbols'], filter_func = filter_func)))