dissect.target 3.19.dev35__py3-none-any.whl → 3.19.dev37__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.
@@ -0,0 +1,40 @@
1
+ from pathlib import Path
2
+
3
+ from defusedxml import ElementTree
4
+
5
+ from dissect.target import container
6
+ from dissect.target.helpers import fsutil
7
+ from dissect.target.loader import Loader
8
+ from dissect.target.target import Target
9
+
10
+
11
+ class LibvirtLoader(Loader):
12
+ """Load libvirt xml configuration files."""
13
+
14
+ def __init__(self, path: Path, **kwargs):
15
+ path = path.resolve()
16
+ self.base_dir = path.parent
17
+ super().__init__(path)
18
+
19
+ @staticmethod
20
+ def detect(path: Path) -> bool:
21
+ if path.suffix.lower() != ".xml":
22
+ return False
23
+
24
+ with path.open("rb") as fh:
25
+ lines = fh.read(512).split(b"\n")
26
+ # From what I've seen, these are are always at the start of the file
27
+ # If its generated using virt-install
28
+ needles = [b"<domain", b"<name>", b"<uuid>"]
29
+ return all(any(needle in line for line in lines) for needle in needles)
30
+
31
+ def map(self, target: Target) -> None:
32
+ xml_data = ElementTree.fromstring(self.path.read_text())
33
+ for disk in xml_data.findall("devices/disk/source"):
34
+ if not (file := disk.get("file")):
35
+ continue
36
+
37
+ for part in [fsutil.basename(file), file]:
38
+ if (path := self.base_dir.joinpath(part)).exists():
39
+ target.disks.add(container.open(path))
40
+ break
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Iterator
4
+
5
+ from dissect.target.exceptions import UnsupportedPluginError
6
+ from dissect.target.helpers.record import ChildTargetRecord
7
+ from dissect.target.plugin import ChildTargetPlugin
8
+
9
+
10
+ class QemuChildTargetPlugin(ChildTargetPlugin):
11
+ """Child target plugin that yields all QEMU domains from a KVM libvirt deamon."""
12
+
13
+ __type__ = "qemu"
14
+
15
+ def check_compatible(self) -> None:
16
+ if not self.target.fs.path("/etc/libvirt/qemu").exists():
17
+ raise UnsupportedPluginError("No libvirt QEMU installation found")
18
+
19
+ def list_children(self) -> Iterator[ChildTargetRecord]:
20
+ for domain in self.target.fs.path("/etc/libvirt/qemu").glob("*.xml"):
21
+ yield ChildTargetRecord(type=self.__type__, path=domain)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  from typing import Callable, Iterator
3
5
 
@@ -8,6 +10,7 @@ from flow.record import Record
8
10
  from flow.record.fieldtypes import windows_path
9
11
 
10
12
  from dissect.target.exceptions import UnsupportedPluginError
13
+ from dissect.target.filesystems.ntfs import NtfsFilesystem
11
14
  from dissect.target.helpers.record import TargetRecordDescriptor
12
15
  from dissect.target.plugin import Plugin, arg, export
13
16
  from dissect.target.plugins.filesystem.ntfs.utils import (
@@ -119,9 +122,12 @@ COMPACT_RECORD_TYPES = {
119
122
 
120
123
 
121
124
  class MftPlugin(Plugin):
125
+ def __init__(self, target):
126
+ super().__init__(target)
127
+ self.ntfs_filesystems = {index: fs for index, fs in enumerate(self.target.filesystems) if fs.__type__ == "ntfs"}
128
+
122
129
  def check_compatible(self) -> None:
123
- ntfs_filesystems = [fs for fs in self.target.filesystems if fs.__type__ == "ntfs"]
124
- if not len(ntfs_filesystems):
130
+ if not len(self.ntfs_filesystems):
125
131
  raise UnsupportedPluginError("No NTFS filesystems found")
126
132
 
127
133
  @export(
@@ -133,12 +139,17 @@ class MftPlugin(Plugin):
133
139
  ]
134
140
  )
135
141
  @arg("--compact", action="store_true", help="compacts the MFT entry timestamps into a single record")
142
+ @arg("--fs", type=int, default=None, help="optional filesystem index, zero indexed")
143
+ @arg("--start", type=int, default=0, help="the first MFT segment number")
144
+ @arg("--end", type=int, default=-1, help="the last MFT segment number")
136
145
  @arg(
137
146
  "--macb",
138
147
  action="store_true",
139
148
  help="compacts the MFT entry timestamps into aggregated records with MACB bitfield",
140
149
  )
141
- def mft(self, compact: bool = False, macb: bool = False):
150
+ def mft(
151
+ self, compact: bool = False, fs: int | None = None, start: int = 0, end: int = -1, macb: bool = False
152
+ ) -> Iterator[Record]:
142
153
  """Return the MFT records of all NTFS filesystems.
143
154
 
144
155
  The Master File Table (MFT) contains primarily metadata about every file and folder on a NFTS filesystem.
@@ -167,55 +178,66 @@ class MftPlugin(Plugin):
167
178
  elif macb:
168
179
  aggr = macb_aggr
169
180
 
170
- for fs in self.target.filesystems:
171
- if fs.__type__ != "ntfs":
172
- continue
173
-
174
- # If this filesystem is a "fake" NTFS filesystem, used to enhance a
175
- # VirtualFilesystem, The driveletter (more accurate mount point)
176
- # returned will be that of the VirtualFilesystem. This makes sure
177
- # the paths returned in the records are actually reachable.
178
- drive_letter = get_drive_letter(self.target, fs)
179
- volume_uuid = get_volume_identifier(fs)
180
-
181
+ if fs is not None:
181
182
  try:
182
- for record in fs.ntfs.mft.segments():
183
- try:
184
- inuse = bool(record.header.Flags & FILE_RECORD_SEGMENT_IN_USE)
185
- owner, _ = get_owner_and_group(record, fs)
186
- resident = False
187
- size = None
188
-
189
- if not record.is_dir():
190
- for data_attribute in record.attributes.DATA:
191
- if data_attribute.name == "":
192
- resident = data_attribute.resident
193
- break
194
-
195
- size = get_record_size(record)
196
-
197
- for path in record.full_paths():
198
- path = f"{drive_letter}{path}"
199
- yield from aggr(
200
- self.mft_records(
201
- drive_letter=drive_letter,
202
- record=record,
203
- segment=record.segment,
204
- path=path,
205
- owner=owner,
206
- size=size,
207
- resident=resident,
208
- inuse=inuse,
209
- volume_uuid=volume_uuid,
210
- record_formatter=record_formatter,
211
- )
183
+ filesystem = self.ntfs_filesystems[fs]
184
+ except KeyError:
185
+ self.target.log.error("NTFS filesystem with index number %s does not exist", fs)
186
+ return
187
+
188
+ yield from self.segments(filesystem, record_formatter, aggr, start, end)
189
+ else:
190
+ for filesystem in self.ntfs_filesystems.values():
191
+ yield from self.segments(filesystem, record_formatter, aggr, start, end)
192
+
193
+ def segments(
194
+ self, fs: NtfsFilesystem, record_formatter: Callable, aggr: Callable, start: int, end: int
195
+ ) -> Iterator[Record]:
196
+ # If this filesystem is a "fake" NTFS filesystem, used to enhance a
197
+ # VirtualFilesystem, The driveletter (more accurate mount point)
198
+ # returned will be that of the VirtualFilesystem. This makes sure
199
+ # the paths returned in the records are actually reachable.
200
+ drive_letter = get_drive_letter(self.target, fs)
201
+ volume_uuid = get_volume_identifier(fs)
202
+
203
+ try:
204
+ for record in fs.ntfs.mft.segments(start, end):
205
+ try:
206
+ inuse = bool(record.header.Flags & FILE_RECORD_SEGMENT_IN_USE)
207
+ owner, _ = get_owner_and_group(record, fs)
208
+ resident = None
209
+ size = None
210
+
211
+ if not record.is_dir():
212
+ for data_attribute in record.attributes.DATA:
213
+ if data_attribute.name == "":
214
+ resident = data_attribute.resident
215
+ break
216
+
217
+ size = get_record_size(record)
218
+
219
+ for path in record.full_paths():
220
+ path = f"{drive_letter}{path}"
221
+ yield from aggr(
222
+ self.mft_records(
223
+ drive_letter=drive_letter,
224
+ record=record,
225
+ segment=record.segment,
226
+ path=path,
227
+ owner=owner,
228
+ size=size,
229
+ resident=resident,
230
+ inuse=inuse,
231
+ volume_uuid=volume_uuid,
232
+ record_formatter=record_formatter,
212
233
  )
213
- except Exception as e:
214
- self.target.log.warning("An error occured parsing MFT segment %d: %s", record.segment, str(e))
215
- self.target.log.debug("", exc_info=e)
234
+ )
235
+ except Exception as e:
236
+ self.target.log.warning("An error occured parsing MFT segment %d: %s", record.segment, str(e))
237
+ self.target.log.debug("", exc_info=e)
216
238
 
217
- except Exception:
218
- log.exception("An error occured constructing FilesystemRecords")
239
+ except Exception:
240
+ log.exception("An error occured constructing FilesystemRecords")
219
241
 
220
242
  def mft_records(
221
243
  self,
@@ -229,7 +251,7 @@ class MftPlugin(Plugin):
229
251
  inuse: bool,
230
252
  volume_uuid: str,
231
253
  record_formatter: Callable,
232
- ):
254
+ ) -> Iterator[Record]:
233
255
  for attr in record.attributes.STANDARD_INFORMATION:
234
256
  yield from record_formatter(
235
257
  attr=attr,
@@ -286,7 +308,7 @@ class MftPlugin(Plugin):
286
308
  )
287
309
 
288
310
 
289
- def compacted_formatter(attr: Attribute, record_type: InformationType, **kwargs):
311
+ def compacted_formatter(attr: Attribute, record_type: InformationType, **kwargs) -> Iterator[Record]:
290
312
  record_desc = COMPACT_RECORD_TYPES.get(record_type)
291
313
  yield record_desc(
292
314
  creation_time=attr.creation_time,
@@ -297,7 +319,7 @@ def compacted_formatter(attr: Attribute, record_type: InformationType, **kwargs)
297
319
  )
298
320
 
299
321
 
300
- def formatter(attr: Attribute, record_type: InformationType, **kwargs):
322
+ def formatter(attr: Attribute, record_type: InformationType, **kwargs) -> Iterator[Record]:
301
323
  record_desc = RECORD_TYPES.get(record_type)
302
324
  for type, timestamp in [
303
325
  ("B", attr.creation_time),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev35
3
+ Version: 3.19.dev37
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
@@ -84,6 +84,7 @@ dissect/target/loaders/dir.py,sha256=F-PgvBw82XmL0rdKyBxznUkDc5Oct6-_Y9xM4fhvA6I
84
84
  dissect/target/loaders/hyperv.py,sha256=_IOUJEO0BXaCBZ6sjIX0DZTkG9UNW5Vs9VcNHYv073w,5928
85
85
  dissect/target/loaders/itunes.py,sha256=rKOhlDRypQBGkuSZudMDS1Mlb9XV6BD5FRvM7tGq9jU,13128
86
86
  dissect/target/loaders/kape.py,sha256=t5TfrGLqPeIpUUpXzIl6aHsqXMEGDqJ5YwDCs07DiBA,1237
87
+ dissect/target/loaders/libvirt.py,sha256=_3EFIytMGbiLMISHx4QXVrDebsRO6J6sMkE3TH68qsg,1374
87
88
  dissect/target/loaders/local.py,sha256=Ul-LCd_fY7SyWOVR6nH-NqbkuNpxoZVmffwrkvQElU8,16453
88
89
  dissect/target/loaders/log.py,sha256=cCkDIRS4aPlX3U-n_jUKaI2FPSV3BDpfqKceaU7rBbo,1507
89
90
  dissect/target/loaders/mqtt.py,sha256=pn2VtFh0jeYXMod4CuZOKGhe2ScQixJ1Xhx6MHe0rzk,16540
@@ -155,6 +156,7 @@ dissect/target/plugins/child/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
155
156
  dissect/target/plugins/child/docker.py,sha256=frBZ8UUzbtkT9VrK1fwUzXDAdkHESdPCb-QI_OP9Jj4,872
156
157
  dissect/target/plugins/child/esxi.py,sha256=GfgQzxntcHcyxAE2QjMJ-TrFhklweSXLbYh0uuv-klg,693
157
158
  dissect/target/plugins/child/hyperv.py,sha256=R2qVeu4p_9V53jO-65znN0LwX9v3FVA-9jbbtOQcEz8,2236
159
+ dissect/target/plugins/child/qemu.py,sha256=vNzQwzFO964jYaI67MlX8vpWyHxpegjIU5F29zHKOGI,791
158
160
  dissect/target/plugins/child/virtuozzo.py,sha256=Mx4ZxEl21g7IYkzraw4FBZup5EfrkFDv4WuTE3hxguw,1206
159
161
  dissect/target/plugins/child/vmware_workstation.py,sha256=8wkA_tSufvBUyp4XQHzRzFETf5ROlyyO_MVS3TExyfw,1570
160
162
  dissect/target/plugins/child/wsl.py,sha256=IssQgYET1T-XR5ZX2lGlNFJ_u_3QECpMF_7kXu09HTE,2469
@@ -166,7 +168,7 @@ dissect/target/plugins/filesystem/resolver.py,sha256=HfyASUFV4F9uD-yFXilFpPTORAs
166
168
  dissect/target/plugins/filesystem/walkfs.py,sha256=e8HEZcV5Wiua26FGWL3xgiQ_PIhcNvGI5KCdsAx2Nmo,2298
167
169
  dissect/target/plugins/filesystem/yara.py,sha256=zh4hU3L_egddLqDeaHDVuCWYhTlNzPYPVak36Q6IMxI,6621
168
170
  dissect/target/plugins/filesystem/ntfs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
- dissect/target/plugins/filesystem/ntfs/mft.py,sha256=2ibCLJA7yUrZshFSPKdjoNt3TpfwTtk-DaErghe91CM,11445
171
+ dissect/target/plugins/filesystem/ntfs/mft.py,sha256=WzvKSlH4egvDsuonRQ3AjYS59t9jjFX_-GOsAPLUeSk,12418
170
172
  dissect/target/plugins/filesystem/ntfs/mft_timeline.py,sha256=vvNFAZbr7s3X2OTYf4ES_L6-XsouTXcTymfxnHfZ1Rw,6791
171
173
  dissect/target/plugins/filesystem/ntfs/usnjrnl.py,sha256=uiT1ipmcAo__6VIUi8R_vvIu22vdnjMACKwLSAbzYjs,3704
172
174
  dissect/target/plugins/filesystem/ntfs/utils.py,sha256=xG7Lgw9NX4tDDrZVRm0vycFVJTOM7j-HrjqzDh0f4uA,3136
@@ -346,10 +348,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
346
348
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
347
349
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
348
350
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
349
- dissect.target-3.19.dev35.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
- dissect.target-3.19.dev35.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
- dissect.target-3.19.dev35.dist-info/METADATA,sha256=3ApP47TWFjPe5wV6_fuejtFbpr7YL-bwmvPePNKfN1M,12719
352
- dissect.target-3.19.dev35.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
- dissect.target-3.19.dev35.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
- dissect.target-3.19.dev35.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
- dissect.target-3.19.dev35.dist-info/RECORD,,
351
+ dissect.target-3.19.dev37.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
352
+ dissect.target-3.19.dev37.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
353
+ dissect.target-3.19.dev37.dist-info/METADATA,sha256=mXarkSk3Roesdmk1FGrFsYXYFROLuwmF6QxdMO3YOVQ,12719
354
+ dissect.target-3.19.dev37.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
355
+ dissect.target-3.19.dev37.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
356
+ dissect.target-3.19.dev37.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
357
+ dissect.target-3.19.dev37.dist-info/RECORD,,