dissect.target 3.17.dev12__py3-none-any.whl → 3.17.dev13__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|