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