dissect.target 3.19.dev37__py3-none-any.whl → 3.19.dev39__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/filesystems/zip.py +81 -46
- dissect/target/helpers/record.py +37 -0
- dissect/target/plugins/general/network.py +82 -0
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/METADATA +1 -1
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/RECORD +10 -9
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/LICENSE +0 -0
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/WHEEL +0 -0
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/top_level.txt +0 -0
@@ -4,16 +4,21 @@ import logging
|
|
4
4
|
import stat
|
5
5
|
import zipfile
|
6
6
|
from datetime import datetime, timezone
|
7
|
-
from typing import BinaryIO,
|
7
|
+
from typing import BinaryIO, Iterator
|
8
8
|
|
9
9
|
from dissect.util.stream import BufferedStream
|
10
10
|
|
11
|
-
from dissect.target.exceptions import
|
11
|
+
from dissect.target.exceptions import (
|
12
|
+
FileNotFoundError,
|
13
|
+
FilesystemError,
|
14
|
+
IsADirectoryError,
|
15
|
+
NotADirectoryError,
|
16
|
+
NotASymlinkError,
|
17
|
+
)
|
12
18
|
from dissect.target.filesystem import (
|
13
19
|
Filesystem,
|
14
20
|
FilesystemEntry,
|
15
21
|
VirtualDirectory,
|
16
|
-
VirtualFile,
|
17
22
|
VirtualFilesystem,
|
18
23
|
)
|
19
24
|
from dissect.target.helpers import fsutil
|
@@ -33,7 +38,7 @@ class ZipFilesystem(Filesystem):
|
|
33
38
|
def __init__(
|
34
39
|
self,
|
35
40
|
fh: BinaryIO,
|
36
|
-
base:
|
41
|
+
base: str | None = None,
|
37
42
|
*args,
|
38
43
|
**kwargs,
|
39
44
|
):
|
@@ -52,12 +57,7 @@ class ZipFilesystem(Filesystem):
|
|
52
57
|
continue
|
53
58
|
|
54
59
|
rel_name = fsutil.normpath(mname[len(self.base) :], alt_separator=self.alt_separator)
|
55
|
-
|
56
|
-
# NOTE: Normally we would check here if the member is a symlink or not
|
57
|
-
|
58
|
-
entry_cls = ZipFilesystemDirectoryEntry if member.is_dir() else ZipFilesystemEntry
|
59
|
-
file_entry = entry_cls(self, rel_name, member)
|
60
|
-
self._fs.map_file_entry(rel_name, file_entry)
|
60
|
+
self._fs.map_file_entry(rel_name, ZipFilesystemEntry(self, rel_name, member))
|
61
61
|
|
62
62
|
@staticmethod
|
63
63
|
def _detect(fh: BinaryIO) -> bool:
|
@@ -69,60 +69,95 @@ class ZipFilesystem(Filesystem):
|
|
69
69
|
return self._fs.get(path, relentry=relentry)
|
70
70
|
|
71
71
|
|
72
|
-
|
72
|
+
# Note: We subclass from VirtualDirectory because VirtualFilesystem is currently only compatible with VirtualDirectory
|
73
|
+
# Subclass from VirtualDirectory so we get that compatibility for free, and override the rest to do our own thing
|
74
|
+
class ZipFilesystemEntry(VirtualDirectory):
|
75
|
+
fs: ZipFilesystem
|
76
|
+
entry: zipfile.ZipInfo
|
77
|
+
|
78
|
+
def __init__(self, fs: ZipFilesystem, path: str, entry: zipfile.ZipInfo):
|
79
|
+
super().__init__(fs, path)
|
80
|
+
self.entry = entry
|
81
|
+
|
73
82
|
def open(self) -> BinaryIO:
|
74
|
-
|
83
|
+
if self.is_dir():
|
84
|
+
raise IsADirectoryError(self.path)
|
85
|
+
|
86
|
+
if self.is_symlink():
|
87
|
+
return self._resolve().open()
|
88
|
+
|
75
89
|
try:
|
76
90
|
return BufferedStream(self.fs.zip.open(self.entry), size=self.entry.file_size)
|
77
91
|
except Exception:
|
78
|
-
raise FileNotFoundError()
|
92
|
+
raise FileNotFoundError(self.path)
|
79
93
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
94
|
+
def iterdir(self) -> Iterator[str]:
|
95
|
+
if not self.is_dir():
|
96
|
+
raise NotADirectoryError(self.path)
|
83
97
|
|
84
|
-
|
85
|
-
|
86
|
-
|
98
|
+
entry = self._resolve()
|
99
|
+
if isinstance(entry, ZipFilesystemEntry):
|
100
|
+
yield from super(ZipFilesystemEntry, entry).iterdir()
|
101
|
+
else:
|
102
|
+
yield from entry.iterdir()
|
87
103
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
104
|
+
def scandir(self) -> Iterator[FilesystemEntry]:
|
105
|
+
if not self.is_dir():
|
106
|
+
raise NotADirectoryError(self.path)
|
91
107
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
stat.S_IFREG | 0o777,
|
98
|
-
self.entry.header_offset,
|
99
|
-
id(self.fs),
|
100
|
-
1,
|
101
|
-
0,
|
102
|
-
0,
|
103
|
-
self.entry.file_size,
|
104
|
-
0,
|
105
|
-
datetime(*self.entry.date_time, tzinfo=timezone.utc).timestamp(),
|
106
|
-
0,
|
107
|
-
]
|
108
|
-
)
|
108
|
+
entry = self._resolve()
|
109
|
+
if isinstance(entry, ZipFilesystemEntry):
|
110
|
+
yield from super(ZipFilesystemEntry, entry).scandir()
|
111
|
+
else:
|
112
|
+
yield from entry.scandir()
|
109
113
|
|
114
|
+
def is_dir(self, follow_symlinks: bool = True) -> bool:
|
115
|
+
try:
|
116
|
+
entry = self._resolve(follow_symlinks=follow_symlinks)
|
117
|
+
except FilesystemError:
|
118
|
+
return False
|
110
119
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
120
|
+
if isinstance(entry, ZipFilesystemEntry):
|
121
|
+
return entry.entry.is_dir()
|
122
|
+
return isinstance(entry, VirtualDirectory)
|
123
|
+
|
124
|
+
def is_file(self, follow_symlinks: bool = True) -> bool:
|
125
|
+
try:
|
126
|
+
entry = self._resolve(follow_symlinks=follow_symlinks)
|
127
|
+
except FilesystemError:
|
128
|
+
return False
|
129
|
+
|
130
|
+
if isinstance(entry, ZipFilesystemEntry):
|
131
|
+
return not entry.entry.is_dir()
|
132
|
+
return False
|
133
|
+
|
134
|
+
def is_symlink(self) -> bool:
|
135
|
+
return stat.S_ISLNK(self.entry.external_attr >> 16)
|
136
|
+
|
137
|
+
def readlink(self) -> str:
|
138
|
+
if not self.is_symlink():
|
139
|
+
raise NotASymlinkError()
|
140
|
+
return self.fs.zip.open(self.entry).read().decode()
|
141
|
+
|
142
|
+
def readlink_ext(self) -> FilesystemEntry:
|
143
|
+
return FilesystemEntry.readlink_ext(self)
|
115
144
|
|
116
145
|
def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
|
117
|
-
|
118
|
-
return self.lstat()
|
146
|
+
return self._resolve(follow_symlinks=follow_symlinks).lstat()
|
119
147
|
|
120
148
|
def lstat(self) -> fsutil.stat_result:
|
121
149
|
"""Return the stat information of the given path, without resolving links."""
|
122
150
|
# ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
|
151
|
+
mode = self.entry.external_attr >> 16
|
152
|
+
|
153
|
+
if self.entry.is_dir() and not stat.S_ISDIR(mode):
|
154
|
+
mode = stat.S_IFDIR | mode
|
155
|
+
elif not self.entry.is_dir() and not stat.S_ISREG(mode):
|
156
|
+
mode = stat.S_IFREG | mode
|
157
|
+
|
123
158
|
return fsutil.stat_result(
|
124
159
|
[
|
125
|
-
|
160
|
+
mode,
|
126
161
|
self.entry.header_offset,
|
127
162
|
id(self.fs),
|
128
163
|
1,
|
dissect/target/helpers/record.py
CHANGED
@@ -142,3 +142,40 @@ EmptyRecord = RecordDescriptor(
|
|
142
142
|
"empty",
|
143
143
|
[],
|
144
144
|
)
|
145
|
+
|
146
|
+
COMMON_INTERFACE_ELEMENTS = [
|
147
|
+
("string", "name"),
|
148
|
+
("string", "type"),
|
149
|
+
("boolean", "enabled"),
|
150
|
+
("string", "mac"),
|
151
|
+
("net.ipaddress[]", "dns"),
|
152
|
+
("net.ipaddress[]", "ip"),
|
153
|
+
("net.ipaddress[]", "gateway"),
|
154
|
+
("string", "source"),
|
155
|
+
]
|
156
|
+
|
157
|
+
|
158
|
+
UnixInterfaceRecord = TargetRecordDescriptor(
|
159
|
+
"unix/network/interface",
|
160
|
+
COMMON_INTERFACE_ELEMENTS,
|
161
|
+
)
|
162
|
+
|
163
|
+
WindowsInterfaceRecord = TargetRecordDescriptor(
|
164
|
+
"windows/network/interface",
|
165
|
+
[
|
166
|
+
*COMMON_INTERFACE_ELEMENTS,
|
167
|
+
("varint", "vlan"),
|
168
|
+
("string", "metric"),
|
169
|
+
("datetime", "last_connected"),
|
170
|
+
],
|
171
|
+
)
|
172
|
+
|
173
|
+
MacInterfaceRecord = TargetRecordDescriptor(
|
174
|
+
"macos/network/interface",
|
175
|
+
[
|
176
|
+
*COMMON_INTERFACE_ELEMENTS,
|
177
|
+
("varint", "vlan"),
|
178
|
+
("string", "proxy"),
|
179
|
+
("varint", "interface_service_order"),
|
180
|
+
],
|
181
|
+
)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Any, Iterator, Union
|
4
|
+
|
5
|
+
from flow.record.fieldtypes.net import IPAddress, IPNetwork
|
6
|
+
|
7
|
+
from dissect.target.helpers.record import (
|
8
|
+
MacInterfaceRecord,
|
9
|
+
UnixInterfaceRecord,
|
10
|
+
WindowsInterfaceRecord,
|
11
|
+
)
|
12
|
+
from dissect.target.plugin import Plugin, export, internal
|
13
|
+
from dissect.target.target import Target
|
14
|
+
|
15
|
+
InterfaceRecord = Union[UnixInterfaceRecord, WindowsInterfaceRecord, MacInterfaceRecord]
|
16
|
+
|
17
|
+
|
18
|
+
class NetworkPlugin(Plugin):
|
19
|
+
__namespace__ = "network"
|
20
|
+
|
21
|
+
def __init__(self, target: Target):
|
22
|
+
super().__init__(target)
|
23
|
+
self._interface_list: list[InterfaceRecord] | None = None
|
24
|
+
|
25
|
+
def check_compatible(self) -> None:
|
26
|
+
pass
|
27
|
+
|
28
|
+
def _interfaces(self) -> Iterator[InterfaceRecord]:
|
29
|
+
yield from ()
|
30
|
+
|
31
|
+
def _get_record_type(self, field_name: str) -> Iterator[Any]:
|
32
|
+
for record in self.interfaces():
|
33
|
+
if (output := getattr(record, field_name, None)) is None:
|
34
|
+
continue
|
35
|
+
|
36
|
+
if isinstance(output, list):
|
37
|
+
yield from output
|
38
|
+
else:
|
39
|
+
yield output
|
40
|
+
|
41
|
+
@export(record=InterfaceRecord)
|
42
|
+
def interfaces(self) -> Iterator[InterfaceRecord]:
|
43
|
+
# Only search for the interfaces once
|
44
|
+
if self._interface_list is None:
|
45
|
+
self._interface_list = list(self._interfaces())
|
46
|
+
|
47
|
+
yield from self._interface_list
|
48
|
+
|
49
|
+
@export
|
50
|
+
def ips(self) -> list[IPAddress]:
|
51
|
+
return list(self._get_record_type("ip"))
|
52
|
+
|
53
|
+
@export
|
54
|
+
def gateways(self) -> list[IPAddress]:
|
55
|
+
return list(self._get_record_type("gateway"))
|
56
|
+
|
57
|
+
@export
|
58
|
+
def macs(self) -> list[str]:
|
59
|
+
return list(self._get_record_type("mac"))
|
60
|
+
|
61
|
+
@export
|
62
|
+
def dns(self) -> list[str]:
|
63
|
+
return list(self._get_record_type("dns"))
|
64
|
+
|
65
|
+
@internal
|
66
|
+
def with_ip(self, ip_addr: str) -> Iterator[InterfaceRecord]:
|
67
|
+
for interface in self.interfaces():
|
68
|
+
if ip_addr in interface.ip:
|
69
|
+
yield interface
|
70
|
+
|
71
|
+
@internal
|
72
|
+
def with_mac(self, mac: str) -> Iterator[InterfaceRecord]:
|
73
|
+
for interface in self.interfaces():
|
74
|
+
if interface.mac == mac:
|
75
|
+
yield interface
|
76
|
+
|
77
|
+
@internal
|
78
|
+
def in_cidr(self, cidr: str) -> Iterator[InterfaceRecord]:
|
79
|
+
cidr = IPNetwork(cidr)
|
80
|
+
for interface in self.interfaces():
|
81
|
+
if any(ip_addr in cidr for ip_addr in interface.ip):
|
82
|
+
yield interface
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.19.
|
3
|
+
Version: 3.19.dev39
|
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
|
@@ -42,7 +42,7 @@ dissect/target/filesystems/tar.py,sha256=kQNhcEDPX005svse039OeR2AGSDigGuGz2AKoVr
|
|
42
42
|
dissect/target/filesystems/vmfs.py,sha256=sRtYBUAKTKcHrjCXqpFJ8GIVU-ERjqxhB2zXBndtcXU,4955
|
43
43
|
dissect/target/filesystems/vmtar.py,sha256=LlKWkTIuLemQmG9yGqL7980uC_AOL77_GWhbJc_grSk,804
|
44
44
|
dissect/target/filesystems/xfs.py,sha256=kIyFGKYlyFYC7H3jaEv-lNKtBW4ZkD92H0WpfGcr1ww,4498
|
45
|
-
dissect/target/filesystems/zip.py,sha256=
|
45
|
+
dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rljwXs,5467
|
46
46
|
dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
|
48
48
|
dissect/target/helpers/config.py,sha256=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
|
@@ -61,7 +61,7 @@ dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q
|
|
61
61
|
dissect/target/helpers/network_managers.py,sha256=ByBSe2K3c8hgQC6dokcf-hHdmPcD8PmrOj0xs1C3yhs,25743
|
62
62
|
dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
|
63
63
|
dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
|
64
|
-
dissect/target/helpers/record.py,sha256=
|
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
67
|
dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
|
@@ -180,6 +180,7 @@ dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHn
|
|
180
180
|
dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
|
181
181
|
dissect/target/plugins/general/example.py,sha256=6B_YOqajRBLNWBEOfIL_HnLaEANBF8KKoc0mweihiug,6034
|
182
182
|
dissect/target/plugins/general/loaders.py,sha256=6iUxhlSAgo7qSE8_XFxgiihK8sdMiP-s4k0W5Iv8m9k,879
|
183
|
+
dissect/target/plugins/general/network.py,sha256=Ol4Ls1w78-7zpmVaQQOZG27rvYOhJLFVhomZj5kwibs,2430
|
183
184
|
dissect/target/plugins/general/osinfo.py,sha256=RdK5mw3-H9H3sGXz8yP8U_p3wUG1Ww7_HBKZpFdsbTE,1358
|
184
185
|
dissect/target/plugins/general/plugins.py,sha256=4URjS6DN1Ey6Cqlbyx6NfFGgQZpWDrqxl8KLcZFODGE,4479
|
185
186
|
dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
|
@@ -348,10 +349,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
348
349
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
349
350
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
350
351
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
351
|
-
dissect.target-3.19.
|
352
|
-
dissect.target-3.19.
|
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.
|
352
|
+
dissect.target-3.19.dev39.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
353
|
+
dissect.target-3.19.dev39.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
354
|
+
dissect.target-3.19.dev39.dist-info/METADATA,sha256=c6QzWVFBBowHJF8fihCzibN_ELCTHhj59sC_YGZIqhY,12719
|
355
|
+
dissect.target-3.19.dev39.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
356
|
+
dissect.target-3.19.dev39.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
357
|
+
dissect.target-3.19.dev39.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
358
|
+
dissect.target-3.19.dev39.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.19.dev37.dist-info → dissect.target-3.19.dev39.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|