dissect.target 3.20.dev47__py3-none-any.whl → 3.20.dev48__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/plugins/os/unix/log/utmp.py +47 -68
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/RECORD +8 -8
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,17 @@
|
|
1
|
-
import
|
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
|
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,
|
108
|
-
self.fh =
|
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(
|
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
|
-
|
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
|
-
|
174
|
-
|
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.
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
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
|
-
"""
|
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
|
-
|
224
|
-
for
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev48
|
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
|
@@ -272,7 +272,7 @@ 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=
|
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
277
|
dissect/target/plugins/os/windows/_os.py,sha256=-Bsp9696JqU7luh_AbqojzG9BxVdYIFl5Ma-LiFBQBo,12505
|
278
278
|
dissect/target/plugins/os/windows/activitiescache.py,sha256=BbGD-vETHm1IRMoazVer_vqSJIoQxxhWcJ_xlBeOMds,6899
|
@@ -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.
|
382
|
-
dissect.target-3.20.
|
383
|
-
dissect.target-3.20.
|
384
|
-
dissect.target-3.20.
|
385
|
-
dissect.target-3.20.
|
386
|
-
dissect.target-3.20.
|
387
|
-
dissect.target-3.20.
|
381
|
+
dissect.target-3.20.dev48.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
382
|
+
dissect.target-3.20.dev48.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
383
|
+
dissect.target-3.20.dev48.dist-info/METADATA,sha256=coXjiskalhbUYyedrn0Fe49ZmJvIbeAaMYawTB3Q9QQ,12897
|
384
|
+
dissect.target-3.20.dev48.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
385
|
+
dissect.target-3.20.dev48.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
386
|
+
dissect.target-3.20.dev48.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
387
|
+
dissect.target-3.20.dev48.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev47.dist-info → dissect.target-3.20.dev48.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|