Python API#

This page really only exists to attract the attention of those looking for how to use Dissect programmatically. There’s nothing special you need to do, because Dissect is designed to be API first, tool second! No longer are you limited with what a single-purposes script or executable does, you get complete freedom to use any component or parser in Dissect however you see fit.

This does make explaining exactly what you can do with the Python API difficult because, well, it’s everything. To not leave you to discover everything on your own, we’ll describe how to use some of the core components of Dissect from your own Python code.

Containers#

Interacting with disk images in different shapes and sizes is a common task in digital forensics. In Dissect we use the term “container” for any data format that contains a raw disk or volume within it. For example, a DD image is a raw or plain container, but a .E01 file is an EWF container. Likewise, .vmdk and .vhdx files are respectively VMDK and VHDX virtual disk containers.

In Dissect we like to keep things simple, so there’s no complex API for working with containers. If you know how to work with a binary file in Python, you know how to work with a container. Let’s open a container using container.open():

import io

from dissect.target import container

fh = container.open("/path/to/file.vmdk")
print(fh.read(512))
fh.seek(-512, io.SEEK_END)
print(fh.read(512))

That’s it! fh will behave like any other Python file-like object. This means it has all the methods you already know when working with binary files in Python and that it’s automatically compatible with any API that accepts a file-like object.

You can also use specific container implementations by using them directly. For example, to open a QCOW2 file, you can use:

from dissect.target.containers.qcow2 import QCow2Container

with open("/path/to/file.qcow2", "rb") as fh:
    disk = QCow2Container(fh)

    print(disk.read(512))

See also

View all available container implementations at dissect.target.containers.

To learn more about containers, continue reading at Containers or read the API documentation of the dissect.target.container module.

Volumes#

After opening a disk, you usually want to parse individual volumes on that disk. This is where the volume parsers of Dissect come into play.

Similarly, like containers, there is no complex API to working with volumes. If you know how to work with a binary file in Python, you know how to work with volumes. Let’s open a volume system using volume.open() and list some volumes from a disk:

from dissect.target import container, volume

# First open a disk. We use a container in this example but this can also be
# any file you open with ``open("/path/to/file.dd", "rb")``.
disk = container.open("/path/to/file.vmdk")
# Opening a volume system (vs) is as easy as passing the file-like object.
vs = volume.open(disk)

for volume in vs.volumes:
    # The Volume object has some useful attributes...
    print(f"Volume #{volume.number} ({volume.name}, {volume.size} bytes)")
    # ... but otherwise also behaves like a file-like object.
    print(volume.read(512))
    print()

Again, volumes are just like any other file-like objects. They have some additional useful attributes, but for all intents and purposes it’s a file-like object.

See also

View all available volume system implementations at dissect.target.volumes.

To learn more about volume systems and volumes (including logical and encrypted volume systems), continue reading at Volumes or read the API documentation of the dissect.target.volume module.

Filesystems#

The next step when interacting with host data is the filesystem. Once again, Dissect tries to make this as painless as possible by adhering as closely to the standard Python functions as possible. Opening a filesystem is very easy using filesystem.open():

from dissect.target import container, filesystem, volume

# First open a disk and some volumes.
# Again, this can be skipped if you directly open a file with a filesystem on it.
disk = container.open("/path/to/file.vmdk")
vs = volume.open(disk)
# Open a filesystem by passing it a file-like object, in this case a volume we opened in the previous step.
fs = filesystem.open(vs.volumes[0])
# There are functions similar to ``os.path`` available on the filesystem object itself...
print(fs.listdir("/"))
# Opening a file is just another file-like object.
print(fs.get("file.txt").open().read(512))
# Or a (mostly) compatible ``pathlib.Path`` object!
path = fs.path("/")
print(list(path.joinpath("some dir").iterdir()))
print(fs.path.joinpath("file.text").read_text())

Just like containers, you can open specific filesystems by importing and using them directly.

from dissect.target.filesystems.ntfs import NtfsFilesystem

with open("/path/to/volume.dd", "rb") as fh:
    fs = NtfsFilesystem(fh)
    print(fs.listdir("/"))

As shown in the examples, there are two main ways of interacting with a filesystem: using os.path-like functions on the Filesystem and FilesystemEntry classes, or by using the (mostly) pathlib.Path compatible TargetPath. The latter is recommended for new code.

See also

View all available filesystem implementations at dissect.target.filesystems.

To learn more about filesystems (including virtual filesystems, the root filesystem, and TargetPath), continue reading at Filesystems or read the API documentation of the dissect.target.filesystem module.

Targets#

Targets are what glues everything together. This is how you interact with a “full” system, from accessing raw disks and volumes, to interpreting and parsing OS specific artefacts. To open one or more targets, you can use the Target.open() or Target.open_all() methods:

from dissect.target import Target

# Open a single target.
t = Target.open("/path/to/target.vmx")
# Open multiple targets.
for t in Target.open_all("/path/to/a/directory/with/targets"):
    print(t.hostname)

You can also manually create a Target object and manually add disks, volumes or filesystems:

from dissect.target import Target

t = Target()
t.disks.add(open_disk())
# And/or
t.volumes.add(open_volume())
# And/or
t.filesystems.add(open_filesystems())
# Calling .apply() will start loading the target.
t.apply()
# Execute a plugin function, in this case the `hostname` function from the OS plugin...
print(t.hostname)
# ... or the `runkeys` plugin function, assuming this was a Windows target.
for entry in t.runkeys():
    print(entry)

See also

To learn more about targets, continue reading at Targets. To learn more about plugins, continue reading at Plugins.

Loading your own modules#

There are a couple of ways you can add your own modules to dissect.target. You can choose from the following options:

  • Specify the path to your module(s) using the DISSECT_PLUGINS environment variable.

  • Specify the path to your module(s) using the --plugin-path argument with the various Dissect Tools.

  • Add a new module in the dissect.target source tree at the correct respective directory.

For regular tool usage or when testing a new functionality, either the environment variable or command line argument options are recommended. These options will allow you to specify directories or individual files. Once you intend to contribute your module back to the Dissect project, you’ll have to move it into the dissect.target source tree.

For example, you could add DISSECT_PLUGINS=~/dissect-plugins to your .bashrc (or equivalent of your shell) to always load addtional modules from ~/dissect-plugins. Or test out a new plugin and loader using target-query --plugin-path ./myplugin.py --plugin-path ./myloader.py.

Note

For more information about developing for Dissect, please continue reading at Developing for Dissect.

Learn how to write your own loader, container, volume system, filesystem or plugin by referring to each respective documentation page.

Other Dissect libraries#

Dissect consists of a lot of libraries. For more details and usage examples on those libraries, please refer to the specific project page under Projects or the API documentation at API Reference.