dissect.target 3.19.dev24__py3-none-any.whl → 3.19.dev26__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,