From e8b44efc17dbfd6e412436d324702ed3e4cc7c7f Mon Sep 17 00:00:00 2001 From: Abyss Watcher Date: Sun, 19 Jan 2025 15:13:26 +0100 Subject: [PATCH] add and leverage write_inode_content_to_stream --- .../framework/plugins/linux/pagecache.py | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/volatility3/framework/plugins/linux/pagecache.py b/volatility3/framework/plugins/linux/pagecache.py index d57bd77f8..42fa7538f 100644 --- a/volatility3/framework/plugins/linux/pagecache.py +++ b/volatility3/framework/plugins/linux/pagecache.py @@ -6,7 +6,7 @@ import math import logging import datetime from dataclasses import dataclass, astuple -from typing import List, Set, Type, Iterable +from typing import List, Set, Type, Iterable, IO from volatility3.framework.constants import architectures from volatility3.framework import renderers, interfaces @@ -452,31 +452,47 @@ class InodePages(plugins.PluginInterface): vollog.error("The inode is not a regular file") return None - # By using truncate/seek, provided the filesystem supports it, a sparse file will be + try: + with open_method(filename) as f: + InodePages.write_inode_content_to_stream(inode, f, vmlinux_layer) + except OSError as e: + vollog.error("Unable to write to file (%s): %s", filename, e) + + @staticmethod + def write_inode_content_to_stream( + inode: interfaces.objects.ObjectInterface, + stream: IO, + vmlinux_layer: interfaces.layers.TranslationLayerInterface, + ) -> None: + """Extracts the inode's contents from the page cache and saves them to a stream + + Args: + inode: The inode to dump + stream: A IO steam to write to, typically FileHandlerInterface or BytesIO + vmlinux_layer: The kernel layer to obtain the page size + """ + + # By using truncate/seek, provided the filesystem supports it, and the + # stream is a File interface, a sparse file will be # created, saving both disk space and I/O time. # Additionally, using the page index will guarantee that each page is written at the # appropriate file position. - try: - with open_method(filename) as f: - inode_size = inode.i_size - f.truncate(inode_size) + inode_size = inode.i_size + stream.truncate(inode_size) - for page_idx, page_content in inode.get_contents(): - current_fp = page_idx * vmlinux_layer.page_size - max_length = inode_size - current_fp - page_bytes = page_content[:max_length] - if current_fp + len(page_bytes) > inode_size: - vollog.error( - "Page out of file bounds: inode 0x%x, inode size %d, page index %d", - inode.vol.offset, - inode_size, - page_idx, - ) - f.seek(current_fp) - f.write(page_bytes) - - except OSError as e: - vollog.error("Unable to write to file (%s): %s", filename, e) + for page_idx, page_content in inode.get_contents(): + current_fp = page_idx * vmlinux_layer.page_size + max_length = inode_size - current_fp + page_bytes = page_content[:max_length] + if current_fp + len(page_bytes) > inode_size: + vollog.error( + "Page out of file bounds: inode 0x%x, inode size %d, page index %d", + inode.vol.offset, + inode_size, + page_idx, + ) + stream.seek(current_fp) + stream.write(page_bytes) def _generator(self): vmlinux_module_name = self.config["kernel"]