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.
- dissect/target/helpers/protobuf.py +6 -10
- dissect/target/helpers/ssh.py +3 -4
- dissect/target/loaders/mqtt.py +147 -2
- dissect/target/plugins/apps/av/trendmicro.py +2 -3
- dissect/target/plugins/apps/container/docker.py +1 -1
- dissect/target/plugins/os/unix/locate/gnulocate.py +1 -2
- dissect/target/plugins/os/unix/locate/mlocate.py +3 -4
- dissect/target/plugins/os/unix/locate/plocate.py +1 -2
- dissect/target/plugins/os/unix/log/atop.py +3 -4
- dissect/target/plugins/os/unix/log/journal.py +5 -4
- dissect/target/plugins/os/unix/log/lastlog.py +2 -3
- dissect/target/plugins/os/unix/log/utmp.py +6 -7
- dissect/target/plugins/os/windows/adpolicy.py +3 -4
- dissect/target/plugins/os/windows/credhist.py +1 -2
- dissect/target/plugins/os/windows/datetime.py +3 -4
- dissect/target/plugins/os/windows/defender.py +3 -4
- dissect/target/plugins/os/windows/dpapi/blob.py +1 -2
- dissect/target/plugins/os/windows/dpapi/master_key.py +2 -3
- dissect/target/plugins/os/windows/notifications.py +1 -2
- dissect/target/plugins/os/windows/prefetch.py +26 -27
- dissect/target/plugins/os/windows/recyclebin.py +10 -8
- dissect/target/plugins/os/windows/regf/auditpol.py +4 -5
- dissect/target/plugins/os/windows/regf/bam.py +2 -3
- dissect/target/plugins/os/windows/regf/cit.py +1 -2
- dissect/target/plugins/os/windows/regf/recentfilecache.py +3 -4
- dissect/target/plugins/os/windows/regf/shellbags.py +1 -2
- dissect/target/plugins/os/windows/regf/shimcache.py +2 -3
- dissect/target/plugins/os/windows/regf/userassist.py +5 -6
- dissect/target/plugins/os/windows/sam.py +4 -5
- dissect/target/plugins/os/windows/task_helpers/tasks_job.py +3 -4
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/METADATA +49 -24
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/RECORD +37 -37
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/LICENSE +0 -0
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/WHEEL +0 -0
- {dissect.target-3.18.dev5.dist-info → dissect.target-3.18.dev7.dist-info}/entry_points.txt +0 -0
- {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(
|
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
|
-
|
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
|
-
|
18
|
+
@classmethod
|
19
|
+
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> int:
|
21
20
|
return decode_varint(stream)
|
22
21
|
|
23
|
-
|
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.
|
dissect/target/helpers/ssh.py
CHANGED
@@ -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
|
-
|
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
|
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-----"
|
dissect/target/loaders/mqtt.py
CHANGED
@@ -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
|
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.
|
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.
|
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})")
|
@@ -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
|
23
|
+
int8 pad0[2]; /* 32-bit total alignment */
|
24
24
|
char root_database;
|
25
25
|
char config_block[conf_size];
|
26
|
-
int8
|
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:
|
@@ -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
|
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[
|
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
|
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[
|
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.
|
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
|
-
|
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 :
|
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
|
-
|
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 =
|
124
|
+
entry = c_utmp.entry(byte_stream)
|
126
125
|
|
127
126
|
r_type = ""
|
128
|
-
if entry.ut_type in
|
129
|
-
r_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
|
-
|
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.
|
28
|
-
c_adpolicy.load(c_def)
|
27
|
+
c_adpolicy = cstruct().load(policy_def)
|
29
28
|
|
30
29
|
ADPolicyRecord = TargetRecordDescriptor(
|
31
30
|
"windows/adpolicy",
|
@@ -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.
|
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:
|
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
|
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 {
|
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!")
|
@@ -29,7 +29,7 @@ struct DomainKey {
|
|
29
29
|
DWORD accessCheckLen;
|
30
30
|
char guid[16];
|
31
31
|
char encryptedSecret[secretLen];
|
32
|
-
char
|
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:
|