dissect.target 3.14.dev29__py3-none-any.whl → 3.15__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/containers/ewf.py +1 -1
- dissect/target/containers/vhd.py +5 -2
- dissect/target/filesystem.py +36 -18
- dissect/target/filesystems/dir.py +10 -4
- dissect/target/filesystems/jffs.py +122 -0
- dissect/target/helpers/compat/path_310.py +506 -0
- dissect/target/helpers/compat/path_311.py +539 -0
- dissect/target/helpers/compat/path_312.py +443 -0
- dissect/target/helpers/compat/path_39.py +545 -0
- dissect/target/helpers/compat/path_common.py +223 -0
- dissect/target/helpers/cyber.py +512 -0
- dissect/target/helpers/fsutil.py +128 -666
- dissect/target/helpers/hashutil.py +17 -57
- dissect/target/helpers/keychain.py +9 -3
- dissect/target/helpers/loaderutil.py +1 -1
- dissect/target/helpers/mount.py +47 -4
- dissect/target/helpers/polypath.py +73 -0
- dissect/target/helpers/record_modifier.py +100 -0
- dissect/target/loader.py +2 -1
- dissect/target/loaders/asdf.py +2 -0
- dissect/target/loaders/cyber.py +37 -0
- dissect/target/loaders/log.py +14 -3
- dissect/target/loaders/raw.py +2 -0
- dissect/target/loaders/remote.py +12 -0
- dissect/target/loaders/tar.py +13 -0
- dissect/target/loaders/targetd.py +2 -0
- dissect/target/loaders/velociraptor.py +12 -3
- dissect/target/loaders/vmwarevm.py +2 -0
- dissect/target/plugin.py +272 -143
- dissect/target/plugins/apps/ssh/openssh.py +11 -54
- dissect/target/plugins/apps/ssh/opensshd.py +4 -3
- dissect/target/plugins/apps/ssh/putty.py +236 -0
- dissect/target/plugins/apps/ssh/ssh.py +58 -0
- dissect/target/plugins/apps/vpn/openvpn.py +6 -0
- dissect/target/plugins/apps/webserver/apache.py +309 -95
- dissect/target/plugins/apps/webserver/caddy.py +5 -2
- dissect/target/plugins/apps/webserver/citrix.py +82 -0
- dissect/target/plugins/apps/webserver/iis.py +9 -12
- dissect/target/plugins/apps/webserver/nginx.py +5 -2
- dissect/target/plugins/apps/webserver/webserver.py +25 -41
- dissect/target/plugins/child/wsl.py +1 -1
- dissect/target/plugins/filesystem/ntfs/mft.py +10 -0
- dissect/target/plugins/filesystem/ntfs/mft_timeline.py +10 -0
- dissect/target/plugins/filesystem/ntfs/usnjrnl.py +10 -0
- dissect/target/plugins/filesystem/ntfs/utils.py +28 -5
- dissect/target/plugins/filesystem/resolver.py +6 -4
- dissect/target/plugins/general/default.py +0 -2
- dissect/target/plugins/general/example.py +0 -1
- dissect/target/plugins/general/loaders.py +3 -5
- dissect/target/plugins/os/unix/_os.py +3 -3
- dissect/target/plugins/os/unix/bsd/citrix/_os.py +68 -28
- dissect/target/plugins/os/unix/bsd/citrix/history.py +130 -0
- dissect/target/plugins/os/unix/generic.py +17 -12
- dissect/target/plugins/os/unix/linux/fortios/__init__.py +0 -0
- dissect/target/plugins/os/unix/linux/fortios/_os.py +534 -0
- dissect/target/plugins/os/unix/linux/fortios/generic.py +30 -0
- dissect/target/plugins/os/unix/linux/fortios/locale.py +109 -0
- dissect/target/plugins/os/windows/log/evt.py +1 -1
- dissect/target/plugins/os/windows/log/schedlgu.py +155 -0
- dissect/target/plugins/os/windows/regf/firewall.py +1 -1
- dissect/target/plugins/os/windows/regf/shimcache.py +1 -1
- dissect/target/plugins/os/windows/regf/trusteddocs.py +1 -1
- dissect/target/plugins/os/windows/registry.py +1 -1
- dissect/target/plugins/os/windows/sam.py +3 -0
- dissect/target/plugins/os/windows/sru.py +41 -28
- dissect/target/plugins/os/windows/tasks.py +5 -2
- dissect/target/target.py +7 -3
- dissect/target/tools/dd.py +7 -1
- dissect/target/tools/fs.py +8 -1
- dissect/target/tools/info.py +22 -16
- dissect/target/tools/mount.py +28 -3
- dissect/target/tools/query.py +146 -117
- dissect/target/tools/reg.py +21 -16
- dissect/target/tools/shell.py +30 -6
- dissect/target/tools/utils.py +28 -0
- dissect/target/volumes/bde.py +14 -10
- dissect/target/volumes/luks.py +18 -10
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/METADATA +4 -3
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/RECORD +85 -67
- dissect/target/plugins/os/unix/linux/fortigate/_os.py +0 -175
- /dissect/target/{plugins/os/unix/linux/fortigate → helpers/compat}/__init__.py +0 -0
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/LICENSE +0 -0
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/WHEEL +0 -0
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.14.dev29.dist-info → dissect.target-3.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import re
|
5
|
+
import warnings
|
6
|
+
from dataclasses import dataclass
|
7
|
+
from datetime import datetime
|
8
|
+
from typing import Iterator, Optional
|
9
|
+
|
10
|
+
from dissect.target import Target
|
11
|
+
from dissect.target.exceptions import UnsupportedPluginError
|
12
|
+
from dissect.target.helpers.record import TargetRecordDescriptor
|
13
|
+
from dissect.target.plugin import Plugin, export
|
14
|
+
|
15
|
+
warnings.simplefilter(action="ignore", category=FutureWarning)
|
16
|
+
log = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
SchedLgURecord = TargetRecordDescriptor(
|
19
|
+
"windows/tasks/log/schedlgu",
|
20
|
+
[
|
21
|
+
("datetime", "ts"),
|
22
|
+
("string", "job"),
|
23
|
+
("string", "command"),
|
24
|
+
("string", "status"),
|
25
|
+
("uint32", "exit_code"),
|
26
|
+
("string", "version"),
|
27
|
+
],
|
28
|
+
)
|
29
|
+
|
30
|
+
JOB_REGEX_PATTERN = re.compile(r"\"(.*?)\" \((.*?)\)")
|
31
|
+
SCHEDLGU_REGEX_PATTERN = re.compile(r"\".+\n.+\n\s{4}.+\n|\".+\n.+", re.MULTILINE)
|
32
|
+
|
33
|
+
|
34
|
+
@dataclass(order=True)
|
35
|
+
class SchedLgU:
|
36
|
+
ts: datetime = None
|
37
|
+
job: str = None
|
38
|
+
status: str = None
|
39
|
+
command: str = None
|
40
|
+
exit_code: int = None
|
41
|
+
version: str = None
|
42
|
+
|
43
|
+
@staticmethod
|
44
|
+
def _sanitize_ts(ts: str) -> datetime:
|
45
|
+
# sometimes "at" exists before the timestamp
|
46
|
+
ts = ts.strip("at ")
|
47
|
+
try:
|
48
|
+
ts = datetime.strptime(ts, "%m/%d/%Y %I:%M:%S %p")
|
49
|
+
except ValueError:
|
50
|
+
ts = datetime.strptime(ts, "%d-%m-%Y %H:%M:%S")
|
51
|
+
|
52
|
+
return ts
|
53
|
+
|
54
|
+
@staticmethod
|
55
|
+
def _parse_job(line: str) -> tuple[str, Optional[str]]:
|
56
|
+
matches = JOB_REGEX_PATTERN.match(line)
|
57
|
+
if matches:
|
58
|
+
return matches.groups()
|
59
|
+
|
60
|
+
log.warning("SchedLgU failed to parse job and command from line: '%s'. Returning line.", line)
|
61
|
+
return line, None
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def from_line(cls, line: str) -> SchedLgU:
|
65
|
+
"""Parse a group of SchedLgU.txt lines."""
|
66
|
+
event = cls()
|
67
|
+
lines = line.splitlines()
|
68
|
+
|
69
|
+
# Events can have 2 or 3 lines as a group in total. An example of a complete task job event is:
|
70
|
+
# "Symantec NetDetect.job" (NDETECT.EXE)
|
71
|
+
# Finished 14-9-2003 13:21:01
|
72
|
+
# Result: The task completed with an exit code of (65).
|
73
|
+
if len(lines) == 3:
|
74
|
+
event.job, event.command = cls._parse_job(lines[0])
|
75
|
+
event.status, event.ts = lines[1].split(maxsplit=1)
|
76
|
+
event.exit_code = int(lines[2].split("(")[1].rstrip(")."))
|
77
|
+
|
78
|
+
# Events that have 2 lines as a group can be started task job event or the Task Scheduler Service. Examples:
|
79
|
+
# "Symantec NetDetect.job" (NDETECT.EXE)
|
80
|
+
# Started at 14-9-2003 13:26:00
|
81
|
+
elif len(lines) == 2 and ".job" in lines[0]:
|
82
|
+
event.job, event.command = cls._parse_job(lines[0])
|
83
|
+
event.status, event.ts = lines[1].split(maxsplit=1)
|
84
|
+
|
85
|
+
# Events without a task job event are the Task Scheduler Service events. Which can look like this:
|
86
|
+
# "Task Scheduler Service"
|
87
|
+
# Exited at 14-9-2003 13:40:24
|
88
|
+
# OR
|
89
|
+
# "Task Scheduler Service"
|
90
|
+
# 6.0.6000.16386 (vista_rtm.061101-2205)
|
91
|
+
elif len(lines) == 2:
|
92
|
+
event.job = lines[0].strip('"')
|
93
|
+
|
94
|
+
if lines[1].startswith("\t") or lines[1].startswith(" "):
|
95
|
+
event.status, event.ts = lines[1].split(maxsplit=1)
|
96
|
+
else:
|
97
|
+
event.version = lines[1]
|
98
|
+
|
99
|
+
if event.ts:
|
100
|
+
event.ts = cls._sanitize_ts(event.ts)
|
101
|
+
|
102
|
+
return event
|
103
|
+
|
104
|
+
|
105
|
+
class SchedLgUPlugin(Plugin):
|
106
|
+
"""Plugin for parsing the Task Scheduler Service transaction log file (SchedLgU.txt)."""
|
107
|
+
|
108
|
+
PATHS = {
|
109
|
+
"sysvol/SchedLgU.txt",
|
110
|
+
"sysvol/windows/SchedLgU.txt",
|
111
|
+
"sysvol/windows/tasks/SchedLgU.txt",
|
112
|
+
"sysvol/winnt/tasks/SchedLgU.txt",
|
113
|
+
}
|
114
|
+
|
115
|
+
def __init__(self, target: Target) -> None:
|
116
|
+
self.target = target
|
117
|
+
self.paths = [self.target.fs.path(path) for path in self.PATHS if self.target.fs.path(path).exists()]
|
118
|
+
|
119
|
+
def check_compatible(self) -> None:
|
120
|
+
if len(self.paths) == 0:
|
121
|
+
raise UnsupportedPluginError("No SchedLgU.txt file found.")
|
122
|
+
|
123
|
+
@export(record=SchedLgURecord)
|
124
|
+
def schedlgu(self) -> Iterator[SchedLgURecord]:
|
125
|
+
"""Return all events in the Task Scheduler Service transaction log file (SchedLgU.txt).
|
126
|
+
|
127
|
+
Older Windows systems may log ``.job`` tasks that get started remotely in the SchedLgU.txt file.
|
128
|
+
In addition, this log file records when the Task Scheduler service starts and stops.
|
129
|
+
|
130
|
+
Adversaries may use malicious ``.job`` files to gain persistence on a system.
|
131
|
+
|
132
|
+
Yield:
|
133
|
+
ts (datetime): The timestamp of the event.
|
134
|
+
job (str): The name of the ``.job`` file.
|
135
|
+
command (str): The command executed.
|
136
|
+
status (str): The status of the event (finished, completed, exited, stopped).
|
137
|
+
exit_code (int): The exit code of the event.
|
138
|
+
version (str): The version of the Task Scheduler service.
|
139
|
+
"""
|
140
|
+
|
141
|
+
for path in self.paths:
|
142
|
+
content = path.read_text(encoding="UTF-16", errors="surrogateescape")
|
143
|
+
|
144
|
+
for match in re.findall(SCHEDLGU_REGEX_PATTERN, content):
|
145
|
+
event = SchedLgU.from_line(match)
|
146
|
+
|
147
|
+
yield SchedLgURecord(
|
148
|
+
ts=event.ts,
|
149
|
+
job=event.job,
|
150
|
+
command=event.command,
|
151
|
+
status=event.status,
|
152
|
+
exit_code=event.exit_code,
|
153
|
+
version=event.version,
|
154
|
+
_target=self.target,
|
155
|
+
)
|
@@ -11,7 +11,7 @@ class FirewallPlugin(Plugin):
|
|
11
11
|
"""Plugin that parses firewall rules from the registry."""
|
12
12
|
|
13
13
|
KEY = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"
|
14
|
-
FIELD_MAP = {"app": "
|
14
|
+
FIELD_MAP = {"app": "path"}
|
15
15
|
VALUE_MAP = {"active": lambda val: val == "TRUE"}
|
16
16
|
|
17
17
|
def check_compatible(self) -> None:
|
@@ -73,7 +73,7 @@ class TrustedDocumentsPlugin(Plugin):
|
|
73
73
|
ts=key.ts,
|
74
74
|
type=value.type,
|
75
75
|
application=application,
|
76
|
-
document_path=self.target.
|
76
|
+
document_path=self.target.resolve(value.name),
|
77
77
|
value=value.value,
|
78
78
|
_key=key,
|
79
79
|
_user=user,
|
@@ -329,7 +329,7 @@ class RegistryPlugin(Plugin):
|
|
329
329
|
@internal
|
330
330
|
def get_user_details(self, key: RegistryKey) -> UserDetails:
|
331
331
|
"""Return user details for the user who owns a registry hive that contains the provided key"""
|
332
|
-
if not key.hive or not key.hive
|
332
|
+
if not key.hive or not getattr(key.hive, "filepath", None):
|
333
333
|
return
|
334
334
|
|
335
335
|
return self._hives_to_users.get(key.hive)
|
@@ -252,6 +252,9 @@ def rid_to_key(rid: int) -> tuple[bytes, bytes]:
|
|
252
252
|
|
253
253
|
|
254
254
|
def decrypt_single_hash(rid: int, samkey: bytes, enc_hash: bytes, apwd: bytes) -> bytes:
|
255
|
+
if not enc_hash:
|
256
|
+
return b""
|
257
|
+
|
255
258
|
sh = c_sam.SAM_HASH(enc_hash)
|
256
259
|
|
257
260
|
if sh.revision not in [0x01, 0x02]:
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from typing import Iterator, Optional, Union
|
2
|
+
|
1
3
|
from dissect.esedb.exceptions import Error
|
2
4
|
from dissect.esedb.tools import sru
|
3
5
|
|
@@ -223,6 +225,22 @@ SdpNetworkProviderRecord = TargetRecordDescriptor(
|
|
223
225
|
],
|
224
226
|
)
|
225
227
|
|
228
|
+
SRURecord = Union[
|
229
|
+
NetworkDataRecord,
|
230
|
+
NetworkConnectivityRecord,
|
231
|
+
EnergyEstimatorRecord,
|
232
|
+
EnergyUsageRecord,
|
233
|
+
EnergyUsageLTRecord,
|
234
|
+
ApplicationRecord,
|
235
|
+
PushNotificationRecord,
|
236
|
+
ApplicationTimelineRecord,
|
237
|
+
VfuRecord,
|
238
|
+
SdpVolumeProviderRecord,
|
239
|
+
SdpPhysicalDiskProviderRecord,
|
240
|
+
SdpCpuProviderRecord,
|
241
|
+
SdpNetworkProviderRecord,
|
242
|
+
]
|
243
|
+
|
226
244
|
FIELD_MAPPINGS = {
|
227
245
|
"ActiveAcTime": "active_ac_time",
|
228
246
|
"ActiveDcTime": "active_dc_time",
|
@@ -322,7 +340,7 @@ FIELD_MAPPINGS = {
|
|
322
340
|
}
|
323
341
|
|
324
342
|
|
325
|
-
def transform_app_id(value):
|
343
|
+
def transform_app_id(value: Optional[Union[bytes, str]]) -> Optional[str]:
|
326
344
|
if value is not None:
|
327
345
|
if isinstance(value, bytes):
|
328
346
|
value = value.decode()
|
@@ -364,10 +382,11 @@ class SRUPlugin(Plugin):
|
|
364
382
|
if not self._sru:
|
365
383
|
raise UnsupportedPluginError("No SRUDB found")
|
366
384
|
|
367
|
-
def read_records(self, table_name, record_type):
|
385
|
+
def read_records(self, table_name: str, record_type: SRURecord) -> Iterator[SRURecord]:
|
368
386
|
table = self._sru.get_table(table_name=table_name)
|
369
387
|
if not table:
|
370
|
-
|
388
|
+
self.target.log.warning("Table not found: %s", table_name)
|
389
|
+
return iter(())
|
371
390
|
|
372
391
|
columns = [c.name for c in table.columns]
|
373
392
|
if columns[:4] != ["AutoIncId", "TimeStamp", "AppId", "UserId"]:
|
@@ -392,90 +411,84 @@ class SRUPlugin(Plugin):
|
|
392
411
|
)
|
393
412
|
|
394
413
|
@export(record=NetworkDataRecord)
|
395
|
-
def network_data(self):
|
396
|
-
"""
|
397
|
-
Return the contents of Windows Network Data Usage Monitor table from the SRUDB.dat file.
|
414
|
+
def network_data(self) -> Iterator[NetworkDataRecord]:
|
415
|
+
"""Return the contents of Windows Network Data Usage Monitor table from the SRUDB.dat file.
|
398
416
|
|
399
417
|
Gives insight into the network usage of the system.
|
400
418
|
"""
|
401
419
|
yield from self.read_records("network_data", NetworkDataRecord)
|
402
420
|
|
403
421
|
@export(record=NetworkConnectivityRecord)
|
404
|
-
def network_connectivity(self):
|
405
|
-
"""
|
406
|
-
Return the contents of Windows Network Connectivity Usage Monitor table from the SRUDB.dat file.
|
422
|
+
def network_connectivity(self) -> Iterator[NetworkConnectivityRecord]:
|
423
|
+
"""Return the contents of Windows Network Connectivity Usage Monitor table from the SRUDB.dat file.
|
407
424
|
|
408
425
|
Gives insight into the network connectivity usage of the system.
|
409
426
|
"""
|
410
427
|
yield from self.read_records("network_connectivity", NetworkConnectivityRecord)
|
411
428
|
|
412
429
|
@export(record=EnergyEstimatorRecord)
|
413
|
-
def energy_estimator(self):
|
430
|
+
def energy_estimator(self) -> Iterator[EnergyEstimatorRecord]:
|
414
431
|
"""Return the contents of Energy Estimator table from the SRUDB.dat file."""
|
415
432
|
yield from self.read_records("energy_estimator", EnergyEstimatorRecord)
|
416
433
|
|
417
434
|
@export(record=EnergyUsageRecord)
|
418
|
-
def energy_usage(self):
|
419
|
-
"""
|
420
|
-
Return the contents of Energy Usage Provider table from the SRUDB.dat file.
|
435
|
+
def energy_usage(self) -> Iterator[EnergyUsageRecord]:
|
436
|
+
"""Return the contents of Energy Usage Provider table from the SRUDB.dat file.
|
421
437
|
|
422
438
|
Gives insight into the energy usage of the system.
|
423
439
|
"""
|
424
440
|
yield from self.read_records("energy_usage", EnergyUsageRecord)
|
425
441
|
|
426
442
|
@export(record=EnergyUsageLTRecord)
|
427
|
-
def energy_usage_lt(self):
|
428
|
-
"""
|
429
|
-
Return the contents of Energy Usage Provider Long Term table from the SRUDB.dat file.
|
443
|
+
def energy_usage_lt(self) -> Iterator[EnergyUsageLTRecord]:
|
444
|
+
"""Return the contents of Energy Usage Provider Long Term table from the SRUDB.dat file.
|
430
445
|
|
431
446
|
Gives insight into the energy usage of the system looking over the long term.
|
432
447
|
"""
|
433
448
|
yield from self.read_records("energy_usage_lt", EnergyUsageLTRecord)
|
434
449
|
|
435
450
|
@export(record=ApplicationRecord)
|
436
|
-
def application(self):
|
437
|
-
"""
|
438
|
-
Return the contents of Application Resource Usage table from the SRUDB.dat file.
|
451
|
+
def application(self) -> Iterator[ApplicationRecord]:
|
452
|
+
"""Return the contents of Application Resource Usage table from the SRUDB.dat file.
|
439
453
|
|
440
454
|
Gives insights into the resource usage of applications on the system.
|
441
455
|
"""
|
442
456
|
yield from self.read_records("application", ApplicationRecord)
|
443
457
|
|
444
458
|
@export(record=PushNotificationRecord)
|
445
|
-
def push_notification(self):
|
446
|
-
"""
|
447
|
-
Return the contents of Windows Push Notification Data table from the SRUDB.dat file.
|
459
|
+
def push_notification(self) -> Iterator[PushNotificationRecord]:
|
460
|
+
"""Return the contents of Windows Push Notification Data table from the SRUDB.dat file.
|
448
461
|
|
449
462
|
Gives insight into the notification usage of the system.
|
450
463
|
"""
|
451
464
|
yield from self.read_records("push_notifications", PushNotificationRecord)
|
452
465
|
|
453
466
|
@export(record=ApplicationTimelineRecord)
|
454
|
-
def application_timeline(self):
|
467
|
+
def application_timeline(self) -> Iterator[ApplicationTimelineRecord]:
|
455
468
|
"""Return the contents of App Timeline Provider table from the SRUDB.dat file."""
|
456
469
|
yield from self.read_records("application_timeline", ApplicationTimelineRecord)
|
457
470
|
|
458
471
|
@export(record=VfuRecord)
|
459
|
-
def vfu(self):
|
472
|
+
def vfu(self) -> Iterator[VfuRecord]:
|
460
473
|
"""Return the contents of vfuprov table from the SRUDB.dat file."""
|
461
474
|
yield from self.read_records("vfu", VfuRecord)
|
462
475
|
|
463
476
|
@export(record=SdpVolumeProviderRecord)
|
464
|
-
def sdp_volume_provider(self):
|
477
|
+
def sdp_volume_provider(self) -> Iterator[SdpVolumeProviderRecord]:
|
465
478
|
"""Return the contents of SDP Volume Provider table from the SRUDB.dat file."""
|
466
479
|
yield from self.read_records("sdp_volume_provider", SdpVolumeProviderRecord)
|
467
480
|
|
468
481
|
@export(record=SdpPhysicalDiskProviderRecord)
|
469
|
-
def sdp_physical_disk_provider(self):
|
482
|
+
def sdp_physical_disk_provider(self) -> Iterator[SdpPhysicalDiskProviderRecord]:
|
470
483
|
"""Return the contents of SDP Physical Disk Provider table from the SRUDB.dat file."""
|
471
484
|
yield from self.read_records("sdp_physical_disk_provider", SdpPhysicalDiskProviderRecord)
|
472
485
|
|
473
486
|
@export(record=SdpCpuProviderRecord)
|
474
|
-
def sdp_cpu_provider(self):
|
487
|
+
def sdp_cpu_provider(self) -> Iterator[SdpCpuProviderRecord]:
|
475
488
|
"""Return the contents of SDP CPU Provider table from the SRUDB.dat file."""
|
476
489
|
yield from self.read_records("sdp_cpu_provider", SdpCpuProviderRecord)
|
477
490
|
|
478
491
|
@export(record=SdpNetworkProviderRecord)
|
479
|
-
def sdp_network_provider(self):
|
492
|
+
def sdp_network_provider(self) -> Iterator[SdpNetworkProviderRecord]:
|
480
493
|
"""Return the contents of SDP Network Provider table from the SRUDB.dat file."""
|
481
494
|
yield from self.read_records("sdp_network_provider", SdpNetworkProviderRecord)
|
@@ -1,16 +1,20 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import logging
|
1
4
|
import warnings
|
2
5
|
from typing import Iterator, Union
|
3
6
|
|
4
7
|
from flow.record import GroupedRecord
|
5
8
|
|
9
|
+
from dissect.target import Target
|
6
10
|
from dissect.target.exceptions import UnsupportedPluginError
|
7
11
|
from dissect.target.helpers.record import DynamicDescriptor, TargetRecordDescriptor
|
8
12
|
from dissect.target.plugin import Plugin, export
|
9
13
|
from dissect.target.plugins.os.windows.task_helpers.tasks_job import AtTask
|
10
14
|
from dissect.target.plugins.os.windows.task_helpers.tasks_xml import ScheduledTasks
|
11
|
-
from dissect.target.target import Target
|
12
15
|
|
13
16
|
warnings.simplefilter(action="ignore", category=FutureWarning)
|
17
|
+
log = logging.getLogger(__name__)
|
14
18
|
|
15
19
|
TaskRecord = TargetRecordDescriptor(
|
16
20
|
"filesystem/windows/task",
|
@@ -42,7 +46,6 @@ TaskRecord = TargetRecordDescriptor(
|
|
42
46
|
("string", "run_level"),
|
43
47
|
("string", "process_token_sid_type"),
|
44
48
|
("string", "required_privileges"),
|
45
|
-
("boolean", "allow_start_on_demand"),
|
46
49
|
("string", "restart_on_failure_interval"),
|
47
50
|
("string", "restart_on_failure_count"),
|
48
51
|
("string", "mutiple_instances_policy"),
|
dissect/target/target.py
CHANGED
@@ -79,7 +79,7 @@ class Target:
|
|
79
79
|
self._functions: dict[str, FunctionTuple] = {}
|
80
80
|
self._loader = None
|
81
81
|
self._os = None
|
82
|
-
self._os_plugin: plugin.OSPlugin = None
|
82
|
+
self._os_plugin: type[plugin.OSPlugin] = None
|
83
83
|
self._child_plugins: dict[str, plugin.ChildTargetPlugin] = {}
|
84
84
|
self._cache = dict()
|
85
85
|
self._errors = []
|
@@ -225,7 +225,10 @@ class Target:
|
|
225
225
|
|
226
226
|
loader_cls = loader.find_loader(path, parsed_path=parsed_path)
|
227
227
|
if loader_cls:
|
228
|
-
|
228
|
+
try:
|
229
|
+
loader_instance = loader_cls(path, parsed_path=parsed_path)
|
230
|
+
except Exception as e:
|
231
|
+
raise TargetError(f"Failed to initiate {loader_cls.__name__} for target {path}: {e}", cause=e)
|
229
232
|
return cls._load(path, loader_instance)
|
230
233
|
return cls.open_raw(path)
|
231
234
|
|
@@ -281,7 +284,8 @@ class Target:
|
|
281
284
|
try:
|
282
285
|
ldr = loader_cls(sub_entry, parsed_path=parsed_path)
|
283
286
|
except Exception as e:
|
284
|
-
getlogger(sub_entry).error("Failed to initiate loader",
|
287
|
+
getlogger(sub_entry).error("Failed to initiate loader: %s", e)
|
288
|
+
getlogger(sub_entry).debug("", exc_info=e)
|
285
289
|
continue
|
286
290
|
|
287
291
|
try:
|
dissect/target/tools/dd.py
CHANGED
@@ -9,6 +9,7 @@ import sys
|
|
9
9
|
from dissect.util.stream import RangeStream
|
10
10
|
|
11
11
|
from dissect.target import Target
|
12
|
+
from dissect.target.exceptions import TargetError
|
12
13
|
from dissect.target.tools.utils import (
|
13
14
|
catch_sigpipe,
|
14
15
|
configure_generic_arguments,
|
@@ -39,7 +40,12 @@ def main():
|
|
39
40
|
|
40
41
|
process_generic_arguments(args)
|
41
42
|
|
42
|
-
|
43
|
+
try:
|
44
|
+
t = Target.open(args.target)
|
45
|
+
except TargetError as e:
|
46
|
+
log.error(e)
|
47
|
+
log.debug("", exc_info=e)
|
48
|
+
parser.exit(1)
|
43
49
|
|
44
50
|
if len(t.disks) > 1:
|
45
51
|
parser.exit("Target has more than one disk")
|
dissect/target/tools/fs.py
CHANGED
@@ -10,6 +10,7 @@ import shutil
|
|
10
10
|
import sys
|
11
11
|
|
12
12
|
from dissect.target import Target
|
13
|
+
from dissect.target.exceptions import TargetError
|
13
14
|
from dissect.target.helpers.fsutil import TargetPath
|
14
15
|
from dissect.target.tools.utils import (
|
15
16
|
catch_sigpipe,
|
@@ -113,7 +114,13 @@ def main():
|
|
113
114
|
|
114
115
|
process_generic_arguments(args)
|
115
116
|
|
116
|
-
|
117
|
+
try:
|
118
|
+
target = Target.open(args.target)
|
119
|
+
except TargetError as e:
|
120
|
+
log.error(e)
|
121
|
+
log.debug("", exc_info=e)
|
122
|
+
parser.exit(1)
|
123
|
+
|
117
124
|
path = target.fs.path(args.path)
|
118
125
|
|
119
126
|
if not path.exists():
|
dissect/target/tools/info.py
CHANGED
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
8
8
|
from typing import Union
|
9
9
|
|
10
10
|
from dissect.target import Target
|
11
|
+
from dissect.target.exceptions import TargetError
|
11
12
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
12
13
|
from dissect.target.tools.query import record_output
|
13
14
|
from dissect.target.tools.utils import (
|
@@ -72,22 +73,27 @@ def main():
|
|
72
73
|
targets = targets[:-1]
|
73
74
|
args.targets = targets
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
76
|
+
try:
|
77
|
+
for i, target in enumerate(Target.open_all(args.targets)):
|
78
|
+
try:
|
79
|
+
if args.jsonlines:
|
80
|
+
print(json.dumps(get_target_info(target), default=str))
|
81
|
+
elif args.json:
|
82
|
+
print(json.dumps(get_target_info(target), indent=4, default=str))
|
83
|
+
elif args.record:
|
84
|
+
rs = record_output(args.strings)
|
85
|
+
rs.write(InfoRecord(**get_target_info(target), _target=target))
|
86
|
+
else:
|
87
|
+
if i > 0:
|
88
|
+
print("-" * 70)
|
89
|
+
print_target_info(target)
|
90
|
+
except Exception as e:
|
91
|
+
target.log.error("Exception in retrieving information for target: `%s`. Use `-vv` for details.", target)
|
92
|
+
target.log.debug("", exc_info=e)
|
93
|
+
except TargetError as e:
|
94
|
+
log.error(e)
|
95
|
+
log.debug("", exc_info=e)
|
96
|
+
parser.exit(1)
|
91
97
|
|
92
98
|
|
93
99
|
def get_target_info(target: Target) -> dict[str, Union[str, list[str]]]:
|
dissect/target/tools/mount.py
CHANGED
@@ -2,7 +2,10 @@ import argparse
|
|
2
2
|
import logging
|
3
3
|
from typing import Union
|
4
4
|
|
5
|
+
from dissect.util.feature import Feature, feature_enabled
|
6
|
+
|
5
7
|
from dissect.target import Target, filesystem
|
8
|
+
from dissect.target.exceptions import TargetError
|
6
9
|
from dissect.target.helpers.utils import parse_options_string
|
7
10
|
from dissect.target.tools.utils import (
|
8
11
|
catch_sigpipe,
|
@@ -10,8 +13,23 @@ from dissect.target.tools.utils import (
|
|
10
13
|
process_generic_arguments,
|
11
14
|
)
|
12
15
|
|
16
|
+
# Setting logging level to info for startup information.
|
17
|
+
logging.basicConfig(level=logging.INFO)
|
18
|
+
|
13
19
|
try:
|
14
|
-
|
20
|
+
if feature_enabled(Feature.BETA):
|
21
|
+
from fuse3 import FUSE3 as FUSE
|
22
|
+
from fuse3 import util
|
23
|
+
|
24
|
+
FUSE_VERSION = "3"
|
25
|
+
FUSE_LIB_PATH = util.libfuse._name
|
26
|
+
else:
|
27
|
+
from fuse import FUSE, _libfuse
|
28
|
+
|
29
|
+
FUSE_VERSION = "2"
|
30
|
+
FUSE_LIB_PATH = _libfuse._name
|
31
|
+
|
32
|
+
logging.info("Using fuse%s library: %s", FUSE_VERSION, FUSE_LIB_PATH)
|
15
33
|
|
16
34
|
from dissect.target.helpers.mount import DissectMount
|
17
35
|
|
@@ -19,6 +37,7 @@ try:
|
|
19
37
|
except Exception:
|
20
38
|
HAS_FUSE = False
|
21
39
|
|
40
|
+
|
22
41
|
log = logging.getLogger(__name__)
|
23
42
|
logging.lastResort = None
|
24
43
|
logging.raiseExceptions = False
|
@@ -44,7 +63,13 @@ def main():
|
|
44
63
|
if not HAS_FUSE:
|
45
64
|
parser.exit("fusepy is not installed: pip install fusepy")
|
46
65
|
|
47
|
-
|
66
|
+
try:
|
67
|
+
t = Target.open(args.target)
|
68
|
+
except TargetError as e:
|
69
|
+
log.error(e)
|
70
|
+
log.debug("", exc_info=e)
|
71
|
+
parser.exit(1)
|
72
|
+
|
48
73
|
vfs = filesystem.VirtualFilesystem()
|
49
74
|
vfs.mount("fs", t.fs)
|
50
75
|
|
@@ -85,7 +110,7 @@ def main():
|
|
85
110
|
|
86
111
|
|
87
112
|
def _format_options(options: dict[str, Union[str, bool]]) -> str:
|
88
|
-
return ",".join(
|
113
|
+
return ",".join(key if value is True else f"{key}={value}" for key, value in options.items())
|
89
114
|
|
90
115
|
|
91
116
|
if __name__ == "__main__":
|