dissect.target 3.9.dev36__py3-none-any.whl → 3.9.dev38__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/plugin.py +37 -33
- dissect/target/tools/query.py +14 -6
- dissect/target/tools/utils.py +50 -18
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/METADATA +2 -2
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/RECORD +10 -10
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/LICENSE +0 -0
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/WHEEL +0 -0
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.9.dev36.dist-info → dissect.target-3.9.dev38.dist-info}/top_level.txt +0 -0
dissect/target/plugin.py
CHANGED
@@ -13,7 +13,7 @@ import logging
|
|
13
13
|
import os
|
14
14
|
import sys
|
15
15
|
import traceback
|
16
|
-
from dataclasses import dataclass
|
16
|
+
from dataclasses import dataclass, field
|
17
17
|
from pathlib import Path
|
18
18
|
from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Type
|
19
19
|
|
@@ -811,13 +811,13 @@ class InternalPlugin(Plugin):
|
|
811
811
|
return cls
|
812
812
|
|
813
813
|
|
814
|
-
@dataclass
|
814
|
+
@dataclass(frozen=True, eq=True)
|
815
815
|
class PluginFunction:
|
816
816
|
name: str
|
817
817
|
output_type: str
|
818
818
|
class_object: str
|
819
819
|
method_name: str
|
820
|
-
plugin_desc: dict
|
820
|
+
plugin_desc: dict = field(hash=False)
|
821
821
|
|
822
822
|
|
823
823
|
def plugin_function_index(target: Target) -> tuple[dict[str, Any], set[str]]:
|
@@ -831,7 +831,9 @@ def plugin_function_index(target: Target) -> tuple[dict[str, Any], set[str]]:
|
|
831
831
|
rootset = set()
|
832
832
|
|
833
833
|
def all_plugins():
|
834
|
-
|
834
|
+
# Filter out plugins based on the target os
|
835
|
+
os_type = type(target._os) if target._os else None
|
836
|
+
yield from plugins(os_type)
|
835
837
|
yield from os_plugins()
|
836
838
|
yield from child_plugins() # Doesn't export anything but added for completeness.
|
837
839
|
|
@@ -885,33 +887,34 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
|
|
885
887
|
pattern += "*"
|
886
888
|
|
887
889
|
if wildcard or treematch:
|
888
|
-
for index_name
|
889
|
-
|
890
|
-
method_name = index_name.split(".")[-1]
|
891
|
-
loaded_plugin_object = load(func)
|
890
|
+
for index_name in fnmatch.filter(functions.keys(), pattern):
|
891
|
+
func = functions[index_name]
|
892
892
|
|
893
|
-
|
894
|
-
|
895
|
-
|
893
|
+
method_name = index_name.split(".")[-1]
|
894
|
+
loaded_plugin_object = load(func)
|
895
|
+
|
896
|
+
# Skip plugins that don't want to be found by wildcards
|
897
|
+
if not loaded_plugin_object.__findable__:
|
898
|
+
continue
|
896
899
|
|
897
|
-
|
900
|
+
fobject = inspect.getattr_static(loaded_plugin_object, method_name)
|
898
901
|
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
continue
|
903
|
-
except Exception:
|
902
|
+
if compatibility:
|
903
|
+
try:
|
904
|
+
if not loaded_plugin_object(target).is_compatible():
|
904
905
|
continue
|
906
|
+
except Exception:
|
907
|
+
continue
|
905
908
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
)
|
909
|
+
result.append(
|
910
|
+
PluginFunction(
|
911
|
+
name=index_name,
|
912
|
+
class_object=loaded_plugin_object,
|
913
|
+
method_name=method_name,
|
914
|
+
output_type=getattr(fobject, "__output__", "text"),
|
915
|
+
plugin_desc=func,
|
914
916
|
)
|
917
|
+
)
|
915
918
|
else:
|
916
919
|
# otherwise match using ~ classic style
|
917
920
|
if pattern.find(".") > -1:
|
@@ -920,15 +923,15 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
|
|
920
923
|
funcname = pattern
|
921
924
|
namespace = None
|
922
925
|
|
923
|
-
|
924
|
-
for
|
926
|
+
plugin_descriptions = []
|
927
|
+
for _, func in functions.items():
|
925
928
|
nsmatch = namespace and func["namespace"] == namespace and funcname in func["exports"]
|
926
929
|
fmatch = not namespace and not func["namespace"] and funcname in func["exports"]
|
927
930
|
if nsmatch or fmatch:
|
928
|
-
|
931
|
+
plugin_descriptions.append(func)
|
929
932
|
|
930
|
-
|
931
|
-
loaded_plugin_object = load(
|
933
|
+
for description in plugin_descriptions:
|
934
|
+
loaded_plugin_object = load(description)
|
932
935
|
fobject = inspect.getattr_static(loaded_plugin_object, funcname)
|
933
936
|
|
934
937
|
if compatibility and not loaded_plugin_object(target).is_compatible():
|
@@ -936,11 +939,12 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
|
|
936
939
|
|
937
940
|
result.append(
|
938
941
|
PluginFunction(
|
939
|
-
name=f"{
|
942
|
+
name=f"{description['module']}.{pattern}",
|
940
943
|
class_object=loaded_plugin_object,
|
941
944
|
method_name=funcname,
|
942
945
|
output_type=getattr(fobject, "__output__", "text"),
|
943
|
-
plugin_desc=
|
946
|
+
plugin_desc=description,
|
944
947
|
)
|
945
948
|
)
|
946
|
-
|
949
|
+
|
950
|
+
return list(set(result))
|
dissect/target/tools/query.py
CHANGED
@@ -202,10 +202,16 @@ def main():
|
|
202
202
|
basic_entries = []
|
203
203
|
yield_entries = []
|
204
204
|
|
205
|
+
# Keep a set of plugins that were already executed on the target.
|
206
|
+
executed_plugins = set()
|
207
|
+
|
205
208
|
first_seen_output_type = default_output_type
|
206
209
|
cli_params_unparsed = rest
|
207
210
|
|
208
|
-
for func_def in
|
211
|
+
for func_def in find_plugin_functions(target, args.function, False):
|
212
|
+
if func_def.method_name in executed_plugins:
|
213
|
+
continue
|
214
|
+
|
209
215
|
try:
|
210
216
|
output_type, result, cli_params_unparsed = execute_function_on_target(
|
211
217
|
target, func_def, cli_params_unparsed
|
@@ -213,17 +219,17 @@ def main():
|
|
213
219
|
except UnsupportedPluginError as e:
|
214
220
|
target.log.error(
|
215
221
|
"Unsupported plugin for `%s`: %s",
|
216
|
-
|
222
|
+
func_def,
|
217
223
|
e.root_cause_str(),
|
218
224
|
)
|
219
225
|
|
220
226
|
target.log.debug("", exc_info=e)
|
221
227
|
continue
|
222
228
|
except PluginNotFoundError:
|
223
|
-
target.log.error("Cannot find plugin `%s`",
|
229
|
+
target.log.error("Cannot find plugin `%s`", func_def)
|
224
230
|
continue
|
225
231
|
except Exception:
|
226
|
-
target.log.error("Exception while executing function `%s`",
|
232
|
+
target.log.error("Exception while executing function `%s`", func_def, exc_info=True)
|
227
233
|
continue
|
228
234
|
|
229
235
|
if first_seen_output_type and output_type != first_seen_output_type:
|
@@ -236,7 +242,7 @@ def main():
|
|
236
242
|
"does not match first seen output `%s`."
|
237
243
|
),
|
238
244
|
output_type,
|
239
|
-
|
245
|
+
func_def,
|
240
246
|
first_seen_output_type,
|
241
247
|
)
|
242
248
|
parser.exit()
|
@@ -244,12 +250,14 @@ def main():
|
|
244
250
|
if not first_seen_output_type:
|
245
251
|
first_seen_output_type = output_type
|
246
252
|
|
253
|
+
executed_plugins.add(func_def.method_name)
|
254
|
+
|
247
255
|
if output_type == "record":
|
248
256
|
record_entries.append(result)
|
249
257
|
elif output_type == "yield":
|
250
258
|
yield_entries.append(result)
|
251
259
|
elif output_type == "none":
|
252
|
-
target.log.info("No result for function `%s` (output type is set to 'none')",
|
260
|
+
target.log.info("No result for function `%s` (output type is set to 'none')", func_def)
|
253
261
|
continue
|
254
262
|
else:
|
255
263
|
basic_entries.append(result)
|
dissect/target/tools/utils.py
CHANGED
@@ -3,7 +3,7 @@ import inspect
|
|
3
3
|
import json
|
4
4
|
from datetime import datetime
|
5
5
|
from pathlib import Path
|
6
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple, Type
|
6
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, Union
|
7
7
|
|
8
8
|
from dissect.target import Target
|
9
9
|
from dissect.target.exceptions import UnsupportedPluginError
|
@@ -129,13 +129,16 @@ def generate_argparse_for_plugin(
|
|
129
129
|
return generate_argparse_for_plugin_class(plugin_instance.__class__, usage_tmpl=usage_tmpl)
|
130
130
|
|
131
131
|
|
132
|
-
def plugin_factory(target: Target,
|
132
|
+
def plugin_factory(target: Target, plugin: Union[type, object], funcname: str) -> tuple[Plugin, str]:
|
133
133
|
if TargetdLoader.instance:
|
134
134
|
return target.get_function(funcname)
|
135
|
+
|
136
|
+
if isinstance(plugin, type):
|
137
|
+
plugin_obj = plugin(target)
|
138
|
+
target_attr = getattr(plugin_obj, funcname)
|
139
|
+
return plugin_obj, target_attr
|
135
140
|
else:
|
136
|
-
|
137
|
-
target_attr = getattr(plugin_obj, funcname)
|
138
|
-
return plugin_obj, target_attr
|
141
|
+
return plugin, getattr(plugin, funcname)
|
139
142
|
|
140
143
|
|
141
144
|
def execute_function_on_target(
|
@@ -149,9 +152,40 @@ def execute_function_on_target(
|
|
149
152
|
|
150
153
|
cli_params = cli_params or []
|
151
154
|
|
152
|
-
|
155
|
+
target_attr = get_target_attribute(target, func)
|
156
|
+
plugin_method, parser = plugin_function_with_argparser(target_attr)
|
157
|
+
|
158
|
+
if parser:
|
159
|
+
parsed_params, cli_params = parser.parse_known_args(cli_params)
|
160
|
+
method_kwargs = vars(parsed_params)
|
161
|
+
value = plugin_method(**method_kwargs)
|
162
|
+
else:
|
163
|
+
value = target_attr
|
164
|
+
|
165
|
+
output_type = getattr(plugin_method, "__output__", "default") if plugin_method else "default"
|
166
|
+
return (output_type, value, cli_params)
|
167
|
+
|
168
|
+
|
169
|
+
def get_target_attribute(target: Target, func: PluginFunction) -> Union[Plugin, Callable]:
|
170
|
+
"""Retrieves the function attribute from the target.
|
171
|
+
|
172
|
+
If the function does not exist yet, it will attempt to load it into the target.
|
173
|
+
|
174
|
+
Args:
|
175
|
+
target: The target we wish to run the function on.
|
176
|
+
func: The function to run on the target.
|
153
177
|
|
154
|
-
|
178
|
+
Returns:
|
179
|
+
The function, either plugin or a callable to execute.
|
180
|
+
|
181
|
+
Raises:
|
182
|
+
UnsupportedPluginError: When the function was incompatible with the target.
|
183
|
+
"""
|
184
|
+
plugin_class = func.class_object
|
185
|
+
if target.has_function(func.method_name):
|
186
|
+
# If the function is already attached, use the one inside the target.
|
187
|
+
plugin_class, _ = target.get_function(func.method_name)
|
188
|
+
elif issubclass(plugin_class, OSPlugin):
|
155
189
|
# OS plugin does not need to be added
|
156
190
|
plugin_class = target._os_plugin
|
157
191
|
else:
|
@@ -162,7 +196,14 @@ def execute_function_on_target(
|
|
162
196
|
f"Unsupported function `{func.method_name}` for target with plugin {func.class_object}", cause=e
|
163
197
|
)
|
164
198
|
|
165
|
-
|
199
|
+
_, target_attr = plugin_factory(target, plugin_class, func.method_name)
|
200
|
+
return target_attr
|
201
|
+
|
202
|
+
|
203
|
+
def plugin_function_with_argparser(
|
204
|
+
target_attr: Union[Plugin, Callable]
|
205
|
+
) -> tuple[Optional[Iterator], Optional[argparse.ArgumentParser]]:
|
206
|
+
"""Resolves which plugin function to execute, and creates the argument parser for said plugin."""
|
166
207
|
plugin_method = None
|
167
208
|
parser = None
|
168
209
|
|
@@ -178,16 +219,7 @@ def execute_function_on_target(
|
|
178
219
|
elif callable(target_attr):
|
179
220
|
plugin_method = target_attr
|
180
221
|
parser = generate_argparse_for_bound_method(target_attr)
|
181
|
-
|
182
|
-
if parser:
|
183
|
-
parsed_params, cli_params = parser.parse_known_args(cli_params)
|
184
|
-
method_kwargs = vars(parsed_params)
|
185
|
-
value = plugin_method(**method_kwargs)
|
186
|
-
else:
|
187
|
-
value = target_attr
|
188
|
-
|
189
|
-
output_type = getattr(plugin_method, "__output__", "default") if plugin_method else "default"
|
190
|
-
return (output_type, value, cli_params)
|
222
|
+
return plugin_method, parser
|
191
223
|
|
192
224
|
|
193
225
|
def persist_execution_report(output_dir: Path, report_data: Dict, timestamp: datetime) -> Path:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.9.
|
3
|
+
Version: 3.9.dev38
|
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
|
@@ -21,7 +21,7 @@ Requires-Dist: dissect.ntfs (<4.0.dev,>=3.4.dev)
|
|
21
21
|
Requires-Dist: dissect.regf (<4.0.dev,>=3.3.dev)
|
22
22
|
Requires-Dist: dissect.util (<4.0.dev,>=3.0.dev)
|
23
23
|
Requires-Dist: dissect.volume (<4.0.dev,>=3.0.dev)
|
24
|
-
Requires-Dist: flow.record (~=3.
|
24
|
+
Requires-Dist: flow.record (~=3.10)
|
25
25
|
Requires-Dist: structlog
|
26
26
|
Provides-Extra: full
|
27
27
|
Requires-Dist: asn1crypto ; extra == 'full'
|
@@ -3,7 +3,7 @@ dissect/target/container.py,sha256=R8M9EE7DqKq8DeMuekcpR1nxtZ827zuqmTmO4s7PYkg,7
|
|
3
3
|
dissect/target/exceptions.py,sha256=w3qNa4dtGpxUkOCfmAQtvseQrpQq9qmO9wIbztRlIDQ,2273
|
4
4
|
dissect/target/filesystem.py,sha256=YJfuRzd37DIm91cVPlMZK-XjiJ1UCZG93htaYcSUrIk,46276
|
5
5
|
dissect/target/loader.py,sha256=_gLb3nTAlyS0MS3JEFFD44Qk7X5MQMfhoRTFWaH7sA4,7072
|
6
|
-
dissect/target/plugin.py,sha256=
|
6
|
+
dissect/target/plugin.py,sha256=GLoCrD1eKMy_tbRkwd8YzkSdPNdym5M0-e-aR8kTt10,32098
|
7
7
|
dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
|
8
8
|
dissect/target/target.py,sha256=TgDY-yAsReOQOG-Phz_m1vdNucdbk9fUI_RMZpMeYG8,28334
|
9
9
|
dissect/target/volume.py,sha256=vHBXdDttpiu-Q_oWycNM7fdJ5N8Ob7-i_UBJK9DEs24,15027
|
@@ -248,10 +248,10 @@ dissect/target/tools/fs.py,sha256=dz6IEz1CKTVsSuMu6oAqdkc1qfIfqnMGABHSDDKKSDM,36
|
|
248
248
|
dissect/target/tools/info.py,sha256=ZYRKLANt99uhpMcP5eHj6WegWAffqq8tS8MNj5txwqE,5141
|
249
249
|
dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
|
250
250
|
dissect/target/tools/mount.py,sha256=gPez-PLaeof7a6s7spJ23F_JcdQ7cUOPx5FX42rqF4A,2500
|
251
|
-
dissect/target/tools/query.py,sha256=
|
251
|
+
dissect/target/tools/query.py,sha256=PHj-lNgcHSOQ401Hlc-HvpupAx4wnQNPeFOrxYyDJg0,11779
|
252
252
|
dissect/target/tools/reg.py,sha256=37g_Xdb5ZbYAkMgQFmZNdKM_wWP9Bcw2Kk6quo1gwZ4,2147
|
253
253
|
dissect/target/tools/shell.py,sha256=HICeIN5kCZYyGmAm_riWO9xrGnQmOzSp-Oici4QeO6Y,36003
|
254
|
-
dissect/target/tools/utils.py,sha256=
|
254
|
+
dissect/target/tools/utils.py,sha256=yDdQxAlWpwlOSnZlN9-ktreMxG_q-NXY_1Z8f7lnAQI,8446
|
255
255
|
dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
256
256
|
dissect/target/tools/dump/run.py,sha256=yHn9xl_VjasgiuLpjtZdnLW32QCbkwHfnnTPY6Ck_aw,9689
|
257
257
|
dissect/target/tools/dump/state.py,sha256=ZBNz4ou2Xk20K1H8R83S1gq6qcqPvPPVAaPWzpKpX34,9123
|
@@ -261,10 +261,10 @@ dissect/target/volumes/bde.py,sha256=gYGg5yF9MNARwNzEkrEfZmKkxyZW4rhLkpdnPJCbhGk
|
|
261
261
|
dissect/target/volumes/disk.py,sha256=95grSsPt1BLVpKwTclwQYzPFGKTkFFqapIk0RoGWf38,968
|
262
262
|
dissect/target/volumes/lvm.py,sha256=_kIB1mdRs1OFhRgoT4VEP5Fv8imQnI7oQ_ie4x710tQ,1814
|
263
263
|
dissect/target/volumes/vmfs.py,sha256=mlAJ8278tYaoRjk1u6tFFlCaDQUrVu5ZZE4ikiFvxi8,1707
|
264
|
-
dissect.target-3.9.
|
265
|
-
dissect.target-3.9.
|
266
|
-
dissect.target-3.9.
|
267
|
-
dissect.target-3.9.
|
268
|
-
dissect.target-3.9.
|
269
|
-
dissect.target-3.9.
|
270
|
-
dissect.target-3.9.
|
264
|
+
dissect.target-3.9.dev38.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
265
|
+
dissect.target-3.9.dev38.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
266
|
+
dissect.target-3.9.dev38.dist-info/METADATA,sha256=F2OhtulG3gqAU4z12JPG5uc8kjBJmrZJ8Jcgl86OdjI,10234
|
267
|
+
dissect.target-3.9.dev38.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
268
|
+
dissect.target-3.9.dev38.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
269
|
+
dissect.target-3.9.dev38.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
270
|
+
dissect.target-3.9.dev38.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|