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 :doc:`/tools/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 :class:`~dissect.target.target.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 :doc:`disks `, :doc:`volumes ` and :doc:`filesystems `, which can be modified or added by :doc:`/advanced/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 :ref:`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 :doc:`/advanced/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`` instance ``t``, it is now available as ``t.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. .. seealso:: For more information on how plugins work, please read their documentation at :doc:`/advanced/plugins`. To learn more about the ``Target`` object, it's recommended to read either the :class:`~dissect.target.target.Target` class documentation or the `source code `_. Initialisation ~~~~~~~~~~~~~~ ``Target`` initialisation usually starts with finding a :doc:`loader ` for a target, unless you're :ref:`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 :func:`volume.open() ` and add to ``Target.volumes``. * Add disk as a raw volume if no additional volumes were discovered. * Iterate ``Target.volumes``. * Discover and open logical volume managers using :func:`volume.open_lvm() ` and add discovered logical volumes to ``Target.volumes``. * Discover and open encrypted volumes using :func:`volume.open_encrypted() ` and add discovered and decrypted volumes to ``Target.volumes``. * Perform filesystem discovery using :func:`filesystem.open() dissect.target.filesystem.open>` and add discovered filesystems to ``Target.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 :ref:`advanced/plugins:os plugins` to find the most specific OS. * Subclasses of different OS plugins are considered "more specific", so e.g. :class:`~dissect.target.plugins.os.unix.debian._os.DebianPlugin` is more specific than :class:`~dissect.target.plugins.os.unix._os.LinuxPlugin`. * 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 :ref:`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 :func:`Target.open() ` method, or :func:`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: .. code-block:: python 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.