dissect.target 3.19.dev24__py3-none-any.whl → 3.19.dev26__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.
@@ -1,9 +1,10 @@
1
1
  import logging
2
- from typing import Callable
2
+ from typing import Callable, Iterator
3
3
 
4
4
  from dissect.ntfs.attr import Attribute
5
5
  from dissect.ntfs.c_ntfs import FILE_RECORD_SEGMENT_IN_USE
6
6
  from dissect.ntfs.mft import MftRecord
7
+ from flow.record import Record
7
8
  from flow.record.fieldtypes import windows_path
8
9
 
9
10
  from dissect.target.exceptions import UnsupportedPluginError
@@ -53,7 +54,6 @@ FilesystemStdRecord = TargetRecordDescriptor(
53
54
  ],
54
55
  )
55
56
 
56
-
57
57
  FilesystemFilenameCompactRecord = TargetRecordDescriptor(
58
58
  "filesystem/ntfs/mft/filename/compact",
59
59
  [
@@ -90,6 +90,21 @@ FilesystemFilenameRecord = TargetRecordDescriptor(
90
90
  ],
91
91
  )
92
92
 
93
+ FilesystemMACBRecord = TargetRecordDescriptor(
94
+ "filesystem/ntfs/mft/macb",
95
+ [
96
+ ("datetime", "ts"),
97
+ ("string", "macb"),
98
+ ("uint32", "filename_index"),
99
+ ("uint32", "segment"),
100
+ ("path", "path"),
101
+ ("string", "owner"),
102
+ ("filesize", "filesize"),
103
+ ("boolean", "resident"),
104
+ ("boolean", "inuse"),
105
+ ("string", "volume_uuid"),
106
+ ],
107
+ )
93
108
 
94
109
  RECORD_TYPES = {
95
110
  InformationType.STANDARD_INFORMATION: FilesystemStdRecord,
@@ -118,7 +133,12 @@ class MftPlugin(Plugin):
118
133
  ]
119
134
  )
120
135
  @arg("--compact", action="store_true", help="compacts the MFT entry timestamps into a single record")
121
- def mft(self, compact: bool = False):
136
+ @arg(
137
+ "--macb",
138
+ action="store_true",
139
+ help="compacts the MFT entry timestamps into aggregated records with MACB bitfield",
140
+ )
141
+ def mft(self, compact: bool = False, macb: bool = False):
122
142
  """Return the MFT records of all NTFS filesystems.
123
143
 
124
144
  The Master File Table (MFT) contains primarily metadata about every file and folder on a NFTS filesystem.
@@ -133,10 +153,19 @@ class MftPlugin(Plugin):
133
153
  - https://docs.microsoft.com/en-us/windows/win32/fileio/master-file-table
134
154
  """
135
155
 
136
- if compact:
156
+ record_formatter = formatter
157
+
158
+ def noaggr(records: list[Record]) -> Iterator[Record]:
159
+ yield from records
160
+
161
+ aggr = noaggr
162
+
163
+ if compact and macb:
164
+ raise ValueError("--macb and --compact are mutually exclusive")
165
+ elif compact:
137
166
  record_formatter = compacted_formatter
138
- else:
139
- record_formatter = formatter
167
+ elif macb:
168
+ aggr = macb_aggr
140
169
 
141
170
  for fs in self.target.filesystems:
142
171
  if fs.__type__ != "ntfs":
@@ -167,17 +196,19 @@ class MftPlugin(Plugin):
167
196
 
168
197
  for path in record.full_paths():
169
198
  path = f"{drive_letter}{path}"
170
- yield from self.mft_records(
171
- drive_letter=drive_letter,
172
- record=record,
173
- segment=record.segment,
174
- path=path,
175
- owner=owner,
176
- size=size,
177
- resident=resident,
178
- inuse=inuse,
179
- volume_uuid=volume_uuid,
180
- record_formatter=record_formatter,
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
+ )
181
212
  )
182
213
  except Exception as e:
183
214
  self.target.log.warning("An error occured parsing MFT segment %d: %s", record.segment, str(e))
@@ -275,3 +306,33 @@ def formatter(attr: Attribute, record_type: InformationType, **kwargs):
275
306
  ("A", attr.last_access_time),
276
307
  ]:
277
308
  yield record_desc(ts=timestamp, ts_type=type, **kwargs)
309
+
310
+
311
+ def macb_aggr(records: list[Record]) -> Iterator[Record]:
312
+ def macb_set(bitfield, index, letter):
313
+ return bitfield[:index] + letter + bitfield[index + 1 :]
314
+
315
+ macbs = []
316
+ for record in records:
317
+ found = False
318
+
319
+ offset_std = int(record._desc.name == "filesystem/ntfs/mft/std") * 5
320
+ offset_ads = (int(record.ads) * 10) if offset_std == 0 else 0
321
+
322
+ field = "MACB".find(record.ts_type) + offset_std + offset_ads
323
+ for macb in macbs:
324
+ if macb.ts == record.ts:
325
+ macb.macb = macb_set(macb.macb, field, record.ts_type)
326
+ found = True
327
+ break
328
+
329
+ if found:
330
+ continue
331
+
332
+ macb = FilesystemMACBRecord.init_from_record(record)
333
+ macb.macb = "..../..../...."
334
+ macb.macb = macb_set(macb.macb, field, record.ts_type)
335
+
336
+ macbs.append(macb)
337
+
338
+ yield from macbs
@@ -2,6 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  import argparse
5
+ import datetime
5
6
  import logging
6
7
  import operator
7
8
  import os
@@ -12,6 +13,7 @@ import sys
12
13
  from dissect.target import Target
13
14
  from dissect.target.exceptions import TargetError
14
15
  from dissect.target.helpers.fsutil import TargetPath
16
+ from dissect.target.tools.shell import stat_modestr
15
17
  from dissect.target.tools.utils import (
16
18
  catch_sigpipe,
17
19
  configure_generic_arguments,
@@ -23,24 +25,90 @@ logging.lastResort = None
23
25
  logging.raiseExceptions = False
24
26
 
25
27
 
26
- def ls(t, path, args):
27
- for e in sorted(path.iterdir(), key=operator.attrgetter("name")):
28
- print(e.name)
28
+ def human_size(bytes: int, units: list[str] = ["", "K", "M", "G", "T", "P", "E"]) -> str:
29
+ """Helper function to return the human readable string representation of bytes."""
30
+ return str(bytes) + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:])
29
31
 
30
32
 
31
- def cat(t, path, args):
33
+ def ls(t: Target, path: TargetPath, args: argparse.Namespace) -> None:
34
+ if args.use_ctime and args.use_atime:
35
+ log.error("Can't specify -c and -u at the same time")
36
+ return
37
+ if not path or not path.exists():
38
+ return
39
+
40
+ _print_ls(args, path, 0)
41
+
42
+
43
+ def _print_ls(args: argparse.Namespace, path: TargetPath, depth: int) -> None:
44
+ subdirs = []
45
+
46
+ if path.is_dir():
47
+ contents = sorted(path.iterdir(), key=operator.attrgetter("name"))
48
+ elif path.is_file():
49
+ contents = [path]
50
+
51
+ if depth > 0:
52
+ print(f"\n{str(path)}:")
53
+
54
+ if not args.l:
55
+ for entry in contents:
56
+ print(entry.name)
57
+
58
+ if entry.is_dir():
59
+ subdirs.append(entry)
60
+ else:
61
+ if len(contents) > 1:
62
+ print(f"total {len(contents)}")
63
+
64
+ for entry in contents:
65
+ _print_extensive_file_stat(args, entry, entry.name)
66
+
67
+ if entry.is_dir():
68
+ subdirs.append(entry)
69
+
70
+ if args.recursive and subdirs:
71
+ for subdir in subdirs:
72
+ _print_ls(args, subdir, depth + 1)
73
+
74
+
75
+ def _print_extensive_file_stat(args: argparse.Namespace, path: TargetPath, name: str) -> None:
76
+ try:
77
+ entry = path.get()
78
+ stat = entry.lstat()
79
+ symlink = f" -> {entry.readlink()}" if entry.is_symlink() else ""
80
+ show_time = stat.st_mtime
81
+
82
+ if args.use_ctime:
83
+ show_time = stat.st_ctime
84
+ elif args.use_atime:
85
+ show_time = stat.st_atime
86
+
87
+ utc_time = datetime.datetime.utcfromtimestamp(show_time).isoformat()
88
+
89
+ if args.human_readable:
90
+ size = human_size(stat.st_size)
91
+ else:
92
+ size = stat.st_size
93
+
94
+ print(f"{stat_modestr(stat)} {stat.st_uid:4d} {stat.st_gid:4d} {size:>6s} {utc_time} {name}{symlink}")
95
+ except FileNotFoundError:
96
+ print(f"?????????? ? ? ? ????-??-??T??:??:??.?????? {name}")
97
+
98
+
99
+ def cat(t: Target, path: TargetPath, args: argparse.Namespace) -> None:
32
100
  stdout = sys.stdout
33
101
  if hasattr(stdout, "buffer"):
34
102
  stdout = stdout.buffer
35
103
  shutil.copyfileobj(path.open(), stdout)
36
104
 
37
105
 
38
- def walk(t, path, args):
106
+ def walk(t: Target, path: TargetPath, args: argparse.Namespace) -> None:
39
107
  for e in path.rglob("*"):
40
108
  print(str(e))
41
109
 
42
110
 
43
- def cp(t, path, args):
111
+ def cp(t: Target, path: TargetPath, args: argparse.Namespace) -> None:
44
112
  output = os.path.abspath(os.path.expanduser(args.output))
45
113
  if path.is_file():
46
114
  _extract_path(path, os.path.join(output, path.name))
@@ -75,7 +143,7 @@ def _extract_path(path: TargetPath, output_path: str) -> None:
75
143
 
76
144
 
77
145
  @catch_sigpipe
78
- def main():
146
+ def main() -> None:
79
147
  help_formatter = argparse.ArgumentDefaultsHelpFormatter
80
148
  parser = argparse.ArgumentParser(
81
149
  description="dissect.target",
@@ -85,24 +153,34 @@ def main():
85
153
  parser.add_argument("target", type=pathlib.Path, help="Target to load", metavar="TARGET")
86
154
 
87
155
  baseparser = argparse.ArgumentParser(add_help=False)
88
- baseparser.add_argument("path", type=str, help="Path to perform an action on", metavar="PATH")
156
+ baseparser.add_argument("path", type=str, help="path to perform an action on", metavar="PATH")
89
157
 
90
- subparsers = parser.add_subparsers(dest="subcommand", help="Subcommands for performing various actions")
91
- parser_ls = subparsers.add_parser("ls", help="Show a directory listing", parents=[baseparser])
158
+ subparsers = parser.add_subparsers(dest="subcommand", help="subcommands for performing various actions")
159
+ parser_ls = subparsers.add_parser(
160
+ "ls", help="Show a directory listing", parents=[baseparser], conflict_handler="resolve"
161
+ )
162
+ parser_ls.add_argument("-l", action="store_true")
163
+ parser_ls.add_argument("-a", "--all", action="store_true") # ignored but included for proper argument parsing
164
+ parser_ls.add_argument("-h", "--human-readable", action="store_true")
165
+ parser_ls.add_argument("-R", "--recursive", action="store_true", help="recursively list subdirectories encountered")
166
+ parser_ls.add_argument(
167
+ "-c", action="store_true", dest="use_ctime", help="show time when file status was last changed"
168
+ )
169
+ parser_ls.add_argument("-u", action="store_true", dest="use_atime", help="show time of last access")
92
170
  parser_ls.set_defaults(handler=ls)
93
171
 
94
- parser_cat = subparsers.add_parser("cat", help="Dump file contents", parents=[baseparser])
172
+ parser_cat = subparsers.add_parser("cat", help="dump file contents", parents=[baseparser])
95
173
  parser_cat.set_defaults(handler=cat)
96
174
 
97
- parser_find = subparsers.add_parser("walk", help="Perform a walk", parents=[baseparser])
175
+ parser_find = subparsers.add_parser("walk", help="perform a walk", parents=[baseparser])
98
176
  parser_find.set_defaults(handler=walk)
99
177
 
100
178
  parser_cp = subparsers.add_parser(
101
179
  "cp",
102
- help="Copy multiple files to a directory specified by --output",
180
+ help="copy multiple files to a directory specified by --output",
103
181
  parents=[baseparser],
104
182
  )
105
- parser_cp.add_argument("-o", "--output", type=str, default=".", help="Output directory")
183
+ parser_cp.add_argument("-o", "--output", type=str, default=".", help="output directory")
106
184
  parser_cp.set_defaults(handler=cp)
107
185
 
108
186
  configure_generic_arguments(parser)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev24
3
+ Version: 3.19.dev26
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
@@ -166,7 +166,7 @@ dissect/target/plugins/filesystem/resolver.py,sha256=HfyASUFV4F9uD-yFXilFpPTORAs
166
166
  dissect/target/plugins/filesystem/walkfs.py,sha256=e8HEZcV5Wiua26FGWL3xgiQ_PIhcNvGI5KCdsAx2Nmo,2298
167
167
  dissect/target/plugins/filesystem/yara.py,sha256=JdWqbqDBhKrht3fTroqX7NpBU9khEQUWyMcDgLv2l2g,6686
168
168
  dissect/target/plugins/filesystem/ntfs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
- dissect/target/plugins/filesystem/ntfs/mft.py,sha256=AD3w2FIjDAf8x2KEbBhz2NeOA_lxIAmw353w6J3ObYU,9565
169
+ dissect/target/plugins/filesystem/ntfs/mft.py,sha256=2ibCLJA7yUrZshFSPKdjoNt3TpfwTtk-DaErghe91CM,11445
170
170
  dissect/target/plugins/filesystem/ntfs/mft_timeline.py,sha256=vvNFAZbr7s3X2OTYf4ES_L6-XsouTXcTymfxnHfZ1Rw,6791
171
171
  dissect/target/plugins/filesystem/ntfs/usnjrnl.py,sha256=uiT1ipmcAo__6VIUi8R_vvIu22vdnjMACKwLSAbzYjs,3704
172
172
  dissect/target/plugins/filesystem/ntfs/utils.py,sha256=xG7Lgw9NX4tDDrZVRm0vycFVJTOM7j-HrjqzDh0f4uA,3136
@@ -325,7 +325,7 @@ dissect/target/plugins/os/windows/task_helpers/tasks_xml.py,sha256=oOsYse2-BrliV
325
325
  dissect/target/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
326
326
  dissect/target/tools/build_pluginlist.py,sha256=5fomcuMwsVzcnYx5Htf5f9lSwsLeUUvomLUXNA4t7m4,849
327
327
  dissect/target/tools/dd.py,sha256=rTM-lgXxrYBpVAtJqFqAatDz45bLoD8-mFt_59Q3Lio,1928
328
- dissect/target/tools/fs.py,sha256=cizCrW8rqdpT1irA8g6mslkaXX7CynWVQ7fvRUrcxNU,3719
328
+ dissect/target/tools/fs.py,sha256=bdFSckOO-dyvvBpxOgPIx_UKGEbWGbOHF7kl6rWyt7U,6654
329
329
  dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
330
330
  dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
331
331
  dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
@@ -346,10 +346,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
346
346
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
347
347
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
348
348
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
349
- dissect.target-3.19.dev24.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
- dissect.target-3.19.dev24.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
- dissect.target-3.19.dev24.dist-info/METADATA,sha256=svE2PfocnTmkG0NldymfR4W2rf2I-Jg2pfXkN_O-cvw,12719
352
- dissect.target-3.19.dev24.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
- dissect.target-3.19.dev24.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
- dissect.target-3.19.dev24.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
- dissect.target-3.19.dev24.dist-info/RECORD,,
349
+ dissect.target-3.19.dev26.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
+ dissect.target-3.19.dev26.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
+ dissect.target-3.19.dev26.dist-info/METADATA,sha256=JqWUrdsr0XSeyFrdAPCqZYCIrmYfCWdPRFP13XZ4v1o,12719
352
+ dissect.target-3.19.dev26.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
+ dissect.target-3.19.dev26.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
+ dissect.target-3.19.dev26.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
+ dissect.target-3.19.dev26.dist-info/RECORD,,