dissect.target 3.20.dev47__py3-none-any.whl → 3.20.dev49__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,6 @@ import logging
4
4
  import re
5
5
  import uuid
6
6
  from pathlib import Path
7
- from struct import unpack
8
7
  from typing import Iterator
9
8
 
10
9
  from flow.record.fieldtypes import posix_path
@@ -19,6 +18,25 @@ from dissect.target.target import Target
19
18
  log = logging.getLogger(__name__)
20
19
 
21
20
 
21
+ # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ISA
22
+ ARCH_MAP = {
23
+ 0x00: "unknown",
24
+ 0x02: "sparc",
25
+ 0x03: "x86",
26
+ 0x08: "mips",
27
+ 0x14: "powerpc32",
28
+ 0x15: "powerpc64",
29
+ 0x16: "s390", # and s390x
30
+ 0x28: "aarch32", # armv7
31
+ 0x2A: "superh",
32
+ 0x32: "ia-64",
33
+ 0x3E: "x86_64",
34
+ 0xB7: "aarch64", # armv8
35
+ 0xF3: "riscv64",
36
+ 0xF7: "bpf",
37
+ }
38
+
39
+
22
40
  class UnixPlugin(OSPlugin):
23
41
  def __init__(self, target: Target):
24
42
  super().__init__(target)
@@ -301,37 +319,30 @@ class UnixPlugin(OSPlugin):
301
319
  continue
302
320
  return os_release
303
321
 
304
- def _get_architecture(self, os: str = "unix", path: str = "/bin/ls") -> str | None:
305
- arch_strings = {
306
- 0x00: "Unknown",
307
- 0x02: "SPARC",
308
- 0x03: "x86",
309
- 0x08: "MIPS",
310
- 0x14: "PowerPC",
311
- 0x16: "S390",
312
- 0x28: "ARM",
313
- 0x2A: "SuperH",
314
- 0x32: "IA-64",
315
- 0x3E: "x86_64",
316
- 0xB7: "AArch64",
317
- 0xF3: "RISC-V",
318
- }
319
-
320
- for fs in self.target.filesystems:
321
- if fs.exists(path):
322
- fh = fs.open(path)
323
- fh.seek(4)
324
- # ELF - e_ident[EI_CLASS]
325
- bits = unpack("B", fh.read(1))[0]
326
- fh.seek(18)
327
- # ELF - e_machine
328
- arch = unpack("H", fh.read(2))[0]
329
- arch = arch_strings.get(arch)
330
-
331
- if bits == 1: # 32 bit system
332
- return f"{arch}_32-{os}"
333
- else:
334
- return f"{arch}-{os}"
322
+ def _get_architecture(self, os: str = "unix", path: Path | str = "/bin/ls") -> str | None:
323
+ """Determine architecture by reading an ELF header of a binary on the target.
324
+
325
+ Resources:
326
+ - https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ISA
327
+ """
328
+
329
+ if not isinstance(path, TargetPath):
330
+ for fs in [self.target.fs, *self.target.filesystems]:
331
+ if (path := fs.path(path)).exists():
332
+ break
333
+
334
+ if not path.exists():
335
+ return
336
+
337
+ fh = path.open("rb")
338
+ fh.seek(4) # ELF - e_ident[EI_CLASS]
339
+ bits = fh.read(1)[0]
340
+
341
+ fh.seek(18) # ELF - e_machine
342
+ e_machine = int.from_bytes(fh.read(2), "little")
343
+ arch = ARCH_MAP.get(e_machine, "unknown")
344
+
345
+ return f"{arch}_32-{os}" if bits == 1 and not arch[-2:] == "32" else f"{arch}-{os}"
335
346
 
336
347
 
337
348
  def parse_fstab(
@@ -6,7 +6,7 @@ from base64 import b64decode
6
6
  from datetime import datetime
7
7
  from io import BytesIO
8
8
  from tarfile import ReadError
9
- from typing import BinaryIO, Iterator, Optional, TextIO, Union
9
+ from typing import BinaryIO, Iterator, TextIO
10
10
 
11
11
  from dissect.util import cpio
12
12
  from dissect.util.compression import xz
@@ -73,10 +73,11 @@ class FortiOSPlugin(LinuxPlugin):
73
73
  return config
74
74
 
75
75
  @classmethod
76
- def detect(cls, target: Target) -> Optional[Filesystem]:
76
+ def detect(cls, target: Target) -> Filesystem | None:
77
77
  for fs in target.filesystems:
78
- # Tested on FortiGate and FortiAnalyzer, other Fortinet devices may look different.
79
- if fs.exists("/rootfs.gz") and (fs.exists("/.fgtsum") or fs.exists("/.fmg_sign") or fs.exists("/flatkc")):
78
+ # Tested on FortiGate, FortiAnalyzer and FortiManager.
79
+ # Other Fortinet devices may look different.
80
+ if fs.exists("/rootfs.gz") and (any(map(fs.exists, (".fgtsum", ".fmg_sign", "flatkc", "system.conf")))):
80
81
  return fs
81
82
 
82
83
  @classmethod
@@ -212,7 +213,7 @@ class FortiOSPlugin(LinuxPlugin):
212
213
  return "FortiOS Unknown"
213
214
 
214
215
  @export(record=FortiOSUserRecord)
215
- def users(self) -> Iterator[Union[FortiOSUserRecord, UnixUserRecord]]:
216
+ def users(self) -> Iterator[FortiOSUserRecord | UnixUserRecord]:
216
217
  """Return local users of the FortiOS system."""
217
218
 
218
219
  # Possible unix-like users
@@ -224,7 +225,7 @@ class FortiOSPlugin(LinuxPlugin):
224
225
  yield FortiOSUserRecord(
225
226
  name=username,
226
227
  password=":".join(entry.get("password", [])),
227
- groups=[entry["accprofile"][0]],
228
+ groups=list(entry.get("accprofile", [])),
228
229
  home="/root",
229
230
  _target=self.target,
230
231
  )
@@ -233,69 +234,72 @@ class FortiOSPlugin(LinuxPlugin):
233
234
  self.target.log.debug("", exc_info=e)
234
235
 
235
236
  # FortiManager administrative users
236
- try:
237
- for username, entry in self._config["global-config"]["system"]["admin"]["user"].items():
238
- yield FortiOSUserRecord(
239
- name=username,
240
- password=":".join(entry.get("password", [])),
241
- groups=[entry["profileid"][0]],
242
- home="/root",
243
- _target=self.target,
244
- )
245
- except KeyError as e:
246
- self.target.log.warning("Exception while parsing FortiManager admin users")
247
- self.target.log.debug("", exc_info=e)
248
-
249
- # Local users
250
- try:
251
- local_groups = local_groups_to_users(self._config["root-config"]["user"]["group"])
252
- for username, entry in self._config["root-config"]["user"].get("local", {}).items():
253
- try:
254
- password = decrypt_password(entry["passwd"][-1])
255
- except (ValueError, RuntimeError):
256
- password = ":".join(entry.get("passwd", []))
257
-
258
- yield FortiOSUserRecord(
259
- name=username,
260
- password=password,
261
- groups=local_groups.get(username, []),
262
- home=None,
263
- _target=self.target,
264
- )
265
- except KeyError as e:
266
- self.target.log.warning("Exception while parsing FortiOS local users")
267
- self.target.log.debug("", exc_info=e)
268
-
269
- # Temporary guest users
270
- try:
271
- for _, entry in self._config["root-config"]["user"]["group"].get("guestgroup", {}).get("guest", {}).items():
272
- try:
273
- password = decrypt_password(entry.get("password")[-1])
274
- except (ValueError, RuntimeError):
275
- password = ":".join(entry.get("password"))
276
-
277
- yield FortiOSUserRecord(
278
- name=entry["user-id"][0],
279
- password=password,
280
- groups=["guestgroup"],
281
- home=None,
282
- _target=self.target,
283
- )
284
- except KeyError as e:
285
- self.target.log.warning("Exception while parsing FortiOS temporary guest users")
286
- self.target.log.debug("", exc_info=e)
237
+ if self._config.get("global-config", {}).get("system", {}).get("admin", {}).get("user"):
238
+ try:
239
+ for username, entry in self._config["global-config"]["system"]["admin"]["user"].items():
240
+ yield FortiOSUserRecord(
241
+ name=username,
242
+ password=":".join(entry.get("password", [])),
243
+ groups=list(entry.get("profileid", [])),
244
+ home="/root",
245
+ _target=self.target,
246
+ )
247
+ except KeyError as e:
248
+ self.target.log.warning("Exception while parsing FortiManager admin users")
249
+ self.target.log.debug("", exc_info=e)
250
+
251
+ if self._config.get("root-config"):
252
+ # Local users
253
+ try:
254
+ local_groups = local_groups_to_users(self._config["root-config"]["user"]["group"])
255
+ for username, entry in self._config["root-config"]["user"].get("local", {}).items():
256
+ try:
257
+ password = decrypt_password(entry["passwd"][-1])
258
+ except (ValueError, RuntimeError):
259
+ password = ":".join(entry.get("passwd", []))
260
+
261
+ yield FortiOSUserRecord(
262
+ name=username,
263
+ password=password,
264
+ groups=local_groups.get(username, []),
265
+ home=None,
266
+ _target=self.target,
267
+ )
268
+ except KeyError as e:
269
+ self.target.log.warning("Exception while parsing FortiOS local users")
270
+ self.target.log.debug("", exc_info=e)
271
+
272
+ # Temporary guest users
273
+ try:
274
+ for _, entry in (
275
+ self._config["root-config"]["user"]["group"].get("guestgroup", {}).get("guest", {}).items()
276
+ ):
277
+ try:
278
+ password = decrypt_password(entry.get("password")[-1])
279
+ except (ValueError, RuntimeError):
280
+ password = ":".join(entry.get("password"))
281
+
282
+ yield FortiOSUserRecord(
283
+ name=entry["user-id"][0],
284
+ password=password,
285
+ groups=["guestgroup"],
286
+ home=None,
287
+ _target=self.target,
288
+ )
289
+ except KeyError as e:
290
+ self.target.log.warning("Exception while parsing FortiOS temporary guest users")
291
+ self.target.log.debug("", exc_info=e)
287
292
 
288
293
  @export(property=True)
289
294
  def os(self) -> str:
290
295
  return OperatingSystem.FORTIOS.value
291
296
 
292
297
  @export(property=True)
293
- def architecture(self) -> Optional[str]:
298
+ def architecture(self) -> str | None:
294
299
  """Return architecture FortiOS runs on."""
295
- paths = ["/lib/libav.so", "/bin/ctr"]
296
- for path in paths:
297
- if self.target.fs.path(path).exists():
298
- return self._get_architecture(path=path)
300
+ for path in ["/lib/libav.so", "/bin/ctr", "/bin/grep"]:
301
+ if (bin := self.target.fs.path(path)).exists():
302
+ return self._get_architecture(path=bin)
299
303
 
300
304
 
301
305
  class ConfigNode(dict):
@@ -528,7 +532,7 @@ def decrypt_rootfs(fh: BinaryIO, key: bytes, iv: bytes) -> BinaryIO:
528
532
  return BytesIO(result)
529
533
 
530
534
 
531
- def _kdf_7_4_x(key_data: Union[str, bytes]) -> tuple[bytes, bytes]:
535
+ def _kdf_7_4_x(key_data: str | bytes) -> tuple[bytes, bytes]:
532
536
  """Derive 32 byte key and 16 byte IV from 32 byte seed.
533
537
 
534
538
  As the IV needs to be 16 bytes, we return the first 16 bytes of the sha256 hash.
@@ -542,7 +546,7 @@ def _kdf_7_4_x(key_data: Union[str, bytes]) -> tuple[bytes, bytes]:
542
546
  return key, iv
543
547
 
544
548
 
545
- def get_kernel_hash(sysvol: Filesystem) -> Optional[str]:
549
+ def get_kernel_hash(sysvol: Filesystem) -> str | None:
546
550
  """Return the SHA256 hash of the (compressed) kernel."""
547
551
  kernel_files = ["flatkc", "vmlinuz", "vmlinux"]
548
552
  for k in kernel_files:
@@ -1,17 +1,17 @@
1
- import gzip
1
+ from __future__ import annotations
2
+
2
3
  import ipaddress
3
4
  import struct
4
5
  from collections import namedtuple
5
6
  from typing import Iterator
6
7
 
7
8
  from dissect.cstruct import cstruct
8
- from dissect.util.stream import BufferedStream
9
9
  from dissect.util.ts import from_unix
10
10
 
11
11
  from dissect.target.exceptions import UnsupportedPluginError
12
- from dissect.target.helpers.fsutil import TargetPath
12
+ from dissect.target.helpers.fsutil import TargetPath, open_decompress
13
13
  from dissect.target.helpers.record import TargetRecordDescriptor
14
- from dissect.target.plugin import OperatingSystem, Plugin, export
14
+ from dissect.target.plugin import Plugin, alias, export
15
15
  from dissect.target.target import Target
16
16
 
17
17
  UTMP_FIELDS = [
@@ -27,16 +27,12 @@ UTMP_FIELDS = [
27
27
 
28
28
  BtmpRecord = TargetRecordDescriptor(
29
29
  "linux/log/btmp",
30
- [
31
- *UTMP_FIELDS,
32
- ],
30
+ UTMP_FIELDS,
33
31
  )
34
32
 
35
33
  WtmpRecord = TargetRecordDescriptor(
36
34
  "linux/log/wtmp",
37
- [
38
- *UTMP_FIELDS,
39
- ],
35
+ UTMP_FIELDS,
40
36
  )
41
37
 
42
38
  utmp_def = """
@@ -104,24 +100,13 @@ UTMP_ENTRY = namedtuple(
104
100
  class UtmpFile:
105
101
  """utmp maintains a full accounting of the current status of the system"""
106
102
 
107
- def __init__(self, target: Target, path: TargetPath):
108
- self.fh = target.fs.path(path).open()
109
-
110
- if "gz" in path:
111
- self.compressed = True
112
- else:
113
- self.compressed = False
103
+ def __init__(self, path: TargetPath):
104
+ self.fh = open_decompress(path, "rb")
114
105
 
115
106
  def __iter__(self):
116
- if self.compressed:
117
- gzip_entry = BufferedStream(gzip.open(self.fh, mode="rb"))
118
- byte_stream = gzip_entry
119
- else:
120
- byte_stream = self.fh
121
-
122
107
  while True:
123
108
  try:
124
- entry = c_utmp.entry(byte_stream)
109
+ entry = c_utmp.entry(self.fh)
125
110
 
126
111
  r_type = ""
127
112
  if entry.ut_type in c_utmp.Type:
@@ -151,7 +136,7 @@ class UtmpFile:
151
136
  # ut_addr_v6 is parsed as IPv4 address. This could not lead to incorrect results.
152
137
  ut_addr = ipaddress.ip_address(struct.pack("<i", entry.ut_addr_v6[0]))
153
138
 
154
- utmp_entry = UTMP_ENTRY(
139
+ yield UTMP_ENTRY(
155
140
  ts=from_unix(entry.ut_tv.tv_sec),
156
141
  ut_type=r_type,
157
142
  ut_pid=entry.ut_pid,
@@ -162,7 +147,6 @@ class UtmpFile:
162
147
  ut_addr=ut_addr,
163
148
  )
164
149
 
165
- yield utmp_entry
166
150
  except EOFError:
167
151
  break
168
152
 
@@ -170,17 +154,28 @@ class UtmpFile:
170
154
  class UtmpPlugin(Plugin):
171
155
  """Unix utmp log plugin."""
172
156
 
173
- WTMP_GLOB = "/var/log/wtmp*"
174
- BTMP_GLOB = "/var/log/btmp*"
157
+ def __init__(self, target: Target):
158
+ super().__init__(target)
159
+ self.btmp_paths = list(self.target.fs.path("/").glob("var/log/btmp*"))
160
+ self.wtmp_paths = list(self.target.fs.path("/").glob("var/log/wtmp*"))
161
+ self.utmp_paths = list(self.target.fs.path("/").glob("var/run/utmp*"))
175
162
 
176
163
  def check_compatible(self) -> None:
177
- if not self.target.os == OperatingSystem.LINUX and not any(
178
- [
179
- list(self.target.fs.glob(self.BTMP_GLOB)),
180
- list(self.target.fs.glob(self.WTMP_GLOB)),
181
- ]
182
- ):
183
- raise UnsupportedPluginError("No WTMP or BTMP log files found")
164
+ if not any(self.btmp_paths + self.wtmp_paths + self.utmp_paths):
165
+ raise UnsupportedPluginError("No wtmp and/or btmp log files found")
166
+
167
+ def _build_record(self, record: TargetRecordDescriptor, entry: UTMP_ENTRY) -> Iterator[BtmpRecord | WtmpRecord]:
168
+ return record(
169
+ ts=entry.ts,
170
+ ut_type=entry.ut_type,
171
+ ut_pid=entry.ut_pid,
172
+ ut_user=entry.ut_user,
173
+ ut_line=entry.ut_line,
174
+ ut_id=entry.ut_id,
175
+ ut_host=entry.ut_host,
176
+ ut_addr=entry.ut_addr,
177
+ _target=self.target,
178
+ )
184
179
 
185
180
  @export(record=BtmpRecord)
186
181
  def btmp(self) -> Iterator[BtmpRecord]:
@@ -192,26 +187,18 @@ class UtmpPlugin(Plugin):
192
187
  - https://en.wikipedia.org/wiki/Utmp
193
188
  - https://www.thegeekdiary.com/what-is-the-purpose-of-utmp-wtmp-and-btmp-files-in-linux/
194
189
  """
195
- btmp_paths = self.target.fs.glob(self.BTMP_GLOB)
196
- for btmp_path in btmp_paths:
197
- btmp = UtmpFile(self.target, btmp_path)
198
-
199
- for entry in btmp:
200
- yield BtmpRecord(
201
- ts=entry.ts,
202
- ut_type=entry.ut_type,
203
- ut_pid=entry.ut_pid,
204
- ut_user=entry.ut_user,
205
- ut_line=entry.ut_line,
206
- ut_id=entry.ut_id,
207
- ut_host=entry.ut_host,
208
- ut_addr=entry.ut_addr,
209
- _target=self.target,
210
- )
190
+ for path in self.btmp_paths:
191
+ if not path.is_file():
192
+ self.target.log.warning("Unable to parse btmp file: %s is not a file", path)
193
+ continue
211
194
 
195
+ for entry in UtmpFile(path):
196
+ yield self._build_record(BtmpRecord, entry)
197
+
198
+ @alias("utmp")
212
199
  @export(record=WtmpRecord)
213
200
  def wtmp(self) -> Iterator[WtmpRecord]:
214
- """Return the content of the wtmp log files.
201
+ """Yield contents of wtmp log files.
215
202
 
216
203
  The wtmp file contains the historical data of the utmp file. The utmp file contains information about users
217
204
  logins at which terminals, logouts, system events and current status of the system, system boot time
@@ -220,19 +207,11 @@ class UtmpPlugin(Plugin):
220
207
  References:
221
208
  - https://www.thegeekdiary.com/what-is-the-purpose-of-utmp-wtmp-and-btmp-files-in-linux/
222
209
  """
223
- wtmp_paths = self.target.fs.glob(self.WTMP_GLOB)
224
- for wtmp_path in wtmp_paths:
225
- wtmp = UtmpFile(self.target, wtmp_path)
226
-
227
- for entry in wtmp:
228
- yield WtmpRecord(
229
- ts=entry.ts,
230
- ut_type=entry.ut_type,
231
- ut_pid=entry.ut_pid,
232
- ut_user=entry.ut_user,
233
- ut_line=entry.ut_line,
234
- ut_id=entry.ut_id,
235
- ut_host=entry.ut_host,
236
- ut_addr=entry.ut_addr,
237
- _target=self.target,
238
- )
210
+
211
+ for path in self.wtmp_paths + self.utmp_paths:
212
+ if not path.is_file():
213
+ self.target.log.warning("Unable to parse wtmp file: %s is not a file", path)
214
+ continue
215
+
216
+ for entry in UtmpFile(path):
217
+ yield self._build_record(WtmpRecord, entry)
@@ -12,6 +12,14 @@ from dissect.target.helpers.record import WindowsUserRecord
12
12
  from dissect.target.plugin import OperatingSystem, OSPlugin, export
13
13
  from dissect.target.target import Target
14
14
 
15
+ ARCH_MAP = {
16
+ "x86": 32,
17
+ "IA64": 64,
18
+ "ARM64": 64,
19
+ "EM64T": 64,
20
+ "AMD64": 64,
21
+ }
22
+
15
23
 
16
24
  class WindowsPlugin(OSPlugin):
17
25
  CURRENT_VERSION_KEY = "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion"
@@ -265,19 +273,11 @@ class WindowsPlugin(OSPlugin):
265
273
  Dict: arch: architecture, bitness: bits
266
274
  """
267
275
 
268
- arch_strings = {
269
- "x86": 32,
270
- "IA64": 64,
271
- "ARM64": 64,
272
- "EM64T": 64,
273
- "AMD64": 64,
274
- }
275
-
276
276
  key = "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
277
277
 
278
278
  try:
279
279
  arch = self.target.registry.key(key).value("PROCESSOR_ARCHITECTURE").value
280
- bits = arch_strings.get(arch)
280
+ bits = ARCH_MAP.get(arch)
281
281
 
282
282
  # return {"arch": arch, "bitness": bits}
283
283
  if bits == 64:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.20.dev47
3
+ Version: 3.20.dev49
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
@@ -196,7 +196,7 @@ dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6
196
196
  dissect/target/plugins/general/users.py,sha256=yy9gvRXfN9BT71v4Xqo5hpwfgN9he9Otu8TBPZ_Tegs,3009
197
197
  dissect/target/plugins/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
198
  dissect/target/plugins/os/unix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
- dissect/target/plugins/os/unix/_os.py,sha256=prvcuIkJ1jmbb_g1CtgHmmJUcGjFaZn3yU6VPNMJ27k,15061
199
+ dissect/target/plugins/os/unix/_os.py,sha256=mk2yxWGqdZMzdr8hyYT5nyjSIEN3F5JuoPaaiz0Rvf8,15311
200
200
  dissect/target/plugins/os/unix/applications.py,sha256=AUgZRP35FzswGyFyChj2o4dfGO34Amc6nqHgiMEaqdI,3129
201
201
  dissect/target/plugins/os/unix/cronjobs.py,sha256=tgWQ3BUZpfyvRzodMwGtwFUdPjZ17k7ZRbZ9Q8wmXPk,3393
202
202
  dissect/target/plugins/os/unix/datetime.py,sha256=gKfBdPyUirt3qmVYfOJ1oZXRPn8wRzssbZxR_ARrtk8,1518
@@ -251,7 +251,7 @@ dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa
251
251
  dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=TPjcfv1n68RCe3Er4aCVQwQDCZwJT-NLvje3kPjDfhk,1744
252
252
  dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  dissect/target/plugins/os/unix/linux/fortios/_keys.py,sha256=jDDHObfsUn9BGoIir9p4J_-rg9rI1rgoOfnL3R3lg4o,123358
254
- dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=Cyw6KyGNc-uZn2WDlD-7G9K7swe_ofxwykIZeQRGYKU,19416
254
+ dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=381VI9TDMR2-XPwLsvCU8hcRgTz1H5yJ-q_sCNQzSiM,19790
255
255
  dissect/target/plugins/os/unix/linux/fortios/generic.py,sha256=dc6YTDLV-VZq9k8IWmY_PE0sTGkkp3yamR-cYNUCtes,1265
256
256
  dissect/target/plugins/os/unix/linux/fortios/locale.py,sha256=Pe7Bdj8UemCiktLeQnQ50TpY_skARAzRJA0ewAB4710,5243
257
257
  dissect/target/plugins/os/unix/linux/redhat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -272,9 +272,9 @@ dissect/target/plugins/os/unix/log/auth.py,sha256=9NJvlo7Vbsp_ENJFpKd04PH_sUuOy6
272
272
  dissect/target/plugins/os/unix/log/journal.py,sha256=xe8p8MM_95uYjFNzNSP5IsoIthJtxwFEDicYR42RYAI,17681
273
273
  dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wr3-2n1-GwckN9mSx-yM55N6_L0PQyx6TGHoEvuc6c0,2515
274
274
  dissect/target/plugins/os/unix/log/messages.py,sha256=XtjZ0a2budgQm_K5JT3fMf7JcjuD0AelcD3zOFN2xpI,5732
275
- dissect/target/plugins/os/unix/log/utmp.py,sha256=n22dcjhclky3koF4MyRz1cBOmEeWwen6jstLsvfnMw8,7784
275
+ dissect/target/plugins/os/unix/log/utmp.py,sha256=k2A69s2qUT2JunJrH8GO6nQ0zMDotXMTaj8OzQ7ljj8,7336
276
276
  dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
277
- dissect/target/plugins/os/windows/_os.py,sha256=-Bsp9696JqU7luh_AbqojzG9BxVdYIFl5Ma-LiFBQBo,12505
277
+ dissect/target/plugins/os/windows/_os.py,sha256=WoXSq-HVOKqI9p3CMXrOFAjsi9MlVmxE2JVHzN7RH0s,12441
278
278
  dissect/target/plugins/os/windows/activitiescache.py,sha256=BbGD-vETHm1IRMoazVer_vqSJIoQxxhWcJ_xlBeOMds,6899
279
279
  dissect/target/plugins/os/windows/adpolicy.py,sha256=ul8lKlG9ExABnd6yVLMPFFgVxN74CG4T3MvcRuBLHJc,7158
280
280
  dissect/target/plugins/os/windows/amcache.py,sha256=1jq-S80_FIzGegrqQ6HqrjmaAPTyxyn69HxnbRBlaUc,27608
@@ -378,10 +378,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
378
378
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
379
379
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
380
380
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
381
- dissect.target-3.20.dev47.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
382
- dissect.target-3.20.dev47.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
383
- dissect.target-3.20.dev47.dist-info/METADATA,sha256=28qUe6AIKvwvWI58yI9VPiy1U86ihuVGMhzR-avNrtc,12897
384
- dissect.target-3.20.dev47.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
385
- dissect.target-3.20.dev47.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
386
- dissect.target-3.20.dev47.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
387
- dissect.target-3.20.dev47.dist-info/RECORD,,
381
+ dissect.target-3.20.dev49.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
382
+ dissect.target-3.20.dev49.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
383
+ dissect.target-3.20.dev49.dist-info/METADATA,sha256=cyNr191yNPZoEE1uGY1DqaExAYn8H9NCuOL2OUWn1oM,12897
384
+ dissect.target-3.20.dev49.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
385
+ dissect.target-3.20.dev49.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
386
+ dissect.target-3.20.dev49.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
387
+ dissect.target-3.20.dev49.dist-info/RECORD,,