dissect.target 3.20.dev41__py3-none-any.whl → 3.20.dev44__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dissect/target/loader.py +1 -0
- dissect/target/loaders/proxmox.py +68 -0
- dissect/target/plugin.py +8 -7
- dissect/target/plugins/child/proxmox.py +23 -0
- dissect/target/plugins/general/loaders.py +18 -5
- dissect/target/plugins/general/plugins.py +50 -11
- dissect/target/plugins/os/unix/_os.py +12 -0
- dissect/target/plugins/os/unix/linux/debian/proxmox/__init__.py +0 -0
- dissect/target/plugins/os/unix/linux/debian/proxmox/_os.py +141 -0
- dissect/target/plugins/os/unix/linux/debian/proxmox/vm.py +29 -0
- dissect/target/tools/query.py +15 -6
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/RECORD +18 -13
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/top_level.txt +0 -0
dissect/target/loader.py
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from dissect.target import container
|
7
|
+
from dissect.target.loader import Loader
|
8
|
+
from dissect.target.target import Target
|
9
|
+
|
10
|
+
RE_VOLUME_ID = re.compile(r"(?:file=)?([^:]+):([^,]+)")
|
11
|
+
|
12
|
+
|
13
|
+
class ProxmoxLoader(Loader):
|
14
|
+
"""Loader for Proxmox VM configuration files.
|
15
|
+
|
16
|
+
Proxmox uses volume identifiers in the format of ``storage_id:volume_id``. The ``storage_id`` maps to a
|
17
|
+
storage configuration in ``/etc/pve/storage.cfg``. The ``volume_id`` is the name of the volume within
|
18
|
+
that configuration.
|
19
|
+
|
20
|
+
This loader currently does not support parsing the storage configuration, so it will attempt to open the
|
21
|
+
volume directly from the same directory as the configuration file, or from ``/dev/pve/`` (default LVM config).
|
22
|
+
If the volume is not found, it will log a warning.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, path: Path, **kwargs):
|
26
|
+
path = path.resolve()
|
27
|
+
super().__init__(path)
|
28
|
+
self.base_dir = path.parent
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
def detect(path: Path) -> bool:
|
32
|
+
if path.suffix.lower() != ".conf":
|
33
|
+
return False
|
34
|
+
|
35
|
+
with path.open("rb") as fh:
|
36
|
+
lines = fh.read(512).split(b"\n")
|
37
|
+
needles = [b"cpu:", b"memory:", b"name:"]
|
38
|
+
return all(any(needle in line for line in lines) for needle in needles)
|
39
|
+
|
40
|
+
def map(self, target: Target) -> None:
|
41
|
+
with self.path.open("rt") as fh:
|
42
|
+
for line in fh:
|
43
|
+
if not (line := line.strip()):
|
44
|
+
continue
|
45
|
+
|
46
|
+
key, value = line.split(":", 1)
|
47
|
+
value = value.strip()
|
48
|
+
|
49
|
+
if key.startswith(("scsi", "sata", "ide", "virtio")) and key[-1].isdigit():
|
50
|
+
# https://pve.proxmox.com/wiki/Storage
|
51
|
+
if match := RE_VOLUME_ID.match(value):
|
52
|
+
storage_id, volume_id = match.groups()
|
53
|
+
|
54
|
+
# TODO: parse the storage information from /etc/pve/storage.cfg
|
55
|
+
# For now, let's try a few assumptions
|
56
|
+
disk_path = None
|
57
|
+
if (path := self.base_dir.joinpath(volume_id)).exists():
|
58
|
+
disk_path = path
|
59
|
+
elif (path := self.base_dir.joinpath("/dev/pve/").joinpath(volume_id)).exists():
|
60
|
+
disk_path = path
|
61
|
+
|
62
|
+
if disk_path:
|
63
|
+
try:
|
64
|
+
target.disks.add(container.open(disk_path))
|
65
|
+
except Exception:
|
66
|
+
target.log.exception("Failed to open disk: %s", disk_path)
|
67
|
+
else:
|
68
|
+
target.log.warning("Unable to find disk: %s:%s", storage_id, volume_id)
|
dissect/target/plugin.py
CHANGED
@@ -57,17 +57,18 @@ log = logging.getLogger(__name__)
|
|
57
57
|
|
58
58
|
|
59
59
|
class OperatingSystem(StrEnum):
|
60
|
-
|
61
|
-
WINDOWS = "windows"
|
62
|
-
ESXI = "esxi"
|
60
|
+
ANDROID = "android"
|
63
61
|
BSD = "bsd"
|
62
|
+
CITRIX = "citrix-netscaler"
|
63
|
+
ESXI = "esxi"
|
64
|
+
FORTIOS = "fortios"
|
65
|
+
IOS = "ios"
|
66
|
+
LINUX = "linux"
|
64
67
|
OSX = "osx"
|
68
|
+
PROXMOX = "proxmox"
|
65
69
|
UNIX = "unix"
|
66
|
-
ANDROID = "android"
|
67
70
|
VYOS = "vyos"
|
68
|
-
|
69
|
-
FORTIOS = "fortios"
|
70
|
-
CITRIX = "citrix-netscaler"
|
71
|
+
WINDOWS = "windows"
|
71
72
|
|
72
73
|
|
73
74
|
def export(*args, **kwargs) -> Callable:
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from typing import Iterator
|
2
|
+
|
3
|
+
from dissect.target.exceptions import UnsupportedPluginError
|
4
|
+
from dissect.target.helpers.record import ChildTargetRecord
|
5
|
+
from dissect.target.plugin import ChildTargetPlugin
|
6
|
+
|
7
|
+
|
8
|
+
class ProxmoxChildTargetPlugin(ChildTargetPlugin):
|
9
|
+
"""Child target plugin that yields from the VM listing."""
|
10
|
+
|
11
|
+
__type__ = "proxmox"
|
12
|
+
|
13
|
+
def check_compatible(self) -> None:
|
14
|
+
if self.target.os != "proxmox":
|
15
|
+
raise UnsupportedPluginError("Not a Proxmox operating system")
|
16
|
+
|
17
|
+
def list_children(self) -> Iterator[ChildTargetRecord]:
|
18
|
+
for vm in self.target.vmlist():
|
19
|
+
yield ChildTargetRecord(
|
20
|
+
type=self.__type__,
|
21
|
+
path=vm.path,
|
22
|
+
_target=self.target,
|
23
|
+
)
|
@@ -1,6 +1,8 @@
|
|
1
|
+
import json
|
2
|
+
|
1
3
|
from dissect.target.helpers.docs import INDENT_STEP, get_docstring
|
2
4
|
from dissect.target.loader import LOADERS_BY_SCHEME
|
3
|
-
from dissect.target.plugin import Plugin, export
|
5
|
+
from dissect.target.plugin import Plugin, arg, export
|
4
6
|
|
5
7
|
|
6
8
|
class LoaderListPlugin(Plugin):
|
@@ -10,7 +12,12 @@ class LoaderListPlugin(Plugin):
|
|
10
12
|
pass
|
11
13
|
|
12
14
|
@export(output="none")
|
13
|
-
|
15
|
+
# NOTE: We would prefer to re-use arguments across plugins from argparse in query.py, but that is not possible yet.
|
16
|
+
# For now we use --as-json, but in the future this should be changed to inherit --json from target-query.
|
17
|
+
# https://github.com/fox-it/dissect.target/pull/841
|
18
|
+
# https://github.com/fox-it/dissect.target/issues/889
|
19
|
+
@arg("--as-json", dest="as_json", action="store_true")
|
20
|
+
def loaders(self, as_json: bool = False) -> None:
|
14
21
|
"""List the available loaders."""
|
15
22
|
|
16
23
|
loaders_info = {}
|
@@ -21,6 +28,12 @@ class LoaderListPlugin(Plugin):
|
|
21
28
|
except ImportError:
|
22
29
|
continue
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
loaders = sorted(loaders_info.items())
|
32
|
+
|
33
|
+
if as_json:
|
34
|
+
print(json.dumps([{"name": name, "description": desc} for name, desc in loaders]), end="")
|
35
|
+
|
36
|
+
else:
|
37
|
+
print("Available loaders:")
|
38
|
+
for loader_name, loader_description in loaders:
|
39
|
+
print(f"{INDENT_STEP}{loader_name} - {loader_description}")
|
@@ -1,5 +1,8 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import json
|
1
4
|
import textwrap
|
2
|
-
from typing import
|
5
|
+
from typing import Iterator, Type
|
3
6
|
|
4
7
|
from dissect.target import plugin
|
5
8
|
from dissect.target.helpers.docs import INDENT_STEP, get_plugin_overview
|
@@ -23,7 +26,8 @@ def categorize_plugins(plugins_selection: list[dict] = None) -> dict:
|
|
23
26
|
return output_dict
|
24
27
|
|
25
28
|
|
26
|
-
def get_exported_plugins():
|
29
|
+
def get_exported_plugins() -> list:
|
30
|
+
"""Returns list of exported plugins."""
|
27
31
|
return [p for p in plugin.plugins() if len(p["exports"])]
|
28
32
|
|
29
33
|
|
@@ -50,10 +54,10 @@ def update_dict_recursive(source_dict: dict, updated_dict: dict) -> dict:
|
|
50
54
|
|
51
55
|
|
52
56
|
def output_plugin_description_recursive(
|
53
|
-
structure_dict:
|
57
|
+
structure_dict: dict | Plugin,
|
54
58
|
print_docs: bool,
|
55
|
-
indentation_step=0,
|
56
|
-
) ->
|
59
|
+
indentation_step: int = 0,
|
60
|
+
) -> list[str]:
|
57
61
|
"""Create plugin overview with identations."""
|
58
62
|
|
59
63
|
if isinstance(structure_dict, type) and issubclass(structure_dict, Plugin):
|
@@ -78,10 +82,10 @@ def get_plugin_description(
|
|
78
82
|
|
79
83
|
|
80
84
|
def get_description_dict(
|
81
|
-
structure_dict:
|
85
|
+
structure_dict: dict,
|
82
86
|
print_docs: bool,
|
83
87
|
indentation_step: int,
|
84
|
-
) ->
|
88
|
+
) -> list[str]:
|
85
89
|
"""Returns a list of indented descriptions."""
|
86
90
|
|
87
91
|
output_descriptions = []
|
@@ -105,10 +109,17 @@ class PluginListPlugin(Plugin):
|
|
105
109
|
|
106
110
|
@export(output="none", cache=False)
|
107
111
|
@arg("--docs", dest="print_docs", action="store_true")
|
108
|
-
|
109
|
-
|
112
|
+
# NOTE: We would prefer to re-use arguments across plugins from argparse in query.py, but that is not possible yet.
|
113
|
+
# For now we use --as-json, but in the future this should be changed to inherit --json from target-query.
|
114
|
+
# https://github.com/fox-it/dissect.target/pull/841
|
115
|
+
# https://github.com/fox-it/dissect.target/issues/889
|
116
|
+
@arg("--as-json", dest="as_json", action="store_true")
|
117
|
+
def plugins(self, plugins: list[Plugin] = None, print_docs: bool = False, as_json: bool = False) -> None:
|
118
|
+
"""Print all available plugins."""
|
119
|
+
|
120
|
+
dict_plugins = list({p.path: p.plugin_desc for p in plugins}.values())
|
121
|
+
categorized_plugins = dict(sorted(categorize_plugins(dict_plugins).items()))
|
110
122
|
|
111
|
-
categorized_plugins = dict(sorted(categorize_plugins(plugins).items()))
|
112
123
|
plugin_descriptions = output_plugin_description_recursive(categorized_plugins, print_docs)
|
113
124
|
|
114
125
|
plugins_list = textwrap.indent(
|
@@ -142,4 +153,32 @@ class PluginListPlugin(Plugin):
|
|
142
153
|
"Failed to load:",
|
143
154
|
failed_list,
|
144
155
|
]
|
145
|
-
|
156
|
+
|
157
|
+
if as_json:
|
158
|
+
out = {"loaded": list(generate_plugins_json(plugins))}
|
159
|
+
|
160
|
+
if failed_plugins := plugin.failed():
|
161
|
+
out["failed"] = [
|
162
|
+
{"module": p["module"], "stacktrace": "".join(p["stacktrace"])} for p in failed_plugins
|
163
|
+
]
|
164
|
+
|
165
|
+
print(json.dumps(out), end="")
|
166
|
+
|
167
|
+
else:
|
168
|
+
print("\n".join(output_lines))
|
169
|
+
|
170
|
+
|
171
|
+
def generate_plugins_json(plugins: list[Plugin]) -> Iterator[dict]:
|
172
|
+
"""Generates JSON output of a list of :class:`Plugin`s."""
|
173
|
+
|
174
|
+
for p in plugins:
|
175
|
+
func = getattr(p.class_object, p.method_name)
|
176
|
+
description = getattr(func, "__doc__", None)
|
177
|
+
summary = description.split("\n\n", 1)[0].strip() if description else None
|
178
|
+
|
179
|
+
yield {
|
180
|
+
"name": p.name,
|
181
|
+
"output": p.output_type,
|
182
|
+
"description": summary,
|
183
|
+
"path": p.path,
|
184
|
+
}
|
@@ -23,6 +23,7 @@ class UnixPlugin(OSPlugin):
|
|
23
23
|
def __init__(self, target: Target):
|
24
24
|
super().__init__(target)
|
25
25
|
self._add_mounts()
|
26
|
+
self._add_devices()
|
26
27
|
self._hostname_dict = self._parse_hostname_string()
|
27
28
|
self._hosts_dict = self._parse_hosts_string()
|
28
29
|
self._os_release = self._parse_os_release()
|
@@ -247,6 +248,17 @@ class UnixPlugin(OSPlugin):
|
|
247
248
|
self.target.log.debug("Mounting %s (%s) at %s", fs, fs.volume, mount_point)
|
248
249
|
self.target.fs.mount(mount_point, fs)
|
249
250
|
|
251
|
+
def _add_devices(self) -> None:
|
252
|
+
"""Add some virtual block devices to the target.
|
253
|
+
|
254
|
+
Currently only adds LVM devices.
|
255
|
+
"""
|
256
|
+
vfs = self.target.fs.append_layer()
|
257
|
+
|
258
|
+
for volume in self.target.volumes:
|
259
|
+
if volume.vs and volume.vs.__type__ == "lvm":
|
260
|
+
vfs.map_file_fh(f"/dev/{volume.raw.vg.name}/{volume.raw.name}", volume)
|
261
|
+
|
250
262
|
def _parse_os_release(self, glob: str | None = None) -> dict[str, str]:
|
251
263
|
"""Parse files containing Unix version information.
|
252
264
|
|
File without changes
|
@@ -0,0 +1,141 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import stat
|
4
|
+
from io import BytesIO
|
5
|
+
from typing import BinaryIO
|
6
|
+
|
7
|
+
from dissect.sql import sqlite3
|
8
|
+
from dissect.util.stream import BufferedStream
|
9
|
+
|
10
|
+
from dissect.target.filesystem import (
|
11
|
+
Filesystem,
|
12
|
+
VirtualDirectory,
|
13
|
+
VirtualFile,
|
14
|
+
VirtualFilesystem,
|
15
|
+
)
|
16
|
+
from dissect.target.helpers import fsutil
|
17
|
+
from dissect.target.plugins.os.unix._os import OperatingSystem, export
|
18
|
+
from dissect.target.plugins.os.unix.linux.debian._os import DebianPlugin
|
19
|
+
from dissect.target.target import Target
|
20
|
+
|
21
|
+
|
22
|
+
class ProxmoxPlugin(DebianPlugin):
|
23
|
+
@classmethod
|
24
|
+
def detect(cls, target: Target) -> Filesystem | None:
|
25
|
+
for fs in target.filesystems:
|
26
|
+
if fs.exists("/etc/pve") or fs.exists("/var/lib/pve"):
|
27
|
+
return fs
|
28
|
+
|
29
|
+
return None
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def create(cls, target: Target, sysvol: Filesystem) -> ProxmoxPlugin:
|
33
|
+
obj = super().create(target, sysvol)
|
34
|
+
|
35
|
+
if (config_db := target.fs.path("/var/lib/pve-cluster/config.db")).exists():
|
36
|
+
with config_db.open("rb") as fh:
|
37
|
+
vfs = _create_pmxcfs(fh, obj.hostname)
|
38
|
+
|
39
|
+
target.fs.mount("/etc/pve", vfs)
|
40
|
+
|
41
|
+
return obj
|
42
|
+
|
43
|
+
@export(property=True)
|
44
|
+
def version(self) -> str:
|
45
|
+
"""Returns Proxmox VE version with underlying OS release."""
|
46
|
+
|
47
|
+
for pkg in self.target.dpkg.status():
|
48
|
+
if pkg.name == "proxmox-ve":
|
49
|
+
distro_name = self._os_release.get("PRETTY_NAME", "")
|
50
|
+
return f"{pkg.name} {pkg.version} ({distro_name})"
|
51
|
+
|
52
|
+
@export(property=True)
|
53
|
+
def os(self) -> str:
|
54
|
+
return OperatingSystem.PROXMOX.value
|
55
|
+
|
56
|
+
|
57
|
+
DT_DIR = 4
|
58
|
+
DT_REG = 8
|
59
|
+
|
60
|
+
|
61
|
+
def _create_pmxcfs(fh: BinaryIO, hostname: str | None = None) -> VirtualFilesystem:
|
62
|
+
# https://pve.proxmox.com/wiki/Proxmox_Cluster_File_System_(pmxcfs)
|
63
|
+
db = sqlite3.SQLite3(fh)
|
64
|
+
|
65
|
+
entries = {row.inode: row for row in db.table("tree")}
|
66
|
+
|
67
|
+
vfs = VirtualFilesystem()
|
68
|
+
for entry in entries.values():
|
69
|
+
if entry.type == DT_DIR:
|
70
|
+
cls = ProxmoxConfigDirectoryEntry
|
71
|
+
elif entry.type == DT_REG:
|
72
|
+
cls = ProxmoxConfigFileEntry
|
73
|
+
else:
|
74
|
+
raise ValueError(f"Unknown pmxcfs file type: {entry.type}")
|
75
|
+
|
76
|
+
parts = []
|
77
|
+
current = entry
|
78
|
+
while current.parent != 0:
|
79
|
+
parts.append(current.name)
|
80
|
+
current = entries[current.parent]
|
81
|
+
parts.append(current.name)
|
82
|
+
|
83
|
+
path = "/".join(parts[::-1])
|
84
|
+
vfs.map_file_entry(path, cls(vfs, path, entry))
|
85
|
+
|
86
|
+
if hostname:
|
87
|
+
node_root = vfs.path(f"nodes/{hostname}")
|
88
|
+
vfs.symlink(str(node_root), "local")
|
89
|
+
vfs.symlink(str(node_root / "lxc"), "lxc")
|
90
|
+
vfs.symlink(str(node_root / "openvz"), "openvz")
|
91
|
+
vfs.symlink(str(node_root / "qemu-server"), "qemu-server")
|
92
|
+
|
93
|
+
# TODO: .version, .members, .vmlist, maybe .clusterlog and .rrd?
|
94
|
+
|
95
|
+
return vfs
|
96
|
+
|
97
|
+
|
98
|
+
class ProxmoxConfigFileEntry(VirtualFile):
|
99
|
+
def open(self) -> BinaryIO:
|
100
|
+
return BufferedStream(BytesIO(self.entry.data or b""))
|
101
|
+
|
102
|
+
def lstat(self) -> fsutil.stat_result:
|
103
|
+
# ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
|
104
|
+
return fsutil.stat_result(
|
105
|
+
[
|
106
|
+
stat.S_IFREG | 0o640,
|
107
|
+
self.entry.inode,
|
108
|
+
id(self.fs),
|
109
|
+
1,
|
110
|
+
0,
|
111
|
+
0,
|
112
|
+
len(self.entry.data) if self.entry.data else 0,
|
113
|
+
0,
|
114
|
+
self.entry.mtime,
|
115
|
+
0,
|
116
|
+
]
|
117
|
+
)
|
118
|
+
|
119
|
+
|
120
|
+
class ProxmoxConfigDirectoryEntry(VirtualDirectory):
|
121
|
+
def __init__(self, fs: VirtualFilesystem, path: str, entry: sqlite3.Row):
|
122
|
+
super().__init__(fs, path)
|
123
|
+
self.entry = entry
|
124
|
+
|
125
|
+
def lstat(self) -> fsutil.stat_result:
|
126
|
+
"""Return the stat information of the given path, without resolving links."""
|
127
|
+
# ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
|
128
|
+
return fsutil.stat_result(
|
129
|
+
[
|
130
|
+
stat.S_IFDIR | 0o755,
|
131
|
+
self.entry.inode,
|
132
|
+
id(self.fs),
|
133
|
+
1,
|
134
|
+
0,
|
135
|
+
0,
|
136
|
+
0,
|
137
|
+
0,
|
138
|
+
self.entry.mtime,
|
139
|
+
0,
|
140
|
+
]
|
141
|
+
)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from typing import Iterator
|
2
|
+
|
3
|
+
from dissect.target.exceptions import UnsupportedPluginError
|
4
|
+
from dissect.target.helpers.record import TargetRecordDescriptor
|
5
|
+
from dissect.target.plugin import Plugin, export
|
6
|
+
|
7
|
+
VirtualMachineRecord = TargetRecordDescriptor(
|
8
|
+
"proxmox/vm",
|
9
|
+
[
|
10
|
+
("string", "path"),
|
11
|
+
],
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class VirtualMachinePlugin(Plugin):
|
16
|
+
"""Plugin to list Proxmox virtual machines."""
|
17
|
+
|
18
|
+
def check_compatible(self) -> None:
|
19
|
+
if self.target.os != "proxmox":
|
20
|
+
raise UnsupportedPluginError("Not a Proxmox operating system")
|
21
|
+
|
22
|
+
@export(record=VirtualMachineRecord)
|
23
|
+
def vmlist(self) -> Iterator[VirtualMachineRecord]:
|
24
|
+
"""List Proxmox virtual machines on this node."""
|
25
|
+
for config in self.target.fs.path("/etc/pve/qemu-server").iterdir():
|
26
|
+
yield VirtualMachineRecord(
|
27
|
+
path=config,
|
28
|
+
_target=self.target,
|
29
|
+
)
|
dissect/target/tools/query.py
CHANGED
@@ -169,31 +169,40 @@ def main():
|
|
169
169
|
# Show the list of available plugins for the given optional target and optional
|
170
170
|
# search pattern, only display plugins that can be applied to ANY targets
|
171
171
|
if args.list:
|
172
|
-
collected_plugins =
|
172
|
+
collected_plugins = []
|
173
173
|
|
174
174
|
if targets:
|
175
175
|
for plugin_target in Target.open_all(targets, args.children):
|
176
176
|
funcs, _ = find_plugin_functions(plugin_target, args.list, compatibility=True, show_hidden=True)
|
177
177
|
for func in funcs:
|
178
|
-
collected_plugins
|
178
|
+
collected_plugins.append(func)
|
179
179
|
else:
|
180
180
|
funcs, _ = find_plugin_functions(Target(), args.list, compatibility=False, show_hidden=True)
|
181
181
|
for func in funcs:
|
182
|
-
collected_plugins
|
182
|
+
collected_plugins.append(func)
|
183
183
|
|
184
|
-
# Display in a user friendly manner
|
185
184
|
target = Target()
|
186
185
|
fparser = generate_argparse_for_bound_method(target.plugins, usage_tmpl=USAGE_FORMAT_TMPL)
|
187
186
|
fargs, rest = fparser.parse_known_args(rest)
|
188
187
|
|
188
|
+
# Display in a user friendly manner
|
189
189
|
if collected_plugins:
|
190
|
-
|
190
|
+
if args.json:
|
191
|
+
print('{"plugins": ', end="")
|
192
|
+
target.plugins(collected_plugins, as_json=args.json)
|
191
193
|
|
192
194
|
# No real targets specified, show the available loaders
|
193
195
|
if not targets:
|
194
196
|
fparser = generate_argparse_for_bound_method(target.loaders, usage_tmpl=USAGE_FORMAT_TMPL)
|
195
197
|
fargs, rest = fparser.parse_known_args(rest)
|
196
|
-
|
198
|
+
del fargs.as_json
|
199
|
+
if args.json:
|
200
|
+
print(', "loaders": ', end="")
|
201
|
+
target.loaders(**vars(fargs), as_json=args.json)
|
202
|
+
|
203
|
+
if args.json:
|
204
|
+
print("}")
|
205
|
+
|
197
206
|
parser.exit()
|
198
207
|
|
199
208
|
if not targets:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev44
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -2,8 +2,8 @@ dissect/target/__init__.py,sha256=Oc7ounTgq2hE4nR6YcNabetc7SQA40ldSa35VEdZcQU,63
|
|
2
2
|
dissect/target/container.py,sha256=0YcwcGmfJjhPXUB6DEcjWEoSuAtTDxMDpoTviMrLsxM,9353
|
3
3
|
dissect/target/exceptions.py,sha256=ULi7NXlqju_d8KENEL3aimmfKTFfbNssfeWhAnOB654,2972
|
4
4
|
dissect/target/filesystem.py,sha256=__p2p72B6mKgIiAOj85EC3ESdZ8gjgkm2pt1KKym3h0,60743
|
5
|
-
dissect/target/loader.py,sha256=
|
6
|
-
dissect/target/plugin.py,sha256=
|
5
|
+
dissect/target/loader.py,sha256=ZlCI7ZyPpysuSKndOiRz_rrGb30_jLMdFD6qObY0Vzg,7374
|
6
|
+
dissect/target/plugin.py,sha256=iUc7OmQJ0wwYJeR7L4VBNJ0AjgYWV69FN0NHgWYaLYI,50682
|
7
7
|
dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
|
8
8
|
dissect/target/target.py,sha256=-UoFO_fqS6XPf77RDHSV4gNRi95wwwpgWq7zIhNVUGk,32719
|
9
9
|
dissect/target/volume.py,sha256=aQZAJiny8jjwkc9UtwIRwy7nINXjCxwpO-_UDfh6-BA,15801
|
@@ -93,6 +93,7 @@ dissect/target/loaders/overlay.py,sha256=tj99HKvNG5_JbGfb1WCv4KNSbXXSnEcPQY5XT-J
|
|
93
93
|
dissect/target/loaders/ovf.py,sha256=ELMq6J2y6cPKbp7pjWAqMMnFYefWxXNqzIiAQdvGGXQ,1061
|
94
94
|
dissect/target/loaders/phobos.py,sha256=XtxF7FZXfZrXJruFUZUQzxlREyfc86dTxph7BNoNMvw,2277
|
95
95
|
dissect/target/loaders/profile.py,sha256=5ylgmzEEGyBFW3izvb-BZ7dGByXN9OFyRnnggR98P9w,1667
|
96
|
+
dissect/target/loaders/proxmox.py,sha256=HP7yCrXze1DmMud7730OPMIwUAEaeoXK0YO6i2XSunM,2778
|
96
97
|
dissect/target/loaders/pvm.py,sha256=b-PvHNTbRVdOnf7-OR5dbikbDTCFlW85b-9Z8PEL2Cs,406
|
97
98
|
dissect/target/loaders/pvs.py,sha256=dMqdYSBQtH9QLM3tdu0mokLBcn73edg_HUtYtqrdi6E,955
|
98
99
|
dissect/target/loaders/raw.py,sha256=tleNWoO0BkC32ExBIPVOpzrQHXXHChZXoZr02oYuC8A,674
|
@@ -163,6 +164,7 @@ dissect/target/plugins/child/docker.py,sha256=frBZ8UUzbtkT9VrK1fwUzXDAdkHESdPCb-
|
|
163
164
|
dissect/target/plugins/child/esxi.py,sha256=EHpBRv5dizvJVIhtVR7frkUnI9GR7lrbSWPGoOpKhRw,753
|
164
165
|
dissect/target/plugins/child/hyperv.py,sha256=R2qVeu4p_9V53jO-65znN0LwX9v3FVA-9jbbtOQcEz8,2236
|
165
166
|
dissect/target/plugins/child/parallels.py,sha256=jeBT_NvTQbQBaUjqGWTy2I5Q5OWlrogoyWHRXjOhLis,2255
|
167
|
+
dissect/target/plugins/child/proxmox.py,sha256=qjeact60fpUXsOciAIJ776fpcnkO6hTZjeUyx92I2HQ,755
|
166
168
|
dissect/target/plugins/child/qemu.py,sha256=vNzQwzFO964jYaI67MlX8vpWyHxpegjIU5F29zHKOGI,791
|
167
169
|
dissect/target/plugins/child/virtuozzo.py,sha256=raXaDTyGPY5JuNgyUOn_qzHaPa0sokG3nGZ5_7eDPyA,1303
|
168
170
|
dissect/target/plugins/child/vmware_workstation.py,sha256=eLvgi_aFUaMN1tC5X4RN85nKQdsuRQygrFYtNMAERTc,2030
|
@@ -186,15 +188,15 @@ dissect/target/plugins/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
186
188
|
dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHnpSnKhC1mjZE,3432
|
187
189
|
dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
|
188
190
|
dissect/target/plugins/general/example.py,sha256=mYAbhtfQmUBj2L2C1DFt9bWpI7rQLJwCIYUsNLcA_pc,6053
|
189
|
-
dissect/target/plugins/general/loaders.py,sha256=
|
191
|
+
dissect/target/plugins/general/loaders.py,sha256=z_t55Q1XNjmTOxq0E4tCwpZ-utFyxiLKyAJIFgJMlJs,1508
|
190
192
|
dissect/target/plugins/general/network.py,sha256=J8aMfUJ7dgwqpaXzZpHHyOUYg-cPef2Qaa3krUj-A-Q,3225
|
191
193
|
dissect/target/plugins/general/osinfo.py,sha256=oU-vmMiA-oaSEQWTSyn6-yQiH2sLQT6aTQHRd0677wo,1415
|
192
|
-
dissect/target/plugins/general/plugins.py,sha256=
|
194
|
+
dissect/target/plugins/general/plugins.py,sha256=9KJ70YvYwBfxt19C9yISv8YE4mOdHNvP16fTCTHC68U,6033
|
193
195
|
dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
|
194
196
|
dissect/target/plugins/general/users.py,sha256=yy9gvRXfN9BT71v4Xqo5hpwfgN9he9Otu8TBPZ_Tegs,3009
|
195
197
|
dissect/target/plugins/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
196
198
|
dissect/target/plugins/os/unix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
197
|
-
dissect/target/plugins/os/unix/_os.py,sha256=
|
199
|
+
dissect/target/plugins/os/unix/_os.py,sha256=prvcuIkJ1jmbb_g1CtgHmmJUcGjFaZn3yU6VPNMJ27k,15061
|
198
200
|
dissect/target/plugins/os/unix/applications.py,sha256=AUgZRP35FzswGyFyChj2o4dfGO34Amc6nqHgiMEaqdI,3129
|
199
201
|
dissect/target/plugins/os/unix/cronjobs.py,sha256=tgWQ3BUZpfyvRzodMwGtwFUdPjZ17k7ZRbZ9Q8wmXPk,3393
|
200
202
|
dissect/target/plugins/os/unix/datetime.py,sha256=gKfBdPyUirt3qmVYfOJ1oZXRPn8wRzssbZxR_ARrtk8,1518
|
@@ -242,6 +244,9 @@ dissect/target/plugins/os/unix/linux/debian/_os.py,sha256=GI19ZqcyfZ1mUYg2NCx93H
|
|
242
244
|
dissect/target/plugins/os/unix/linux/debian/apt.py,sha256=uKfW77pbgxYSe9g6hpih19-xfgvCB954Ygx21j-ydzk,4388
|
243
245
|
dissect/target/plugins/os/unix/linux/debian/dpkg.py,sha256=6A3mb77NREdDWDTFrmcfCF_XxHMmD4_5eHqHEl_7Vqg,5816
|
244
246
|
dissect/target/plugins/os/unix/linux/debian/snap.py,sha256=YVz1N54eQsj2_22LUJIj6Gg-huwT4xDEcOAZn9Tvgw4,3023
|
247
|
+
dissect/target/plugins/os/unix/linux/debian/proxmox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
248
|
+
dissect/target/plugins/os/unix/linux/debian/proxmox/_os.py,sha256=OrQsPcz3fERVnSJKRMXwWQCoVN6CFuC-pJIVHtU-g9M,4257
|
249
|
+
dissect/target/plugins/os/unix/linux/debian/proxmox/vm.py,sha256=Z5IDA5Oot4P2hh65n5cgUMUi_TMIgSexIO2ngOgxPo0,911
|
245
250
|
dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
246
251
|
dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=TPjcfv1n68RCe3Er4aCVQwQDCZwJT-NLvje3kPjDfhk,1744
|
247
252
|
dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -356,7 +361,7 @@ dissect/target/tools/fsutils.py,sha256=q0t9gFwKHaPr2Ya-MN2o4LsYledde7kp2DZZTd8ro
|
|
356
361
|
dissect/target/tools/info.py,sha256=8nnbqFUYeo4NLPE7ORcTBcDL-TioGB2Nqc1TKcu5qdY,5715
|
357
362
|
dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
|
358
363
|
dissect/target/tools/mount.py,sha256=8GRYnu4xEmFBHxuIZAYhOMyyTGX8fat1Ou07DNiUnW4,3945
|
359
|
-
dissect/target/tools/query.py,sha256=
|
364
|
+
dissect/target/tools/query.py,sha256=OYWVmCx2nFx85x1r8Y6D17UdUIi8PJm304xBfT-H8vs,15605
|
360
365
|
dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
|
361
366
|
dissect/target/tools/shell.py,sha256=qY-JIwFQKBHTbqOiFxeO9OYeOlesQlx0r8PHghSAV8I,54207
|
362
367
|
dissect/target/tools/utils.py,sha256=JJZDSso1CEK2sv4Z3HJNgqxH6G9S5lbmV-C3h-XmcMo,12035
|
@@ -373,10 +378,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
373
378
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
374
379
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
375
380
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
376
|
-
dissect.target-3.20.
|
377
|
-
dissect.target-3.20.
|
378
|
-
dissect.target-3.20.
|
379
|
-
dissect.target-3.20.
|
380
|
-
dissect.target-3.20.
|
381
|
-
dissect.target-3.20.
|
382
|
-
dissect.target-3.20.
|
381
|
+
dissect.target-3.20.dev44.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
382
|
+
dissect.target-3.20.dev44.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
383
|
+
dissect.target-3.20.dev44.dist-info/METADATA,sha256=Sgsho9JR5_66txpbY0u-KInNUTMmgflhCY23idL5Iuo,12897
|
384
|
+
dissect.target-3.20.dev44.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
385
|
+
dissect.target-3.20.dev44.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
386
|
+
dissect.target-3.20.dev44.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
387
|
+
dissect.target-3.20.dev44.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev41.dist-info → dissect.target-3.20.dev44.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|