From 46576a8b21511b11ba88157bf48bb7bb49d68add Mon Sep 17 00:00:00 2001 From: Andreia Ocanoaia Date: Fri, 12 Sep 2025 18:37:51 +0300 Subject: [PATCH 1/2] Add documentation for loading QCOW2 snapshots Add a section on how to use QCOW2 module to load images that are based on backing files. This is related to changes on: https://github.com/fox-it/dissect.hypervisor/issues/64 Signed-off-by: Andreia Ocanoaia --- .../projects/dissect.hypervisor/index.rst | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/source/projects/dissect.hypervisor/index.rst b/docs/source/projects/dissect.hypervisor/index.rst index 4c248cc..49b6685 100644 --- a/docs/source/projects/dissect.hypervisor/index.rst +++ b/docs/source/projects/dissect.hypervisor/index.rst @@ -57,6 +57,49 @@ a VMDK for reading: Many of the parsers in this package behave in a very similar way, so check the API reference to see how to utilize the parser you need. +Open QCOW2 snapshots +~~~~~~~~~~~~~~~~~~~~ + +For `qcow2` images there is support for backing-files and it can either be automatically loaded when opening a target. +The backing-file will automatically be read from the `qcow2` headers and dissect will attempt to load it. + +.. code-block:: python + target = Target.open(target_path) + print(target.users()) + +Or, for more control, the path to the backing file can be passed when initializing a `qcow2` disk: + +.. code-block:: python + def open_qcow2_with_backing_file(snapshot_path: Path, backing_path: Path): + # Open base QCOW2 image + backing_fh = backing_path.open("rb") + base_qcow2 = qcow2.QCow2(backing_fh) + base_stream = base_qcow2.open() + + # Open snapshot QCOW2 image with base as backing file + snapshot_fh = snapshot_path.open("rb") + snapshot_qcow2 = qcow2.QCow2( + snapshot_fh, + backing_file=base_stream + ) + snapshot_stream = snapshot_qcow2.open() + + return snapshot_stream, snapshot_fh, backing_fh, base_stream + + def analyze_image(snapshot_path: Path, backing_path: Path): + # Open the QCOW2 snapshot along with its backing file and get file/stream handles + snapshot_stream, snapshot_fh, backing_fh, base_stream = open_qcow2_with_backing_file(snapshot_path, backing_path) + + # Create a new Dissect target to analyze the disk image + target = Target() + # Add the snapshot stream to the target’s disks + target.disks.add(snapshot_stream) + # Resolve all disks, volumes and filesystems and load an operating system on the current + target.apply() + + # Collect data from the snapshot + print(target.users()) + Tools ----- From 474264a1952ec216178fb80d5f38c5bba6ee7bd0 Mon Sep 17 00:00:00 2001 From: Andreia Ocanoaia Date: Tue, 17 Mar 2026 14:13:19 +0200 Subject: [PATCH 2/2] Update docs/source/projects/dissect.hypervisor/index.rst Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com> --- .../projects/dissect.hypervisor/index.rst | 58 +++++++------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/docs/source/projects/dissect.hypervisor/index.rst b/docs/source/projects/dissect.hypervisor/index.rst index 49b6685..8d0abe2 100644 --- a/docs/source/projects/dissect.hypervisor/index.rst +++ b/docs/source/projects/dissect.hypervisor/index.rst @@ -57,48 +57,34 @@ a VMDK for reading: Many of the parsers in this package behave in a very similar way, so check the API reference to see how to utilize the parser you need. -Open QCOW2 snapshots -~~~~~~~~~~~~~~~~~~~~ +Opening QCOW2 disks & backing-files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For `qcow2` images there is support for backing-files and it can either be automatically loaded when opening a target. -The backing-file will automatically be read from the `qcow2` headers and dissect will attempt to load it. +The QCOW2 parser provided by this library can be used to open QCOW2 disk images, including snapshots and backing-files. +When opening a QCOW2 image as a target, any backing-files are automatically loaded and resolved. + +For example, to open a QCOW2 image along with its backing-file: .. code-block:: python - target = Target.open(target_path) - print(target.users()) -Or, for more control, the path to the backing file can be passed when initializing a `qcow2` disk: + from dissect.hypervisor.disk.qcow2 import QCow2 + + with open("/path/to/base-image.qcow2", "rb") as base_image, \ + open("/path/to/backing-file.qcow2", "rb") as backing_file: + qcow2 = QCow2(base_image, backing_file=backing_file) + + print(qcow2.open().read(512)) + print(qcow2.backing_file.read(512)) + +When opening a QCOW2 image using the dissect.target :class:`~dissect.target.target.Target` class, any backing-files +are automatically loaded and resolved. For example: .. code-block:: python - def open_qcow2_with_backing_file(snapshot_path: Path, backing_path: Path): - # Open base QCOW2 image - backing_fh = backing_path.open("rb") - base_qcow2 = qcow2.QCow2(backing_fh) - base_stream = base_qcow2.open() - - # Open snapshot QCOW2 image with base as backing file - snapshot_fh = snapshot_path.open("rb") - snapshot_qcow2 = qcow2.QCow2( - snapshot_fh, - backing_file=base_stream - ) - snapshot_stream = snapshot_qcow2.open() - - return snapshot_stream, snapshot_fh, backing_fh, base_stream - - def analyze_image(snapshot_path: Path, backing_path: Path): - # Open the QCOW2 snapshot along with its backing file and get file/stream handles - snapshot_stream, snapshot_fh, backing_fh, base_stream = open_qcow2_with_backing_file(snapshot_path, backing_path) - - # Create a new Dissect target to analyze the disk image - target = Target() - # Add the snapshot stream to the target’s disks - target.disks.add(snapshot_stream) - # Resolve all disks, volumes and filesystems and load an operating system on the current - target.apply() - - # Collect data from the snapshot - print(target.users()) + + from dissect.target import Target + + target = Target("/path/to/base-image.qcow2") # Automatically resolves backing-files + print(target.hostname) Tools -----