dissect.target 3.18.dev5__py3-none-any.whl → 3.18.dev7__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.
Files changed (37) hide show
  1. dissect/target/helpers/protobuf.py +6 -10
  2. dissect/target/helpers/ssh.py +3 -4
  3. dissect/target/loaders/mqtt.py +147 -2
  4. dissect/target/plugins/apps/av/trendmicro.py +2 -3
  5. dissect/target/plugins/apps/container/docker.py +1 -1
  6. dissect/target/plugins/os/unix/locate/gnulocate.py +1 -2
  7. dissect/target/plugins/os/unix/locate/mlocate.py +3 -4
  8. dissect/target/plugins/os/unix/locate/plocate.py +1 -2
  9. dissect/target/plugins/os/unix/log/atop.py +3 -4
  10. dissect/target/plugins/os/unix/log/journal.py +5 -4
  11. dissect/target/plugins/os/unix/log/lastlog.py +2 -3
  12. dissect/target/plugins/os/unix/log/utmp.py +6 -7
  13. dissect/target/plugins/os/windows/adpolicy.py +3 -4
  14. dissect/target/plugins/os/windows/credhist.py +1 -2
  15. dissect/target/plugins/os/windows/datetime.py +3 -4
  16. dissect/target/plugins/os/windows/defender.py +3 -4
  17. dissect/target/plugins/os/windows/dpapi/blob.py +1 -2
  18. dissect/target/plugins/os/windows/dpapi/master_key.py +2 -3
  19. dissect/target/plugins/os/windows/notifications.py +1 -2
  20. dissect/target/plugins/os/windows/prefetch.py +26 -27
  21. dissect/target/plugins/os/windows/recyclebin.py +10 -8
  22. dissect/target/plugins/os/windows/regf/auditpol.py +4 -5
  23. dissect/target/plugins/os/windows/regf/bam.py +2 -3
  24. dissect/target/plugins/os/windows/regf/cit.py +1 -2
  25. dissect/target/plugins/os/windows/regf/recentfilecache.py +3 -4
  26. dissect/target/plugins/os/windows/regf/shellbags.py +1 -2
  27. dissect/target/plugins/os/windows/regf/shimcache.py +2 -3
  28. dissect/target/plugins/os/windows/regf/userassist.py +5 -6
  29. dissect/target/plugins/os/windows/sam.py +4 -5
  30. dissect/target/plugins/os/windows/task_helpers/tasks_job.py +3 -4
  31. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/METADATA +49 -24
  32. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/RECORD +37 -37
  33. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/COPYRIGHT +0 -0
  34. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/LICENSE +0 -0
  35. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/WHEEL +0 -0
  36. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/entry_points.txt +0 -0
  37. {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/top_level.txt +0 -0
@@ -3,30 +3,26 @@ from __future__ import annotations
3
3
  from typing import Any, BinaryIO
4
4
 
5
5
  from dissect.cstruct.types.base import BaseType
6
- from dissect.cstruct.types.bytesinteger import BytesInteger
7
6
 
8
7
 
9
- class ProtobufVarint(BytesInteger):
8
+ class ProtobufVarint(BaseType):
10
9
  """Implements a protobuf integer type for dissect.cstruct that can span a variable amount of bytes.
11
10
 
12
- Mainly follows the cstruct BytesInteger implementation with minor tweaks
13
- to support protobuf's msb varint implementation.
11
+ Supports protobuf's msb varint implementation.
14
12
 
15
13
  Resources:
16
14
  - https://protobuf.dev/programming-guides/encoding/
17
15
  - https://github.com/protocolbuffers/protobuf/blob/main/python/google/protobuf/internal/decoder.py
18
16
  """
19
17
 
20
- def _read(self, stream: BinaryIO, context: dict[str, Any] = None) -> int:
18
+ @classmethod
19
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> int:
21
20
  return decode_varint(stream)
22
21
 
23
- def _write(self, stream: BinaryIO, data: int) -> int:
22
+ @classmethod
23
+ def _write(cls, stream: BinaryIO, data: int) -> int:
24
24
  return stream.write(encode_varint(data))
25
25
 
26
- _read_array = BaseType._read_array
27
-
28
- _write_array = BaseType._write_array
29
-
30
26
 
31
27
  def decode_varint(stream: BinaryIO) -> int:
32
28
  """Reads a varint from the provided buffer stream.
@@ -1,9 +1,9 @@
1
1
  import base64
2
2
  import binascii
3
3
 
4
- from dissect import cstruct
4
+ from dissect.cstruct import cstruct
5
5
 
6
- c_rfc4716_def = """
6
+ rfc4716_def = """
7
7
  struct ssh_string {
8
8
  uint32 length;
9
9
  char value[length];
@@ -23,8 +23,7 @@ struct ssh_private_key {
23
23
  }
24
24
  """
25
25
 
26
- c_rfc4716 = cstruct.cstruct(endian=">")
27
- c_rfc4716.load(c_rfc4716_def)
26
+ c_rfc4716 = cstruct(endian=">").load(rfc4716_def)
28
27
 
29
28
  RFC4716_MARKER_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
30
29
  RFC4716_MARKER_END = b"-----END OPENSSH PRIVATE KEY-----"
@@ -1,13 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import atexit
3
4
  import logging
5
+ import math
6
+ import os
4
7
  import ssl
8
+ import sys
5
9
  import time
6
10
  import urllib
7
11
  from dataclasses import dataclass
8
12
  from functools import lru_cache
9
13
  from pathlib import Path
10
14
  from struct import pack, unpack_from
15
+ from threading import Thread
11
16
  from typing import Any, Callable, Iterator, Optional, Union
12
17
 
13
18
  import paho.mqtt.client as mqtt
@@ -51,6 +56,34 @@ class SeekMessage:
51
56
  data: bytes = b""
52
57
 
53
58
 
59
+ class MQTTTransferRatePerSecond:
60
+ def __init__(self, window_size: int = 10):
61
+ self.window_size = window_size
62
+ self.timestamps = []
63
+ self.bytes = []
64
+
65
+ def record(self, timestamp: float, byte_count: int) -> MQTTTransferRatePerSecond:
66
+ while self.timestamps and (timestamp - self.timestamps[0] > self.window_size):
67
+ self.timestamps.pop(0)
68
+ self.bytes.pop(0)
69
+
70
+ self.timestamps.append(timestamp)
71
+ self.bytes.append(byte_count)
72
+ return self
73
+
74
+ def value(self, current_time: float) -> float:
75
+ if not self.timestamps:
76
+ return 0
77
+
78
+ elapsed_time = current_time - self.timestamps[0]
79
+ if elapsed_time == 0:
80
+ return 0
81
+
82
+ total_bytes = self.bytes[-1] - self.bytes[0]
83
+
84
+ return total_bytes / elapsed_time
85
+
86
+
54
87
  class MQTTStream(AlignedStream):
55
88
  def __init__(self, stream: MQTTConnection, disk_id: int, size: Optional[int] = None):
56
89
  self.stream = stream
@@ -62,12 +95,108 @@ class MQTTStream(AlignedStream):
62
95
  return data
63
96
 
64
97
 
98
+ class MQTTDiagnosticLine:
99
+ def __init__(self, connection: MQTTConnection, total_peers: int):
100
+ self.connection = connection
101
+ self.total_peers = total_peers
102
+ self._columns, self._rows = os.get_terminal_size(0)
103
+ atexit.register(self._detach)
104
+ self._attach()
105
+
106
+ def _attach(self) -> None:
107
+ # save cursor position
108
+ sys.stderr.write("\0337")
109
+ # set top and bottom margins of the scrolling region to default
110
+ sys.stderr.write("\033[r")
111
+ # restore cursor position
112
+ sys.stderr.write("\0338")
113
+ # move cursor down one line in the same column; if at the bottom, the screen scrolls up
114
+ sys.stderr.write("\033D")
115
+ # move cursor up one line in the same column; if at the top, screen scrolls down
116
+ sys.stderr.write("\033M")
117
+ # save cursor position again
118
+ sys.stderr.write("\0337")
119
+ # restrict scrolling to a region from the first line to one before the last line
120
+ sys.stderr.write(f"\033[1;{self._rows - 1}r")
121
+ # restore cursor position after setting scrolling region
122
+ sys.stderr.write("\0338")
123
+
124
+ def _detach(self) -> None:
125
+ # save cursor position
126
+ sys.stderr.write("\0337")
127
+ # move cursor to the specified position (last line, first column)
128
+ sys.stderr.write(f"\033[{self._rows};1H")
129
+ # clear from cursor to end of the line
130
+ sys.stderr.write("\033[K")
131
+ # reset scrolling region to include the entire display
132
+ sys.stderr.write("\033[r")
133
+ # restore cursor position
134
+ sys.stderr.write("\0338")
135
+ # ensure the written content is displayed (flush output)
136
+ sys.stderr.flush()
137
+
138
+ def display(self) -> None:
139
+ # prepare: set background color to blue and text color to white at the beginning of the line
140
+ prefix = "\x1b[44m\x1b[37m\r"
141
+ # reset all attributes (colors, styles) to their defaults afterwards
142
+ suffix = "\x1b[0m"
143
+ # separator to set background color to red and text style to bold
144
+ separator = "\x1b[41m\x1b[1m"
145
+ logo = "TARGETD"
146
+
147
+ start = time.time()
148
+ transfer_rate = MQTTTransferRatePerSecond(window_size=7)
149
+
150
+ while True:
151
+ time.sleep(0.05)
152
+ peers = "?"
153
+ try:
154
+ peers = len(self.connection.broker.peers(self.connection.host))
155
+ except Exception:
156
+ pass
157
+
158
+ recv = self.connection.broker.bytes_received
159
+ now = time.time()
160
+ transfer = transfer_rate.record(now, recv).value(now) / 1000 # convert to KB/s
161
+ failures = self.connection.retries
162
+ seconds_elapsed = round(now - start) % 60
163
+ minutes_elapsed = math.floor((now - start) / 60) % 60
164
+ hours_elapsed = math.floor((now - start) / 60**2)
165
+ timer = f"{hours_elapsed:02d}:{minutes_elapsed:02d}:{seconds_elapsed:02d}"
166
+ display = f"{timer} {peers}/{self.total_peers} peers {transfer:>8.2f} KB p/s {failures:>4} failures"
167
+ rest = self._columns - len(display)
168
+ padding = (rest - len(logo)) * " "
169
+
170
+ # save cursor position
171
+ sys.stderr.write("\0337")
172
+ # move cursor to specified position (last line, first column)
173
+ sys.stderr.write(f"\033[{self._rows};1H")
174
+ # disable line wrapping
175
+ sys.stderr.write("\033[?7l")
176
+ # reset all attributes
177
+ sys.stderr.write("\033[0m")
178
+ # write the display line with prefix, calculated display content, padding, separator, and logo
179
+ sys.stderr.write(prefix + display + padding + separator + logo + suffix)
180
+ # enable line wrapping again
181
+ sys.stderr.write("\033[?7h")
182
+ # restore cursor position
183
+ sys.stderr.write("\0338")
184
+ # flush output to ensure it is displayed
185
+ sys.stderr.flush()
186
+
187
+ def start(self) -> None:
188
+ t = Thread(target=self.display)
189
+ t.daemon = True
190
+ t.start()
191
+
192
+
65
193
  class MQTTConnection:
66
194
  broker = None
67
195
  host = None
68
196
  prev = -1
69
197
  factor = 1
70
198
  prefetch_factor_inc = 10
199
+ retries = 0
71
200
 
72
201
  def __init__(self, broker: Broker, host: str):
73
202
  self.broker = broker
@@ -125,6 +254,7 @@ class MQTTConnection:
125
254
  # message might have not reached agent, resend...
126
255
  self.broker.seek(self.host, disk_id, offset, flength, optimization_strategy)
127
256
  attempts = 0
257
+ self.retries += 1
128
258
 
129
259
  return message.data
130
260
 
@@ -138,6 +268,8 @@ class Broker:
138
268
  mqtt_client = None
139
269
  connected = False
140
270
  case = None
271
+ bytes_received = 0
272
+ monitor = False
141
273
 
142
274
  diskinfo = {}
143
275
  index = {}
@@ -217,6 +349,9 @@ class Broker:
217
349
  if casename != self.case:
218
350
  return
219
351
 
352
+ if self.monitor:
353
+ self.bytes_received += len(msg.payload)
354
+
220
355
  if response == "DISKS":
221
356
  self._on_disk(hostname, msg.payload)
222
357
  elif response == "READ":
@@ -238,9 +373,12 @@ class Broker:
238
373
  self.mqtt_client.publish(f"{self.case}/{host}/INFO")
239
374
 
240
375
  def topology(self, host: str) -> None:
241
- self.topo[host] = []
376
+ if host not in self.topo:
377
+ self.topo[host] = []
242
378
  self.mqtt_client.subscribe(f"{self.case}/{host}/ID")
243
379
  time.sleep(1) # need some time to avoid race condition, i.e. MQTT might react too fast
380
+ # send a simple clear command (invalid, just clears the prev. msg) just in case TOPO is stale
381
+ self.mqtt_client.publish(f"{self.case}/{host}/CLR")
244
382
  self.mqtt_client.publish(f"{self.case}/{host}/TOPO")
245
383
 
246
384
  def connect(self) -> None:
@@ -272,6 +410,7 @@ class Broker:
272
410
  @arg("--mqtt-crt", dest="crt", help="client certificate file")
273
411
  @arg("--mqtt-ca", dest="ca", help="certificate authority file")
274
412
  @arg("--mqtt-command", dest="command", help="direct command to client(s)")
413
+ @arg("--mqtt-diag", action="store_true", dest="diag", help="show MQTT diagnostic information")
275
414
  class MQTTLoader(Loader):
276
415
  """Load remote targets through a broker."""
277
416
 
@@ -292,6 +431,7 @@ class MQTTLoader(Loader):
292
431
  def find_all(path: Path, **kwargs) -> Iterator[str]:
293
432
  cls = MQTTLoader
294
433
  num_peers = 1
434
+
295
435
  if cls.broker is None:
296
436
  if (uri := kwargs.get("parsed_path")) is None:
297
437
  raise LoaderError("No URI connection details have been passed.")
@@ -299,8 +439,13 @@ class MQTTLoader(Loader):
299
439
  cls.broker = Broker(**options)
300
440
  cls.broker.connect()
301
441
  num_peers = int(options.get("peers", 1))
442
+ cls.connection = MQTTConnection(cls.broker, path)
443
+ if options.get("diag", None):
444
+ cls.broker.monitor = True
445
+ MQTTDiagnosticLine(cls.connection, num_peers).start()
446
+ else:
447
+ cls.connection = MQTTConnection(cls.broker, path)
302
448
 
303
- cls.connection = MQTTConnection(cls.broker, path)
304
449
  cls.peers = cls.connection.topo(num_peers)
305
450
  yield from cls.peers
306
451
 
@@ -1,6 +1,6 @@
1
1
  from typing import Iterator
2
2
 
3
- from dissect import cstruct
3
+ from dissect.cstruct import cstruct
4
4
  from dissect.util.ts import from_unix
5
5
 
6
6
  from dissect.target import Target
@@ -47,8 +47,7 @@ struct firewall_entry {
47
47
  char _pad3[10];
48
48
  };
49
49
  """
50
- c_pfwlog = cstruct.cstruct()
51
- c_pfwlog.load(pfwlog_def)
50
+ c_pfwlog = cstruct().load(pfwlog_def)
52
51
 
53
52
 
54
53
  class TrendMicroPlugin(Plugin):
@@ -88,7 +88,7 @@ struct entry {
88
88
  """
89
89
 
90
90
  c_local = cstruct(endian=">")
91
- c_local.addtype("varint", ProtobufVarint(c_local, "varint", size=None, signed=False, alignment=1))
91
+ c_local.add_custom_type("varint", ProtobufVarint, size=None, alignment=1, signed=False)
92
92
  c_local.load(local_def, compiled=False)
93
93
 
94
94
  RE_DOCKER_NS = re.compile(r"\.(?P<nanoseconds>\d{7,})(?P<postfix>Z|\+\d{2}:\d{2})")
@@ -26,8 +26,7 @@ GNULocateRecord = TargetRecordDescriptor(
26
26
  ],
27
27
  )
28
28
 
29
- c_gnulocate = cstruct()
30
- c_gnulocate.load(gnulocate_def)
29
+ c_gnulocate = cstruct().load(gnulocate_def)
31
30
 
32
31
 
33
32
  class GNULocateFile:
@@ -20,10 +20,10 @@ struct header_config {
20
20
  int32 conf_size;
21
21
  int8 version; /* file format version */
22
22
  int8 require_visibility;
23
- int8 pad[2]; /* 32-bit total alignment */
23
+ int8 pad0[2]; /* 32-bit total alignment */
24
24
  char root_database;
25
25
  char config_block[conf_size];
26
- int8 pad;
26
+ int8 pad1;
27
27
  };
28
28
 
29
29
  enum DBE_TYPE: uint8 { /* database entry type */
@@ -68,8 +68,7 @@ MLocateRecord = TargetRecordDescriptor(
68
68
  ],
69
69
  )
70
70
 
71
- c_mlocate = cstruct(endian=">")
72
- c_mlocate.load(mlocate_def)
71
+ c_mlocate = cstruct(endian=">").load(mlocate_def)
73
72
 
74
73
 
75
74
  class MLocateFile:
@@ -65,8 +65,7 @@ PLocateRecord = TargetRecordDescriptor(
65
65
  ],
66
66
  )
67
67
 
68
- c_plocate = cstruct()
69
- c_plocate.load(plocate_def)
68
+ c_plocate = cstruct().load(plocate_def)
70
69
 
71
70
 
72
71
  class PLocateFile:
@@ -2,7 +2,7 @@ import zlib
2
2
  from io import BytesIO
3
3
  from typing import BinaryIO, Iterator
4
4
 
5
- from dissect.cstruct import Instance, cstruct
5
+ from dissect.cstruct import cstruct
6
6
 
7
7
  from dissect.target.exceptions import UnsupportedPluginError
8
8
  from dissect.target.helpers.record import TargetRecordDescriptor
@@ -178,8 +178,7 @@ struct tstat {
178
178
  };
179
179
  """ # noqa: E501
180
180
 
181
- c_atop = cstruct()
182
- c_atop.load(atop_def)
181
+ c_atop = cstruct().load(atop_def)
183
182
  c_atop.load(atop_tstat_def, align=True)
184
183
 
185
184
  AtopRecord = TargetRecordDescriptor(
@@ -226,7 +225,7 @@ class AtopFile:
226
225
  self.header = c_atop.rawheader(self.fh)
227
226
  self.version = self.version()
228
227
 
229
- def __iter__(self) -> Iterator[Instance]:
228
+ def __iter__(self) -> Iterator[c_atop.tstat]:
230
229
  while True:
231
230
  try:
232
231
  record = c_atop.rawrecord(self.fh)
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import lzma
2
4
  from typing import BinaryIO, Callable, Iterator
3
5
 
4
6
  import zstandard
5
- from dissect.cstruct import Instance, cstruct
7
+ from dissect.cstruct import cstruct
6
8
  from dissect.util import ts
7
9
  from dissect.util.compression import lz4
8
10
 
@@ -252,8 +254,7 @@ struct EntryArrayObject_Compact {
252
254
  };
253
255
  """ # noqa: E501
254
256
 
255
- c_journal = cstruct()
256
- c_journal.load(journal_def)
257
+ c_journal = cstruct().load(journal_def)
257
258
 
258
259
 
259
260
  def get_optional(value: str, to_type: Callable):
@@ -314,7 +315,7 @@ class JournalFile:
314
315
 
315
316
  return key, value
316
317
 
317
- def __iter__(self) -> Iterator[Instance]:
318
+ def __iter__(self) -> Iterator[dict[str, int | str]]:
318
319
  "Iterate over the entry objects to read payloads."
319
320
 
320
321
  for offset in self.entry_object_offsets():
@@ -1,6 +1,6 @@
1
1
  from typing import BinaryIO
2
2
 
3
- from dissect import cstruct
3
+ from dissect.cstruct import cstruct
4
4
  from dissect.util import ts
5
5
 
6
6
  from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
@@ -36,8 +36,7 @@ struct entry {
36
36
  };
37
37
  """
38
38
 
39
- c_lastlog = cstruct.cstruct()
40
- c_lastlog.load(lastlog_def)
39
+ c_lastlog = cstruct().load(lastlog_def)
41
40
 
42
41
 
43
42
  class LastLogFile:
@@ -39,14 +39,14 @@ WtmpRecord = TargetRecordDescriptor(
39
39
  ],
40
40
  )
41
41
 
42
- c_utmp = """
42
+ utmp_def = """
43
43
  #define UT_LINESIZE 32
44
44
  #define UT_NAMESIZE 32
45
45
  #define UT_HOSTSIZE 256
46
46
 
47
47
  typedef uint32 pid_t;
48
48
 
49
- enum Type : char {
49
+ enum Type : uint8_t {
50
50
  EMPTY = 0x0,
51
51
  RUN_LVL = 0x1,
52
52
  BOOT_TIME = 0x2,
@@ -84,8 +84,7 @@ struct entry {
84
84
  };
85
85
  """ # noqa: E501
86
86
 
87
- utmp = cstruct()
88
- utmp.load(c_utmp)
87
+ c_utmp = cstruct().load(utmp_def)
89
88
 
90
89
  UTMP_ENTRY = namedtuple(
91
90
  "UTMPRecord",
@@ -122,11 +121,11 @@ class UtmpFile:
122
121
 
123
122
  while True:
124
123
  try:
125
- entry = utmp.entry(byte_stream)
124
+ entry = c_utmp.entry(byte_stream)
126
125
 
127
126
  r_type = ""
128
- if entry.ut_type in utmp.Type.reverse:
129
- r_type = utmp.Type.reverse[entry.ut_type]
127
+ if entry.ut_type in c_utmp.Type:
128
+ r_type = c_utmp.Type(entry.ut_type).name
130
129
 
131
130
  ut_host = entry.ut_host.decode(errors="surrogateescape").strip("\x00")
132
131
  ut_addr = None
@@ -1,7 +1,7 @@
1
1
  from struct import unpack
2
2
 
3
3
  from defusedxml import ElementTree
4
- from dissect import cstruct
4
+ from dissect.cstruct import cstruct
5
5
  from dissect.regf.c_regf import (
6
6
  REG_BINARY,
7
7
  REG_DWORD,
@@ -18,14 +18,13 @@ from dissect.target.exceptions import UnsupportedPluginError
18
18
  from dissect.target.helpers.record import TargetRecordDescriptor
19
19
  from dissect.target.plugin import Plugin, export
20
20
 
21
- c_def = """
21
+ policy_def = """
22
22
  struct registry_policy_header {
23
23
  uint32 signature;
24
24
  uint32 version;
25
25
  };
26
26
  """
27
- c_adpolicy = cstruct.cstruct()
28
- c_adpolicy.load(c_def)
27
+ c_adpolicy = cstruct().load(policy_def)
29
28
 
30
29
  ADPolicyRecord = TargetRecordDescriptor(
31
30
  "windows/adpolicy",
@@ -53,8 +53,7 @@ struct entry {
53
53
  };
54
54
  """
55
55
 
56
- c_credhist = cstruct()
57
- c_credhist.load(credhist_def)
56
+ c_credhist = cstruct().load(credhist_def)
58
57
 
59
58
 
60
59
  @dataclass
@@ -3,7 +3,7 @@ from collections import namedtuple
3
3
  from datetime import datetime, timedelta, timezone, tzinfo
4
4
  from typing import Dict, Tuple
5
5
 
6
- from dissect import cstruct
6
+ from dissect.cstruct import cstruct
7
7
 
8
8
  from dissect.target.exceptions import (
9
9
  RegistryError,
@@ -34,8 +34,7 @@ typedef struct _REG_TZI_FORMAT {
34
34
  SYSTEMTIME DaylightDate;
35
35
  } REG_TZI_FORMAT;
36
36
  """
37
- c_tz = cstruct.cstruct()
38
- c_tz.load(tz_def)
37
+ c_tz = cstruct().load(tz_def)
39
38
 
40
39
 
41
40
  # Althoug calendar.SUNDAY is only officially documented since Python 3.10, it
@@ -63,7 +62,7 @@ ZERO = timedelta(0)
63
62
  HOUR = timedelta(hours=1)
64
63
 
65
64
 
66
- def parse_systemtime_transition(systemtime: cstruct.Instance, year: int) -> datetime:
65
+ def parse_systemtime_transition(systemtime: c_tz._SYSTEMTIME, year: int) -> datetime:
67
66
  """Return the transition datetime for a given year using the SYSTEMTIME of a STD or DST transition date.
68
67
 
69
68
  The SYSTEMTIME date of a TZI structure needs to be used to calculate the actual date for a given year.
@@ -237,8 +237,7 @@ struct QuarantineEntryResourceField {
237
237
  };
238
238
  """
239
239
 
240
- c_defender = cstruct()
241
- c_defender.load(defender_def)
240
+ c_defender = cstruct().load(defender_def)
242
241
 
243
242
  STREAM_ID = c_defender.STREAM_ID
244
243
  STREAM_ATTRIBUTES = c_defender.STREAM_ATTRIBUTES
@@ -381,7 +380,7 @@ class QuarantineEntryResource:
381
380
  self.last_access_time = ts.wintimestamp(int.from_bytes(field.Data, "little"))
382
381
  elif field.Identifier == FIELD_IDENTIFIER.LastWriteTime:
383
382
  self.last_write_time = ts.wintimestamp(int.from_bytes(field.Data, "little"))
384
- elif field.Identifier not in FIELD_IDENTIFIER.values.values():
383
+ elif field.Identifier not in FIELD_IDENTIFIER:
385
384
  self.unknown_fields.append(field)
386
385
 
387
386
 
@@ -526,7 +525,7 @@ class MicrosoftDefenderPlugin(plugin.Plugin):
526
525
  subdir = resource.resource_id[0:2]
527
526
  resourcedata_location = resourcedata_directory.joinpath(subdir).joinpath(resource.resource_id)
528
527
  if not resourcedata_location.exists():
529
- self.target.log.warning(f"Could not find a ResourceData file for {entry.resource_id}.")
528
+ self.target.log.warning(f"Could not find a ResourceData file for {resource.resource_id}.")
530
529
  continue
531
530
  if not resourcedata_location.is_file():
532
531
  self.target.log.warning(f"{resourcedata_location} is not a file!")
@@ -36,8 +36,7 @@ struct DPAPIBlob {
36
36
  };
37
37
  """
38
38
 
39
- c_blob = cstruct()
40
- c_blob.load(blob_def)
39
+ c_blob = cstruct().load(blob_def)
41
40
 
42
41
 
43
42
  class Blob:
@@ -29,7 +29,7 @@ struct DomainKey {
29
29
  DWORD accessCheckLen;
30
30
  char guid[16];
31
31
  char encryptedSecret[secretLen];
32
- char accessCheckLen[accessCheckLen];
32
+ char accessCheck[accessCheckLen];
33
33
  };
34
34
 
35
35
  struct CredHist {
@@ -66,8 +66,7 @@ struct MasterKeyFileHeader {
66
66
  QWORD qwDomainKeySize;
67
67
  };
68
68
  """
69
- c_master_key = cstruct()
70
- c_master_key.load(master_key_def)
69
+ c_master_key = cstruct().load(master_key_def)
71
70
 
72
71
 
73
72
  class MasterKey:
@@ -91,8 +91,7 @@ typedef struct {
91
91
  } Chunk; // size: 0x23810
92
92
  """
93
93
 
94
- c_appdb = cstruct(endian="<")
95
- c_appdb.load(appdb_def)
94
+ c_appdb = cstruct(endian="<").load(appdb_def)
96
95
 
97
96
  APPDB_MAGIC = b"DNPW"
98
97
  NUM_APPDB_CHUNKS = 256