Targets (or target) is the terminology we use for the type of source data supported by
dissect.target. It includes
anything that can, one way or another, be used to describe a certain state of a system.
Some examples include:
Raw disk images, virtual disks (
.vhdx, etc) and evidence containers (
Virtual machine descriptor files (
Local live systems (
Forensic packages such as
.tararchives created by acquire or KAPE output directories
… and more
It’s important to be aware of the difference between virtual machine descriptor (like
.vmx) and virtual disk
.vmdk) files. For example, when using a
.vmdk with Dissect, only that VMDK will be loaded, but when
.vmx, all the
.vmdk files described in that VMX file will be loaded instead. This ensures that systems
with multiple virtual disks work as intended. A
.vmx file and
.vmdk file are thus both valid targets. Keep this
in mind when working with investigative data.
On the technical side, everything eventually gets loaded into a
Target Python object. The following sections
will go into more detail on how this object works. This is generally only useful information if you’re looking to
interact with a target from your own Python code or writing a plugin for
Target class is your primary interaction point in
dissect.target. It represents
some system in some specific state, loaded from some target files. Any interaction you want to do with that system is
done through the
The next important attribute to know about is
Target.fs, which is the
root filesystem. This object behaves like any other filesystem in
dissect.target, but exists to allow other filesystems to be mounted at arbitrary paths or drive letters within
the context of a
Target. Use this when you need to interact with “the filesystem” of a target.
Finally, there are the Plugins. Plugins are functions that can be executed on a target. They can be as
simple as reading the hostname from
/etc/hostname, or as advanced as parsing a specific artefact like the
Windows event log.
Plugins are dynamically made available on the
Target object. For example, if you wanted to run the
Target instance named
t, you would do
t.evtx(), which will:
Check if the
evtxfunction is already cached, otherwise perform a lookup which will:
Find a plugin that exports the
evtxfunction and see if it’s compatible.
Cache it and register it on the
t, it is now available as
If the plugin function is marked as a
property, it will behave like a Python
If it’s a function, calling it (
t.evtx()) will execute the plugin function.
For more information on how plugins work, please read their documentation at Plugins.
Target initialisation usually starts with finding a loader for a target, unless you’re
manually opening a target. If no compatible loader is found, the
default behaviour is to treat the target path as a container and add it as a disk.
After all of the discovered disks, volumes and/or filesystems are added by a loader (or the default behaviour), the
Target object will continue initialisation.
Initialisation roughly consists of the following steps:
Perform volume discovery using
volume.open()and add to
Add disk as a raw volume if no additional volumes were discovered.
Discover and open logical volume managers using
volume.open_lvm()and add discovered logical volumes to
Discover and open encrypted volumes using
volume.open_encrypted()and add discovered and decrypted volumes to
Perform filesystem discovery using
filesystem.open() dissect.target.filesystem.open>()and add discovered filesystems to
Basic initialisation is now done, OS detection still has to happen but that can fallback to a “no-op” OS plugin, leaving you able to interact with just the disks, volumes and filesystems.
Find and iterate over all OS plugins to find the most specific OS.
Subclasses of different OS plugins are considered “more specific”, so e.g.
DebianPluginis more specific than
OS initialisation is performed, this is can be super simple or extremely complex. Generally it consists of:
All the filesystems are mounted at their correct mount points in the root filesystem.
Optional additional OS specific parsing or initialisation is performed.
Targetis now fully initialised.
Manually opening a target#
There are multiple ways to open a
Target object. The recommended way to open a single target is to use the
Target.open() method, or
Target.open_all() to open multiple targets from a single path. These
will return (or yield) a fully initialized and loaded
Target object for you to start interacting with.
You can also opt to manually open a target. This can be useful when you’re writing some code that needs to modify
attributes, or manually add things like filesystems. For example, a recovery script that scrapes for a filesystem
that may have had its superblock or
from dissect.target import Target t = Target() t.filesystems.add(recover_filesystem()) t.apply() print(t.hostname)
Targets in targets#
Dissect also supports the concept of targets within targets, referred to as child targets. For example, when a
target contains a
.vmdk file within itself, we can tell
dissect.target to load that target from within the
context of the first target. This can be useful when dealing with hypervisors.
Say, for example, we opened a Hyper-V target locally from
\\.\PhysicalDrive0, we can parse the metadata
ProgramData/Microsoft/Windows/Hyper-V/data.vmcx that tells us where all of the virtual machines are stored.
Then we can then use these paths and tell
dissect.target to load another target from there. Reading all of these
files will still happen from
\\.\PhysicalDrive0, passing through the various abstraction layers of
This allows Dissect to read the disks from running virtual machines, regardless of locks the operating has on these files.