-
Notifications
You must be signed in to change notification settings - Fork 8
Add documentation for loading QCOW2 snapshots #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add documentation for loading QCOW2 snapshots #98
Conversation
Add a section on how to use QCOW2 module to load images that are based on backing files. This is related to changes on: fox-it/dissect.hypervisor#64 Signed-off-by: Andreia Ocanoaia <andreia.ocanoaia@gmail.com>
| 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()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @andreia-oca thank you for writing some documentation! I suggested some changes. Let me know what you think.
| 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()) | |
| Opening QCOW2 disks & backing-files | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 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 | |
| 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 | |
| from dissect.target import Target | |
| target = Target("/path/to/base-image.qcow2") # Automatically resolves backing-files | |
| print(target.hostname) |
| 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()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With regards to the previous suggestion. I deliberately removed this from here. I think this would be nicer suited in the /usage/use-cases.rst page.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this more concise in the suggestion comment. Would you mind checking if it still works?
Horofic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I meant to click "Request changes"
Add a section on how to use the QCOW2 module to load
images that are based on backing files.
This is related to changes on: fox-it/dissect.hypervisor#64