Targets¶
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 (
.vmdk
,.vhdx
, etc) and evidence containers (.E01
)Virtual machine descriptor files (
.vmx
,.vmcx
, etc)Local live systems (
\\.\PhysicalDrive0
,/dev/sda
)Forensic packages such as
.tar
archives 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
(like .vmdk
) files. For example, when using a .vmdk
with Dissect, only that VMDK will be loaded, but when
using a .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 dissect.target
.
The Target
object¶
The 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 Target
class.
Conceptually a target consists of one or more disks, volumes
and filesystems, which can be modified or added by Loaders.
These items are available as attributes on a Target
instance we’ve named t
at:
t.disks
t.volumes
t.filesystems
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 evtx
plugin
on a Target
instance named t
, you would do t.evtx()
, which will:
Check if the
evtx
function is already cached, otherwise perform a lookup which will:Find a plugin that exports the
evtx
function and see if it’s compatible.Cache it and register it on the
Target
instancet
, it is now available ast.evtx
.
If the plugin function is marked as a
property
, it will behave like a Python@property
.If it’s a function, calling it (
t.evtx()
) will execute the plugin function.
See also
For more information on how plugins work, please read their documentation at Plugins.
To learn more about the Target
object, it’s recommended to read either the Target
class documentation or the source code.
Initialisation¶
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:
Iterate
Target.disks
.Perform volume discovery using
volume.open()
and add toTarget.volumes
.Add disk as a raw volume if no additional volumes were discovered.
Iterate
Target.volumes
.Discover and open logical volume managers using
volume.open_lvm()
and add discovered logical volumes toTarget.volumes
.Discover and open encrypted volumes using
volume.open_encrypted()
and add discovered and decrypted volumes toTarget.volumes
.Perform filesystem discovery using
filesystem.open() dissect.target.filesystem.open>()
and add discovered filesystems toTarget.filesystems
.
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.
DebianPlugin
is more specific thanLinuxPlugin
.
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.
The
Target
is 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 $BOOT
destroyed:
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
in 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 dissect.target
.
This allows Dissect to read the disks from running virtual machines, regardless of locks the operating has on these files.