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)
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.