acquire 3.14.dev7__tar.gz → 3.14.dev9__tar.gz

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.
Files changed (65) hide show
  1. {acquire-3.14.dev7/acquire.egg-info → acquire-3.14.dev9}/PKG-INFO +1 -1
  2. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/acquire.py +117 -199
  3. acquire-3.14.dev9/acquire/collector.py +786 -0
  4. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/utils.py +40 -48
  5. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/version.py +2 -2
  6. {acquire-3.14.dev7 → acquire-3.14.dev9/acquire.egg-info}/PKG-INFO +1 -1
  7. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/conftest.py +4 -0
  8. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_acquire_command.py +6 -1
  9. acquire-3.14.dev9/tests/test_collector.py +659 -0
  10. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_utils.py +128 -87
  11. acquire-3.14.dev7/acquire/collector.py +0 -600
  12. acquire-3.14.dev7/tests/test_collector.py +0 -392
  13. {acquire-3.14.dev7 → acquire-3.14.dev9}/COPYRIGHT +0 -0
  14. {acquire-3.14.dev7 → acquire-3.14.dev9}/LICENSE +0 -0
  15. {acquire-3.14.dev7 → acquire-3.14.dev9}/MANIFEST.in +0 -0
  16. {acquire-3.14.dev7 → acquire-3.14.dev9}/README.md +0 -0
  17. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/__init__.py +0 -0
  18. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/crypt.py +0 -0
  19. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/__init__.py +0 -0
  20. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/__init__.py +0 -0
  21. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/collect.py +0 -0
  22. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/exceptions.py +0 -0
  23. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/handles.py +0 -0
  24. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/named_objects.py +0 -0
  25. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/ntdll.py +0 -0
  26. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/dynamic/windows/types.py +0 -0
  27. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/esxi.py +0 -0
  28. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/__init__.py +0 -0
  29. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/base.py +0 -0
  30. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/gui/win32.py +0 -0
  31. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/hashes.py +0 -0
  32. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/log.py +0 -0
  33. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/__init__.py +0 -0
  34. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/base.py +0 -0
  35. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/dir.py +0 -0
  36. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/tar.py +0 -0
  37. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/outputs/zip.py +0 -0
  38. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/tools/__init__.py +0 -0
  39. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/tools/decrypter.py +0 -0
  40. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/__init__.py +0 -0
  41. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/minio.py +0 -0
  42. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/plugin.py +0 -0
  43. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/uploaders/plugin_registry.py +0 -0
  44. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire/volatilestream.py +0 -0
  45. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/SOURCES.txt +0 -0
  46. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/dependency_links.txt +0 -0
  47. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/entry_points.txt +0 -0
  48. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/requires.txt +0 -0
  49. {acquire-3.14.dev7 → acquire-3.14.dev9}/acquire.egg-info/top_level.txt +0 -0
  50. {acquire-3.14.dev7 → acquire-3.14.dev9}/pyproject.toml +0 -0
  51. {acquire-3.14.dev7 → acquire-3.14.dev9}/setup.cfg +0 -0
  52. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/__init__.py +0 -0
  53. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/Makefile +0 -0
  54. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/conf.py +0 -0
  55. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/docs/index.rst +0 -0
  56. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_acquire_modules.py +0 -0
  57. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_decryptor_funcs.py +0 -0
  58. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_esxi_memory.py +0 -0
  59. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_file_sorting.py +0 -0
  60. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_minio_uploader.py +0 -0
  61. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_misc_users.py +0 -0
  62. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_outputs_dir.py +0 -0
  63. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_outputs_tar.py +0 -0
  64. {acquire-3.14.dev7 → acquire-3.14.dev9}/tests/test_plugin.py +0 -0
  65. {acquire-3.14.dev7 → acquire-3.14.dev9}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: acquire
3
- Version: 3.14.dev7
3
+ Version: 3.14.dev9
4
4
  Summary: A tool to quickly gather forensic artifacts from disk images or a live system into a lightweight container
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -1,9 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import argparse
2
4
  import enum
3
5
  import functools
4
6
  import io
5
7
  import itertools
6
- import json
7
8
  import logging
8
9
  import os
9
10
  import platform
@@ -16,16 +17,15 @@ import urllib.request
16
17
  from collections import defaultdict, namedtuple
17
18
  from itertools import product
18
19
  from pathlib import Path
19
- from typing import Iterator, Optional, Union
20
+ from typing import BinaryIO, Callable, Iterator, Optional, Union
20
21
 
21
- from dissect.target import Target, exceptions
22
+ from dissect.target import Target
22
23
  from dissect.target.filesystem import Filesystem
23
24
  from dissect.target.filesystems import ntfs
24
25
  from dissect.target.helpers import fsutil
25
- from dissect.target.loaders.remote import RemoteStreamConnection
26
- from dissect.target.loaders.targetd import TargetdLoader
27
26
  from dissect.target.plugins.apps.webserver import iis
28
27
  from dissect.target.plugins.os.windows.log import evt, evtx
28
+ from dissect.target.tools.utils import args_to_uri
29
29
  from dissect.util.stream import RunlistStream
30
30
 
31
31
  from acquire.collector import Collector, get_full_formatted_report, get_report_summary
@@ -92,7 +92,6 @@ MODULE_LOOKUP = {}
92
92
 
93
93
  CLI_ARGS_MODULE = "cli-args"
94
94
 
95
-
96
95
  log = logging.getLogger("acquire")
97
96
  log.propagate = 0
98
97
  log_file_handler = None
@@ -149,46 +148,48 @@ MISC_MAPPING = {
149
148
  def from_user_home(target: Target, path: str) -> Iterator[str]:
150
149
  try:
151
150
  for user_details in target.user_details.all_with_home():
152
- yield normalize_path(target, user_details.home_path.joinpath(path), lower_case=False)
151
+ yield user_details.home_path.joinpath(path).as_posix()
153
152
  except Exception as e:
154
153
  log.warning("Error occurred when requesting all user homes")
155
154
  log.debug("", exc_info=e)
156
155
 
157
156
  misc_user_homes = MISC_MAPPING.get(target.os, misc_unix_user_homes)
158
157
  for user_dir in misc_user_homes(target):
159
- yield str(user_dir.joinpath(path))
158
+ yield user_dir.joinpath(path).as_posix()
160
159
 
161
160
 
162
- def iter_ntfs_filesystems(target: Target) -> Iterator[tuple[ntfs.NtfsFilesystem, str, str]]:
161
+ def iter_ntfs_filesystems(target: Target) -> Iterator[tuple[ntfs.NtfsFilesystem, Optional[str], str, str]]:
163
162
  mount_lookup = defaultdict(list)
164
163
  for mount, fs in target.fs.mounts.items():
165
164
  mount_lookup[fs].append(mount)
166
165
 
167
- sysvol = target.fs.mounts["sysvol"]
168
166
  for fs in target.filesystems:
169
- if fs in mount_lookup:
170
- mountpoints = ", ".join(mount_lookup[fs])
171
- else:
172
- mountpoints = "No mounts"
173
-
174
167
  # The attr check is needed to correctly collect fake NTFS filesystems
175
168
  # where the MFT etc. are added to a VirtualFilesystem. This happens for
176
169
  # instance when the target is an acquired tar target.
177
170
  if not isinstance(fs, ntfs.NtfsFilesystem) and not hasattr(fs, "ntfs"):
178
- log.warning("Skipping %s (%s) - not an NTFS filesystem", fs, mountpoints)
171
+ log.warning("Skipping %s - not an NTFS filesystem", fs)
179
172
  continue
180
173
 
181
- if fs == sysvol:
182
- name = "sysvol"
183
- elif fs in mount_lookup:
184
- name = mount_lookup[fs][0]
174
+ if fs in mount_lookup:
175
+ mountpoints = mount_lookup[fs]
176
+
177
+ for main_mountpoint in mountpoints:
178
+ if main_mountpoint != "sysvol":
179
+ break
180
+
181
+ name = main_mountpoint
182
+ mountpoints = ", ".join(mountpoints)
185
183
  else:
184
+ main_mountpoint = None
186
185
  name = f"vol-{fs.ntfs.serial:x}"
186
+ mountpoints = "No mounts"
187
+ log.warning("Unmounted NTFS filesystem found %s (%s)", fs, name)
187
188
 
188
- yield fs, name, mountpoints
189
+ yield fs, main_mountpoint, name, mountpoints
189
190
 
190
191
 
191
- def iter_esxi_filesystems(target: Target) -> Iterator[tuple[str, str, Filesystem]]:
192
+ def iter_esxi_filesystems(target: Target) -> Iterator[tuple[Filesystem, str, str, Optional[str]]]:
192
193
  for mount, fs in target.fs.mounts.items():
193
194
  if not mount.startswith("/vmfs/volumes/"):
194
195
  continue
@@ -200,11 +201,11 @@ def iter_esxi_filesystems(target: Target) -> Iterator[tuple[str, str, Filesystem
200
201
  elif fs.__type__ == "vmfs":
201
202
  name = fs.vmfs.label
202
203
 
203
- yield uuid, name, fs
204
+ yield fs, mount, uuid, name
204
205
 
205
206
 
206
- def register_module(*args, **kwargs):
207
- def wrapper(module_cls):
207
+ def register_module(*args, **kwargs) -> Callable[[type[Module]], type[Module]]:
208
+ def wrapper(module_cls: type[Module]) -> type[Module]:
208
209
  name = module_cls.__name__
209
210
 
210
211
  if name in MODULES:
@@ -228,8 +229,8 @@ def register_module(*args, **kwargs):
228
229
  return wrapper
229
230
 
230
231
 
231
- def module_arg(*args, **kwargs):
232
- def wrapper(module_cls):
232
+ def module_arg(*args, **kwargs) -> Callable[[type[Module]], type[Module]]:
233
+ def wrapper(module_cls: type[Module]) -> type[Module]:
233
234
  if not hasattr(module_cls, "__cli_args__"):
234
235
  module_cls.__cli_args__ = []
235
236
  module_cls.__cli_args__.append((args, kwargs))
@@ -238,7 +239,7 @@ def module_arg(*args, **kwargs):
238
239
  return wrapper
239
240
 
240
241
 
241
- def local_module(cls):
242
+ def local_module(cls: type[object]) -> object:
242
243
  """A decorator that sets property `__local__` on a module class to mark it for local target only"""
243
244
  cls.__local__ = True
244
245
  return cls
@@ -308,22 +309,25 @@ class NTFS(Module):
308
309
 
309
310
  @classmethod
310
311
  def _run(cls, target: Target, cli_args: argparse.Namespace, collector: Collector) -> None:
311
- for fs, name, mountpoints in iter_ntfs_filesystems(target):
312
- log.info("Acquiring %s (%s)", fs, mountpoints)
312
+ for fs, main_mountpoint, name, mountpoints in iter_ntfs_filesystems(target):
313
+ log.info("Acquiring from %s as %s (%s)", fs, name, mountpoints)
314
+
315
+ for filename in ("$MFT", "$Boot", "$Secure:$SDS"):
316
+ if main_mountpoint is not None:
317
+ path = fsutil.join(main_mountpoint, filename)
318
+ collector.collect_path(path)
313
319
 
314
- collector.collect_file(fs.path("$MFT"), outpath=name + "/$MFT")
315
- collector.collect_file(fs.path("$Boot"), outpath=name + "/$Boot")
320
+ else:
321
+ # In case the NTFS filesystem is not mounted, which should not occur but
322
+ # iter_ntfs_filesystems allows for the possibility, we fall back to raw file
323
+ # collection.
324
+ collector.collect_file_raw(filename, fs, name)
316
325
 
317
326
  cls.collect_usnjrnl(collector, fs, name)
318
- cls.collect_ntfs_secure(collector, fs, name)
319
327
 
320
328
  @classmethod
321
329
  def collect_usnjrnl(cls, collector: Collector, fs: Filesystem, name: str) -> None:
322
- try:
323
- usnjrnl_path = fs.path("$Extend/$Usnjrnl:$J")
324
- entry = usnjrnl_path.get()
325
- journal = entry.open()
326
-
330
+ def usnjrnl_accessor(journal: BinaryIO) -> tuple[BinaryIO, int]:
327
331
  # If the filesystem is a virtual NTFS filesystem, journal will be
328
332
  # plain BinaryIO, not a RunlistStream.
329
333
  if isinstance(journal, RunlistStream):
@@ -331,57 +335,18 @@ class NTFS(Module):
331
335
  while journal.runlist[i][0] is None:
332
336
  journal.seek(journal.runlist[i][1] * journal.block_size, io.SEEK_CUR)
333
337
  i += 1
338
+ size = journal.size - journal.tell()
339
+ else:
340
+ size = journal.size
334
341
 
335
- # Use the same method to construct the output path as is used in
336
- # collector.collect_file()
337
- outpath = collector._output_path(f"{name}/$Extend/$Usnjrnl:$J")
338
-
339
- collector.output.write(
340
- outpath,
341
- journal,
342
- size=journal.size - journal.tell(),
343
- entry=entry,
344
- )
345
- collector.report.add_file_collected(cls.__name__, usnjrnl_path)
346
- result = "OK"
347
- except exceptions.FileNotFoundError:
348
- collector.report.add_file_missing(cls.__name__, usnjrnl_path)
349
- result = "File not found"
350
- except Exception as err:
351
- log.debug("Failed to acquire UsnJrnl", exc_info=True)
352
- collector.report.add_file_failed(cls.__name__, usnjrnl_path)
353
- result = repr(err)
354
-
355
- log.info("- Collecting file $Extend/$Usnjrnl:$J: %s", result)
356
-
357
- @classmethod
358
- def collect_ntfs_secure(cls, collector: Collector, fs: Filesystem, name: str) -> None:
359
- try:
360
- secure_path = fs.path("$Secure:$SDS")
361
- entry = secure_path.get()
362
- sds = entry.open()
363
-
364
- # Use the same method to construct the output path as is used in
365
- # collector.collect_file()
366
- outpath = collector._output_path(f"{name}/$Secure:$SDS")
367
-
368
- collector.output.write(
369
- outpath,
370
- sds,
371
- size=sds.size,
372
- entry=entry,
373
- )
374
- collector.report.add_file_collected(cls.__name__, secure_path)
375
- result = "OK"
376
- except FileNotFoundError:
377
- collector.report.add_file_missing(cls.__name__, secure_path)
378
- result = "File not found"
379
- except Exception as err:
380
- log.debug("Failed to acquire SDS", exc_info=True)
381
- collector.report.add_file_failed(cls.__name__, secure_path)
382
- result = repr(err)
342
+ return (journal, size)
383
343
 
384
- log.info("- Collecting file $Secure:$SDS: %s", result)
344
+ collector.collect_file_raw(
345
+ "$Extend/$Usnjrnl:$J",
346
+ fs,
347
+ name,
348
+ file_accessor=usnjrnl_accessor,
349
+ )
385
350
 
386
351
 
387
352
  @register_module("-r", "--registry")
@@ -722,13 +687,20 @@ class RecycleBin(Module):
722
687
  patterns.extend(["$Recycle.Bin/$R*", "$Recycle.Bin/*/$R*", "RECYCLE*/D*"])
723
688
 
724
689
  with collector.file_filter(large_files_filter):
725
- for fs, name, mountpoints in iter_ntfs_filesystems(target):
726
- log.info("Acquiring recycle bin from %s (%s)", fs, mountpoints)
690
+ for fs, main_mountpoint, name, mountpoints in iter_ntfs_filesystems(target):
691
+ log.info("Acquiring recycle bin from %s as %s (%s)", fs, name, mountpoints)
727
692
 
728
693
  for pattern in patterns:
729
- for entry in fs.path().glob(pattern):
730
- if entry.is_file():
731
- collector.collect_file(entry, outpath=fsutil.join(name, str(entry)))
694
+ if main_mountpoint is not None:
695
+ pattern = fsutil.join(main_mountpoint, pattern)
696
+ collector.collect_glob(pattern)
697
+ else:
698
+ # In case the NTFS filesystem is not mounted, which should not occur but
699
+ # iter_ntfs_filesystems allows for the possibility, we fall back to raw file
700
+ # collection.
701
+ for entry in fs.path().glob(pattern):
702
+ if entry.is_file():
703
+ collector.collect_file_raw(fs, entry, name)
732
704
 
733
705
 
734
706
  @register_module("--drivers")
@@ -1294,8 +1266,9 @@ class Boot(Module):
1294
1266
 
1295
1267
 
1296
1268
  def private_key_filter(path: fsutil.TargetPath) -> bool:
1297
- with path.open("rt") as file:
1298
- return "PRIVATE KEY" in file.readline()
1269
+ if path.is_file() and not path.is_symlink():
1270
+ with path.open("rt") as file:
1271
+ return "PRIVATE KEY" in file.readline()
1299
1272
 
1300
1273
 
1301
1274
  @register_module("--home")
@@ -1441,21 +1414,24 @@ class Bootbanks(Module):
1441
1414
  "bootbank": "BOOTBANK1",
1442
1415
  "altbootbank": "BOOTBANK2",
1443
1416
  }
1444
- boot_fs = []
1417
+ boot_fs = {}
1445
1418
 
1446
1419
  for boot_dir, boot_vol in boot_dirs.items():
1447
1420
  dir_path = target.fs.path(boot_dir)
1448
1421
  if dir_path.is_symlink() and dir_path.exists():
1449
1422
  dst = dir_path.readlink()
1450
- boot_fs.append((dst.name, boot_vol, dst.get().top.fs))
1423
+ fs = dst.get().top.fs
1424
+ boot_fs[fs] = boot_vol
1451
1425
 
1452
- for uuid, name, fs in boot_fs:
1453
- log.info("Acquiring /vmfs/volumes/%s (%s)", uuid, name)
1454
- base = f"fs/{uuid}:{name}"
1455
- for path in fs.path("/").rglob("*"):
1456
- if not path.is_file():
1457
- continue
1458
- collector.collect_file(path, outpath=path, base=base)
1426
+ for fs, mountpoint, uuid, _ in iter_esxi_filesystems(target):
1427
+ if fs in boot_fs:
1428
+ name = boot_fs[fs]
1429
+ log.info("Acquiring %s (%s)", mountpoint, name)
1430
+ mountpoint_len = len(mountpoint)
1431
+ base = f"fs/{uuid}:{name}"
1432
+ for path in target.fs.path(mountpoint).rglob("*"):
1433
+ outpath = path.as_posix()[mountpoint_len:]
1434
+ collector.collect_path(path, outpath=outpath, base=base)
1459
1435
 
1460
1436
 
1461
1437
  @register_module("--esxi")
@@ -1478,16 +1454,16 @@ class VMFS(Module):
1478
1454
 
1479
1455
  @classmethod
1480
1456
  def _run(cls, target: Target, cli_args: argparse.Namespace, collector: Collector) -> None:
1481
- for uuid, name, fs in iter_esxi_filesystems(target):
1457
+ for fs, mountpoint, uuid, name in iter_esxi_filesystems(target):
1482
1458
  if not fs.__type__ == "vmfs":
1483
1459
  continue
1484
1460
 
1485
- log.info("Acquiring /vmfs/volumes/%s (%s)", uuid, name)
1461
+ log.info("Acquiring %s (%s)", mountpoint, name)
1462
+ mountpoint_len = len(mountpoint)
1486
1463
  base = f"fs/{uuid}:{name}"
1487
- for path in fs.path("/").glob("*.sf"):
1488
- if not path.is_file():
1489
- continue
1490
- collector.collect_file(path, outpath=path, base=base)
1464
+ for path in target.fs.path(mountpoint).glob("*.sf"):
1465
+ outpath = path.as_posix()[mountpoint_len:]
1466
+ collector.collect_path(path, outpath=outpath, base=base)
1491
1467
 
1492
1468
 
1493
1469
  @register_module("--activities-cache")
@@ -1658,45 +1634,6 @@ def print_acquire_warning(target: Target) -> None:
1658
1634
  log.warning("========================================== WARNING ==========================================")
1659
1635
 
1660
1636
 
1661
- def modargs2json(args: argparse.Namespace) -> dict:
1662
- json_opts = {}
1663
- for module in MODULES.values():
1664
- cli_arg = module.__cli_args__[-1:][0][1]
1665
- if opt := cli_arg.get("dest"):
1666
- json_opts[opt] = getattr(args, opt)
1667
- return json_opts
1668
-
1669
-
1670
- def acquire_target(target: Target, *args, **kwargs) -> list[str]:
1671
- if isinstance(target._loader, TargetdLoader):
1672
- files = acquire_target_targetd(target, *args, **kwargs)
1673
- else:
1674
- files = acquire_target_regular(target, *args, **kwargs)
1675
- return files
1676
-
1677
-
1678
- def acquire_target_targetd(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
1679
- files = []
1680
- # debug logs contain references to flow objects and will give errors
1681
- logging.getLogger().setLevel(logging.CRITICAL)
1682
- if not len(target.hostname()):
1683
- log.error("Unable to initialize targetd.")
1684
- return files
1685
- json_opts = modargs2json(args)
1686
- json_opts["profile"] = args.profile
1687
- json_opts["file"] = args.file
1688
- json_opts["directory"] = args.directory
1689
- json_opts["glob"] = args.glob
1690
- m = {"targetd-meta": "acquire", "args": json_opts}
1691
- json_str = json.dumps(m)
1692
- targetd = target._loader.instance.client
1693
- targetd.send_message(json_str.encode("utf-8"))
1694
- targetd.sync()
1695
- for stream in targetd.streams:
1696
- files.append(stream.out_file)
1697
- return files
1698
-
1699
-
1700
1637
  def _add_modules_for_profile(choice: str, operating_system: str, profile: dict, msg: str) -> Optional[dict]:
1701
1638
  modules_selected = dict()
1702
1639
 
@@ -1712,7 +1649,7 @@ def _add_modules_for_profile(choice: str, operating_system: str, profile: dict,
1712
1649
  return modules_selected
1713
1650
 
1714
1651
 
1715
- def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
1652
+ def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
1716
1653
  acquire_gui = GUI()
1717
1654
  files = []
1718
1655
  output_ts = output_ts or get_utc_now_str()
@@ -1727,7 +1664,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
1727
1664
  if log_file:
1728
1665
  files.append(log_file)
1729
1666
  if target.path.name == "local":
1730
- skip_list.add(normalize_path(target, log_file, resolve=True))
1667
+ skip_list.add(normalize_path(target, log_file, resolve_parents=True, preserve_case=False))
1731
1668
 
1732
1669
  print_disks_overview(target)
1733
1670
  print_volumes_overview(target)
@@ -1817,7 +1754,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
1817
1754
  log.info("Logging to file %s", log_path)
1818
1755
  files = [log_file_handler.baseFilename]
1819
1756
  if target.path.name == "local":
1820
- skip_list = {normalize_path(target, log_path, resolve=True)}
1757
+ skip_list = {normalize_path(target, log_path, resolve_parents=True, preserve_case=False)}
1821
1758
 
1822
1759
  output_path = args.output or args.output_file
1823
1760
  if output_path.is_dir():
@@ -1833,7 +1770,7 @@ def acquire_target_regular(target: Target, args: argparse.Namespace, output_ts:
1833
1770
  )
1834
1771
  files.append(output.path)
1835
1772
  if target.path.name == "local":
1836
- skip_list.add(normalize_path(target, output.path, resolve=True))
1773
+ skip_list.add(normalize_path(target, output.path, resolve_parents=True, preserve_case=False))
1837
1774
 
1838
1775
  log.info("Writing output to %s", output.path)
1839
1776
  if skip_list:
@@ -2092,7 +2029,7 @@ VOLATILE = {
2092
2029
 
2093
2030
  def main() -> None:
2094
2031
  parser = create_argument_parser(PROFILES, VOLATILE, MODULES)
2095
- args = parse_acquire_args(parser, config=CONFIG)
2032
+ args, rest = parse_acquire_args(parser, config=CONFIG)
2096
2033
 
2097
2034
  # start GUI if requested through CLI / config
2098
2035
  flavour = None
@@ -2144,26 +2081,6 @@ def main() -> None:
2144
2081
  log.exception(err)
2145
2082
  parser.exit(1)
2146
2083
 
2147
- if args.targetd:
2148
- from targetd.tools.targetd import start_client
2149
-
2150
- # set @auto hostname to real hostname
2151
- if args.targetd_hostname == "@auto":
2152
- args.targetd_hostname = f"/host/{Target.open('local').hostname}"
2153
-
2154
- config = {
2155
- "function": args.targetd_func,
2156
- "topics": [args.targetd_hostname, args.targetd_groupname, args.targetd_globalname],
2157
- "link": args.targetd_link,
2158
- "address": args.targetd_ip,
2159
- "port": args.targetd_port,
2160
- "cacert_str": args.targetd_cacert,
2161
- "service": args.targetd_func == "agent",
2162
- "cacert": None,
2163
- }
2164
- start_client(args, presets=config)
2165
- return
2166
-
2167
2084
  if args.upload:
2168
2085
  try:
2169
2086
  upload_files(args.upload, args.upload_plugin, args.no_proxy)
@@ -2171,43 +2088,44 @@ def main() -> None:
2171
2088
  log.exception("Failed to upload files")
2172
2089
  return
2173
2090
 
2174
- RemoteStreamConnection.configure(args.cagent_key, args.cagent_certificate)
2175
-
2176
- target_path = args.target
2177
-
2178
- if target_path == "local":
2179
- target_query = {}
2180
- if args.force_fallback:
2181
- target_query.update({"force-directory-fs": 1})
2091
+ target_paths = []
2092
+ for target_path in args.targets:
2093
+ target_path = args_to_uri([target_path], args.loader, rest)[0] if args.loader else target_path
2094
+ if target_path == "local":
2095
+ target_query = {}
2096
+ if args.force_fallback:
2097
+ target_query.update({"force-directory-fs": 1})
2182
2098
 
2183
- if args.fallback:
2184
- target_query.update({"fallback-to-directory-fs": 1})
2099
+ if args.fallback:
2100
+ target_query.update({"fallback-to-directory-fs": 1})
2185
2101
 
2186
- target_query = urllib.parse.urlencode(target_query)
2187
- target_path = f"{target_path}?{target_query}"
2188
-
2189
- log.info("Loading target %s", target_path)
2102
+ target_query = urllib.parse.urlencode(target_query)
2103
+ target_path = f"{target_path}?{target_query}"
2104
+ target_paths.append(target_path)
2190
2105
 
2191
2106
  try:
2192
- target = Target.open(target_path)
2193
- log.info(target)
2107
+ target_name = "Unknown" # just in case open_all already fails
2108
+ for target in Target.open_all(target_paths):
2109
+ target_name = "Unknown" # overwrite previous target name
2110
+ target_name = target.name
2111
+ log.info("Loading target %s", target_name)
2112
+ log.info(target)
2113
+ if target.os == "esxi" and target.name == "local":
2114
+ # Loader found that we are running on an esxi host
2115
+ # Perform operations to "enhance" memory
2116
+ with esxi_memory_context_manager():
2117
+ acquire_children_and_targets(target, args)
2118
+ else:
2119
+ acquire_children_and_targets(target, args)
2194
2120
  except Exception:
2195
2121
  if not is_user_admin():
2196
- log.error("Failed to load target, try re-running as administrator/root.")
2122
+ log.error("Failed to load target: %s, try re-running as administrator/root", target_name)
2197
2123
  acquire_gui.message("This application must be run as administrator.")
2198
2124
  acquire_gui.wait_for_quit()
2199
2125
  parser.exit(1)
2200
- log.exception("Failed to load target")
2126
+ log.exception("Failed to load target: %s", target_name)
2201
2127
  raise
2202
2128
 
2203
- if target.os == "esxi" and target.name == "local":
2204
- # Loader found that we are running on an esxi host
2205
- # Perform operations to "enhance" memory
2206
- with esxi_memory_context_manager():
2207
- acquire_children_and_targets(target, args)
2208
- else:
2209
- acquire_children_and_targets(target, args)
2210
-
2211
2129
 
2212
2130
  def load_child(target: Target, child_path: Path) -> None:
2213
2131
  log.info("")