dissect.target 3.19.dev41__py3-none-any.whl → 3.19.dev42__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/helpers/shell_application_ids.py +732 -0
- dissect/target/helpers/utils.py +11 -0
- dissect/target/plugins/os/windows/catroot.py +1 -11
- dissect/target/plugins/os/windows/jumplist.py +292 -0
- dissect/target/plugins/os/windows/lnk.py +84 -89
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/METADATA +2 -1
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/RECORD +12 -10
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/LICENSE +0 -0
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/WHEEL +0 -0
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/top_level.txt +0 -0
dissect/target/helpers/utils.py
CHANGED
@@ -13,6 +13,17 @@ from dissect.target.helpers import fsutil
|
|
13
13
|
log = logging.getLogger(__name__)
|
14
14
|
|
15
15
|
|
16
|
+
def findall(buf: bytes, needle: bytes) -> Iterator[int]:
|
17
|
+
offset = 0
|
18
|
+
while True:
|
19
|
+
offset = buf.find(needle, offset)
|
20
|
+
if offset == -1:
|
21
|
+
break
|
22
|
+
|
23
|
+
yield offset
|
24
|
+
offset += 1
|
25
|
+
|
26
|
+
|
16
27
|
class StrEnum(str, Enum):
|
17
28
|
"""Sortable and serializible string-based enum"""
|
18
29
|
|
@@ -5,6 +5,7 @@ from flow.record.fieldtypes import digest
|
|
5
5
|
|
6
6
|
from dissect.target.exceptions import UnsupportedPluginError
|
7
7
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
8
|
+
from dissect.target.helpers.utils import findall
|
8
9
|
from dissect.target.plugin import Plugin, export
|
9
10
|
|
10
11
|
try:
|
@@ -36,17 +37,6 @@ CatrootRecord = TargetRecordDescriptor(
|
|
36
37
|
)
|
37
38
|
|
38
39
|
|
39
|
-
def findall(buf: bytes, needle: bytes) -> Iterator[int]:
|
40
|
-
offset = 0
|
41
|
-
while True:
|
42
|
-
offset = buf.find(needle, offset)
|
43
|
-
if offset == -1:
|
44
|
-
break
|
45
|
-
|
46
|
-
yield offset
|
47
|
-
offset += 1
|
48
|
-
|
49
|
-
|
50
40
|
def _get_package_name(sequence: Sequence) -> str:
|
51
41
|
"""Parse sequences within a sequence and return the 'PackageName' value if it exists."""
|
52
42
|
for value in sequence.native.values():
|
@@ -0,0 +1,292 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import io
|
4
|
+
import logging
|
5
|
+
from struct import error as StructError
|
6
|
+
from typing import BinaryIO, Iterator
|
7
|
+
|
8
|
+
from dissect.cstruct import cstruct
|
9
|
+
from dissect.ole import OLE
|
10
|
+
from dissect.ole.exceptions import Error as OleError
|
11
|
+
from dissect.shellitem.lnk import Lnk
|
12
|
+
|
13
|
+
from dissect.target import Target
|
14
|
+
from dissect.target.exceptions import UnsupportedPluginError
|
15
|
+
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
16
|
+
from dissect.target.helpers.record import create_extended_descriptor
|
17
|
+
from dissect.target.helpers.shell_application_ids import APPLICATION_IDENTIFIERS
|
18
|
+
from dissect.target.helpers.utils import findall
|
19
|
+
from dissect.target.plugin import Plugin, export
|
20
|
+
from dissect.target.plugins.os.windows.lnk import LnkRecord, parse_lnk_file
|
21
|
+
|
22
|
+
log = logging.getLogger(__name__)
|
23
|
+
|
24
|
+
LNK_GUID = b"\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46"
|
25
|
+
|
26
|
+
JumpListRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
27
|
+
"windows/jumplist",
|
28
|
+
[
|
29
|
+
("string", "type"),
|
30
|
+
("string", "application_id"),
|
31
|
+
("string", "application_name"),
|
32
|
+
*LnkRecord.target_fields,
|
33
|
+
],
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
custom_destination_def = """
|
38
|
+
struct header {
|
39
|
+
int version;
|
40
|
+
int unknown1;
|
41
|
+
int unknown2;
|
42
|
+
int value_type;
|
43
|
+
}
|
44
|
+
|
45
|
+
struct header_end {
|
46
|
+
int number_of_entries;
|
47
|
+
}
|
48
|
+
|
49
|
+
struct header_end_0 {
|
50
|
+
uint16 name_length;
|
51
|
+
wchar name[name_length];
|
52
|
+
int number_of_entries;
|
53
|
+
}
|
54
|
+
|
55
|
+
struct footer {
|
56
|
+
char magic[4];
|
57
|
+
}
|
58
|
+
"""
|
59
|
+
|
60
|
+
c_custom_destination = cstruct()
|
61
|
+
c_custom_destination.load(custom_destination_def)
|
62
|
+
|
63
|
+
|
64
|
+
class JumpListFile:
|
65
|
+
def __init__(self, fh: BinaryIO, file_name: str):
|
66
|
+
self.fh = fh
|
67
|
+
self.file_name = file_name
|
68
|
+
|
69
|
+
self.application_id, self.application_type = file_name.split(".")
|
70
|
+
self.application_type = self.application_type.split("-")[0]
|
71
|
+
self.application_name = APPLICATION_IDENTIFIERS.get(self.application_id)
|
72
|
+
|
73
|
+
def __iter__(self) -> Iterator[Lnk]:
|
74
|
+
raise NotImplementedError
|
75
|
+
|
76
|
+
@property
|
77
|
+
def name(self) -> str:
|
78
|
+
"""Return the name of the application."""
|
79
|
+
return self.application_name
|
80
|
+
|
81
|
+
@property
|
82
|
+
def id(self) -> str:
|
83
|
+
"""Return the application identifier."""
|
84
|
+
return self.application_id
|
85
|
+
|
86
|
+
@property
|
87
|
+
def type(self) -> str:
|
88
|
+
"""Return the type of the Jump List file."""
|
89
|
+
return self.application_type
|
90
|
+
|
91
|
+
|
92
|
+
class AutomaticDestinationFile(JumpListFile):
|
93
|
+
"""Parse Jump List AutomaticDestination file."""
|
94
|
+
|
95
|
+
def __init__(self, fh: BinaryIO, file_name: str):
|
96
|
+
super().__init__(fh, file_name)
|
97
|
+
self.ole = OLE(self.fh)
|
98
|
+
|
99
|
+
def __iter__(self) -> Iterator[Lnk]:
|
100
|
+
for dir_name in self.ole.root.listdir():
|
101
|
+
if dir_name == "DestList":
|
102
|
+
continue
|
103
|
+
|
104
|
+
dir = self.ole.get(dir_name)
|
105
|
+
|
106
|
+
for item in dir.open():
|
107
|
+
try:
|
108
|
+
yield Lnk(io.BytesIO(item))
|
109
|
+
except StructError:
|
110
|
+
continue
|
111
|
+
except Exception as e:
|
112
|
+
log.warning("Failed to parse LNK file from directory %s", dir_name)
|
113
|
+
log.debug("", exc_info=e)
|
114
|
+
continue
|
115
|
+
|
116
|
+
|
117
|
+
class CustomDestinationFile(JumpListFile):
|
118
|
+
"""Parse Jump List CustomDestination file."""
|
119
|
+
|
120
|
+
MAGIC_FOOTER = 0xBABFFBAB
|
121
|
+
VERSIONS = [2]
|
122
|
+
|
123
|
+
def __init__(self, fh: BinaryIO, file_name: str):
|
124
|
+
super().__init__(fh, file_name)
|
125
|
+
|
126
|
+
self.fh.seek(-4, io.SEEK_END)
|
127
|
+
self.footer = c_custom_destination.footer(self.fh.read(4))
|
128
|
+
self.magic = int.from_bytes(self.footer.magic, "little")
|
129
|
+
|
130
|
+
self.fh.seek(0, io.SEEK_SET)
|
131
|
+
self.header = c_custom_destination.header(self.fh)
|
132
|
+
self.version = self.header.version
|
133
|
+
|
134
|
+
if self.header.value_type == 0:
|
135
|
+
self.header_end = c_custom_destination.header_end_0(self.fh)
|
136
|
+
elif self.header.value_type in [1, 2]:
|
137
|
+
self.header_end = c_custom_destination.header_end(self.fh)
|
138
|
+
else:
|
139
|
+
raise NotImplementedError(
|
140
|
+
f"The value_type ({self.header.value_type}) of the CustomDestination file is not implemented"
|
141
|
+
)
|
142
|
+
|
143
|
+
if self.version not in self.VERSIONS:
|
144
|
+
raise NotImplementedError(f"The CustomDestination file has an unsupported version: {self.version}")
|
145
|
+
|
146
|
+
if not self.MAGIC_FOOTER == self.magic:
|
147
|
+
raise ValueError(f"The CustomDestination file has an invalid magic footer: {self.magic}")
|
148
|
+
|
149
|
+
def __iter__(self) -> Iterator[Lnk]:
|
150
|
+
# Searches for all LNK GUID's because the number of entries in the header is not always correct.
|
151
|
+
buf = self.fh.read()
|
152
|
+
|
153
|
+
for offset in findall(buf, LNK_GUID):
|
154
|
+
try:
|
155
|
+
lnk = Lnk(io.BytesIO(buf[offset + len(LNK_GUID) :]))
|
156
|
+
yield lnk
|
157
|
+
except EOFError:
|
158
|
+
break
|
159
|
+
except Exception as e:
|
160
|
+
log.warning("Failed to parse LNK file from a CustomDestination file")
|
161
|
+
log.debug("", exc_info=e)
|
162
|
+
continue
|
163
|
+
|
164
|
+
|
165
|
+
class JumpListPlugin(Plugin):
|
166
|
+
"""Jump List is a Windows feature introduced in Windows 7.
|
167
|
+
|
168
|
+
It stores information about recently accessed applications and files.
|
169
|
+
|
170
|
+
References:
|
171
|
+
- https://forensics.wiki/jump_lists
|
172
|
+
- https://github.com/libyal/dtformats/blob/main/documentation/Jump%20lists%20format.asciidoc
|
173
|
+
"""
|
174
|
+
|
175
|
+
__namespace__ = "jumplist"
|
176
|
+
|
177
|
+
def __init__(self, target: Target):
|
178
|
+
super().__init__(target)
|
179
|
+
self.automatic_destinations = []
|
180
|
+
self.custom_destinations = []
|
181
|
+
|
182
|
+
for user_details in self.target.user_details.all_with_home():
|
183
|
+
for destination in user_details.home_path.glob(
|
184
|
+
"AppData/Roaming/Microsoft/Windows/Recent/CustomDestinations/*.customDestinations-ms"
|
185
|
+
):
|
186
|
+
self.custom_destinations.append([destination, user_details.user])
|
187
|
+
|
188
|
+
for user_details in self.target.user_details.all_with_home():
|
189
|
+
for destination in user_details.home_path.glob(
|
190
|
+
"AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/*.automaticDestinations-ms"
|
191
|
+
):
|
192
|
+
self.automatic_destinations.append([destination, user_details.user])
|
193
|
+
|
194
|
+
def check_compatible(self) -> None:
|
195
|
+
if not any([self.automatic_destinations, self.custom_destinations]):
|
196
|
+
raise UnsupportedPluginError("No Jump List files found")
|
197
|
+
|
198
|
+
@export(record=JumpListRecord)
|
199
|
+
def custom_destination(self) -> Iterator[JumpListRecord]:
|
200
|
+
"""Return the content of CustomDestination Windows Jump Lists.
|
201
|
+
|
202
|
+
These are created when a user pins an application or a file in a Jump List.
|
203
|
+
|
204
|
+
Yields JumpListRecord with fields:
|
205
|
+
|
206
|
+
.. code-block:: text
|
207
|
+
|
208
|
+
type (string): Type of Jump List.
|
209
|
+
application_id (string): ID of the application.
|
210
|
+
application_name (string): Name of the application.
|
211
|
+
lnk_path (path): Path of the link (.lnk) file.
|
212
|
+
lnk_name (string): Name of the link (.lnk) file.
|
213
|
+
lnk_mtime (datetime): Modification time of the link (.lnk) file.
|
214
|
+
lnk_atime (datetime): Access time of the link (.lnk) file.
|
215
|
+
lnk_ctime (datetime): Creation time of the link (.lnk) file.
|
216
|
+
lnk_relativepath (path): Relative path of target file to the link (.lnk) file.
|
217
|
+
lnk_workdir (path): Path of the working directory the link (.lnk) file will execute from.
|
218
|
+
lnk_iconlocation (path): Path of the display icon used for the link (.lnk) file.
|
219
|
+
lnk_arguments (string): Command-line arguments passed to the target (linked) file.
|
220
|
+
local_base_path (string): Absolute path of the target (linked) file.
|
221
|
+
common_path_suffix (string): Suffix of the local_base_path.
|
222
|
+
lnk_full_path (string): Full path of the linked file. Made from local_base_path and common_path_suffix.
|
223
|
+
lnk_net_name (string): Specifies a server share path; for example, "\\\\server\\share".
|
224
|
+
lnk_device_name (string): Specifies a device; for example, the drive letter "D:"
|
225
|
+
machine_id (string): The NetBIOS name of the machine where the linked file was last known to reside.
|
226
|
+
target_mtime (datetime): Modification time of the target (linked) file.
|
227
|
+
target_atime (datetime): Access time of the target (linked) file.
|
228
|
+
target_ctime (datetime): Creation time of the target (linked) file.
|
229
|
+
"""
|
230
|
+
yield from self._generate_records(self.custom_destinations, CustomDestinationFile)
|
231
|
+
|
232
|
+
@export(record=JumpListRecord)
|
233
|
+
def automatic_destination(self) -> Iterator[JumpListRecord]:
|
234
|
+
"""Return the content of AutomaticDestination Windows Jump Lists.
|
235
|
+
|
236
|
+
These are created automatically when a user opens an application or file.
|
237
|
+
|
238
|
+
Yields JumpListRecord with fields:
|
239
|
+
|
240
|
+
.. code-block:: text
|
241
|
+
|
242
|
+
type (string): Type of Jump List.
|
243
|
+
application_id (string): ID of the application.
|
244
|
+
application_name (string): Name of the application.
|
245
|
+
lnk_path (path): Path of the link (.lnk) file.
|
246
|
+
lnk_name (string): Name of the link (.lnk) file.
|
247
|
+
lnk_mtime (datetime): Modification time of the link (.lnk) file.
|
248
|
+
lnk_atime (datetime): Access time of the link (.lnk) file.
|
249
|
+
lnk_ctime (datetime): Creation time of the link (.lnk) file.
|
250
|
+
lnk_relativepath (path): Relative path of target file to the link (.lnk) file.
|
251
|
+
lnk_workdir (path): Path of the working directory the link (.lnk) file will execute from.
|
252
|
+
lnk_iconlocation (path): Path of the display icon used for the link (.lnk) file.
|
253
|
+
lnk_arguments (string): Command-line arguments passed to the target (linked) file.
|
254
|
+
local_base_path (string): Absolute path of the target (linked) file.
|
255
|
+
common_path_suffix (string): Suffix of the local_base_path.
|
256
|
+
lnk_full_path (string): Full path of the linked file. Made from local_base_path and common_path_suffix.
|
257
|
+
lnk_net_name (string): Specifies a server share path; for example, "\\\\server\\share".
|
258
|
+
lnk_device_name (string): Specifies a device; for example, the drive letter "D:"
|
259
|
+
machine_id (string): The NetBIOS name of the machine where the linked file was last known to reside.
|
260
|
+
target_mtime (datetime): Modification time of the target (linked) file.
|
261
|
+
target_atime (datetime): Access time of the target (linked) file.
|
262
|
+
target_ctime (datetime): Creation time of the target (linked) file.
|
263
|
+
"""
|
264
|
+
yield from self._generate_records(self.automatic_destinations, AutomaticDestinationFile)
|
265
|
+
|
266
|
+
def _generate_records(
|
267
|
+
self,
|
268
|
+
destinations: list,
|
269
|
+
destination_file: AutomaticDestinationFile | CustomDestinationFile,
|
270
|
+
) -> Iterator[JumpListRecord]:
|
271
|
+
for destination, user in destinations:
|
272
|
+
fh = destination.open("rb")
|
273
|
+
|
274
|
+
try:
|
275
|
+
jumplist = destination_file(fh, destination.name)
|
276
|
+
except OleError:
|
277
|
+
continue
|
278
|
+
except Exception as e:
|
279
|
+
self.target.log.warning("Failed to parse Jump List file: %s", destination)
|
280
|
+
self.target.log.debug("", exc_info=e)
|
281
|
+
continue
|
282
|
+
|
283
|
+
for lnk in jumplist:
|
284
|
+
if lnk := parse_lnk_file(self.target, lnk, destination):
|
285
|
+
yield JumpListRecord(
|
286
|
+
type=jumplist.type,
|
287
|
+
application_name=jumplist.name,
|
288
|
+
application_id=jumplist.id,
|
289
|
+
**lnk._asdict(),
|
290
|
+
_user=user,
|
291
|
+
_target=self.target,
|
292
|
+
)
|
@@ -34,6 +34,89 @@ LnkRecord = TargetRecordDescriptor(
|
|
34
34
|
)
|
35
35
|
|
36
36
|
|
37
|
+
def parse_lnk_file(target: Target, lnk_file: Lnk, lnk_path: TargetPath) -> Iterator[LnkRecord]:
|
38
|
+
# we need to get the active codepage from the system to properly decode some values
|
39
|
+
codepage = target.codepage or "ascii"
|
40
|
+
|
41
|
+
lnk_net_name = lnk_device_name = None
|
42
|
+
|
43
|
+
if lnk_file.link_header:
|
44
|
+
lnk_name = lnk_file.stringdata.name_string.string if lnk_file.flag("has_name") else None
|
45
|
+
|
46
|
+
lnk_mtime = ts.from_unix(lnk_path.stat().st_mtime)
|
47
|
+
lnk_atime = ts.from_unix(lnk_path.stat().st_atime)
|
48
|
+
lnk_ctime = ts.from_unix(lnk_path.stat().st_ctime)
|
49
|
+
|
50
|
+
lnk_relativepath = (
|
51
|
+
target.fs.path(lnk_file.stringdata.relative_path.string) if lnk_file.flag("has_relative_path") else None
|
52
|
+
)
|
53
|
+
lnk_workdir = (
|
54
|
+
target.fs.path(lnk_file.stringdata.working_dir.string) if lnk_file.flag("has_working_dir") else None
|
55
|
+
)
|
56
|
+
lnk_iconlocation = (
|
57
|
+
target.fs.path(lnk_file.stringdata.icon_location.string) if lnk_file.flag("has_icon_location") else None
|
58
|
+
)
|
59
|
+
lnk_arguments = lnk_file.stringdata.command_line_arguments.string if lnk_file.flag("has_arguments") else None
|
60
|
+
local_base_path = (
|
61
|
+
lnk_file.linkinfo.local_base_path.decode(codepage)
|
62
|
+
if lnk_file.flag("has_link_info") and lnk_file.linkinfo.flag("volumeid_and_local_basepath")
|
63
|
+
else None
|
64
|
+
)
|
65
|
+
common_path_suffix = (
|
66
|
+
lnk_file.linkinfo.common_path_suffix.decode(codepage) if lnk_file.flag("has_link_info") else None
|
67
|
+
)
|
68
|
+
|
69
|
+
if local_base_path and common_path_suffix:
|
70
|
+
lnk_full_path = target.fs.path(local_base_path + common_path_suffix)
|
71
|
+
elif local_base_path and not common_path_suffix:
|
72
|
+
lnk_full_path = target.fs.path(local_base_path)
|
73
|
+
else:
|
74
|
+
lnk_full_path = None
|
75
|
+
|
76
|
+
if lnk_file.flag("has_link_info"):
|
77
|
+
if lnk_file.linkinfo.flag("common_network_relative_link_and_pathsuffix"):
|
78
|
+
lnk_net_name = (
|
79
|
+
lnk_file.linkinfo.common_network_relative_link.net_name.decode()
|
80
|
+
if lnk_file.linkinfo.common_network_relative_link.net_name
|
81
|
+
else None
|
82
|
+
)
|
83
|
+
lnk_device_name = (
|
84
|
+
lnk_file.linkinfo.common_network_relative_link.device_name.decode()
|
85
|
+
if lnk_file.linkinfo.common_network_relative_link.device_name
|
86
|
+
else None
|
87
|
+
)
|
88
|
+
try:
|
89
|
+
machine_id = lnk_file.extradata.TRACKER_PROPS.machine_id.decode(codepage).strip("\x00")
|
90
|
+
except AttributeError:
|
91
|
+
machine_id = None
|
92
|
+
|
93
|
+
target_mtime = ts.wintimestamp(lnk_file.link_header.write_time)
|
94
|
+
target_atime = ts.wintimestamp(lnk_file.link_header.access_time)
|
95
|
+
target_ctime = ts.wintimestamp(lnk_file.link_header.creation_time)
|
96
|
+
|
97
|
+
return LnkRecord(
|
98
|
+
lnk_path=lnk_path,
|
99
|
+
lnk_name=lnk_name,
|
100
|
+
lnk_mtime=lnk_mtime,
|
101
|
+
lnk_atime=lnk_atime,
|
102
|
+
lnk_ctime=lnk_ctime,
|
103
|
+
lnk_relativepath=lnk_relativepath,
|
104
|
+
lnk_workdir=lnk_workdir,
|
105
|
+
lnk_iconlocation=lnk_iconlocation,
|
106
|
+
lnk_arguments=lnk_arguments,
|
107
|
+
local_base_path=local_base_path,
|
108
|
+
common_path_suffix=common_path_suffix,
|
109
|
+
lnk_full_path=lnk_full_path,
|
110
|
+
lnk_net_name=lnk_net_name,
|
111
|
+
lnk_device_name=lnk_device_name,
|
112
|
+
machine_id=machine_id,
|
113
|
+
target_mtime=target_mtime,
|
114
|
+
target_atime=target_atime,
|
115
|
+
target_ctime=target_ctime,
|
116
|
+
_target=target,
|
117
|
+
)
|
118
|
+
|
119
|
+
|
37
120
|
class LnkPlugin(Plugin):
|
38
121
|
def __init__(self, target: Target) -> None:
|
39
122
|
super().__init__(target)
|
@@ -74,97 +157,9 @@ class LnkPlugin(Plugin):
|
|
74
157
|
target_ctime (datetime): Creation time of the target (linked) file.
|
75
158
|
"""
|
76
159
|
|
77
|
-
# we need to get the active codepage from the system to properly decode some values
|
78
|
-
codepage = self.target.codepage or "ascii"
|
79
|
-
|
80
160
|
for entry in self.lnk_entries(path):
|
81
161
|
lnk_file = Lnk(entry.open())
|
82
|
-
|
83
|
-
|
84
|
-
if lnk_file.link_header:
|
85
|
-
lnk_path = entry
|
86
|
-
lnk_name = lnk_file.stringdata.name_string.string if lnk_file.flag("has_name") else None
|
87
|
-
|
88
|
-
lnk_mtime = ts.from_unix(entry.stat().st_mtime)
|
89
|
-
lnk_atime = ts.from_unix(entry.stat().st_atime)
|
90
|
-
lnk_ctime = ts.from_unix(entry.stat().st_ctime)
|
91
|
-
|
92
|
-
lnk_relativepath = (
|
93
|
-
self.target.fs.path(lnk_file.stringdata.relative_path.string)
|
94
|
-
if lnk_file.flag("has_relative_path")
|
95
|
-
else None
|
96
|
-
)
|
97
|
-
lnk_workdir = (
|
98
|
-
self.target.fs.path(lnk_file.stringdata.working_dir.string)
|
99
|
-
if lnk_file.flag("has_working_dir")
|
100
|
-
else None
|
101
|
-
)
|
102
|
-
lnk_iconlocation = (
|
103
|
-
self.target.fs.path(lnk_file.stringdata.icon_location.string)
|
104
|
-
if lnk_file.flag("has_icon_location")
|
105
|
-
else None
|
106
|
-
)
|
107
|
-
lnk_arguments = (
|
108
|
-
lnk_file.stringdata.command_line_arguments.string if lnk_file.flag("has_arguments") else None
|
109
|
-
)
|
110
|
-
local_base_path = (
|
111
|
-
lnk_file.linkinfo.local_base_path.decode(codepage)
|
112
|
-
if lnk_file.flag("has_link_info") and lnk_file.linkinfo.flag("volumeid_and_local_basepath")
|
113
|
-
else None
|
114
|
-
)
|
115
|
-
common_path_suffix = (
|
116
|
-
lnk_file.linkinfo.common_path_suffix.decode(codepage) if lnk_file.flag("has_link_info") else None
|
117
|
-
)
|
118
|
-
|
119
|
-
if local_base_path and common_path_suffix:
|
120
|
-
lnk_full_path = self.target.fs.path(local_base_path + common_path_suffix)
|
121
|
-
elif local_base_path and not common_path_suffix:
|
122
|
-
lnk_full_path = self.target.fs.path(local_base_path)
|
123
|
-
else:
|
124
|
-
lnk_full_path = None
|
125
|
-
|
126
|
-
if lnk_file.flag("has_link_info"):
|
127
|
-
if lnk_file.linkinfo.flag("common_network_relative_link_and_pathsuffix"):
|
128
|
-
lnk_net_name = (
|
129
|
-
lnk_file.linkinfo.common_network_relative_link.net_name.decode()
|
130
|
-
if lnk_file.linkinfo.common_network_relative_link.net_name
|
131
|
-
else None
|
132
|
-
)
|
133
|
-
lnk_device_name = (
|
134
|
-
lnk_file.linkinfo.common_network_relative_link.device_name.decode()
|
135
|
-
if lnk_file.linkinfo.common_network_relative_link.device_name
|
136
|
-
else None
|
137
|
-
)
|
138
|
-
try:
|
139
|
-
machine_id = lnk_file.extradata.TRACKER_PROPS.machine_id.decode(codepage).strip("\x00")
|
140
|
-
except AttributeError:
|
141
|
-
machine_id = None
|
142
|
-
|
143
|
-
target_mtime = ts.wintimestamp(lnk_file.link_header.write_time)
|
144
|
-
target_atime = ts.wintimestamp(lnk_file.link_header.access_time)
|
145
|
-
target_ctime = ts.wintimestamp(lnk_file.link_header.creation_time)
|
146
|
-
|
147
|
-
yield LnkRecord(
|
148
|
-
lnk_path=lnk_path,
|
149
|
-
lnk_name=lnk_name,
|
150
|
-
lnk_mtime=lnk_mtime,
|
151
|
-
lnk_atime=lnk_atime,
|
152
|
-
lnk_ctime=lnk_ctime,
|
153
|
-
lnk_relativepath=lnk_relativepath,
|
154
|
-
lnk_workdir=lnk_workdir,
|
155
|
-
lnk_iconlocation=lnk_iconlocation,
|
156
|
-
lnk_arguments=lnk_arguments,
|
157
|
-
local_base_path=local_base_path,
|
158
|
-
common_path_suffix=common_path_suffix,
|
159
|
-
lnk_full_path=lnk_full_path,
|
160
|
-
lnk_net_name=lnk_net_name,
|
161
|
-
lnk_device_name=lnk_device_name,
|
162
|
-
machine_id=machine_id,
|
163
|
-
target_mtime=target_mtime,
|
164
|
-
target_atime=target_atime,
|
165
|
-
target_ctime=target_ctime,
|
166
|
-
_target=self.target,
|
167
|
-
)
|
162
|
+
yield parse_lnk_file(self.target, lnk_file, entry)
|
168
163
|
|
169
164
|
def lnk_entries(self, path: Optional[str] = None) -> Iterator[TargetPath]:
|
170
165
|
if path:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.19.
|
3
|
+
Version: 3.19.dev42
|
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
|
@@ -72,6 +72,7 @@ Requires-Dist: dissect.extfs <4,>=3 ; extra == 'full'
|
|
72
72
|
Requires-Dist: dissect.fat <4,>=3 ; extra == 'full'
|
73
73
|
Requires-Dist: dissect.ffs <4,>=3 ; extra == 'full'
|
74
74
|
Requires-Dist: dissect.jffs <2,>=1 ; extra == 'full'
|
75
|
+
Requires-Dist: dissect.ole <4,>=3 ; extra == 'full'
|
75
76
|
Requires-Dist: dissect.shellitem <4,>=3 ; extra == 'full'
|
76
77
|
Requires-Dist: dissect.squashfs <2,>=1 ; extra == 'full'
|
77
78
|
Requires-Dist: dissect.sql <4,>=3 ; extra == 'full'
|
@@ -64,9 +64,10 @@ dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdO
|
|
64
64
|
dissect/target/helpers/record.py,sha256=zwqEnFSgxgX6JdhhF4zycMMZK09crCTWWEFzRxZSuC8,5658
|
65
65
|
dissect/target/helpers/record_modifier.py,sha256=3I_rC5jqvl0TsW3V8OQ6Dltz_D8J4PU1uhhzbJGKm9c,3245
|
66
66
|
dissect/target/helpers/regutil.py,sha256=kX-sSZbW8Qkg29Dn_9zYbaQrwLumrr4Y8zJ1EhHXIAM,27337
|
67
|
+
dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
|
67
68
|
dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
|
68
69
|
dissect/target/helpers/targetd.py,sha256=ELhUulzQ4OgXgHsWhsLgM14vut8Wm6btr7qTynlwKaE,1812
|
69
|
-
dissect/target/helpers/utils.py,sha256=
|
70
|
+
dissect/target/helpers/utils.py,sha256=K3xVq9D0FwIhTBAuiWN8ph7Pq2GABgG3hOz-3AmKuEA,4244
|
70
71
|
dissect/target/helpers/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
72
|
dissect/target/helpers/compat/path_310.py,sha256=PsLDIodlp3Hv5u-w7GDl6_LnTtchBYcRjz2MicX1egg,16982
|
72
73
|
dissect/target/helpers/compat/path_311.py,sha256=2aydxCMWu1pN8PTBCo8HUbHRMC1xO-hj013j4QxaogE,18182
|
@@ -262,7 +263,7 @@ dissect/target/plugins/os/windows/_os.py,sha256=uBa0dVkFxDsxHAU3T23UEIOCgAx5R6cI
|
|
262
263
|
dissect/target/plugins/os/windows/activitiescache.py,sha256=Q2aILnhJ2rp2AwEbWwyBuSLjMbGqaYJTsavSbfkcFKE,6741
|
263
264
|
dissect/target/plugins/os/windows/adpolicy.py,sha256=fULRFO_I_QxAn6G9SCwlLL-TLVliS13JEGnGotf7lSA,6983
|
264
265
|
dissect/target/plugins/os/windows/amcache.py,sha256=ZZNOs3bILTf0AGkDkhoatndl0j39DXkstN7oOyxJECU,27188
|
265
|
-
dissect/target/plugins/os/windows/catroot.py,sha256=
|
266
|
+
dissect/target/plugins/os/windows/catroot.py,sha256=QVwMF5nuMzCkWnoOMs5BkwYoKN61HKmlxo8mKMoD3w8,10937
|
266
267
|
dissect/target/plugins/os/windows/cim.py,sha256=jsrpu6TZpBUh7VWI9AV2Ib5bebTwsvqOwRfa5gjJd7c,3056
|
267
268
|
dissect/target/plugins/os/windows/clfs.py,sha256=begVsZ-CY97Ksh6S1g03LjyBgu8ERY2hfNDWYPj0GXI,4872
|
268
269
|
dissect/target/plugins/os/windows/credhist.py,sha256=YSjuyd53Augdy_lKKzZHtx5Ozt0HzF6LDYIOb-8P1Pw,7058
|
@@ -270,7 +271,8 @@ dissect/target/plugins/os/windows/datetime.py,sha256=YKHUZU6lkKJocq15y0yCwvIIOb1
|
|
270
271
|
dissect/target/plugins/os/windows/defender.py,sha256=zh3brEvJmknD5ef0PGuLZ1G95Fgdh-dlgi-ZEbADKXo,32716
|
271
272
|
dissect/target/plugins/os/windows/env.py,sha256=-u9F9xWy6PUbQmu5Tv_MDoVmy6YB-7CbHokIK_T3S44,13891
|
272
273
|
dissect/target/plugins/os/windows/generic.py,sha256=BSvDPfB9faU0uquMj0guw5tnR_97Nn0XAEE4k05BFSQ,22273
|
273
|
-
dissect/target/plugins/os/windows/
|
274
|
+
dissect/target/plugins/os/windows/jumplist.py,sha256=3gZk6O1B3lKK2Jxe0B-HapOCEehk94CYNvCVDpQC9nQ,11773
|
275
|
+
dissect/target/plugins/os/windows/lnk.py,sha256=toEZV00CESLUsF7UmN65-ivWk0Ijg-ZPST0qyD-antY,7860
|
274
276
|
dissect/target/plugins/os/windows/locale.py,sha256=yXVdclpUqss9h8Nq7N4kg3OHwWGDfjdfiLiUZR3wqv8,2324
|
275
277
|
dissect/target/plugins/os/windows/notifications.py,sha256=T1CIvQgpW__qDR0Rq5zpeWmRWwjNDpvdMnvJJ_6tZXs,17378
|
276
278
|
dissect/target/plugins/os/windows/prefetch.py,sha256=v4OgSKMwcihz0SOuA0o0Ec8wsAKuiuEmJolqZmHFgJA,10491
|
@@ -350,10 +352,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
350
352
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
351
353
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
352
354
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
353
|
-
dissect.target-3.19.
|
354
|
-
dissect.target-3.19.
|
355
|
-
dissect.target-3.19.
|
356
|
-
dissect.target-3.19.
|
357
|
-
dissect.target-3.19.
|
358
|
-
dissect.target-3.19.
|
359
|
-
dissect.target-3.19.
|
355
|
+
dissect.target-3.19.dev42.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
356
|
+
dissect.target-3.19.dev42.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
357
|
+
dissect.target-3.19.dev42.dist-info/METADATA,sha256=XF7DS_1vdvBgQLUXWDQ8JfwXd5FAffF06CNgi2LA0DM,12897
|
358
|
+
dissect.target-3.19.dev42.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
359
|
+
dissect.target-3.19.dev42.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
360
|
+
dissect.target-3.19.dev42.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
361
|
+
dissect.target-3.19.dev42.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.19.dev41.dist-info → dissect.target-3.19.dev42.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|