dissect.target 3.20.dev10__py3-none-any.whl → 3.20.dev12__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/messages.py +6 -11
- dissect/target/plugins/os/unix/trash.py +132 -0
- dissect/target/tools/utils.py +4 -0
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/RECORD +10 -9
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ from dissect.target import Target
|
|
6
6
|
from dissect.target.exceptions import UnsupportedPluginError
|
7
7
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
8
8
|
from dissect.target.helpers.utils import year_rollover_helper
|
9
|
-
from dissect.target.plugin import Plugin, export
|
9
|
+
from dissect.target.plugin import Plugin, alias, export
|
10
10
|
|
11
11
|
MessagesRecord = TargetRecordDescriptor(
|
12
12
|
"linux/log/messages",
|
@@ -24,7 +24,9 @@ RE_TS = re.compile(r"(\w+\s{1,2}\d+\s\d{2}:\d{2}:\d{2})")
|
|
24
24
|
RE_DAEMON = re.compile(r"^[^:]+:\d+:\d+[^\[\]:]+\s([^\[:]+)[\[|:]{1}")
|
25
25
|
RE_PID = re.compile(r"\w\[(\d+)\]")
|
26
26
|
RE_MSG = re.compile(r"[^:]+:\d+:\d+[^:]+:\s(.*)$")
|
27
|
-
RE_CLOUD_INIT_LINE = re.compile(
|
27
|
+
RE_CLOUD_INIT_LINE = re.compile(
|
28
|
+
r"^(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) - (?P<daemon>.*)\[(?P<log_level>\w+)\]\: (?P<message>.*)$"
|
29
|
+
)
|
28
30
|
|
29
31
|
|
30
32
|
class MessagesPlugin(Plugin):
|
@@ -43,19 +45,12 @@ class MessagesPlugin(Plugin):
|
|
43
45
|
if not self.log_files:
|
44
46
|
raise UnsupportedPluginError("No log files found")
|
45
47
|
|
46
|
-
@
|
47
|
-
def syslog(self) -> Iterator[MessagesRecord]:
|
48
|
-
"""Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
|
49
|
-
|
50
|
-
See ``messages`` for more information.
|
51
|
-
"""
|
52
|
-
return self.messages()
|
53
|
-
|
48
|
+
@alias("syslog")
|
54
49
|
@export(record=MessagesRecord)
|
55
50
|
def messages(self) -> Iterator[MessagesRecord]:
|
56
51
|
"""Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
|
57
52
|
|
58
|
-
|
53
|
+
Due to year rollover detection, the contents of the files are returned in reverse.
|
59
54
|
|
60
55
|
The messages log file holds information about a variety of events such as the system error messages, system
|
61
56
|
startups and shutdowns, change in the network configuration, etc. Aims to store valuable, non-debug and
|
@@ -0,0 +1,132 @@
|
|
1
|
+
from typing import Iterator
|
2
|
+
|
3
|
+
from dissect.target.exceptions import UnsupportedPluginError
|
4
|
+
from dissect.target.helpers import configutil
|
5
|
+
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
6
|
+
from dissect.target.helpers.fsutil import TargetPath
|
7
|
+
from dissect.target.helpers.record import create_extended_descriptor
|
8
|
+
from dissect.target.plugin import Plugin, alias, export
|
9
|
+
from dissect.target.plugins.general.users import UserDetails
|
10
|
+
from dissect.target.target import Target
|
11
|
+
|
12
|
+
TrashRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
13
|
+
"linux/filesystem/recyclebin",
|
14
|
+
[
|
15
|
+
("datetime", "ts"),
|
16
|
+
("path", "path"),
|
17
|
+
("filesize", "filesize"),
|
18
|
+
("path", "deleted_path"),
|
19
|
+
("path", "source"),
|
20
|
+
],
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
class GnomeTrashPlugin(Plugin):
|
25
|
+
"""Linux GNOME Trash plugin."""
|
26
|
+
|
27
|
+
PATHS = [
|
28
|
+
# Default $XDG_DATA_HOME/Trash
|
29
|
+
".local/share/Trash",
|
30
|
+
]
|
31
|
+
|
32
|
+
def __init__(self, target: Target):
|
33
|
+
super().__init__(target)
|
34
|
+
self.trashes = list(self._garbage_collector())
|
35
|
+
|
36
|
+
def _garbage_collector(self) -> Iterator[tuple[UserDetails, TargetPath]]:
|
37
|
+
"""it aint much, but its honest work"""
|
38
|
+
for user_details in self.target.user_details.all_with_home():
|
39
|
+
for trash_path in self.PATHS:
|
40
|
+
if (path := user_details.home_path.joinpath(trash_path)).exists():
|
41
|
+
yield user_details, path
|
42
|
+
|
43
|
+
def check_compatible(self) -> None:
|
44
|
+
if not self.trashes:
|
45
|
+
raise UnsupportedPluginError("No Trash folder(s) found")
|
46
|
+
|
47
|
+
@export(record=TrashRecord)
|
48
|
+
@alias(name="recyclebin")
|
49
|
+
def trash(self) -> Iterator[TrashRecord]:
|
50
|
+
"""Yield deleted files from GNOME Trash folders.
|
51
|
+
|
52
|
+
Recovers deleted files and artifacts from ``$HOME/.local/share/Trash``.
|
53
|
+
Probably also works with other desktop interfaces as long as they follow the Trash specification from FreeDesktop.
|
54
|
+
|
55
|
+
Currently does not parse media trash locations such as ``/media/$Label/.Trash-1000/*``.
|
56
|
+
|
57
|
+
Resources:
|
58
|
+
- https://specifications.freedesktop.org/trash-spec/latest/
|
59
|
+
- https://github.com/GNOME/glib/blob/main/gio/glocalfile.c
|
60
|
+
- https://specifications.freedesktop.org/basedir-spec/latest/
|
61
|
+
|
62
|
+
Yields ``TrashRecord``s with the following fields:
|
63
|
+
|
64
|
+
.. code-block:: text
|
65
|
+
|
66
|
+
ts (datetime): timestamp when the file was deleted or for expunged files when it could not be permanently deleted
|
67
|
+
path (path): path where the file was located before it was deleted
|
68
|
+
filesize (filesize): size in bytes of the deleted file
|
69
|
+
deleted_path (path): path to the current location of the deleted file
|
70
|
+
source (path): path to the .trashinfo file
|
71
|
+
""" # noqa: E501
|
72
|
+
|
73
|
+
for user_details, trash in self.trashes:
|
74
|
+
for trash_info_file in trash.glob("info/*.trashinfo"):
|
75
|
+
trash_info = configutil.parse(trash_info_file, hint="ini").get("Trash Info", {})
|
76
|
+
original_path = self.target.fs.path(trash_info.get("Path", ""))
|
77
|
+
|
78
|
+
# We use the basename of the .trashinfo file and not the Path variable inside the
|
79
|
+
# ini file. This way we can keep duplicate basenames of trashed files separated correctly.
|
80
|
+
deleted_path = trash / "files" / trash_info_file.name.replace(".trashinfo", "")
|
81
|
+
|
82
|
+
if deleted_path.exists():
|
83
|
+
deleted_files = [deleted_path]
|
84
|
+
|
85
|
+
if deleted_path.is_dir():
|
86
|
+
for child in deleted_path.rglob("*"):
|
87
|
+
deleted_files.append(child)
|
88
|
+
|
89
|
+
for file in deleted_files:
|
90
|
+
# NOTE: We currently do not 'fix' the original_path of files inside deleted directories.
|
91
|
+
# This would require guessing where the parent folder starts, which is impossible without
|
92
|
+
# making assumptions.
|
93
|
+
yield TrashRecord(
|
94
|
+
ts=trash_info.get("DeletionDate", 0),
|
95
|
+
path=original_path,
|
96
|
+
filesize=file.lstat().st_size if file.is_file() else None,
|
97
|
+
deleted_path=file,
|
98
|
+
source=trash_info_file,
|
99
|
+
_user=user_details.user,
|
100
|
+
_target=self.target,
|
101
|
+
)
|
102
|
+
|
103
|
+
# We cannot determine if the deleted entry is a directory since the path does
|
104
|
+
# not exist at $TRASH/files, so we work with what we have instead.
|
105
|
+
else:
|
106
|
+
self.target.log.warning(f"Expected trashed file(s) at {deleted_path}")
|
107
|
+
yield TrashRecord(
|
108
|
+
ts=trash_info.get("DeletionDate", 0),
|
109
|
+
path=original_path,
|
110
|
+
filesize=0,
|
111
|
+
deleted_path=deleted_path,
|
112
|
+
source=trash_info_file,
|
113
|
+
_user=user_details.user,
|
114
|
+
_target=self.target,
|
115
|
+
)
|
116
|
+
|
117
|
+
# We also iterate expunged folders, they can contain files that could not be
|
118
|
+
# deleted when the user pressed the "empty trash" button in the file manager.
|
119
|
+
# Resources:
|
120
|
+
# - https://gitlab.gnome.org/GNOME/glib/-/issues/1665
|
121
|
+
# - https://bugs.launchpad.net/ubuntu/+source/nautilus/+bug/422012
|
122
|
+
for item in (trash / "expunged").rglob("*/*"):
|
123
|
+
stat = item.lstat()
|
124
|
+
yield TrashRecord(
|
125
|
+
ts=stat.st_mtime, # NOTE: This is the timestamp at which the file failed to delete
|
126
|
+
path=None, # We do not know the original file path
|
127
|
+
filesize=stat.st_size if item.is_file() else None,
|
128
|
+
deleted_path=item,
|
129
|
+
source=trash / "expunged",
|
130
|
+
_user=user_details.user,
|
131
|
+
_target=self.target,
|
132
|
+
)
|
dissect/target/tools/utils.py
CHANGED
@@ -90,6 +90,10 @@ def generate_argparse_for_unbound_method(
|
|
90
90
|
raise ValueError(f"Value `{method}` is not an unbound plugin method")
|
91
91
|
|
92
92
|
desc = method.__doc__ or docs.get_func_description(method, with_docstrings=True)
|
93
|
+
|
94
|
+
if "\n" in desc:
|
95
|
+
desc = inspect.cleandoc(desc)
|
96
|
+
|
93
97
|
help_formatter = argparse.RawDescriptionHelpFormatter
|
94
98
|
parser = argparse.ArgumentParser(description=desc, formatter_class=help_formatter, conflict_handler="resolve")
|
95
99
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev12
|
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
|
@@ -198,6 +198,7 @@ dissect/target/plugins/os/unix/history.py,sha256=rvRlcHw3wEtgdyfjX-RBLQUQAd0uHzf
|
|
198
198
|
dissect/target/plugins/os/unix/locale.py,sha256=XOcKBwfK3YJ266eBFKNc1xaZgY8QEQGJOS8PJRJt4ME,4292
|
199
199
|
dissect/target/plugins/os/unix/packagemanager.py,sha256=Wm2AAJOD_B3FAcZNXgWtSm_YwbvrHBYOP8bPmOXNjG4,2427
|
200
200
|
dissect/target/plugins/os/unix/shadow.py,sha256=W6W6rMru7IVnuBc6sl5wsRWTOrJdS1s7_2_q7QRf7Is,4148
|
201
|
+
dissect/target/plugins/os/unix/trash.py,sha256=wzq9nprRKjFs9SHaENz8PMbpQMfFoLH1KW4EPWMLJdA,6099
|
201
202
|
dissect/target/plugins/os/unix/bsd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
202
203
|
dissect/target/plugins/os/unix/bsd/_os.py,sha256=e5rttTOFOmd7e2HqP9ZZFMEiPLBr-8rfH0XH1IIeroQ,1372
|
203
204
|
dissect/target/plugins/os/unix/bsd/citrix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -258,7 +259,7 @@ dissect/target/plugins/os/unix/log/audit.py,sha256=OjorWTmCFvCI5RJq6m6WNW0Lhb-po
|
|
258
259
|
dissect/target/plugins/os/unix/log/auth.py,sha256=l7gCuRdvv9gL0U1N0yrR9hVsMnr4t_k4t-n-f6PrOxg,2388
|
259
260
|
dissect/target/plugins/os/unix/log/journal.py,sha256=auVRfrW4NRU7HguoDLTz4l_IwNdPZLPAqD7jhrOTzH8,17404
|
260
261
|
dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wq89wRSFZSBsoKVCxjDofnC4yw9XJ4iOF0XJe9EucCo,2448
|
261
|
-
dissect/target/plugins/os/unix/log/messages.py,sha256=
|
262
|
+
dissect/target/plugins/os/unix/log/messages.py,sha256=O10Uw3PGTanfGpphUWYqOwOIR7XiiM-clfboVCoiP0U,4501
|
262
263
|
dissect/target/plugins/os/unix/log/utmp.py,sha256=1nPHIaBUHt_9z6PDrvyqg4huKLihUaWLrMmgMsbaeIo,7755
|
263
264
|
dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
264
265
|
dissect/target/plugins/os/windows/_os.py,sha256=-x5TD5BvFw-7zEfqT6WG7n04YSeyr7wVLO07y6xkBP8,12476
|
@@ -350,7 +351,7 @@ dissect/target/tools/mount.py,sha256=8GRYnu4xEmFBHxuIZAYhOMyyTGX8fat1Ou07DNiUnW4
|
|
350
351
|
dissect/target/tools/query.py,sha256=e-yAN9zdQjuOiTuoOQoo17mVEQGGcOgaA9YkF4GYpkM,15394
|
351
352
|
dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
|
352
353
|
dissect/target/tools/shell.py,sha256=dmshIriwdd_UwrdUcTfWkcYD8Z0mjzbDqwyZG-snDdM,50482
|
353
|
-
dissect/target/tools/utils.py,sha256=
|
354
|
+
dissect/target/tools/utils.py,sha256=JJZDSso1CEK2sv4Z3HJNgqxH6G9S5lbmV-C3h-XmcMo,12035
|
354
355
|
dissect/target/tools/yara.py,sha256=70k-2VMulf1EdkX03nCACzejaOEcsFHOyX-4E40MdQU,2044
|
355
356
|
dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
356
357
|
dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
|
@@ -364,10 +365,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
364
365
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
365
366
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
366
367
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
367
|
-
dissect.target-3.20.
|
368
|
-
dissect.target-3.20.
|
369
|
-
dissect.target-3.20.
|
370
|
-
dissect.target-3.20.
|
371
|
-
dissect.target-3.20.
|
372
|
-
dissect.target-3.20.
|
373
|
-
dissect.target-3.20.
|
368
|
+
dissect.target-3.20.dev12.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
369
|
+
dissect.target-3.20.dev12.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
370
|
+
dissect.target-3.20.dev12.dist-info/METADATA,sha256=PD0L9p9grMOhAAKSl17OUW03GOBhlRdnI6qX6nyKWT0,12897
|
371
|
+
dissect.target-3.20.dev12.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
372
|
+
dissect.target-3.20.dev12.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
373
|
+
dissect.target-3.20.dev12.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
374
|
+
dissect.target-3.20.dev12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev10.dist-info → dissect.target-3.20.dev12.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|