dissect.target 3.20.dev39__py3-none-any.whl → 3.20.dev40__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.
@@ -185,3 +185,24 @@ MacInterfaceRecord = TargetRecordDescriptor(
185
185
  ("boolean", "dhcp"),
186
186
  ],
187
187
  )
188
+
189
+
190
+ COMMON_APPLICATION_FIELDS = [
191
+ ("datetime", "ts_modified"),
192
+ ("datetime", "ts_installed"),
193
+ ("string", "name"),
194
+ ("string", "version"),
195
+ ("string", "author"),
196
+ ("string", "type"),
197
+ ("path", "path"),
198
+ ]
199
+
200
+ UnixApplicationRecord = TargetRecordDescriptor(
201
+ "unix/application",
202
+ COMMON_APPLICATION_FIELDS,
203
+ )
204
+
205
+ WindowsApplicationRecord = TargetRecordDescriptor(
206
+ "windows/application",
207
+ COMMON_APPLICATION_FIELDS,
208
+ )
@@ -310,6 +310,7 @@ class VirtualKey(RegistryKey):
310
310
  self._class_name = class_name
311
311
  self._values: dict[str, RegistryValue] = {}
312
312
  self._subkeys: dict[str, RegistryKey] = {}
313
+ self._timestamp: datetime = None
313
314
  self.top: RegistryKey = None
314
315
  super().__init__(hive=hive)
315
316
 
@@ -339,11 +340,19 @@ class VirtualKey(RegistryKey):
339
340
  return self._path
340
341
 
341
342
  @property
342
- def timestamp(self) -> datetime:
343
+ def timestamp(self) -> datetime | None:
343
344
  if self.top:
344
345
  return self.top.timestamp
346
+
347
+ if self._timestamp:
348
+ return self._timestamp
349
+
345
350
  return None
346
351
 
352
+ @timestamp.setter
353
+ def timestamp(self, ts: datetime) -> None:
354
+ self._timestamp = ts
355
+
347
356
  def subkey(self, subkey: str) -> RegistryKey:
348
357
  try:
349
358
  return self._subkeys[subkey.lower()]
@@ -0,0 +1,78 @@
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.fsutil import TargetPath
6
+ from dissect.target.helpers.record import UnixApplicationRecord
7
+ from dissect.target.plugin import Plugin, export
8
+ from dissect.target.target import Target
9
+
10
+
11
+ class UnixApplicationsPlugin(Plugin):
12
+ """Unix Applications plugin."""
13
+
14
+ SYSTEM_PATHS = [
15
+ "/usr/share/applications/",
16
+ "/usr/local/share/applications/",
17
+ "/var/lib/snapd/desktop/applications/",
18
+ "/var/lib/flatpak/exports/share/applications/",
19
+ ]
20
+
21
+ USER_PATHS = [
22
+ ".local/share/applications/",
23
+ ]
24
+
25
+ SYSTEM_APPS = ("org.gnome.",)
26
+
27
+ def __init__(self, target: Target):
28
+ super().__init__(target)
29
+ self.desktop_files = list(self._find_desktop_files())
30
+
31
+ def _find_desktop_files(self) -> Iterator[TargetPath]:
32
+ for dir in self.SYSTEM_PATHS:
33
+ for file in self.target.fs.path(dir).glob("*.desktop"):
34
+ yield file
35
+
36
+ for user_details in self.target.user_details.all_with_home():
37
+ for dir in self.USER_PATHS:
38
+ for file in user_details.home_path.joinpath(dir).glob("*.desktop"):
39
+ yield file
40
+
41
+ def check_compatible(self) -> None:
42
+ if not self.desktop_files:
43
+ raise UnsupportedPluginError("No application .desktop files found")
44
+
45
+ @export(record=UnixApplicationRecord)
46
+ def applications(self) -> Iterator[UnixApplicationRecord]:
47
+ """Yield installed Unix GUI applications from GNOME and XFCE.
48
+
49
+ Resources:
50
+ - https://wiki.archlinux.org/title/Desktop_entries
51
+ - https://specifications.freedesktop.org/desktop-entry-spec/latest/
52
+ - https://unix.stackexchange.com/questions/582928/where-gnome-apps-are-installed
53
+
54
+ Yields ``UnixApplicationRecord`` records with the following fields:
55
+
56
+ .. code-block:: text
57
+
58
+ ts_modified (datetime): timestamp when the installation was modified
59
+ ts_installed (datetime): timestamp when the application was installed on the system
60
+ name (string): name of the application
61
+ version (string): version of the application
62
+ author (string): author of the application
63
+ type (string): type of the application, either user or system
64
+ path (string): path to the desktop file entry of the application
65
+ """
66
+ for file in self.desktop_files:
67
+ config = configutil.parse(file, hint="ini").get("Desktop Entry") or {}
68
+ stat = file.lstat()
69
+
70
+ yield UnixApplicationRecord(
71
+ ts_modified=stat.st_mtime,
72
+ ts_installed=stat.st_btime if hasattr(stat, "st_btime") else None,
73
+ name=config.get("Name"),
74
+ version=config.get("Version"),
75
+ path=config.get("Exec"),
76
+ type="system" if config.get("Icon", "").startswith(self.SYSTEM_APPS) else "user",
77
+ _target=self.target,
78
+ )
@@ -0,0 +1,79 @@
1
+ from typing import Iterator
2
+
3
+ from dissect.target.exceptions import UnsupportedPluginError
4
+ from dissect.target.filesystems.squashfs import SquashFSFilesystem
5
+ from dissect.target.helpers import configutil
6
+ from dissect.target.helpers.fsutil import TargetPath
7
+ from dissect.target.helpers.record import UnixApplicationRecord
8
+ from dissect.target.plugin import Plugin, alias, export
9
+ from dissect.target.target import Target
10
+
11
+
12
+ class SnapPlugin(Plugin):
13
+ """Canonical Linux Snapcraft plugin."""
14
+
15
+ PATHS = [
16
+ "/var/lib/snapd/snaps",
17
+ ]
18
+
19
+ def __init__(self, target: Target):
20
+ super().__init__(target)
21
+ self.installs = list(self._find_installs())
22
+
23
+ def check_compatible(self) -> None:
24
+ if not configutil.HAS_YAML:
25
+ raise UnsupportedPluginError("Missing required dependency ruamel.yaml")
26
+
27
+ if not self.installs:
28
+ raise UnsupportedPluginError("No snapd install folder(s) found")
29
+
30
+ def _find_installs(self) -> Iterator[TargetPath]:
31
+ for str_path in self.PATHS:
32
+ if (path := self.target.fs.path(str_path)).exists():
33
+ yield path
34
+
35
+ @export(record=UnixApplicationRecord)
36
+ @alias("snaps")
37
+ def snap(self) -> Iterator[UnixApplicationRecord]:
38
+ """Yields installed Canonical Linux Snapcraft (snaps) applications on the target system.
39
+
40
+ Reads information from installed SquashFS ``*.snap`` files found in ``/var/lib/snapd/snaps``.
41
+ Logs of the ``snapd`` daemon can be parsed using the ``journal`` or ``syslog`` plugins.
42
+
43
+ Resources:
44
+ - https://github.com/canonical/snapcraft
45
+ - https://en.wikipedia.org/wiki/Snap_(software)
46
+
47
+ Yields ``UnixApplicationRecord`` records with the following fields:
48
+
49
+ .. code-block:: text
50
+
51
+ ts_modified (datetime): timestamp when the installation was modified
52
+ name (string): name of the application
53
+ version (string): version of the application
54
+ path (string): path to the application snap file
55
+ """
56
+
57
+ for install_path in self.installs:
58
+ for snap in install_path.glob("*.snap"):
59
+ try:
60
+ squashfs = SquashFSFilesystem(snap.open())
61
+
62
+ except (ValueError, NotImplementedError) as e:
63
+ self.target.log.warning("Unable to open snap file %s", snap)
64
+ self.target.log.debug("", exc_info=e)
65
+ continue
66
+
67
+ if not (meta := squashfs.path("meta/snap.yaml")).exists():
68
+ self.target.log.warning("Snap %s has no meta/snap.yaml file")
69
+ continue
70
+
71
+ meta_data = configutil.parse(meta, hint="yaml")
72
+
73
+ yield UnixApplicationRecord(
74
+ ts_modified=meta.lstat().st_mtime,
75
+ name=meta_data.get("name"),
76
+ version=meta_data.get("version"),
77
+ path=snap,
78
+ _target=self.target,
79
+ )
@@ -6,7 +6,7 @@ from dissect.target.helpers.record import TargetRecordDescriptor
6
6
  from dissect.target.plugin import NamespacePlugin
7
7
 
8
8
  PackageManagerLogRecord = TargetRecordDescriptor(
9
- "unix/log/packagemanager",
9
+ "unix/packagemanager/log",
10
10
  [
11
11
  ("datetime", "ts"),
12
12
  ("string", "package_manager"),
@@ -0,0 +1,62 @@
1
+ from datetime import datetime
2
+ from typing import Iterator
3
+
4
+ from dissect.target.exceptions import UnsupportedPluginError
5
+ from dissect.target.helpers.record import WindowsApplicationRecord
6
+ from dissect.target.plugin import Plugin, export
7
+ from dissect.target.target import Target
8
+
9
+
10
+ class WindowsApplicationsPlugin(Plugin):
11
+ """Windows Applications plugin."""
12
+
13
+ def __init__(self, target: Target):
14
+ super().__init__(target)
15
+ self.keys = list(self.target.registry.keys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"))
16
+
17
+ def check_compatible(self) -> None:
18
+ if not self.target.has_function("registry"):
19
+ raise UnsupportedPluginError("No Windows registry found")
20
+
21
+ if not self.keys:
22
+ raise UnsupportedPluginError("No 'Uninstall' registry keys found")
23
+
24
+ @export(record=WindowsApplicationRecord)
25
+ def applications(self) -> Iterator[WindowsApplicationRecord]:
26
+ """Yields currently installed applications from the Windows registry.
27
+
28
+ Use the Windows eventlog plugin (``evtx``, ``evt``) to parse install and uninstall events
29
+ of applications and services (e.g. ``4697``, ``110707``, ``1034`` and ``11724``).
30
+
31
+ Resources:
32
+ - https://learn.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key
33
+
34
+ Yields ``WindowsApplicationRecord`` records with the following fields:
35
+
36
+ .. code-block:: text
37
+
38
+ ts_modified (datetime): timestamp when the installation was modified according to the registry
39
+ ts_installed (datetime): timestamp when the application was installed according to the application
40
+ name (string): name of the application
41
+ version (string): version of the application
42
+ author (string): author of the application
43
+ type (string): type of the application, either user or system
44
+ path (string): path to the installed location or installer of the application
45
+ """
46
+ for uninstall in self.keys:
47
+ for app in uninstall.subkeys():
48
+ values = {value.name: value.value for value in app.values()}
49
+
50
+ if install_date := values.get("InstallDate"):
51
+ install_date = datetime.strptime(install_date, "%Y%m%d")
52
+
53
+ yield WindowsApplicationRecord(
54
+ ts_modified=app.ts,
55
+ ts_installed=install_date,
56
+ name=values.get("DisplayName") or app.name,
57
+ version=values.get("DisplayVersion"),
58
+ author=values.get("Publisher"),
59
+ type="system" if values.get("SystemComponent") or not values else "user",
60
+ path=values.get("DisplayIcon") or values.get("InstallLocation") or values.get("InstallSource"),
61
+ _target=self.target,
62
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.20.dev39
3
+ Version: 3.20.dev40
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
@@ -60,9 +60,9 @@ dissect/target/helpers/mount.py,sha256=7Y4B0uY2c00p23hmuMKSgwAwxkKcY4spyAMO1kdcV
60
60
  dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
61
61
  dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
62
62
  dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
63
- dissect/target/helpers/record.py,sha256=7Se6ZV8cvwEaGSjRd9bKhVnUAn4W4KR2eqP6AbQhTH4,5892
63
+ dissect/target/helpers/record.py,sha256=TxG1lwW5N0V-7kuq4s2vnQh-hAbT7rVwwZLf4sIDNjM,6334
64
64
  dissect/target/helpers/record_modifier.py,sha256=cRNDhUYMmx4iEKyEr5Pqy9xiFgxr_GBNJPp_omkQsEU,4094
65
- dissect/target/helpers/regutil.py,sha256=ti-ht2N9UxbMjhUBP2bybY76_dAvbCz0txPBszvSKVw,28171
65
+ dissect/target/helpers/regutil.py,sha256=dnmpceitwTeS-9L8HU_HG5OXMzCcH58o8uhj5olDWyg,28383
66
66
  dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
67
67
  dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
68
68
  dissect/target/helpers/utils.py,sha256=K3xVq9D0FwIhTBAuiWN8ph7Pq2GABgG3hOz-3AmKuEA,4244
@@ -195,12 +195,13 @@ dissect/target/plugins/general/users.py,sha256=yy9gvRXfN9BT71v4Xqo5hpwfgN9he9Otu
195
195
  dissect/target/plugins/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
196
196
  dissect/target/plugins/os/unix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
197
197
  dissect/target/plugins/os/unix/_os.py,sha256=I-waC7Hn9rMJRI2JUDreLnU_LS54Zj3hix7BfJLV-xg,14651
198
+ dissect/target/plugins/os/unix/applications.py,sha256=AUgZRP35FzswGyFyChj2o4dfGO34Amc6nqHgiMEaqdI,3129
198
199
  dissect/target/plugins/os/unix/cronjobs.py,sha256=tgWQ3BUZpfyvRzodMwGtwFUdPjZ17k7ZRbZ9Q8wmXPk,3393
199
200
  dissect/target/plugins/os/unix/datetime.py,sha256=gKfBdPyUirt3qmVYfOJ1oZXRPn8wRzssbZxR_ARrtk8,1518
200
201
  dissect/target/plugins/os/unix/generic.py,sha256=qC4zQiqpm8ilc-d1ITsccOQbbUUM8HylWB4dsFKJjAw,2171
201
202
  dissect/target/plugins/os/unix/history.py,sha256=95EXJrD5ko7iFLsYhAwvR-2wDbVBSNsyXsfZKEWMJq8,6533
202
203
  dissect/target/plugins/os/unix/locale.py,sha256=l42abqG1nGk90SDRt1DmkvqPvO5L-1GtaEwg6dj89qI,4323
203
- dissect/target/plugins/os/unix/packagemanager.py,sha256=XKKJuJ1p9mEQ4TGW7SEwcj1rg8fPp2aWIhnt_0i8Rhc,1279
204
+ dissect/target/plugins/os/unix/packagemanager.py,sha256=xmisH9vMY7uR2d6lFVNuWrZrbVnKa6mYk6C3sbcLtlc,1279
204
205
  dissect/target/plugins/os/unix/shadow.py,sha256=TVQQcGhPFYV685ouqOagk4P1AsqAHseFN5lWYHPf3tI,4172
205
206
  dissect/target/plugins/os/unix/trash.py,sha256=Z-1r5VQorLauj_85TgURRLQI7ns1Q1N_922Vq8q_v18,6106
206
207
  dissect/target/plugins/os/unix/bsd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -240,6 +241,7 @@ dissect/target/plugins/os/unix/linux/debian/__init__.py,sha256=47DEQpj8HBSa-_TIm
240
241
  dissect/target/plugins/os/unix/linux/debian/_os.py,sha256=GI19ZqcyfZ1mUYg2NCx93HkZje9MWMU8FYNKQv-G6go,498
241
242
  dissect/target/plugins/os/unix/linux/debian/apt.py,sha256=uKfW77pbgxYSe9g6hpih19-xfgvCB954Ygx21j-ydzk,4388
242
243
  dissect/target/plugins/os/unix/linux/debian/dpkg.py,sha256=6A3mb77NREdDWDTFrmcfCF_XxHMmD4_5eHqHEl_7Vqg,5816
244
+ dissect/target/plugins/os/unix/linux/debian/snap.py,sha256=YVz1N54eQsj2_22LUJIj6Gg-huwT4xDEcOAZn9Tvgw4,3023
243
245
  dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
246
  dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=TPjcfv1n68RCe3Er4aCVQwQDCZwJT-NLvje3kPjDfhk,1744
245
247
  dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -324,6 +326,7 @@ dissect/target/plugins/os/windows/log/pfro.py,sha256=d53Mm7ovZa9crSwVRPwjMVxTd_j
324
326
  dissect/target/plugins/os/windows/log/schedlgu.py,sha256=JaP8H8eTEypWXhx2aFSR_IMam6rQiksbLKhMr_U4fz8,5570
325
327
  dissect/target/plugins/os/windows/regf/7zip.py,sha256=Ox8cLyQtbyYQS7m4eY3onNv1K8N2IkS5wexrC55Urd4,3444
326
328
  dissect/target/plugins/os/windows/regf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
329
+ dissect/target/plugins/os/windows/regf/applications.py,sha256=AZwaLXsVmqMjoZYI3dhYLvJL_8s1k1aRjSgjvqC2AxI,2898
327
330
  dissect/target/plugins/os/windows/regf/appxdebugkeys.py,sha256=X8MYLcD76pIZoIWwS_DgUp6q6pi2WO7jhZeoc4uGLak,3966
328
331
  dissect/target/plugins/os/windows/regf/auditpol.py,sha256=vTqWw0_vu9p_emWC8FuYcYQpOXhEFQQDLV0K6-18i9c,5208
329
332
  dissect/target/plugins/os/windows/regf/bam.py,sha256=jJ0i-82uteBU0hPgs81f8NV8NCeRtIklK82Me2S_ro0,2131
@@ -370,10 +373,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
370
373
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
371
374
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
372
375
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
373
- dissect.target-3.20.dev39.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
374
- dissect.target-3.20.dev39.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
375
- dissect.target-3.20.dev39.dist-info/METADATA,sha256=kytETAlhT5qsu2qAkum-3O6NZxRkd7N0Ytx-GwcMqCE,12897
376
- dissect.target-3.20.dev39.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
377
- dissect.target-3.20.dev39.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
378
- dissect.target-3.20.dev39.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
379
- dissect.target-3.20.dev39.dist-info/RECORD,,
376
+ dissect.target-3.20.dev40.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
377
+ dissect.target-3.20.dev40.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
378
+ dissect.target-3.20.dev40.dist-info/METADATA,sha256=sfc-H38qJzXkx1449zv0Y7SgicuUfJeki3JFiC6EJ4c,12897
379
+ dissect.target-3.20.dev40.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
380
+ dissect.target-3.20.dev40.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
381
+ dissect.target-3.20.dev40.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
382
+ dissect.target-3.20.dev40.dist-info/RECORD,,