dissect.target 3.14.dev20__py3-none-any.whl → 3.14.dev23__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. dissect/target/filesystem.py +1 -1
  2. dissect/target/filesystems/btrfs.py +2 -2
  3. dissect/target/helpers/cache.py +2 -2
  4. dissect/target/helpers/fsutil.py +9 -6
  5. dissect/target/helpers/hashutil.py +1 -5
  6. dissect/target/loaders/log.py +2 -2
  7. dissect/target/loaders/smb.py +23 -13
  8. dissect/target/plugins/apps/av/sophos.py +1 -2
  9. dissect/target/plugins/apps/av/trendmicro.py +2 -3
  10. dissect/target/plugins/apps/browser/chromium.py +4 -11
  11. dissect/target/plugins/apps/browser/firefox.py +2 -6
  12. dissect/target/plugins/child/hyperv.py +1 -2
  13. dissect/target/plugins/child/vmware_workstation.py +1 -3
  14. dissect/target/plugins/filesystem/acquire_handles.py +2 -0
  15. dissect/target/plugins/filesystem/acquire_hash.py +1 -7
  16. dissect/target/plugins/filesystem/ntfs/usnjrnl.py +1 -2
  17. dissect/target/plugins/filesystem/resolver.py +1 -1
  18. dissect/target/plugins/filesystem/unix/capability.py +77 -66
  19. dissect/target/plugins/filesystem/walkfs.py +23 -19
  20. dissect/target/plugins/filesystem/yara.py +20 -19
  21. dissect/target/plugins/os/unix/_os.py +1 -3
  22. dissect/target/plugins/os/unix/bsd/osx/user.py +1 -3
  23. dissect/target/plugins/os/unix/esxi/_os.py +1 -2
  24. dissect/target/plugins/os/unix/log/journal.py +7 -6
  25. dissect/target/plugins/os/windows/_os.py +2 -1
  26. dissect/target/plugins/os/windows/amcache.py +9 -10
  27. dissect/target/plugins/os/windows/catroot.py +2 -2
  28. dissect/target/plugins/os/windows/generic.py +10 -11
  29. dissect/target/plugins/os/windows/lnk.py +5 -6
  30. dissect/target/plugins/os/windows/log/amcache.py +3 -5
  31. dissect/target/plugins/os/windows/log/pfro.py +1 -3
  32. dissect/target/plugins/os/windows/prefetch.py +5 -6
  33. dissect/target/plugins/os/windows/recyclebin.py +3 -4
  34. dissect/target/plugins/os/windows/regf/7zip.py +2 -4
  35. dissect/target/plugins/os/windows/regf/bam.py +1 -2
  36. dissect/target/plugins/os/windows/regf/cit.py +4 -5
  37. dissect/target/plugins/os/windows/regf/muicache.py +1 -3
  38. dissect/target/plugins/os/windows/regf/recentfilecache.py +1 -2
  39. dissect/target/plugins/os/windows/regf/shimcache.py +1 -2
  40. dissect/target/plugins/os/windows/regf/trusteddocs.py +1 -1
  41. dissect/target/plugins/os/windows/regf/userassist.py +1 -2
  42. dissect/target/plugins/os/windows/services.py +2 -4
  43. dissect/target/plugins/os/windows/sru.py +4 -4
  44. dissect/target/plugins/os/windows/startupinfo.py +5 -6
  45. dissect/target/plugins/os/windows/syscache.py +1 -2
  46. dissect/target/target.py +2 -1
  47. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/METADATA +1 -1
  48. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/RECORD +53 -53
  49. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/COPYRIGHT +0 -0
  50. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/LICENSE +0 -0
  51. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/WHEEL +0 -0
  52. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/entry_points.txt +0 -0
  53. {dissect.target-3.14.dev20.dist-info → dissect.target-3.14.dev23.dist-info}/top_level.txt +0 -0
@@ -147,7 +147,7 @@ class Filesystem:
147
147
  except NotImplementedError:
148
148
  raise
149
149
  except Exception as e:
150
- log.warning("Failed to detect ID on %s filesystem", cls.__fstype__)
150
+ log.warning("Failed to detect ID on %s filesystem", cls.__type__)
151
151
  log.debug("", exc_info=e)
152
152
  finally:
153
153
  fh.seek(offset)
@@ -17,7 +17,7 @@ from dissect.target.helpers import fsutil
17
17
 
18
18
 
19
19
  class BtrfsFilesystem(Filesystem):
20
- __fstype__ = "btrfs"
20
+ __type__ = "btrfs"
21
21
  __multi_volume__ = True
22
22
 
23
23
  def __init__(self, fh: Union[BinaryIO, list[BinaryIO]], *args, **kwargs):
@@ -55,7 +55,7 @@ class BtrfsFilesystem(Filesystem):
55
55
 
56
56
 
57
57
  class BtrfsSubvolumeFilesystem(Filesystem):
58
- __fstype__ = "btrfs"
58
+ __type__ = "btrfs"
59
59
 
60
60
  def __init__(self, fs: BtrfsFilesystem, subvol: Optional[str] = None, subvolid: Optional[int] = None):
61
61
  super().__init__(fs.volume, alt_separator=fs.alt_separator, case_sensitive=fs.case_sensitive)
@@ -144,7 +144,7 @@ class Cache:
144
144
 
145
145
  if read_file_cache:
146
146
  target.log.debug("Reading from cache file: %s", cache_file)
147
- if os.access(cache_file, os.R_OK, effective_ids=True):
147
+ if os.access(cache_file, os.R_OK, effective_ids=bool(os.supports_effective_ids)):
148
148
  if os.stat(cache_file).st_size != 0:
149
149
  try:
150
150
  return self.open_reader(cache_file, output)
@@ -173,7 +173,7 @@ class Cache:
173
173
  err,
174
174
  )
175
175
 
176
- if os.access(temp_dir, os.W_OK | os.R_OK | os.X_OK, effective_ids=True):
176
+ if os.access(temp_dir, os.W_OK | os.R_OK | os.X_OK, effective_ids=bool(os.supports_effective_ids)):
177
177
  if os.path.exists(temp_path):
178
178
  try:
179
179
  os.remove(temp_path)
@@ -725,12 +725,15 @@ class TargetPath(Path, PureDissectPath):
725
725
 
726
726
  def open(self, mode='rb', buffering=0, encoding=None,
727
727
  errors=None, newline=None):
728
- # CPython >= 3.10
729
- if "b" not in mode and hasattr(io, "text_encoding"):
730
- # Vermin linting needs to be skipped for this line as this is
731
- # guarded by an explicit check for availability.
732
- # novermin
733
- encoding = io.text_encoding(encoding)
728
+
729
+ if "b" not in mode:
730
+ encoding = encoding or "UTF-8"
731
+ # CPython >= 3.10
732
+ if hasattr(io, "text_encoding"):
733
+ # Vermin linting needs to be skipped for this line as this is
734
+ # guarded by an explicit check for availability.
735
+ # novermin
736
+ encoding = io.text_encoding(encoding)
734
737
  return self._accessor.open(self, mode, buffering, encoding, errors,
735
738
  newline)
736
739
 
@@ -72,10 +72,6 @@ def hash_uri_records(target: Target, record: Record) -> Record:
72
72
 
73
73
  def hash_path_records(target: Target, record: Record) -> Record:
74
74
  """Hash files from path fields inside the record."""
75
- if target.os == "windows":
76
- path_type = fieldtypes.windows_path
77
- else:
78
- path_type = fieldtypes.posix_path
79
75
 
80
76
  hash_records = []
81
77
 
@@ -93,7 +89,7 @@ def hash_path_records(target: Target, record: Record) -> Record:
93
89
  except (FileNotFoundError, IsADirectoryError):
94
90
  pass
95
91
  else:
96
- resolved_path = path_type(resolved_path)
92
+ resolved_path = target.fs.path(resolved_path)
97
93
  record_kwargs = dict()
98
94
  record_def = list()
99
95
 
@@ -26,12 +26,12 @@ class LogLoader(Loader):
26
26
 
27
27
  def map(self, target: Target) -> None:
28
28
  self.target = target
29
- vfs = VirtualFilesystem(case_sensitive=False)
29
+ vfs = VirtualFilesystem(case_sensitive=False, alt_separator=target.fs.alt_separator)
30
30
  for entry in self.path.parent.glob(self.path.name):
31
31
  ext = self.options.get("hint", entry.suffix.lower()).strip(".")
32
32
  if (mapping := self.LOGS_DIRS.get(ext, None)) is None:
33
33
  continue
34
- mapping = str(Path(mapping).joinpath(entry.name))
34
+ mapping = str(vfs.path(mapping).joinpath(entry.name))
35
35
  vfs.map_file(mapping, str(entry))
36
36
  target.filesystems.add(vfs)
37
37
  target.fs = vfs
@@ -11,6 +11,7 @@ from urllib.parse import ParseResult, parse_qsl
11
11
  from dissect.regf import regf
12
12
  from dissect.util import ts
13
13
  from impacket.dcerpc.v5 import rpcrt, rrp, scmr, transport
14
+ from impacket.dcerpc.v5.rpcrt import DCERPCException
14
15
  from impacket.smbconnection import SessionError, SMBConnection
15
16
 
16
17
  from dissect.target import Target
@@ -189,18 +190,24 @@ class SmbRegistry(RegistryPlugin):
189
190
  return False
190
191
 
191
192
  def _init_registry(self) -> None:
192
- self._svcctl = _connect_rpc(self.conn, "ncacn_np:445[\\pipe\\svcctl]", scmr.MSRPC_UUID_SCMR)
193
- self._check_service_status()
193
+ try:
194
+ self._svcctl = _connect_rpc(self.conn, "ncacn_np:445[\\pipe\\svcctl]", scmr.MSRPC_UUID_SCMR)
195
+ self._check_service_status()
194
196
 
195
- self._winreg = _connect_rpc(self.conn, "ncacn_np:445[\\pipe\\winreg]", rrp.MSRPC_UUID_RRP)
197
+ self._winreg = _connect_rpc(self.conn, "ncacn_np:445[\\pipe\\winreg]", rrp.MSRPC_UUID_RRP)
196
198
 
197
- hklm_hive = SmbRegistryHive(self._winreg, "HKEY_LOCAL_MACHINE", rrp.hOpenLocalMachine(self._winreg)["phKey"])
198
- hku_hive = SmbRegistryHive(self._winreg, "HKEY_USERS", rrp.hOpenUsers(self._winreg)["phKey"])
199
+ hklm_hive = SmbRegistryHive(
200
+ self._winreg, "HKEY_LOCAL_MACHINE", rrp.hOpenLocalMachine(self._winreg)["phKey"]
201
+ )
202
+ hku_hive = SmbRegistryHive(self._winreg, "HKEY_USERS", rrp.hOpenUsers(self._winreg)["phKey"])
199
203
 
200
- self._add_hive("HKLM", hklm_hive, TargetPath(self.target.fs, "HKLM"))
201
- self._add_hive("HKU", hku_hive, TargetPath(self.target.fs, "HKU"))
202
- self._map_hive("HKEY_LOCAL_MACHINE", hklm_hive)
203
- self._map_hive("HKEY_USERS", hku_hive)
204
+ self._add_hive("HKLM", hklm_hive, TargetPath(self.target.fs, "HKLM"))
205
+ self._add_hive("HKU", hku_hive, TargetPath(self.target.fs, "HKU"))
206
+ self._map_hive("HKEY_LOCAL_MACHINE", hklm_hive)
207
+ self._map_hive("HKEY_USERS", hku_hive)
208
+ except SessionError:
209
+ self.target.log.info("Failed to open remote registry, registry will not be available")
210
+ return # no registry access, probably no access rights
204
211
 
205
212
  def _init_users(self) -> None:
206
213
  pass
@@ -212,15 +219,18 @@ class SmbRegistry(RegistryPlugin):
212
219
  if hasattr(self, "_was_disabled") and self._was_disabled:
213
220
  scmr.hRChangeServiceConfigW(self._svcctl, self._svc_handle, dwStartType=0x4)
214
221
 
215
- if hasattr(self, "_svcctl"):
222
+ if getattr(self, "_svcctl", None):
216
223
  self._svcctl.disconnect()
217
224
 
218
- if hasattr(self, "_winreg"):
225
+ if getattr(self, "_winreg", None):
219
226
  self._winreg.disconnect()
220
227
 
221
228
  def _check_service_status(self) -> None:
222
- manager_handle = scmr.hROpenSCManagerW(self._svcctl)["lpScHandle"]
223
- self._svc_handle = scmr.hROpenServiceW(self._svcctl, manager_handle, "RemoteRegistry")["lpServiceHandle"]
229
+ try:
230
+ manager_handle = scmr.hROpenSCManagerW(self._svcctl)["lpScHandle"]
231
+ self._svc_handle = scmr.hROpenServiceW(self._svcctl, manager_handle, "RemoteRegistry")["lpServiceHandle"]
232
+ except DCERPCException:
233
+ return
224
234
 
225
235
  current_state = scmr.hRQueryServiceStatus(self._svcctl, self._svc_handle)["lpServiceStatus"]["dwCurrentState"]
226
236
  if current_state == scmr.SERVICE_STOPPED:
@@ -3,7 +3,6 @@ from typing import Iterator
3
3
 
4
4
  from dissect.sql import sqlite3
5
5
  from dissect.util.ts import wintimestamp
6
- from flow.record.fieldtypes import path
7
6
 
8
7
  from dissect.target import Target
9
8
  from dissect.target.exceptions import UnsupportedPluginError
@@ -105,7 +104,7 @@ class SophosPlugin(Plugin):
105
104
  yield SophosLogRecord(
106
105
  ts=ts,
107
106
  description=details.get("threat_name", details),
108
- path=path.from_windows(path_to_infected_file),
107
+ path=self.target.fs.path(path_to_infected_file),
109
108
  _target=self.target,
110
109
  )
111
110
  except Exception as error:
@@ -2,7 +2,6 @@ from typing import Iterator
2
2
 
3
3
  from dissect import cstruct
4
4
  from dissect.util.ts import from_unix
5
- from flow.record.fieldtypes import path
6
5
 
7
6
  from dissect.target import Target
8
7
  from dissect.target.exceptions import UnsupportedPluginError
@@ -86,7 +85,7 @@ class TrendMicroPlugin(Plugin):
86
85
  yield TrendMicroWFLogRecord(
87
86
  ts=from_unix(int(cells[9])),
88
87
  threat=cells[2],
89
- path=path.from_windows(cells[6] + cells[7]),
88
+ path=self.target.fs.path(cells[6] + cells[7]),
90
89
  lineno=lineno,
91
90
  )
92
91
 
@@ -115,7 +114,7 @@ class TrendMicroPlugin(Plugin):
115
114
  remote_ip=entry.remote_ip.strip(b"\x00").decode(self.codepage),
116
115
  port=entry.port,
117
116
  direction=("out" if entry.direction == b"\x01" else "in"),
118
- path=path.from_windows(entry.path.strip(b"\x00").decode(self.codepage)),
117
+ path=self.target.fs.path(entry.path.strip(b"\x00").decode(self.codepage)),
119
118
  description=entry.description.strip("\x00"),
120
119
  )
121
120
  except EOFError:
@@ -6,7 +6,6 @@ from dissect.sql import sqlite3
6
6
  from dissect.sql.exceptions import Error as SQLError
7
7
  from dissect.sql.sqlite3 import SQLite3
8
8
  from dissect.util.ts import webkittimestamp
9
- from flow.record.fieldtypes import path
10
9
 
11
10
  from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
12
11
  from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
@@ -142,11 +141,8 @@ class ChromiumMixin:
142
141
  chain.sort(key=lambda row: row.chain_index)
143
142
 
144
143
  for row in db.table("downloads").rows():
145
- download_path = row.target_path
146
- if download_path and self.target.os == "windows":
147
- download_path = path.from_windows(download_path)
148
- elif download_path:
149
- download_path = path.from_posix(download_path)
144
+ if download_path := row.target_path:
145
+ download_path = self.target.fs.path(download_path)
150
146
 
151
147
  url = None
152
148
  download_chain = download_chains.get(row.id)
@@ -216,11 +212,8 @@ class ChromiumMixin:
216
212
  if ts_update:
217
213
  ts_update = webkittimestamp(ts_update)
218
214
 
219
- ext_path = extension_data.get("path")
220
- if ext_path and self.target.os == "windows":
221
- ext_path = path.from_windows(ext_path)
222
- elif ext_path:
223
- ext_path = path.from_posix(ext_path)
215
+ if ext_path := extension_data.get("path"):
216
+ ext_path = self.target.fs.path(ext_path)
224
217
 
225
218
  manifest = extension_data.get("manifest")
226
219
  if manifest:
@@ -5,7 +5,6 @@ from dissect.sql import sqlite3
5
5
  from dissect.sql.exceptions import Error as SQLError
6
6
  from dissect.sql.sqlite3 import SQLite3
7
7
  from dissect.util.ts import from_unix_ms, from_unix_us
8
- from flow.record.fieldtypes import path
9
8
 
10
9
  from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
11
10
  from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
@@ -197,12 +196,9 @@ class FirefoxPlugin(BrowserPlugin):
197
196
  state = content.get("state")
198
197
 
199
198
  dest_file_info = annotation.get("downloads/destinationFileURI", {})
200
- download_path = dest_file_info.get("content")
201
199
 
202
- if download_path and self.target.os == "windows":
203
- download_path = path.from_windows(download_path)
204
- elif download_path:
205
- download_path = path.from_posix(download_path)
200
+ if download_path := dest_file_info.get("content"):
201
+ download_path = self.target.fs.path(download_path)
206
202
 
207
203
  place = places.get(place_id)
208
204
  url = place.get("url")
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Iterator
4
4
 
5
5
  from dissect.hypervisor import hyperv
6
- from flow.record.fieldtypes import path
7
6
 
8
7
  from dissect.target.exceptions import UnsupportedPluginError
9
8
  from dissect.target.helpers.record import ChildTargetRecord
@@ -47,7 +46,7 @@ class HyperVChildTargetPlugin(ChildTargetPlugin):
47
46
  for vm_path in virtual_machines.values():
48
47
  yield ChildTargetRecord(
49
48
  type=self.__type__,
50
- path=path.from_windows(vm_path),
49
+ path=self.target.fs.path(vm_path),
51
50
  _target=self.target,
52
51
  )
53
52
 
@@ -1,5 +1,3 @@
1
- from flow.record.fieldtypes import path
2
-
3
1
  from dissect.target.exceptions import UnsupportedPluginError
4
2
  from dissect.target.helpers.record import ChildTargetRecord
5
3
  from dissect.target.plugin import ChildTargetPlugin
@@ -42,6 +40,6 @@ class WorkstationChildTargetPlugin(ChildTargetPlugin):
42
40
 
43
41
  yield ChildTargetRecord(
44
42
  type=self.__type__,
45
- path=path.from_windows(value.strip('"')),
43
+ path=self.target.fs.path(value.strip('"')),
46
44
  _target=self.target,
47
45
  )
@@ -43,4 +43,6 @@ class OpenHandlesPlugin(Plugin):
43
43
  """
44
44
  with self.open_handles_file.open() as fh:
45
45
  for row in csv.DictReader(gzip.open(fh, "rt")):
46
+ if name := row.get("name"):
47
+ row.update({"name": self.target.fs.path(name)})
46
48
  yield AcquireOpenHandlesRecord(_target=self.target, **row)
@@ -1,8 +1,6 @@
1
1
  import csv
2
2
  import gzip
3
3
 
4
- from flow.record.fieldtypes import posix_path, windows_path
5
-
6
4
  from dissect.target.exceptions import UnsupportedPluginError
7
5
  from dissect.target.helpers.record import TargetRecordDescriptor
8
6
  from dissect.target.plugin import Plugin, export
@@ -35,15 +33,11 @@ class AcquireHashPlugin(Plugin):
35
33
  An Acquire file container contains a file hashes csv when the hashes module was used. The content of this csv
36
34
  file is returned.
37
35
  """
38
- if self.target.os == "windows":
39
- path_type = windows_path
40
- else:
41
- path_type = posix_path
42
36
 
43
37
  with self.hash_file.open() as fh:
44
38
  for row in csv.DictReader(gzip.open(fh, "rt")):
45
39
  yield AcquireHashRecord(
46
- path=path_type(row["path"]),
40
+ path=self.target.fs.path((row["path"])),
47
41
  filesize=row["file-size"],
48
42
  digest=(row["md5"] or None, row["sha1"] or None, row["sha256"] or None),
49
43
  _target=self.target,
@@ -1,7 +1,6 @@
1
1
  from typing import Iterator
2
2
 
3
3
  from dissect.ntfs.c_ntfs import segment_reference
4
- from flow.record.fieldtypes import path as rpath
5
4
 
6
5
  from dissect.target.helpers.record import TargetRecordDescriptor
7
6
  from dissect.target.plugin import Plugin, export
@@ -64,7 +63,7 @@ class UsnjrnlPlugin(Plugin):
64
63
  yield UsnjrnlRecord(
65
64
  ts=ts,
66
65
  segment=f"{segment}#{record.FileReferenceNumber.SequenceNumber}",
67
- path=rpath.from_windows(path),
66
+ path=self.target.fs.path(path),
68
67
  usn=record.Usn,
69
68
  reason=str(record.Reason).replace("USN_REASON.", ""),
70
69
  attr=str(record.FileAttributes).replace("FILE_ATTRIBUTE.", ""),
@@ -98,7 +98,7 @@ class ResolverPlugin(Plugin):
98
98
  return lookup_ext
99
99
 
100
100
  for search_path in search_paths:
101
- lookup_path = "/".join([search_path, lookup_ext])
101
+ lookup_path = fsutil.join(search_path, lookup_ext, alt_separator=self.target.fs.alt_separator)
102
102
  if self.target.fs.exists(lookup_path):
103
103
  return lookup_path
104
104
 
@@ -2,9 +2,10 @@ import struct
2
2
  from enum import IntEnum
3
3
  from io import BytesIO
4
4
 
5
- from dissect.target.exceptions import UnsupportedPluginError
5
+ from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
6
6
  from dissect.target.helpers.record import TargetRecordDescriptor
7
7
  from dissect.target.plugin import Plugin, export
8
+ from dissect.target.plugins.filesystem.walkfs import generate_record
8
9
 
9
10
  CapabilityRecord = TargetRecordDescriptor(
10
11
  "filesystem/unix/capability",
@@ -87,72 +88,82 @@ class CapabilityPlugin(Plugin):
87
88
  @export(record=CapabilityRecord)
88
89
  def capability_binaries(self):
89
90
  """Find all files that have capabilities set."""
90
- for entry, record in self.target.walkfs_ext():
91
- try:
92
- attrs = entry.get().lattr()
93
- except Exception:
94
- self.target.log.exception("Failed to get attrs for entry %s", entry)
95
- continue
96
-
97
- for attr in attrs:
98
- if attr.name != "security.capability":
91
+ for path_entries, _, files in self.target.fs.walk_ext("/"):
92
+ entries = [path_entries[-1]] + files
93
+ for entry in entries:
94
+ path = self.target.fs.path(entry.path)
95
+ try:
96
+ record = generate_record(self.target, path)
97
+ except FileNotFoundError:
99
98
  continue
100
-
101
- buf = BytesIO(attr.value)
102
-
103
- # Reference: https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
104
- # The struct is small enough we can just use struct
105
- magic_etc = struct.unpack("<I", buf.read(4))[0]
106
- cap_revision = magic_etc & VFS_CAP_REVISION_MASK
107
-
108
- permitted_caps = []
109
- inheritable_caps = []
110
- rootid = None
111
-
112
- if cap_revision == VFS_CAP_REVISION_1:
113
- num_caps = VFS_CAP_U32_1
114
- data_len = (1 + 2 * VFS_CAP_U32_1) * 4
115
- elif cap_revision == VFS_CAP_REVISION_2:
116
- num_caps = VFS_CAP_U32_2
117
- data_len = (1 + 2 * VFS_CAP_U32_2) * 4
118
- elif cap_revision == VFS_CAP_REVISION_3:
119
- num_caps = VFS_CAP_U32_3
120
- data_len = (2 + 2 * VFS_CAP_U32_2) * 4
121
- else:
122
- self.target.log.error("Unexpected capability revision: %s", entry)
99
+ try:
100
+ attrs = path.get().lattr()
101
+ except TypeError:
102
+ # Virtual(File|Directory|Symlink) instances don't have a functional lattr()
123
103
  continue
124
-
125
- if data_len != len(attr.value):
126
- self.target.log.error("Unexpected capability length: %s", entry)
104
+ except Exception:
105
+ self.target.log.exception("Failed to get attrs for entry %s", entry)
127
106
  continue
128
107
 
129
- for _ in range(num_caps):
130
- permitted_val, inheritable_val = struct.unpack("<2I", buf.read(8))
131
- permitted_caps.append(permitted_val)
132
- inheritable_caps.append(inheritable_val)
133
-
134
- if cap_revision == VFS_CAP_REVISION_3:
135
- rootid = struct.unpack("<I", buf.read(4))[0]
136
-
137
- permitted = []
138
- inheritable = []
139
-
140
- for capability in Capabilities:
141
- for caps, results in [(permitted_caps, permitted), (inheritable_caps, inheritable)]:
142
- # CAP_TO_INDEX
143
- cap_index = capability.value >> 5
144
- if cap_index >= len(caps):
145
- # We loop over all capabilities, but might only have a version 1 caps list
146
- continue
147
-
148
- if caps[cap_index] & (1 << (capability.value & 31)) != 0:
149
- results.append(capability.name)
150
-
151
- yield CapabilityRecord(
152
- record=record,
153
- permitted=permitted,
154
- inheritable=inheritable,
155
- effective=magic_etc & VFS_CAP_FLAGS_EFFECTIVE != 0,
156
- rootid=rootid,
157
- _target=self.target,
158
- )
108
+ for attr in attrs:
109
+ if attr.name != "security.capability":
110
+ continue
111
+
112
+ buf = BytesIO(attr.value)
113
+
114
+ # Reference: https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
115
+ # The struct is small enough we can just use struct
116
+ magic_etc = struct.unpack("<I", buf.read(4))[0]
117
+ cap_revision = magic_etc & VFS_CAP_REVISION_MASK
118
+
119
+ permitted_caps = []
120
+ inheritable_caps = []
121
+ rootid = None
122
+
123
+ if cap_revision == VFS_CAP_REVISION_1:
124
+ num_caps = VFS_CAP_U32_1
125
+ data_len = (1 + 2 * VFS_CAP_U32_1) * 4
126
+ elif cap_revision == VFS_CAP_REVISION_2:
127
+ num_caps = VFS_CAP_U32_2
128
+ data_len = (1 + 2 * VFS_CAP_U32_2) * 4
129
+ elif cap_revision == VFS_CAP_REVISION_3:
130
+ num_caps = VFS_CAP_U32_3
131
+ data_len = (2 + 2 * VFS_CAP_U32_2) * 4
132
+ else:
133
+ self.target.log.error("Unexpected capability revision: %s", entry)
134
+ continue
135
+
136
+ if data_len != len(attr.value):
137
+ self.target.log.error("Unexpected capability length: %s", entry)
138
+ continue
139
+
140
+ for _ in range(num_caps):
141
+ permitted_val, inheritable_val = struct.unpack("<2I", buf.read(8))
142
+ permitted_caps.append(permitted_val)
143
+ inheritable_caps.append(inheritable_val)
144
+
145
+ if cap_revision == VFS_CAP_REVISION_3:
146
+ rootid = struct.unpack("<I", buf.read(4))[0]
147
+
148
+ permitted = []
149
+ inheritable = []
150
+
151
+ for capability in Capabilities:
152
+ for caps, results in [(permitted_caps, permitted), (inheritable_caps, inheritable)]:
153
+ # CAP_TO_INDEX
154
+ cap_index = capability.value >> 5
155
+ if cap_index >= len(caps):
156
+ # We loop over all capabilities, but might only have a version 1 caps list
157
+ continue
158
+
159
+ if caps[cap_index] & (1 << (capability.value & 31)) != 0:
160
+ results.append(capability.name)
161
+
162
+ yield CapabilityRecord(
163
+ record=record,
164
+ permitted=permitted,
165
+ inheritable=inheritable,
166
+ effective=magic_etc & VFS_CAP_FLAGS_EFFECTIVE != 0,
167
+ rootid=rootid,
168
+ _target=self.target,
169
+ )
@@ -1,8 +1,13 @@
1
+ from typing import Iterable
2
+
1
3
  from dissect.util.ts import from_unix
2
4
 
3
5
  from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
6
+ from dissect.target.filesystem import RootFilesystemEntry
7
+ from dissect.target.helpers.fsutil import TargetPath
4
8
  from dissect.target.helpers.record import TargetRecordDescriptor
5
- from dissect.target.plugin import Plugin, export, internal
9
+ from dissect.target.plugin import Plugin, export
10
+ from dissect.target.target import Target
6
11
 
7
12
  FilesystemRecord = TargetRecordDescriptor(
8
13
  "filesystem/entry",
@@ -17,8 +22,7 @@ FilesystemRecord = TargetRecordDescriptor(
17
22
  ("uint32", "mode"),
18
23
  ("uint32", "uid"),
19
24
  ("uint32", "gid"),
20
- ("string", "fstype"),
21
- ("uint32", "fsidx"),
25
+ ("string[]", "fstypes"),
22
26
  ],
23
27
  )
24
28
 
@@ -29,36 +33,36 @@ class WalkFSPlugin(Plugin):
29
33
  raise UnsupportedPluginError("No filesystems found")
30
34
 
31
35
  @export(record=FilesystemRecord)
32
- def walkfs(self):
36
+ def walkfs(self) -> Iterable[FilesystemRecord]:
33
37
  """Walk a target's filesystem and return all filesystem entries."""
34
- for _, record in self.walkfs_ext():
35
- yield record
36
-
37
- @internal
38
- def walkfs_ext(self, root="/", pattern="*"):
39
- for idx, fs in enumerate(self.target.filesystems):
40
- for entry in fs.path(root).rglob(pattern):
38
+ for path_entries, _, files in self.target.fs.walk_ext("/"):
39
+ entries = [path_entries[-1]] + files
40
+ for entry in entries:
41
+ path = self.target.fs.path(entry.path)
41
42
  try:
42
- yield entry, generate_record(self.target, entry, idx)
43
+ record = generate_record(self.target, path)
43
44
  except FileNotFoundError:
44
45
  continue
45
- except Exception:
46
- self.target.log.exception("Failed to generate record from entry %s", entry)
46
+ yield record
47
47
 
48
48
 
49
- def generate_record(target, entry, idx):
50
- stat = entry.lstat()
49
+ def generate_record(target: Target, path: TargetPath) -> FilesystemRecord:
50
+ stat = path.lstat()
51
+ entry = path.get()
52
+ if isinstance(entry, RootFilesystemEntry):
53
+ fs_types = [sub_entry.fs.__type__ for sub_entry in entry.entries]
54
+ else:
55
+ fs_types = [entry.fs.__type__]
51
56
  return FilesystemRecord(
52
57
  atime=from_unix(stat.st_atime),
53
58
  mtime=from_unix(stat.st_mtime),
54
59
  ctime=from_unix(stat.st_ctime),
55
60
  ino=stat.st_ino,
56
- path=entry,
61
+ path=path,
57
62
  size=stat.st_size,
58
63
  mode=stat.st_mode,
59
64
  uid=stat.st_uid,
60
65
  gid=stat.st_gid,
61
- fstype=entry.get().fs.__type__,
62
- fsidx=idx,
66
+ fstypes=fs_types,
63
67
  _target=target,
64
68
  )