dissect.target 3.17.dev26__py3-none-any.whl → 3.17.dev28__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/helpers/network_managers.py +66 -49
- dissect/target/loaders/mqtt.py +41 -16
- dissect/target/plugins/os/unix/log/messages.py +53 -8
- dissect/target/tools/query.py +1 -2
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/METADATA +1 -1
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/RECORD +11 -11
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/LICENSE +0 -0
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/WHEEL +0 -0
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ import re
|
|
5
5
|
from collections import defaultdict
|
6
6
|
from configparser import ConfigParser, MissingSectionHeaderError
|
7
7
|
from io import StringIO
|
8
|
+
from itertools import chain
|
8
9
|
from re import compile, sub
|
9
10
|
from typing import Any, Callable, Iterable, Match, Optional
|
10
11
|
|
@@ -299,7 +300,8 @@ class Parser:
|
|
299
300
|
return
|
300
301
|
|
301
302
|
if section:
|
302
|
-
|
303
|
+
# account for values of sections which are None
|
304
|
+
config = config.get(section, {}) or {}
|
303
305
|
|
304
306
|
for key, value in config.items():
|
305
307
|
if key == option:
|
@@ -508,7 +510,7 @@ class LinuxNetworkManager:
|
|
508
510
|
|
509
511
|
|
510
512
|
def parse_unix_dhcp_log_messages(target) -> list[str]:
|
511
|
-
"""Parse local syslog and cloud init
|
513
|
+
"""Parse local syslog, journal and cloud init-log files for DHCP lease IPs.
|
512
514
|
|
513
515
|
Args:
|
514
516
|
target: Target to discover and obtain network information from.
|
@@ -516,53 +518,68 @@ def parse_unix_dhcp_log_messages(target) -> list[str]:
|
|
516
518
|
Returns:
|
517
519
|
List of DHCP ip addresses.
|
518
520
|
"""
|
519
|
-
ips =
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
521
|
+
ips = set()
|
522
|
+
messages = set()
|
523
|
+
|
524
|
+
for log_func in ["messages", "journal"]:
|
525
|
+
try:
|
526
|
+
messages = chain(messages, getattr(target, log_func)())
|
527
|
+
except PluginError:
|
528
|
+
target.log.debug(f"Could not search for DHCP leases in {log_func} files.")
|
529
|
+
|
530
|
+
if not messages:
|
531
|
+
target.log.warning(f"Could not search for DHCP leases using {log_func}: No log entries found.")
|
532
|
+
|
533
|
+
for record in messages:
|
534
|
+
line = record.message
|
535
|
+
|
536
|
+
# Ubuntu cloud-init
|
537
|
+
if "Received dhcp lease on" in line:
|
538
|
+
interface, ip, netmask = re.search(r"Received dhcp lease on (\w{0,}) for (\S+)\/(\S+)", line).groups()
|
539
|
+
ips.add(ip)
|
540
|
+
continue
|
541
|
+
|
542
|
+
# Ubuntu DHCP
|
543
|
+
if ("DHCPv4" in line or "DHCPv6" in line) and " address " in line and " via " in line:
|
544
|
+
ip = line.split(" address ")[1].split(" via ")[0].strip().split("/")[0]
|
545
|
+
ips.add(ip)
|
546
|
+
continue
|
547
|
+
|
548
|
+
# Ubuntu DHCP NetworkManager
|
549
|
+
if "option ip_address" in line and ("dhcp4" in line or "dhcp6" in line) and "=> '" in line:
|
550
|
+
ip = line.split("=> '")[1].replace("'", "").strip()
|
551
|
+
ips.add(ip)
|
552
|
+
continue
|
553
|
+
|
554
|
+
# Debian and CentOS dhclient
|
555
|
+
if hasattr(record, "daemon") and record.daemon == "dhclient" and "bound to" in line:
|
556
|
+
ip = line.split("bound to")[1].split(" ")[1].strip()
|
557
|
+
ips.add(ip)
|
558
|
+
continue
|
559
|
+
|
560
|
+
# CentOS DHCP and general NetworkManager
|
561
|
+
if " address " in line and ("dhcp4" in line or "dhcp6" in line):
|
562
|
+
ip = line.split(" address ")[1].strip()
|
563
|
+
ips.add(ip)
|
564
|
+
continue
|
565
|
+
|
566
|
+
# Ubuntu/Debian DHCP networkd (Journal)
|
567
|
+
if (
|
568
|
+
hasattr(record, "code_func")
|
569
|
+
and record.code_func == "dhcp_lease_acquired"
|
570
|
+
and " address " in line
|
571
|
+
and " via " in line
|
572
|
+
):
|
573
|
+
interface, ip, netmask, gateway = re.search(
|
574
|
+
r"^(\S+): DHCPv[4|6] address (\S+)\/(\S+) via (\S+)", line
|
575
|
+
).groups()
|
576
|
+
ips.add(ip)
|
577
|
+
continue
|
578
|
+
|
579
|
+
# Journals and syslogs can be large and slow to iterate,
|
580
|
+
# so we stop if we have some results and have reached the journal plugin.
|
581
|
+
if len(ips) >= 2 and record._desc.name == "linux/log/journal":
|
582
|
+
break
|
566
583
|
|
567
584
|
return ips
|
568
585
|
|
dissect/target/loaders/mqtt.py
CHANGED
@@ -65,6 +65,9 @@ class MQTTStream(AlignedStream):
|
|
65
65
|
class MQTTConnection:
|
66
66
|
broker = None
|
67
67
|
host = None
|
68
|
+
prev = -1
|
69
|
+
factor = 1
|
70
|
+
prefetch_factor_inc = 10
|
68
71
|
|
69
72
|
def __init__(self, broker: Broker, host: str):
|
70
73
|
self.broker = broker
|
@@ -95,20 +98,32 @@ class MQTTConnection:
|
|
95
98
|
|
96
99
|
def read(self, disk_id: int, offset: int, length: int, optimization_strategy: int) -> bytes:
|
97
100
|
message = None
|
98
|
-
self.broker.seek(self.host, disk_id, offset, length, optimization_strategy)
|
99
101
|
|
102
|
+
message = self.broker.read(self.host, disk_id, offset, length)
|
103
|
+
if message:
|
104
|
+
return message.data
|
105
|
+
|
106
|
+
if self.prev == offset - (length * self.factor):
|
107
|
+
if self.factor < 500:
|
108
|
+
self.factor += self.prefetch_factor_inc
|
109
|
+
else:
|
110
|
+
self.factor = 1
|
111
|
+
|
112
|
+
self.prev = offset
|
113
|
+
flength = length * self.factor
|
114
|
+
self.broker.factor = self.factor
|
115
|
+
self.broker.seek(self.host, disk_id, offset, flength, optimization_strategy)
|
100
116
|
attempts = 0
|
101
117
|
while True:
|
102
|
-
message
|
103
|
-
|
104
|
-
if message:
|
118
|
+
if message := self.broker.read(self.host, disk_id, offset, length):
|
119
|
+
# don't waste time with sleep if we have a response
|
105
120
|
break
|
106
121
|
|
107
122
|
attempts += 1
|
108
|
-
time.sleep(0.
|
109
|
-
if attempts >
|
123
|
+
time.sleep(0.1)
|
124
|
+
if attempts > 300:
|
110
125
|
# message might have not reached agent, resend...
|
111
|
-
self.broker.seek(self.host, disk_id, offset,
|
126
|
+
self.broker.seek(self.host, disk_id, offset, flength, optimization_strategy)
|
112
127
|
attempts = 0
|
113
128
|
|
114
129
|
return message.data
|
@@ -127,6 +142,7 @@ class Broker:
|
|
127
142
|
diskinfo = {}
|
128
143
|
index = {}
|
129
144
|
topo = {}
|
145
|
+
factor = 1
|
130
146
|
|
131
147
|
def __init__(self, broker: Broker, port: str, key: str, crt: str, ca: str, case: str, **kwargs):
|
132
148
|
self.broker_host = broker
|
@@ -137,10 +153,13 @@ class Broker:
|
|
137
153
|
self.case = case
|
138
154
|
self.command = kwargs.get("command", None)
|
139
155
|
|
156
|
+
def clear_cache(self) -> None:
|
157
|
+
self.index = {}
|
158
|
+
|
140
159
|
@suppress
|
141
160
|
def read(self, host: str, disk_id: int, seek_address: int, read_length: int) -> SeekMessage:
|
142
161
|
key = f"{host}-{disk_id}-{seek_address}-{read_length}"
|
143
|
-
return self.index.
|
162
|
+
return self.index.get(key)
|
144
163
|
|
145
164
|
@suppress
|
146
165
|
def disk(self, host: str) -> DiskMessage:
|
@@ -165,14 +184,15 @@ class Broker:
|
|
165
184
|
disk_id = tokens[3]
|
166
185
|
seek_address = int(tokens[4], 16)
|
167
186
|
read_length = int(tokens[5], 16)
|
168
|
-
msg = SeekMessage(data=payload)
|
169
187
|
|
170
|
-
|
188
|
+
for i in range(self.factor):
|
189
|
+
sublength = int(read_length / self.factor)
|
190
|
+
start = i * sublength
|
191
|
+
key = f"{hostname}-{disk_id}-{seek_address+start}-{sublength}"
|
192
|
+
if key in self.index:
|
193
|
+
continue
|
171
194
|
|
172
|
-
|
173
|
-
return
|
174
|
-
|
175
|
-
self.index[key] = msg
|
195
|
+
self.index[key] = SeekMessage(data=payload[start : start + sublength])
|
176
196
|
|
177
197
|
def _on_id(self, hostname: str, payload: bytes) -> None:
|
178
198
|
key = hostname
|
@@ -204,9 +224,14 @@ class Broker:
|
|
204
224
|
elif response == "ID":
|
205
225
|
self._on_id(hostname, msg.payload)
|
206
226
|
|
207
|
-
def seek(self, host: str, disk_id: int, offset: int,
|
227
|
+
def seek(self, host: str, disk_id: int, offset: int, flength: int, optimization_strategy: int) -> None:
|
228
|
+
length = int(flength / self.factor)
|
229
|
+
key = f"{host}-{disk_id}-{offset}-{length}"
|
230
|
+
if key in self.index:
|
231
|
+
return
|
232
|
+
|
208
233
|
self.mqtt_client.publish(
|
209
|
-
f"{self.case}/{host}/SEEK/{disk_id}/{hex(offset)}/{hex(
|
234
|
+
f"{self.case}/{host}/SEEK/{disk_id}/{hex(offset)}/{hex(flength)}", pack("<I", optimization_strategy)
|
210
235
|
)
|
211
236
|
|
212
237
|
def info(self, host: str) -> None:
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import re
|
2
|
-
from
|
2
|
+
from pathlib import Path
|
3
3
|
from typing import Iterator
|
4
4
|
|
5
|
+
from dissect.target import Target
|
5
6
|
from dissect.target.exceptions import UnsupportedPluginError
|
6
7
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
7
8
|
from dissect.target.helpers.utils import year_rollover_helper
|
@@ -23,17 +24,28 @@ RE_TS = re.compile(r"(\w+\s{1,2}\d+\s\d{2}:\d{2}:\d{2})")
|
|
23
24
|
RE_DAEMON = re.compile(r"^[^:]+:\d+:\d+[^\[\]:]+\s([^\[:]+)[\[|:]{1}")
|
24
25
|
RE_PID = re.compile(r"\w\[(\d+)\]")
|
25
26
|
RE_MSG = re.compile(r"[^:]+:\d+:\d+[^:]+:\s(.*)$")
|
27
|
+
RE_CLOUD_INIT_LINE = re.compile(r"(?P<ts>.*) - (?P<daemon>.*)\[(?P<log_level>\w+)\]\: (?P<message>.*)$")
|
26
28
|
|
27
29
|
|
28
30
|
class MessagesPlugin(Plugin):
|
31
|
+
def __init__(self, target: Target):
|
32
|
+
super().__init__(target)
|
33
|
+
self.log_files = set(self._find_log_files())
|
34
|
+
|
35
|
+
def _find_log_files(self) -> Iterator[Path]:
|
36
|
+
log_dirs = ["/var/log/", "/var/log/installer/"]
|
37
|
+
file_globs = ["syslog*", "messages*", "cloud-init.log*"]
|
38
|
+
for log_dir in log_dirs:
|
39
|
+
for glob in file_globs:
|
40
|
+
yield from self.target.fs.path(log_dir).glob(glob)
|
41
|
+
|
29
42
|
def check_compatible(self) -> None:
|
30
|
-
|
31
|
-
|
32
|
-
raise UnsupportedPluginError("No message files found")
|
43
|
+
if not self.log_files:
|
44
|
+
raise UnsupportedPluginError("No log files found")
|
33
45
|
|
34
46
|
@export(record=MessagesRecord)
|
35
47
|
def syslog(self) -> Iterator[MessagesRecord]:
|
36
|
-
"""Return contents of /var/log/messages
|
48
|
+
"""Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
|
37
49
|
|
38
50
|
See ``messages`` for more information.
|
39
51
|
"""
|
@@ -41,7 +53,7 @@ class MessagesPlugin(Plugin):
|
|
41
53
|
|
42
54
|
@export(record=MessagesRecord)
|
43
55
|
def messages(self) -> Iterator[MessagesRecord]:
|
44
|
-
"""Return contents of /var/log/messages
|
56
|
+
"""Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
|
45
57
|
|
46
58
|
Note: due to year rollover detection, the contents of the files are returned in reverse.
|
47
59
|
|
@@ -52,12 +64,16 @@ class MessagesPlugin(Plugin):
|
|
52
64
|
References:
|
53
65
|
- https://geek-university.com/linux/var-log-messages-file/
|
54
66
|
- https://www.geeksforgeeks.org/file-timestamps-mtime-ctime-and-atime-in-linux/
|
67
|
+
- https://cloudinit.readthedocs.io/en/latest/development/logging.html#logging-command-output
|
55
68
|
"""
|
56
69
|
|
57
70
|
tzinfo = self.target.datetime.tzinfo
|
58
71
|
|
59
|
-
|
60
|
-
|
72
|
+
for log_file in self.log_files:
|
73
|
+
if "cloud-init" in log_file.name:
|
74
|
+
yield from self._parse_cloud_init_log(log_file)
|
75
|
+
continue
|
76
|
+
|
61
77
|
for ts, line in year_rollover_helper(log_file, RE_TS, DEFAULT_TS_LOG_FORMAT, tzinfo):
|
62
78
|
daemon = dict(enumerate(RE_DAEMON.findall(line))).get(0)
|
63
79
|
pid = dict(enumerate(RE_PID.findall(line))).get(0)
|
@@ -71,3 +87,32 @@ class MessagesPlugin(Plugin):
|
|
71
87
|
source=log_file,
|
72
88
|
_target=self.target,
|
73
89
|
)
|
90
|
+
|
91
|
+
def _parse_cloud_init_log(self, log_file: Path) -> Iterator[MessagesRecord]:
|
92
|
+
"""Parse a cloud-init.log file.
|
93
|
+
|
94
|
+
Lines are structured in the following format:
|
95
|
+
``YYYY-MM-DD HH:MM:SS,000 - dhcp.py[DEBUG]: Received dhcp lease on IFACE for IP/MASK``
|
96
|
+
|
97
|
+
NOTE: ``cloud-init-output.log`` files are not supported as they do not contain structured logs.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
``log_file``: path to cloud-init.log file.
|
101
|
+
|
102
|
+
Returns: ``MessagesRecord``
|
103
|
+
"""
|
104
|
+
for line in log_file.open("rt").readlines():
|
105
|
+
if line := line.strip():
|
106
|
+
if match := RE_CLOUD_INIT_LINE.match(line):
|
107
|
+
match = match.groupdict()
|
108
|
+
yield MessagesRecord(
|
109
|
+
ts=match["ts"].split(",")[0],
|
110
|
+
daemon=match["daemon"],
|
111
|
+
pid=None,
|
112
|
+
message=match["message"],
|
113
|
+
source=log_file,
|
114
|
+
_target=self.target,
|
115
|
+
)
|
116
|
+
else:
|
117
|
+
self.target.log.warning("Could not match cloud-init log line")
|
118
|
+
self.target.log.debug("No match for line '%s'", line)
|
dissect/target/tools/query.py
CHANGED
@@ -173,8 +173,7 @@ def main():
|
|
173
173
|
collected_plugins = {}
|
174
174
|
|
175
175
|
if targets:
|
176
|
-
for
|
177
|
-
plugin_target = Target.open(target)
|
176
|
+
for plugin_target in Target.open_all(targets, args.children):
|
178
177
|
if isinstance(plugin_target._loader, ProxyLoader):
|
179
178
|
parser.error("can't list compatible plugins for remote targets.")
|
180
179
|
funcs, _ = find_plugin_functions(plugin_target, args.list, compatibility=True, show_hidden=True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.17.
|
3
|
+
Version: 3.17.dev28
|
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
|
@@ -57,7 +57,7 @@ dissect/target/helpers/loaderutil.py,sha256=kiyMWra_gVxfNSGwLlgxLcuuqAYuCMDc5NiC
|
|
57
57
|
dissect/target/helpers/localeutil.py,sha256=Y4Fh4jDSGfm5356xSLMriUCN8SZP_FAHg_iodkAxNq4,1504
|
58
58
|
dissect/target/helpers/mount.py,sha256=JxhUYyEbDnHfzPpfuWy4nV9OwCJPoDSGdHHNiyvd_l0,3949
|
59
59
|
dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
|
60
|
-
dissect/target/helpers/network_managers.py,sha256=
|
60
|
+
dissect/target/helpers/network_managers.py,sha256=uRh_P8ICbKke2N7eFJ6AS2-I5DmIRiaQUlxR7oqxPaU,24975
|
61
61
|
dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
|
62
62
|
dissect/target/helpers/protobuf.py,sha256=NwKrZD4q9v7J8GnZX9gbzMUMV5pR78eAV17jgWOz_EY,1730
|
63
63
|
dissect/target/helpers/record.py,sha256=lWl7k2Mp9Axllm0tXzPGJx2zj2zONsyY_p5g424T0Lc,4826
|
@@ -85,7 +85,7 @@ dissect/target/loaders/itunes.py,sha256=69aMTQiiGYpmD_EYSmf9mO1re8C3jAZIEStmwlMx
|
|
85
85
|
dissect/target/loaders/kape.py,sha256=t5TfrGLqPeIpUUpXzIl6aHsqXMEGDqJ5YwDCs07DiBA,1237
|
86
86
|
dissect/target/loaders/local.py,sha256=Ul-LCd_fY7SyWOVR6nH-NqbkuNpxoZVmffwrkvQElU8,16453
|
87
87
|
dissect/target/loaders/log.py,sha256=cCkDIRS4aPlX3U-n_jUKaI2FPSV3BDpfqKceaU7rBbo,1507
|
88
|
-
dissect/target/loaders/mqtt.py,sha256=
|
88
|
+
dissect/target/loaders/mqtt.py,sha256=D8AmdOz2atD92z8bhjVFi3tC1H7pYmP4UrOCtMgfwMY,10396
|
89
89
|
dissect/target/loaders/multiraw.py,sha256=4a3ZST0NwjnfPDxHkcEfAcX2ddUlT_C-rcrMHNg1wp4,1046
|
90
90
|
dissect/target/loaders/ova.py,sha256=6h4O-7i87J394C6KgLsPkdXRAKNwtPubzLNS3vBGs7U,744
|
91
91
|
dissect/target/loaders/ovf.py,sha256=ELMq6J2y6cPKbp7pjWAqMMnFYefWxXNqzIiAQdvGGXQ,1061
|
@@ -247,7 +247,7 @@ dissect/target/plugins/os/unix/log/audit.py,sha256=OjorWTmCFvCI5RJq6m6WNW0Lhb-po
|
|
247
247
|
dissect/target/plugins/os/unix/log/auth.py,sha256=l7gCuRdvv9gL0U1N0yrR9hVsMnr4t_k4t-n-f6PrOxg,2388
|
248
248
|
dissect/target/plugins/os/unix/log/journal.py,sha256=eiNNVLmKWFj4dTQX8PNRNgKpVwzQWEHEsKyYfGUAPXQ,17376
|
249
249
|
dissect/target/plugins/os/unix/log/lastlog.py,sha256=eL_dbB1sPoy0tyavIjT457ZLVfXcCr17GiwDrMEEh8A,2458
|
250
|
-
dissect/target/plugins/os/unix/log/messages.py,sha256=
|
250
|
+
dissect/target/plugins/os/unix/log/messages.py,sha256=CXA-SkMPLaCgnTQg9nzII-7tO8Il_ENQmuYvDxo33rI,4698
|
251
251
|
dissect/target/plugins/os/unix/log/utmp.py,sha256=21tvzG977LqzRShV6uAoU-83WDcLUrI_Tv__2ZVi9rw,7756
|
252
252
|
dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
253
253
|
dissect/target/plugins/os/windows/_os.py,sha256=EA9B9Rgb1C9NMvlX3gXhTRFXYaI6zrrKRg0OYq4v1ts,12589
|
@@ -320,7 +320,7 @@ dissect/target/tools/fs.py,sha256=cizCrW8rqdpT1irA8g6mslkaXX7CynWVQ7fvRUrcxNU,37
|
|
320
320
|
dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
|
321
321
|
dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
|
322
322
|
dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
|
323
|
-
dissect/target/tools/query.py,sha256=
|
323
|
+
dissect/target/tools/query.py,sha256=ONHu2FVomLccikb84qBrlhNmEfRoHYFQMcahk_y2c9A,15580
|
324
324
|
dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
|
325
325
|
dissect/target/tools/shell.py,sha256=4v6Z06YJDjKv6e6SRvWNjQ2n_KHo_CjL4P0w1_gY_ro,44827
|
326
326
|
dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
|
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
336
336
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
337
337
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
338
338
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
339
|
-
dissect.target-3.17.
|
340
|
-
dissect.target-3.17.
|
341
|
-
dissect.target-3.17.
|
342
|
-
dissect.target-3.17.
|
343
|
-
dissect.target-3.17.
|
344
|
-
dissect.target-3.17.
|
345
|
-
dissect.target-3.17.
|
339
|
+
dissect.target-3.17.dev28.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
340
|
+
dissect.target-3.17.dev28.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
341
|
+
dissect.target-3.17.dev28.dist-info/METADATA,sha256=BUIYxmU-67ACgU91l-B3EP-B6QpBkjk-OBY7EOMDfBU,11300
|
342
|
+
dissect.target-3.17.dev28.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
343
|
+
dissect.target-3.17.dev28.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
344
|
+
dissect.target-3.17.dev28.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
345
|
+
dissect.target-3.17.dev28.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.17.dev26.dist-info → dissect.target-3.17.dev28.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|