dissect.target 3.16.dev31__py3-none-any.whl → 3.16.dev33__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/filesystems/tar.py +4 -1
- dissect/target/plugins/os/windows/wer.py +94 -71
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/METADATA +1 -1
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/RECORD +9 -9
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/LICENSE +0 -0
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/WHEEL +0 -0
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/top_level.txt +0 -0
@@ -60,7 +60,10 @@ class TarFilesystem(Filesystem):
|
|
60
60
|
@staticmethod
|
61
61
|
def _detect(fh: BinaryIO) -> bool:
|
62
62
|
"""Detect a tar file on a given file-like object."""
|
63
|
-
|
63
|
+
fh = fsutil.open_decompress(fileobj=fh)
|
64
|
+
|
65
|
+
fh.seek(257)
|
66
|
+
return fh.read(8) in (tarfile.GNU_MAGIC, tarfile.POSIX_MAGIC)
|
64
67
|
|
65
68
|
def get(self, path: str, relentry: Optional[FilesystemEntry] = None) -> FilesystemEntry:
|
66
69
|
"""Returns a TarFilesystemEntry object corresponding to the given path."""
|
@@ -14,69 +14,6 @@ from dissect.target.target import Target
|
|
14
14
|
camel_case_patterns = [re.compile(r"(\S)([A-Z][a-z]+)"), re.compile(r"([a-z0-9])([A-Z])"), re.compile(r"(\w)[.\s](\w)")]
|
15
15
|
|
16
16
|
|
17
|
-
def _collect_wer_data(wer_file: Path) -> tuple[list[tuple[str, str]], dict[str, str]]:
|
18
|
-
"""Parse data from a .wer file."""
|
19
|
-
record_values = {}
|
20
|
-
record_fields = []
|
21
|
-
key = None
|
22
|
-
|
23
|
-
# Default encoding when no BOM is present
|
24
|
-
encoding = "utf-16-le"
|
25
|
-
|
26
|
-
# If a BOM header is present we can decode it using utf-16
|
27
|
-
with wer_file.open("rb") as fh:
|
28
|
-
if fh.read(len(codecs.BOM)) in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
|
29
|
-
encoding = "utf-16"
|
30
|
-
|
31
|
-
for line in wer_file.read_text(encoding).splitlines():
|
32
|
-
if len(line_split := line.rstrip().split("=", 1)) == 2:
|
33
|
-
name, value = line_split
|
34
|
-
record_type = "string"
|
35
|
-
|
36
|
-
# dynamic entry with key and value on separate lines
|
37
|
-
if "].Name" in name and not key:
|
38
|
-
key = value
|
39
|
-
# set key and continue to get value on the next line
|
40
|
-
continue
|
41
|
-
|
42
|
-
# dynamic entry with key and value on the same line
|
43
|
-
elif "]." in name and not key:
|
44
|
-
category, name = name.split(".", 1)
|
45
|
-
key = f"{category.split('[')[0]}{name}"
|
46
|
-
|
47
|
-
if "EventTime" in name:
|
48
|
-
value = wintimestamp(int(value))
|
49
|
-
record_type = "datetime"
|
50
|
-
key = "ts"
|
51
|
-
|
52
|
-
key = _key_to_snake_case(key if key else name)
|
53
|
-
|
54
|
-
record_values[key] = value
|
55
|
-
record_fields.append((record_type, key)) if key != "ts" else record_fields.insert(0, (record_type, key))
|
56
|
-
# reset key necessary for dynamic entries and ts
|
57
|
-
key = None
|
58
|
-
|
59
|
-
return record_fields, record_values
|
60
|
-
|
61
|
-
|
62
|
-
def _collect_wer_metadata(metadata_xml_file: Path) -> tuple[list[tuple[str, str]], dict[str, str]]:
|
63
|
-
"""Parse data from a metadata .xml file linked to a .wer file."""
|
64
|
-
record_fields = []
|
65
|
-
record_values = {}
|
66
|
-
file = metadata_xml_file.read_text("utf-16")
|
67
|
-
|
68
|
-
tree = ElementTree.fromstring(file)
|
69
|
-
for metadata in tree.iter("WERReportMetadata"):
|
70
|
-
for category in metadata:
|
71
|
-
for value in category:
|
72
|
-
if record_value := value.text.strip("\t\n"):
|
73
|
-
key = _key_to_snake_case(f"{category.tag}{value.tag}")
|
74
|
-
record_fields.append(("string", key))
|
75
|
-
record_values[key] = record_value
|
76
|
-
|
77
|
-
return record_fields, record_values
|
78
|
-
|
79
|
-
|
80
17
|
def _create_record_descriptor(record_name: str, record_fields: list[tuple[str, str]]) -> TargetRecordDescriptor:
|
81
18
|
record_fields.extend(
|
82
19
|
[
|
@@ -87,12 +24,6 @@ def _create_record_descriptor(record_name: str, record_fields: list[tuple[str, s
|
|
87
24
|
return TargetRecordDescriptor(record_name, record_fields)
|
88
25
|
|
89
26
|
|
90
|
-
def _key_to_snake_case(key: str) -> str:
|
91
|
-
for pattern in camel_case_patterns:
|
92
|
-
key = pattern.sub(r"\1_\2", key)
|
93
|
-
return key.lower()
|
94
|
-
|
95
|
-
|
96
27
|
class WindowsErrorReportingPlugin(Plugin):
|
97
28
|
"""Plugin for parsing Windows Error Reporting files."""
|
98
29
|
|
@@ -116,6 +47,98 @@ class WindowsErrorReportingPlugin(Plugin):
|
|
116
47
|
if not self.wer_files:
|
117
48
|
raise UnsupportedPluginError("No Windows Error Reporting directories found.")
|
118
49
|
|
50
|
+
def _sanitize_key(self, key: str) -> str:
|
51
|
+
# Convert camel case to snake case
|
52
|
+
for pattern in camel_case_patterns:
|
53
|
+
key = pattern.sub(r"\1_\2", key)
|
54
|
+
|
55
|
+
# Keep only basic characters in key
|
56
|
+
key = re.sub(r"[^a-zA-Z0-9_]", "", key)
|
57
|
+
|
58
|
+
return key.lower()
|
59
|
+
|
60
|
+
def _collect_wer_data(self, wer_file: Path) -> tuple[list[tuple[str, str]], dict[str, str]]:
|
61
|
+
"""Parse data from a .wer file."""
|
62
|
+
record_values = {}
|
63
|
+
record_fields = []
|
64
|
+
key = None
|
65
|
+
|
66
|
+
# Default encoding when no BOM is present
|
67
|
+
encoding = "utf-16-le"
|
68
|
+
|
69
|
+
# If a BOM header is present we can decode it using utf-16
|
70
|
+
with wer_file.open("rb") as fh:
|
71
|
+
if fh.read(len(codecs.BOM)) in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
|
72
|
+
encoding = "utf-16"
|
73
|
+
|
74
|
+
for line in wer_file.read_text(encoding).splitlines():
|
75
|
+
if len(line_split := line.rstrip().split("=", 1)) != 2:
|
76
|
+
continue
|
77
|
+
|
78
|
+
name, value = line_split
|
79
|
+
record_type = "string"
|
80
|
+
|
81
|
+
# Dynamic entry with key and value on separate lines
|
82
|
+
if "].Name" in name and not key:
|
83
|
+
key = value
|
84
|
+
# Set key and continue to get value on the next line
|
85
|
+
continue
|
86
|
+
|
87
|
+
# Dynamic entry with key and value on the same line
|
88
|
+
elif "]." in name and not key:
|
89
|
+
category, name = name.split(".", 1)
|
90
|
+
key = f"{category.split('[')[0]}{name}"
|
91
|
+
|
92
|
+
if "EventTime" in name:
|
93
|
+
value = wintimestamp(int(value))
|
94
|
+
record_type = "datetime"
|
95
|
+
key = "ts"
|
96
|
+
|
97
|
+
key = self._sanitize_key(key if key else name)
|
98
|
+
if not key:
|
99
|
+
self.target.log.warning(f"Sanitizing key resulted in empty key, skipping line '{line}'.")
|
100
|
+
key = None
|
101
|
+
continue
|
102
|
+
|
103
|
+
if key in record_values:
|
104
|
+
self.target.log.warning(f"Key does already exist, skipping line '{line}'.")
|
105
|
+
key = None
|
106
|
+
continue
|
107
|
+
|
108
|
+
record_values[key] = value
|
109
|
+
record_fields.append((record_type, key)) if key != "ts" else record_fields.insert(0, (record_type, key))
|
110
|
+
# Reset key necessary for dynamic entries and ts
|
111
|
+
key = None
|
112
|
+
|
113
|
+
return record_fields, record_values
|
114
|
+
|
115
|
+
def _collect_wer_metadata(self, metadata_xml_file: Path) -> tuple[list[tuple[str, str]], dict[str, str]]:
|
116
|
+
"""Parse data from a metadata .xml file linked to a .wer file."""
|
117
|
+
record_fields = []
|
118
|
+
record_values = {}
|
119
|
+
file = metadata_xml_file.read_text("utf-16")
|
120
|
+
|
121
|
+
tree = ElementTree.fromstring(file)
|
122
|
+
for metadata in tree.iter("WERReportMetadata"):
|
123
|
+
for category in metadata:
|
124
|
+
for value in category:
|
125
|
+
if not (record_value := value.text.strip("\t\n")):
|
126
|
+
continue
|
127
|
+
|
128
|
+
key = self._sanitize_key(f"{category.tag}{value.tag}")
|
129
|
+
if not key:
|
130
|
+
self.target.log.warning(f"Sanitizing key resulted in empty key, skipping value '{value}'.")
|
131
|
+
continue
|
132
|
+
|
133
|
+
if key in record_values:
|
134
|
+
self.target.log.warning(f"Key already exists, skipping value '{value}'.")
|
135
|
+
continue
|
136
|
+
|
137
|
+
record_fields.append(("string", key))
|
138
|
+
record_values[key] = record_value
|
139
|
+
|
140
|
+
return record_fields, record_values
|
141
|
+
|
119
142
|
@export(record=DynamicDescriptor(["path", "string", "datetime"]))
|
120
143
|
def wer(self) -> Iterator[DynamicDescriptor]:
|
121
144
|
"""Return information from Windows Error Reporting (WER) files.
|
@@ -158,13 +181,13 @@ class WindowsErrorReportingPlugin(Plugin):
|
|
158
181
|
for file in files:
|
159
182
|
if file.suffix == ".wer":
|
160
183
|
record_values["wer_file_path"] = file
|
161
|
-
wer_report_fields, wer_report_values = _collect_wer_data(file)
|
184
|
+
wer_report_fields, wer_report_values = self._collect_wer_data(file)
|
162
185
|
# make sure wer_report_fields are the first entries in the list
|
163
186
|
record_fields = wer_report_fields + record_fields
|
164
187
|
record_values = record_values | wer_report_values
|
165
188
|
elif ".WERInternalMetadata" in file.suffixes:
|
166
189
|
record_values["metadata_file_path"] = file
|
167
|
-
metadata_fields, metadata_values = _collect_wer_metadata(file)
|
190
|
+
metadata_fields, metadata_values = self._collect_wer_metadata(file)
|
168
191
|
record_fields.extend(metadata_fields)
|
169
192
|
record_values = metadata_values | record_values
|
170
193
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.16.
|
3
|
+
Version: 3.16.dev33
|
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
|
@@ -36,7 +36,7 @@ dissect/target/filesystems/jffs.py,sha256=Ceqa5Em2pepnXMH_XZFmSNjQyWPo1uWTthBFSM
|
|
36
36
|
dissect/target/filesystems/ntfs.py,sha256=fGgCKjdO5GrPC21DDr0SwIxmwR7KruNIqGUzysboirA,7068
|
37
37
|
dissect/target/filesystems/smb.py,sha256=uxfcOWwEoDCw8Qpsa94T5Pn-SKd4WXs4OOrzVVI55d8,6406
|
38
38
|
dissect/target/filesystems/squashfs.py,sha256=ehzlThXB7n96XUvQnsK5tWLsA9HIxYN-Zxl7aO9D7ts,3921
|
39
|
-
dissect/target/filesystems/tar.py,sha256=
|
39
|
+
dissect/target/filesystems/tar.py,sha256=kQNhcEDPX005svse039OeR2AGSDigGuGz2AKoVrgg84,5692
|
40
40
|
dissect/target/filesystems/vmfs.py,sha256=sRtYBUAKTKcHrjCXqpFJ8GIVU-ERjqxhB2zXBndtcXU,4955
|
41
41
|
dissect/target/filesystems/xfs.py,sha256=kIyFGKYlyFYC7H3jaEv-lNKtBW4ZkD92H0WpfGcr1ww,4498
|
42
42
|
dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgGQRTQ,4462
|
@@ -270,7 +270,7 @@ dissect/target/plugins/os/windows/syscache.py,sha256=WBDx6rixaVnCRsJHLLN_9YWoTDb
|
|
270
270
|
dissect/target/plugins/os/windows/tasks.py,sha256=8DRsIAuIJPaH_G18l8RYfnK_WkEqVx2xDJ1FnIc_i0g,5716
|
271
271
|
dissect/target/plugins/os/windows/thumbcache.py,sha256=23YjOjTNoE7BYITmg8s9Zs8Wih2e73BkJJEaKlfotcI,4133
|
272
272
|
dissect/target/plugins/os/windows/ual.py,sha256=TYF-R46klEa_HHb86UJd6mPrXwHlAMOUTzC0pZ8uiq0,9787
|
273
|
-
dissect/target/plugins/os/windows/wer.py,sha256=
|
273
|
+
dissect/target/plugins/os/windows/wer.py,sha256=1kwkBvgmEU1QRCLWVmUFNIWAqXEEGtAj2c8uj0iusOE,8625
|
274
274
|
dissect/target/plugins/os/windows/dpapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
275
275
|
dissect/target/plugins/os/windows/dpapi/blob.py,sha256=oFhksgx2BAaeAbpPwOM-o0Dw5MKaMLGMF6ETdxIS708,5051
|
276
276
|
dissect/target/plugins/os/windows/dpapi/crypto.py,sha256=Xd15dFLYOQtvk9SlkdM1XX0c2vxSj9j4RFEwV70eP5Y,9074
|
@@ -331,10 +331,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
331
331
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
332
332
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
333
333
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
334
|
-
dissect.target-3.16.
|
335
|
-
dissect.target-3.16.
|
336
|
-
dissect.target-3.16.
|
337
|
-
dissect.target-3.16.
|
338
|
-
dissect.target-3.16.
|
339
|
-
dissect.target-3.16.
|
340
|
-
dissect.target-3.16.
|
334
|
+
dissect.target-3.16.dev33.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
335
|
+
dissect.target-3.16.dev33.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
336
|
+
dissect.target-3.16.dev33.dist-info/METADATA,sha256=CslSUR0Z9B6LGdHxl0SzzwC405b76fEMiaTuFa24Myk,11107
|
337
|
+
dissect.target-3.16.dev33.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
338
|
+
dissect.target-3.16.dev33.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
339
|
+
dissect.target-3.16.dev33.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
340
|
+
dissect.target-3.16.dev33.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.16.dev31.dist-info → dissect.target-3.16.dev33.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|