:py:mod:`dissect.target.plugin` =============================== .. py:module:: dissect.target.plugin .. autoapi-nested-parse:: Dissect plugin system. See ``dissect/target/plugins/general/example.py`` for an example plugin. Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: dissect.target.plugin.OperatingSystem dissect.target.plugin.PluginDescriptor dissect.target.plugin.FunctionDescriptor dissect.target.plugin.FailureDescriptor dissect.target.plugin.PluginDescriptorLookup dissect.target.plugin.FunctionDescriptorLookup dissect.target.plugin.PluginRegistry dissect.target.plugin.Plugin dissect.target.plugin.OSPlugin dissect.target.plugin.ChildTargetPlugin dissect.target.plugin.NamespacePlugin dissect.target.plugin.InternalPlugin dissect.target.plugin.InternalNamespacePlugin Functions ~~~~~~~~~ .. autoapisummary:: :nosignatures: dissect.target.plugin.export dissect.target.plugin.internal dissect.target.plugin.arg dissect.target.plugin.alias dissect.target.plugin.clone_alias dissect.target.plugin.register dissect.target.plugin.plugins dissect.target.plugin.os_plugins dissect.target.plugin.child_plugins dissect.target.plugin.functions dissect.target.plugin.lookup dissect.target.plugin.load dissect.target.plugin.os_match dissect.target.plugin.failed dissect.target.plugin.find_functions dissect.target.plugin.find_functions_by_record_field_type dissect.target.plugin.generate dissect.target.plugin.load_module_from_name dissect.target.plugin.load_module_from_file dissect.target.plugin.load_modules_from_paths dissect.target.plugin.get_external_module_paths dissect.target.plugin.environment_variable_paths Attributes ~~~~~~~~~~ .. autoapisummary:: dissect.target.plugin.log dissect.target.plugin.MODULE_PATH dissect.target.plugin.OS_MODULE_PATH dissect.target.plugin.OUTPUTS dissect.target.plugin.PLUGINS dissect.target.plugin.GENERATED dissect.target.plugin.__INTERNAL_PLUGIN_METHOD_NAMES__ .. py:data:: log .. py:data:: MODULE_PATH :value: 'dissect.target.plugins' The base module path to the in-tree plugins. .. py:data:: OS_MODULE_PATH :value: 'dissect.target.plugins.os' .. py:data:: OUTPUTS :value: ('default', 'record', 'yield', 'none') The different output types supported by ``@export``. .. py:class:: OperatingSystem Bases: :py:obj:`dissect.target.helpers.utils.StrEnum` Sortable and serializible string-based enum. .. py:attribute:: ANDROID :value: 'android' .. py:attribute:: BSD :value: 'bsd' .. py:attribute:: CITRIX :value: 'citrix-netscaler' .. py:attribute:: ESXI :value: 'esxi' .. py:attribute:: FORTIOS :value: 'fortios' .. py:attribute:: IOS :value: 'ios' .. py:attribute:: LINUX :value: 'linux' .. py:attribute:: MACOS :value: 'macos' .. py:attribute:: OSX :value: 'osx' .. py:attribute:: PROXMOX :value: 'proxmox' .. py:attribute:: UNIX :value: 'unix' .. py:attribute:: VYOS :value: 'vyos' .. py:attribute:: WINDOWS :value: 'windows' .. py:class:: PluginDescriptor .. py:attribute:: module :type: str .. py:attribute:: qualname :type: str .. py:attribute:: namespace :type: str .. py:attribute:: path :type: str .. py:attribute:: findable :type: bool .. py:attribute:: functions :type: list[str] .. py:attribute:: exports :type: list[str] .. py:property:: cls :type: type[Plugin] .. py:class:: FunctionDescriptor .. py:attribute:: name :type: str .. py:attribute:: namespace :type: str .. py:attribute:: path :type: str .. py:attribute:: exported :type: bool .. py:attribute:: internal :type: bool .. py:attribute:: findable :type: bool .. py:attribute:: alias :type: bool .. py:attribute:: output :type: str .. py:attribute:: method_name :type: str .. py:attribute:: module :type: str .. py:attribute:: qualname :type: str .. py:property:: cls :type: type[Plugin] .. py:property:: func :type: collections.abc.Callable[Ellipsis, Any] .. py:property:: record :type: flow.record.RecordDescriptor | list[flow.record.RecordDescriptor] | None .. py:property:: args :type: list[tuple[list[str], dict[str, Any]]] .. py:class:: FailureDescriptor .. py:attribute:: module :type: str .. py:attribute:: stacktrace :type: list[str] .. py:class:: PluginDescriptorLookup .. py:attribute:: __regular__ :type: dict[str, PluginDescriptor] .. py:attribute:: __os__ :type: dict[str, PluginDescriptor] .. py:attribute:: __child__ :type: dict[str, PluginDescriptor] .. py:class:: FunctionDescriptorLookup .. py:attribute:: __regular__ :type: dict[str, dict[str, FunctionDescriptor]] .. py:attribute:: __os__ :type: dict[str, dict[str, FunctionDescriptor]] .. py:attribute:: __child__ :type: dict[str, dict[str, FunctionDescriptor]] .. py:class:: PluginRegistry .. py:attribute:: __plugins__ :type: PluginDescriptorLookup .. py:attribute:: __functions__ :type: FunctionDescriptorLookup .. py:attribute:: __ostree__ :type: _OSTree .. py:attribute:: __failed__ :type: list[FailureDescriptor] :value: [] .. py:data:: PLUGINS :type: PluginRegistry The plugin registry. Note: It's very important that all values in this dictionary are serializable. The plugin registry can be stored in a file and loaded later. Plain Python syntax is used to store the registry. An exception is made for :class:`FailureDescriptor`, :class:`FunctionDescriptor` and :class:`PluginDescriptor`. .. py:data:: GENERATED :value: False .. py:function:: export(*args, **kwargs) -> collections.abc.Callable[Ellipsis, Any] Decorator to be used on Plugin functions that should be exported. Supported keyword arguments: property (bool): Whether this export should be regarded as a property. Properties are implicitly cached. cache (bool): Whether the result of this function should be cached. record (RecordDescriptor): The :class:`flow.record.RecordDescriptor` for the records that this function yields. If multiple record types are yielded, specificy each descriptor in a list or tuple. If the records are dynamically made, use :func:`dissect.target.helpers.record.DynamicDescriptor` instead. output (str): The output type of this function. Must be one of: - default: Single return value - record: Yields records. Implicit when record argument is given. - yield: Yields printable values. - none: No return value. Plugin is responsible for output formatting and should return ``None``. The ``export`` decorator adds some additional private attributes to an exported method or property: - ``__output__``: The output type to expect for this function, this is the same as ``output``. - ``__record__``: The type of record to expect, this value is the same as ``record``. - ``__exported__``: set to ``True`` to indicate the method or property is exported. :raises ValueError: if there was an invalid output type. :returns: An exported function from a plugin. .. py:function:: internal(*args, **kwargs) -> collections.abc.Callable[Ellipsis, Any] Decorator to be used on plugin functions that should be internal only. Making a plugin internal means that it's only callable from the Python API and not through ``target-query``. This decorator adds the ``__internal__`` private attribute to a method or property. The attribute is always set to ``True``, to tell :func:`register` that it is an internal method or property. .. py:function:: arg(*args, **kwargs) -> collections.abc.Callable[Ellipsis, Any] Decorator to be used on Plugin functions that accept additional command line arguments. Command line arguments can be added using the ``@arg`` decorator. Arguments to this decorator are directly forwarded to the ``ArgumentParser.add_argument`` function of ``argparse``. Resulting arguments are passed to the function using kwargs. The keyword argument name must match the argparse argument name. This decorator adds the ``__args__`` private attribute to a method or property. This attribute holds all the command line arguments that were added to the plugin function. .. py:function:: alias(*args, **kwargs: dict[str, Any]) -> collections.abc.Callable[Ellipsis, Any] Decorator to be used on :class:`Plugin` functions to register an alias of that function. .. py:function:: clone_alias(cls: type, attr: collections.abc.Callable[Ellipsis, Any], alias: str) -> None Clone the given attribute to an alias in the provided class. .. py:class:: Plugin(target: dissect.target.target.Target) Base class for plugins. Plugins can optionally be namespaced by specifying the ``__namespace__`` class attribute. Namespacing results in your plugin needing to be prefixed with this namespace when being called. For example, if your plugin has specified ``test`` as namespace and a function called ``example``, you must call your plugin with ``test.example``. A ``Plugin`` class has the following private class attributes: - ``__namespace__`` - ``__record_descriptors__`` With the following two being assigned in :func:`register`: - ``__functions__`` - ``__exports__`` Additionally, the methods and attributes of :class:`Plugin` receive more private attributes by using decorators. The :func:`export` decorator adds the following private attributes - ``__exported__`` - ``__output__``: Set with the :func:`export` decorator. - ``__record__``: Set with the :func:`export` decorator. The :func:`internal` decorator and :class:`InternalPlugin` set the ``__internal__`` attribute. Finally. :func:`args` decorator sets the ``__args__`` attribute. The :func:`alias` decorator populates the ``__aliases__`` private attribute of :class:`Plugin` methods. Resulting clones of the :class:`Plugin` are populated with the boolean ``__alias__`` attribute set to ``True``. :param target: The :class:`~dissect.target.target.Target` object to load the plugin for. .. py:attribute:: __namespace__ :type: str :value: None Defines the plugin namespace. .. py:attribute:: __record_descriptors__ :type: list[flow.record.RecordDescriptor] :value: None Defines a list of :class:`~flow.record.RecordDescriptor` of the exported plugin functions. .. py:attribute:: __register__ :type: bool :value: True Determines whether this plugin will be registered. .. py:attribute:: __findable__ :type: bool :value: True Determines whether this plugin will be revealed when using search patterns. Some (meta)-plugins are not very suitable for wild cards on CLI or plugin searches, because they will produce duplicate records or results. For instance a plugin that offers the same functions as subplugins will produce redundant results when used with a wild card (browser.* -> browser.history + browser.*.history). .. py:attribute:: __functions__ :type: list[str] Internal. A list of all method names decorated with ``@internal`` or ``@export``. .. py:attribute:: __exports__ :type: list[str] Internal. A list of all method names decorated with ``@export``. .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:attribute:: target .. py:method:: __getattr__(name: str, /) -> Any .. py:method:: is_compatible() -> bool Perform a compatibility check with the target. .. py:method:: check_compatible() -> None :abstractmethod: Perform a compatibility check with the target. This function should return ``None`` if the plugin is compatible with the current target (``self.target``). For example, check if a certain file exists. Otherwise it should raise an :class:`UnsupportedPluginError`. :raises UnsupportedPluginError: If the plugin could not be loaded. .. py:method:: __call__(*args, **kwargs) -> collections.abc.Iterator[flow.record.Record | Any] Return the records of all exported methods. :raises PluginError: If the subclass is not a namespace plugin. .. py:method:: get_paths() -> collections.abc.Iterator[pathlib.Path] Return all artifact paths. .. py:method:: get_all_paths() -> collections.abc.Iterator[pathlib.Path] Return all artifact and auxiliary paths. The implementation of this function will probably change in the future, but the interface should stay the same. .. py:function:: register(plugincls: type[Plugin]) -> None Register a plugin, and put related data inside :attr:`PLUGINS`. This function uses the following private attributes that are set using decorators: - ``__exported__``: Set in :func:`export`. - ``__internal__``: Set in :func:`internal`. Additionally, ``register`` sets the following private attributes on the `plugincls`: - ``__functions__``: A list of all the methods and properties that are ``__internal__`` or ``__exported__``. - ``__exports__``: A list of all the methods or properties that were explicitly exported. If a plugincls ``__register__`` attribute is set to ``False``, the plugin will not be registered, but the plugin will still be processed for the private attributes mentioned above. :param plugincls: A plugin class to register. :raises ValueError: If ``plugincls`` is not a subclass of :class:`Plugin`. .. py:function:: plugins(osfilter: type[OSPlugin] | None = None, *, index: str = '__regular__') -> collections.abc.Iterator[PluginDescriptor] Walk the plugin registry and return plugin descriptors. If ``osfilter`` is specified, only plugins related to the provided OSPlugin, or plugins with no OS relation are returned. If ``osfilter`` is ``None``, all plugins will be returned. One exception to this is if the ``osfilter`` is a (sub-)class of ``DefaultOSPlugin``, then plugins are returned as if no ``osfilter`` was specified. The ``index`` parameter can be used to specify the index to return plugins from. By default, this is set to return regular plugins. Other possible values are ``__os__`` and ``__child__``. These return :class:`OSPlugin` and :class:`ChildTargetPlugin` respectively. :param osfilter: The optional :class:`OSPlugin` to filter the returned plugins on. :param index: The plugin index to return plugins from. Defaults to regular plugins. :Yields: Plugin descriptors in the plugin registry based on the given filter criteria. .. py:function:: os_plugins() -> collections.abc.Iterator[PluginDescriptor] Retrieve all OS plugin descriptors. .. py:function:: child_plugins() -> collections.abc.Iterator[PluginDescriptor] Retrieve all child plugin descriptors. .. py:function:: functions(osfilter: type[OSPlugin] | None = None, *, index: str = '__regular__') -> collections.abc.Iterator[FunctionDescriptor] Retrieve all function descriptors. :param osfilter: The optional :class:`OSPlugin` to filter the returned functions on. :param index: The plugin index to return functions from. Defaults to regular functions. :Yields: Function descriptors in the plugin registry based on the given filter criteria. .. py:function:: lookup(function_name: str, osfilter: type[OSPlugin] | None = None, *, index: str = '__regular__') -> collections.abc.Iterator[FunctionDescriptor] Lookup a function descriptor by function name. :param func_name: Function name to lookup. :param osfilter: The optional ``OSPlugin`` to filter results with for compatibility. :param index: The plugin index to return plugins from. Defaults to regular functions. :Yields: Function descriptors that match the given function name and filter criteria. .. py:function:: load(desc: FunctionDescriptor | PluginDescriptor) -> type[Plugin] Helper function that loads a plugin from a given function or plugin descriptor. :param desc: Function descriptor as returned by :func:`plugin.lookup` or plugin descriptor as returned by :func:`plugin.plugins`. :returns: The plugin class. :raises PluginError: Raised when any other exception occurs while trying to load the plugin. .. py:function:: os_match(target: dissect.target.target.Target, descriptor: PluginDescriptor) -> bool Check if a plugin descriptor is compatible with the target OS. :param target: The target to check compatibility with. :param descriptor: The plugin descriptor to check compatibility for. .. py:function:: failed() -> list[FailureDescriptor] Return all plugins that failed to load. .. py:function:: find_functions(patterns: str, target: dissect.target.target.Target | None = None, compatibility: bool = False, ignore_load_errors: bool = False, show_hidden: bool = False) -> tuple[list[FunctionDescriptor], set[str]] Finds exported plugin functions that match the target and the patterns. Given a target, a comma separated list of patterns and an optional compatibility flag, this function finds matching plugins, optionally checking compatibility. :returns: A tuple containing a list of matching function descriptors and a set of invalid patterns. .. py:function:: find_functions_by_record_field_type(field_types: str | list[str], target: dissect.target.target.Target | None = None, compatibility: bool = False, ignore_load_errors: bool = False) -> collections.abc.Iterator[FunctionDescriptor] Find functions that yield records with a specific field type. :param field_types: The field type to search for. :param target: The target to check compatibility with. :param compatibility: Whether to check compatibility with the target. :param ignore_load_errors: Whether to ignore load errors. .. py:function:: generate() -> dict[str, Any] Internal function to generate the list of available plugins. Walks the plugins directory and imports any ``.py`` files in there. Plugins will be automatically registered. :returns: The global ``PLUGINS`` dictionary. .. py:function:: load_module_from_name(module_path: str) -> None Load a module from ``module_path``. .. py:function:: load_module_from_file(path: pathlib.Path, base_path: pathlib.Path) -> None Loads a module from a file indicated by ``path`` relative to ``base_path``. The module is added to ``sys.modules`` so it can be found everywhere. :param path: The file to load as module. :param base_path: The base directory of the module. .. py:function:: load_modules_from_paths(paths: list[pathlib.Path]) -> None Iterate over the ``paths`` and load all ``.py`` files. .. py:function:: get_external_module_paths(path_list: list[pathlib.Path]) -> list[pathlib.Path] Return a list of external plugin directories. .. py:function:: environment_variable_paths() -> list[pathlib.Path] Return additional plugin directories specified by the ``DISSECT_PLUGINS`` environment variable. .. py:class:: OSPlugin(target: dissect.target.target.Target) Bases: :py:obj:`Plugin` Base class for OS plugins. This provides a base class for certain common functions of OS's, which each OS plugin has to implement separately. For example, it provides an interface for retrieving the hostname and users of a target. All derived classes MUST implement ALL the classmethods and exported methods with the same ``@classmethod`` or ``@export(...)`` annotation. .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:method:: check_compatible() -> bool OSPlugin's use a different compatibility check, override the one from the :class:`Plugin` class. :returns: This function always returns ``True``. .. py:method:: detect(fs: dissect.target.filesystem.Filesystem) -> dissect.target.filesystem.Filesystem | None :classmethod: :abstractmethod: Provide detection of this OSPlugin on a given filesystem. :param fs: :class:`~dissect.target.filesystem.Filesystem` to detect the OS on. :returns: The root filesystem / sysvol when found. .. py:method:: create(target: dissect.target.target.Target, sysvol: dissect.target.filesystem.Filesystem) -> Self :classmethod: :abstractmethod: Initiate this OSPlugin with the given target and detected filesystem. :param target: The :class:`~dissect.target.target.Target` object. :param sysvol: The filesystem that was detected in the ``detect()`` function. :returns: An instantiated version of the OSPlugin. .. py:method:: hostname() -> str | None :abstractmethod: Return the target's hostname. :returns: The hostname as string. .. py:method:: ips() -> list[str] :abstractmethod: Return the IP addresses configured in the target. :returns: The IPs as list. .. py:method:: version() -> str | None :abstractmethod: Return the target's OS version. :returns: The OS version as string. .. py:method:: users() -> list[flow.record.Record] :abstractmethod: Return the users available in the target. :returns: A list of user records. .. py:method:: os() -> str :abstractmethod: Return a slug of the target's OS name. :returns: A slug of the OS name, e.g. 'windows' or 'linux'. .. py:method:: misc_user_paths() -> collections.abc.Iterator[tuple[str, tuple[str, str] | None]] :abstractmethod: Yields miscellaneous user paths and user keys. .. rubric:: Example ("c:/Windows/ServiceProfiles/LocalService", ("sid", "S-1-5-19")) .. py:method:: os_tree() -> list[str] Returns the :func:`os` value of this and all the OS plugin parents. .. py:method:: architecture() -> str | None :abstractmethod: Return a slug of the target's OS architecture. :returns: A slug of the OS architecture, e.g. 'x86_32-unix', 'MIPS-linux' or 'AMD64-win32', or 'unknown' if the architecture is unknown. .. py:class:: ChildTargetPlugin(target: dissect.target.target.Target) Bases: :py:obj:`Plugin` A Child target is a special plugin that can list more Targets. For example, :class:`~dissect.target.plugins.child.esxi.ESXiChildTargetPlugin` can list all of the Virtual Machines on the host. .. py:attribute:: __type__ :value: None .. py:method:: list_children() -> collections.abc.Iterator[dissect.target.helpers.record.ChildTargetRecord] :abstractmethod: Yield :class:`~dissect.target.helpers.record.ChildTargetRecord` records of all possible child targets on this target. .. py:class:: NamespacePlugin(target: dissect.target.target.Target) Bases: :py:obj:`Plugin` A namespace plugin provides services to access functionality from a group of subplugins. Support is currently limited to shared exported functions with output type ``record`` and ``yield``. .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:method:: __init_subclass_subplugin__(**kwargs) -> None .. py:method:: check_compatible() -> None Perform a compatibility check with the target. This function should return ``None`` if the plugin is compatible with the current target (``self.target``). For example, check if a certain file exists. Otherwise it should raise an :class:`UnsupportedPluginError`. :raises UnsupportedPluginError: If the plugin could not be loaded. .. py:data:: __INTERNAL_PLUGIN_METHOD_NAMES__ .. py:class:: InternalPlugin(target: dissect.target.target.Target) Bases: :py:obj:`Plugin` Parent class for internal plugins. InternalPlugin marks all non-private methods internal by default (same as ``@internal`` decorator). .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:class:: InternalNamespacePlugin(target: dissect.target.target.Target) Bases: :py:obj:`NamespacePlugin`, :py:obj:`InternalPlugin` A namespace plugin provides services to access functionality from a group of subplugins. Support is currently limited to shared exported functions with output type ``record`` and ``yield``.