dissect.target 3.17.dev12__py3-none-any.whl → 3.17.dev13__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/tools/dump/run.py +23 -28
- dissect/target/tools/dump/state.py +11 -8
- dissect/target/tools/dump/utils.py +5 -4
- dissect/target/tools/query.py +2 -13
- dissect/target/tools/utils.py +23 -0
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/METADATA +1 -1
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/RECORD +12 -12
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/LICENSE +0 -0
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/WHEEL +0 -0
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/top_level.txt +0 -0
dissect/target/tools/dump/run.py
CHANGED
@@ -7,7 +7,7 @@ import sys
|
|
7
7
|
from collections import deque
|
8
8
|
from dataclasses import dataclass
|
9
9
|
from pathlib import Path
|
10
|
-
from typing import Any,
|
10
|
+
from typing import Any, Iterable, Iterator, Optional
|
11
11
|
|
12
12
|
import structlog
|
13
13
|
from flow.record import Record
|
@@ -25,10 +25,12 @@ from dissect.target.tools.dump.utils import (
|
|
25
25
|
Compression,
|
26
26
|
Serialization,
|
27
27
|
cached_sink_writers,
|
28
|
-
get_nested_attr,
|
29
28
|
)
|
30
29
|
from dissect.target.tools.utils import (
|
30
|
+
PluginFunction,
|
31
31
|
configure_generic_arguments,
|
32
|
+
execute_function_on_target,
|
33
|
+
find_and_filter_plugins,
|
32
34
|
process_generic_arguments,
|
33
35
|
)
|
34
36
|
|
@@ -44,13 +46,13 @@ class RecordStreamElement:
|
|
44
46
|
sink_path: Optional[Path] = None
|
45
47
|
|
46
48
|
|
47
|
-
def get_targets(targets:
|
49
|
+
def get_targets(targets: list[str]) -> Iterator[Target]:
|
48
50
|
"""Return a generator with `Target` objects for provided paths"""
|
49
51
|
for target in Target.open_all(targets):
|
50
52
|
yield target
|
51
53
|
|
52
54
|
|
53
|
-
def execute_function(target: Target, function:
|
55
|
+
def execute_function(target: Target, function: PluginFunction) -> TargetRecordDescriptor:
|
54
56
|
"""
|
55
57
|
Execute function `function` on provided target `target` and return a generator
|
56
58
|
with the records produced.
|
@@ -62,7 +64,7 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
|
|
62
64
|
local_log.debug("Function execution")
|
63
65
|
|
64
66
|
try:
|
65
|
-
target_attr =
|
67
|
+
output_type, target_attr, _ = execute_function_on_target(target, function)
|
66
68
|
except UnsupportedPluginError:
|
67
69
|
local_log.error("Function is not supported for target", exc_info=True)
|
68
70
|
return
|
@@ -70,15 +72,8 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
|
|
70
72
|
local_log.error("Plugin error while executing function for target", exc_info=True)
|
71
73
|
return
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
output = getattr(target_attr, "__output__", "default") if hasattr(target_attr, "__output__") else None
|
76
|
-
except PluginError as e:
|
77
|
-
local_log.error("Plugin error while fetching an attribute", exc_info=e)
|
78
|
-
return
|
79
|
-
|
80
|
-
if output != "record":
|
81
|
-
local_log.warn("Output format is not supported", output=output)
|
75
|
+
if output_type != "record":
|
76
|
+
local_log.warn("Output format is not supported", output=output_type)
|
82
77
|
return
|
83
78
|
|
84
79
|
# no support for function-specific arguments
|
@@ -94,9 +89,9 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
|
|
94
89
|
|
95
90
|
def produce_target_func_pairs(
|
96
91
|
targets: Iterable[Target],
|
97
|
-
functions:
|
92
|
+
functions: str,
|
98
93
|
state: DumpState,
|
99
|
-
) ->
|
94
|
+
) -> Iterator[tuple[Target, PluginFunction]]:
|
100
95
|
"""
|
101
96
|
Return a generator with target and function pairs for execution.
|
102
97
|
|
@@ -107,20 +102,20 @@ def produce_target_func_pairs(
|
|
107
102
|
pairs_to_skip.update((str(sink.target_path), sink.func) for sink in state.finished_sinks)
|
108
103
|
|
109
104
|
for target in targets:
|
110
|
-
for
|
111
|
-
if state and (target.path,
|
105
|
+
for func_def in find_and_filter_plugins(target, functions):
|
106
|
+
if state and (target.path, func_def.name) in pairs_to_skip:
|
112
107
|
log.info(
|
113
108
|
"Skipping target/func pair since its marked as done in provided state",
|
114
109
|
target=target.path,
|
115
|
-
func=
|
110
|
+
func=func_def.name,
|
116
111
|
state=state.path,
|
117
112
|
)
|
118
113
|
continue
|
119
|
-
yield (target,
|
120
|
-
state.mark_as_finished(target,
|
114
|
+
yield (target, func_def)
|
115
|
+
state.mark_as_finished(target, func_def.name)
|
121
116
|
|
122
117
|
|
123
|
-
def execute_functions(target_func_stream: Iterable[
|
118
|
+
def execute_functions(target_func_stream: Iterable[tuple[Target, str]]) -> Iterable[RecordStreamElement]:
|
124
119
|
"""
|
125
120
|
Execute a function on a target for target / function pairs in the stream.
|
126
121
|
|
@@ -131,7 +126,7 @@ def execute_functions(target_func_stream: Iterable[Tuple[Target, str]]) -> Gener
|
|
131
126
|
yield RecordStreamElement(target=target, func=func, record=record)
|
132
127
|
|
133
128
|
|
134
|
-
def log_progress(stream: Iterable[Any], step_size: int = 1000) ->
|
129
|
+
def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Iterable[Any]:
|
135
130
|
"""
|
136
131
|
Log a number of items that went though the generator stream
|
137
132
|
after every N element (N is configured in `step_size`).
|
@@ -155,7 +150,7 @@ def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Generator[Any,
|
|
155
150
|
def sink_records(
|
156
151
|
record_stream: Iterable[RecordStreamElement],
|
157
152
|
state: DumpState,
|
158
|
-
) ->
|
153
|
+
) -> Iterator[RecordStreamElement]:
|
159
154
|
"""
|
160
155
|
Persist records from the stream into appropriate sinks, per serialization, compression and record type.
|
161
156
|
"""
|
@@ -168,7 +163,7 @@ def sink_records(
|
|
168
163
|
def persist_processing_state(
|
169
164
|
record_stream: Iterable[RecordStreamElement],
|
170
165
|
state: DumpState,
|
171
|
-
) ->
|
166
|
+
) -> Iterator[RecordStreamElement]:
|
172
167
|
"""
|
173
168
|
Keep track of the pipeline state in a persistent state object.
|
174
169
|
"""
|
@@ -179,8 +174,8 @@ def persist_processing_state(
|
|
179
174
|
|
180
175
|
|
181
176
|
def execute_pipeline(
|
182
|
-
targets:
|
183
|
-
functions:
|
177
|
+
targets: list[str],
|
178
|
+
functions: str,
|
184
179
|
output_dir: Path,
|
185
180
|
serialization: Serialization,
|
186
181
|
compression: Optional[Compression] = None,
|
@@ -297,7 +292,7 @@ def main():
|
|
297
292
|
try:
|
298
293
|
execute_pipeline(
|
299
294
|
targets=args.targets,
|
300
|
-
functions=args.function
|
295
|
+
functions=args.function,
|
301
296
|
output_dir=args.output,
|
302
297
|
serialization=Serialization(args.serialization),
|
303
298
|
compression=Compression(args.compression),
|
@@ -6,7 +6,7 @@ import json
|
|
6
6
|
from contextlib import contextmanager
|
7
7
|
from dataclasses import dataclass
|
8
8
|
from pathlib import Path
|
9
|
-
from typing import Any, Callable, Iterator,
|
9
|
+
from typing import Any, Callable, Iterator, Optional, TextIO
|
10
10
|
|
11
11
|
import structlog
|
12
12
|
|
@@ -35,17 +35,20 @@ class Sink:
|
|
35
35
|
record_count: int = 0
|
36
36
|
size_bytes: int = 0
|
37
37
|
|
38
|
+
def __post_init__(self):
|
39
|
+
self.func = getattr(self.func, "name", self.func)
|
40
|
+
|
38
41
|
|
39
42
|
@dataclass
|
40
43
|
class DumpState:
|
41
|
-
target_paths:
|
42
|
-
functions:
|
44
|
+
target_paths: list[str]
|
45
|
+
functions: list[str]
|
43
46
|
serialization: str
|
44
47
|
compression: str
|
45
48
|
start_time: datetime.datetime
|
46
49
|
last_update_time: datetime.datetime
|
47
50
|
|
48
|
-
sinks:
|
51
|
+
sinks: list[Sink] = dataclasses.field(default_factory=list)
|
49
52
|
|
50
53
|
# Volatile properties
|
51
54
|
output_dir: Optional[Path] = None
|
@@ -56,7 +59,7 @@ class DumpState:
|
|
56
59
|
return sum(s.record_count for s in self.sinks)
|
57
60
|
|
58
61
|
@property
|
59
|
-
def finished_sinks(self) ->
|
62
|
+
def finished_sinks(self) -> list[Sink]:
|
60
63
|
return [sink for sink in self.sinks if not sink.is_dirty]
|
61
64
|
|
62
65
|
@property
|
@@ -178,7 +181,7 @@ class DumpState:
|
|
178
181
|
state.output_dir = output_dir
|
179
182
|
return state
|
180
183
|
|
181
|
-
def get_invalid_sinks(self) ->
|
184
|
+
def get_invalid_sinks(self) -> list[Sink]:
|
182
185
|
"""Return sinks that have a mismatch between recorded size and a real file size"""
|
183
186
|
invalid_sinks = []
|
184
187
|
for sink in self.sinks:
|
@@ -214,8 +217,8 @@ class DumpState:
|
|
214
217
|
def create_state(
|
215
218
|
*,
|
216
219
|
output_dir: Path,
|
217
|
-
target_paths:
|
218
|
-
functions:
|
220
|
+
target_paths: list[str],
|
221
|
+
functions: list[str],
|
219
222
|
serialization: Serialization,
|
220
223
|
compression: Compression = None,
|
221
224
|
) -> DumpState:
|
@@ -32,6 +32,7 @@ from flow.record.adapter.jsonfile import JsonfileWriter
|
|
32
32
|
from flow.record.jsonpacker import JsonRecordPacker
|
33
33
|
|
34
34
|
from dissect.target import Target
|
35
|
+
from dissect.target.plugin import PluginFunction
|
35
36
|
|
36
37
|
log = structlog.get_logger(__name__)
|
37
38
|
|
@@ -69,14 +70,14 @@ def get_nested_attr(obj: Any, nested_attr: str) -> Any:
|
|
69
70
|
|
70
71
|
|
71
72
|
@lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
|
72
|
-
def get_sink_dir_by_target(target: Target, function:
|
73
|
-
func_first_name, _, _ = function.partition(".")
|
73
|
+
def get_sink_dir_by_target(target: Target, function: PluginFunction) -> Path:
|
74
|
+
func_first_name, _, _ = function.name.partition(".")
|
74
75
|
return Path(target.name) / func_first_name
|
75
76
|
|
76
77
|
|
77
78
|
@functools.lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
|
78
|
-
def get_sink_dir_by_func(target: Target, function:
|
79
|
-
func_first_name, _, _ = function.partition(".")
|
79
|
+
def get_sink_dir_by_func(target: Target, function: PluginFunction) -> Path:
|
80
|
+
func_first_name, _, _ = function.name.partition(".")
|
80
81
|
return Path(func_first_name) / target.name
|
81
82
|
|
82
83
|
|
dissect/target/tools/query.py
CHANGED
@@ -26,6 +26,7 @@ from dissect.target.tools.utils import (
|
|
26
26
|
catch_sigpipe,
|
27
27
|
configure_generic_arguments,
|
28
28
|
execute_function_on_target,
|
29
|
+
find_and_filter_plugins,
|
29
30
|
generate_argparse_for_bound_method,
|
30
31
|
generate_argparse_for_plugin_class,
|
31
32
|
generate_argparse_for_unbound_method,
|
@@ -270,25 +271,13 @@ def main():
|
|
270
271
|
basic_entries = []
|
271
272
|
yield_entries = []
|
272
273
|
|
273
|
-
# Keep a set of plugins that were already executed on the target.
|
274
|
-
executed_plugins = set()
|
275
|
-
|
276
274
|
first_seen_output_type = default_output_type
|
277
275
|
cli_params_unparsed = rest
|
278
276
|
|
279
|
-
func_defs, _ = find_plugin_functions(target, args.function, compatibility=False)
|
280
277
|
excluded_funcs, _ = find_plugin_functions(target, args.excluded_functions, compatibility=False)
|
281
278
|
excluded_func_paths = {excluded_func.path for excluded_func in excluded_funcs}
|
282
279
|
|
283
|
-
for func_def in
|
284
|
-
if func_def.path in excluded_func_paths:
|
285
|
-
continue
|
286
|
-
|
287
|
-
# Avoid executing same plugin for multiple OSes (like hostname)
|
288
|
-
if func_def.name in executed_plugins:
|
289
|
-
continue
|
290
|
-
executed_plugins.add(func_def.name)
|
291
|
-
|
280
|
+
for func_def in find_and_filter_plugins(target, args.function, excluded_func_paths):
|
292
281
|
# If the default type is record (meaning we skip everything else)
|
293
282
|
# and actual output type is not record, continue.
|
294
283
|
# We perform this check here because plugins that require output files/dirs
|
dissect/target/tools/utils.py
CHANGED
@@ -21,6 +21,7 @@ from dissect.target.plugin import (
|
|
21
21
|
OSPlugin,
|
22
22
|
Plugin,
|
23
23
|
PluginFunction,
|
24
|
+
find_plugin_functions,
|
24
25
|
get_external_module_paths,
|
25
26
|
load_modules_from_paths,
|
26
27
|
)
|
@@ -289,3 +290,25 @@ def args_to_uri(targets: list[str], loader_name: str, rest: list[str]) -> list[s
|
|
289
290
|
for target in targets:
|
290
291
|
uris.append(f"{loader_name}://{target}" + (("?" + urllib.parse.urlencode(args)) if args else ""))
|
291
292
|
return uris
|
293
|
+
|
294
|
+
|
295
|
+
def find_and_filter_plugins(
|
296
|
+
target: Target, functions: str, excluded_func_paths: set[str] = None
|
297
|
+
) -> Iterator[PluginFunction]:
|
298
|
+
# Keep a set of plugins that were already executed on the target.
|
299
|
+
executed_plugins = set()
|
300
|
+
excluded_func_paths = excluded_func_paths or set()
|
301
|
+
|
302
|
+
func_defs, _ = find_plugin_functions(target, functions, compatibility=False)
|
303
|
+
|
304
|
+
for func_def in func_defs:
|
305
|
+
if func_def.path in excluded_func_paths:
|
306
|
+
continue
|
307
|
+
|
308
|
+
# Avoid executing same plugin for multiple OSes (like hostname)
|
309
|
+
if func_def.name in executed_plugins:
|
310
|
+
continue
|
311
|
+
|
312
|
+
executed_plugins.add(func_def.name)
|
313
|
+
|
314
|
+
yield func_def
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.17.
|
3
|
+
Version: 3.17.dev13
|
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
|
@@ -320,14 +320,14 @@ dissect/target/tools/fs.py,sha256=cizCrW8rqdpT1irA8g6mslkaXX7CynWVQ7fvRUrcxNU,37
|
|
320
320
|
dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
|
321
321
|
dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
|
322
322
|
dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
|
323
|
-
dissect/target/tools/query.py,sha256=
|
323
|
+
dissect/target/tools/query.py,sha256=6zz9SXS6YnHj7eguORS8Je7N4iM0i1PZDIQ-gyJ1nPY,15593
|
324
324
|
dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
|
325
325
|
dissect/target/tools/shell.py,sha256=6GF-mr4EpzX34G9sqTNKcccFJXhNTlrUqriRYIW7P7o,43544
|
326
|
-
dissect/target/tools/utils.py,sha256=
|
326
|
+
dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
|
327
327
|
dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
328
|
-
dissect/target/tools/dump/run.py,sha256=
|
329
|
-
dissect/target/tools/dump/state.py,sha256=
|
330
|
-
dissect/target/tools/dump/utils.py,sha256=
|
328
|
+
dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
|
329
|
+
dissect/target/tools/dump/state.py,sha256=YYgCff0kZZ-tx27lJlc9LQ7AfoGnLK5Gyi796OnktA8,9205
|
330
|
+
dissect/target/tools/dump/utils.py,sha256=onQIWPPjkz8HmZMndVVP_4ywFixXwXGOjHi4x8D3Xqw,7645
|
331
331
|
dissect/target/volumes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
332
332
|
dissect/target/volumes/bde.py,sha256=3lCb7inPmu8MR5WxPWUauJ8t3Emymh9D-o2yCL-SB9g,3974
|
333
333
|
dissect/target/volumes/ddf.py,sha256=X0yBIFbG7Z_CJDVqxEBLDynM_NoSwIWEKVsQFOS-Tn4,1749
|
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
336
336
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
337
337
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
338
338
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
339
|
-
dissect.target-3.17.
|
340
|
-
dissect.target-3.17.
|
341
|
-
dissect.target-3.17.
|
342
|
-
dissect.target-3.17.
|
343
|
-
dissect.target-3.17.
|
344
|
-
dissect.target-3.17.
|
345
|
-
dissect.target-3.17.
|
339
|
+
dissect.target-3.17.dev13.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
340
|
+
dissect.target-3.17.dev13.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
341
|
+
dissect.target-3.17.dev13.dist-info/METADATA,sha256=r2xQoOApd3QGiaNTpuo27XM3t1YkKLaNSLkywaWejJ8,11300
|
342
|
+
dissect.target-3.17.dev13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
343
|
+
dissect.target-3.17.dev13.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
344
|
+
dissect.target-3.17.dev13.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
345
|
+
dissect.target-3.17.dev13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.17.dev12.dist-info → dissect.target-3.17.dev13.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|