diff --git a/volatility3/framework/plugins/windows/memmap.py b/volatility3/framework/plugins/windows/memmap.py index ecc20267c..e67a6a877 100644 --- a/volatility3/framework/plugins/windows/memmap.py +++ b/volatility3/framework/plugins/windows/memmap.py @@ -27,6 +27,8 @@ class Memmap(interfaces.plugins.PluginInterface): architectures = ["Intel32", "Intel64"]), requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"), requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)), + requirements.BooleanRequirement(name = 'coalesce', description = 'Clump output where possible', + default = False, optional = True), requirements.IntRequirement(name = 'pid', description = "Process ID to include (all other processes are excluded)", optional = True), @@ -36,6 +38,29 @@ class Memmap(interfaces.plugins.PluginInterface): optional = True) ] + @classmethod + def coalesce(cls, mapping_generator): + stashed_offset = stashed_mapped_offset = stashed_size = stashed_mapped_size = stashed_mapped_layer = None + for offset, size, mapped_offset, mapped_size, map_layer in mapping_generator: + if stashed_offset is None or (stashed_offset + stashed_size != offset) or ( + stashed_mapped_offset + stashed_mapped_size != mapped_offset) or (stashed_map_layer != map_layer): + # The block isn't contiguous + if stashed_offset is not None: + yield stashed_offset, stashed_size, stashed_mapped_offset, stashed_mapped_size, stashed_map_layer + # Update all the stashed values after output + stashed_offset = offset + stashed_mapped_offset = mapped_offset + stashed_size = size + stashed_mapped_size = mapped_size + stashed_map_layer = map_layer + else: + # Part of an existing block + stashed_size += size + stashed_mapped_size += mapped_size + # Yield whatever's left + if stashed_offset is not None: + yield stashed_offset, stashed_size, stashed_mapped_offset, stashed_mapped_size, stashed_map_layer + def _generator(self, procs): for proc in procs: pid = "Unknown" @@ -49,6 +74,10 @@ class Memmap(interfaces.plugins.PluginInterface): excp.layer_name)) continue + if self.config['coalesce']: + coalesce = self.coalesce + else: + coalesce = lambda x: x if self.config['dump']: file_handle = self.open(f"pid.{pid}.dmp") else: @@ -56,11 +85,10 @@ class Memmap(interfaces.plugins.PluginInterface): file_handle = contextlib.ExitStack() with file_handle as file_data: file_offset = 0 - for mapval in proc_layer.mapping(0x0, proc_layer.maximum_address, ignore_errors = True): + for mapval in coalesce(proc_layer.mapping(0x0, proc_layer.maximum_address, ignore_errors = True)): offset, size, mapped_offset, mapped_size, maplayer = mapval file_output = "Disabled" - file_offset += size if self.config['dump']: try: data = proc_layer.read(offset, size, pad = True) @@ -71,15 +99,19 @@ class Memmap(interfaces.plugins.PluginInterface): vollog.debug("Unable to write {}'s address {} to {}".format( proc_layer_name, offset, file_handle.preferred_filename)) - yield (0, (format_hints.Hex(offset), format_hints.Hex(mapped_offset), format_hints.Hex(mapped_size), + yield (0, (format_hints.Hex(offset), format_hints.Hex(mapped_offset), + format_hints.Hex(mapped_size), format_hints.Hex(file_offset), file_output)) + + file_offset += mapped_size offset += mapped_size def run(self): filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) return renderers.TreeGrid([("Virtual", format_hints.Hex), ("Physical", format_hints.Hex), - ("Size", format_hints.Hex), ("Offset in File", format_hints.Hex), ("File output", str)], + ("Size", format_hints.Hex), ("Offset in File", format_hints.Hex), + ("File output", str)], self._generator( pslist.PsList.list_processes(context = self.context, layer_name = self.config['primary'],