acquire 3.15.dev5__tar.gz → 3.16.dev2__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 (64) hide show
  1. {acquire-3.15.dev5/acquire.egg-info → acquire-3.16.dev2}/PKG-INFO +1 -1
  2. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/acquire.py +139 -90
  3. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/uploaders/plugin.py +3 -1
  4. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/utils.py +5 -2
  5. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/version.py +2 -2
  6. {acquire-3.15.dev5 → acquire-3.16.dev2/acquire.egg-info}/PKG-INFO +1 -1
  7. {acquire-3.15.dev5 → acquire-3.16.dev2}/COPYRIGHT +0 -0
  8. {acquire-3.15.dev5 → acquire-3.16.dev2}/LICENSE +0 -0
  9. {acquire-3.15.dev5 → acquire-3.16.dev2}/MANIFEST.in +0 -0
  10. {acquire-3.15.dev5 → acquire-3.16.dev2}/README.md +0 -0
  11. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/__init__.py +0 -0
  12. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/collector.py +0 -0
  13. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/crypt.py +0 -0
  14. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/__init__.py +0 -0
  15. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/__init__.py +0 -0
  16. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/collect.py +0 -0
  17. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/exceptions.py +0 -0
  18. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/handles.py +0 -0
  19. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/named_objects.py +0 -0
  20. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/ntdll.py +0 -0
  21. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/dynamic/windows/types.py +0 -0
  22. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/esxi.py +0 -0
  23. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/gui/__init__.py +0 -0
  24. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/gui/base.py +0 -0
  25. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/gui/win32.py +0 -0
  26. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/hashes.py +0 -0
  27. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/log.py +0 -0
  28. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/outputs/__init__.py +0 -0
  29. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/outputs/base.py +0 -0
  30. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/outputs/dir.py +0 -0
  31. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/outputs/tar.py +0 -0
  32. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/outputs/zip.py +0 -0
  33. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/tools/__init__.py +0 -0
  34. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/tools/decrypter.py +0 -0
  35. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/uploaders/__init__.py +0 -0
  36. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/uploaders/minio.py +0 -0
  37. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/uploaders/plugin_registry.py +0 -0
  38. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire/volatilestream.py +0 -0
  39. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire.egg-info/SOURCES.txt +0 -0
  40. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire.egg-info/dependency_links.txt +0 -0
  41. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire.egg-info/entry_points.txt +0 -0
  42. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire.egg-info/requires.txt +0 -0
  43. {acquire-3.15.dev5 → acquire-3.16.dev2}/acquire.egg-info/top_level.txt +0 -0
  44. {acquire-3.15.dev5 → acquire-3.16.dev2}/pyproject.toml +0 -0
  45. {acquire-3.15.dev5 → acquire-3.16.dev2}/setup.cfg +0 -0
  46. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/__init__.py +0 -0
  47. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/conftest.py +0 -0
  48. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/docs/Makefile +0 -0
  49. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/docs/conf.py +0 -0
  50. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/docs/index.rst +0 -0
  51. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_acquire_command.py +0 -0
  52. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_acquire_modules.py +0 -0
  53. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_collector.py +0 -0
  54. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_decryptor_funcs.py +0 -0
  55. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_esxi_memory.py +0 -0
  56. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_file_sorting.py +0 -0
  57. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_gui.py +0 -0
  58. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_minio_uploader.py +0 -0
  59. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_misc_users.py +0 -0
  60. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_outputs_dir.py +0 -0
  61. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_outputs_tar.py +0 -0
  62. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_plugin.py +0 -0
  63. {acquire-3.15.dev5 → acquire-3.16.dev2}/tests/test_utils.py +0 -0
  64. {acquire-3.15.dev5 → acquire-3.16.dev2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: acquire
3
- Version: 3.15.dev5
3
+ Version: 3.16.dev2
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
@@ -896,6 +896,16 @@ class ThumbnailCache(Module):
896
896
  ]
897
897
 
898
898
 
899
+ @register_module("--text-editor")
900
+ class TextEditor(Module):
901
+ DESC = "text editor (un)saved tab contents"
902
+ # Only Windows 11 notepad & Notepad++ tabs for now, but locations for other text editors may be added later.
903
+ SPEC = [
904
+ ("dir", "AppData/Local/Packages/Microsoft.WindowsNotepad_8wekyb3d8bbwe/LocalState/TabState/", from_user_home),
905
+ ("dir", "AppData/Roaming/Notepad++/backup/", from_user_home),
906
+ ]
907
+
908
+
899
909
  @register_module("--misc")
900
910
  class Misc(Module):
901
911
  DESC = "miscellaneous Windows artefacts"
@@ -1658,7 +1668,7 @@ def _add_modules_for_profile(choice: str, operating_system: str, profile: dict,
1658
1668
  return modules_selected
1659
1669
 
1660
1670
 
1661
- def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str]:
1671
+ def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional[str] = None) -> list[str | Path]:
1662
1672
  acquire_gui = GUI()
1663
1673
  files = []
1664
1674
  output_ts = output_ts or get_utc_now_str()
@@ -1866,16 +1876,18 @@ def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional
1866
1876
  return files
1867
1877
 
1868
1878
 
1869
- def upload_files(paths: list[Path], upload_plugin: UploaderPlugin, no_proxy: bool = False) -> None:
1879
+ def upload_files(paths: list[str | Path], upload_plugin: UploaderPlugin, no_proxy: bool = False) -> None:
1870
1880
  proxies = None if no_proxy else urllib.request.getproxies()
1871
1881
  log.debug("Proxies: %s (no_proxy = %s)", proxies, no_proxy)
1872
1882
 
1883
+ log.info('Uploading files: "%s"', " ".join(map(str, paths)))
1873
1884
  try:
1874
1885
  upload_files_using_uploader(upload_plugin, paths, proxies)
1875
1886
  except Exception:
1876
- log.error("Upload %s FAILED. See log file for details.", paths)
1877
- GUI().message("Upload failed.")
1878
- log.exception("")
1887
+ log.error('Upload FAILED for files: "%s". See log file for details.', " ".join(map(str, paths)))
1888
+ raise
1889
+ else:
1890
+ log.info("Upload succeeded.")
1879
1891
 
1880
1892
 
1881
1893
  class WindowsProfile:
@@ -1915,6 +1927,7 @@ class WindowsProfile:
1915
1927
  WindowsNotifications,
1916
1928
  SSH,
1917
1929
  IIS,
1930
+ TextEditor,
1918
1931
  ]
1919
1932
 
1920
1933
 
@@ -2036,29 +2049,30 @@ VOLATILE = {
2036
2049
  }
2037
2050
 
2038
2051
 
2052
+ def exit_success(default_args: list[str]):
2053
+ log.info("Acquire finished successful")
2054
+ log.info("Arguments: %s", " ".join(sys.argv[1:]))
2055
+ log.info("Default Arguments: %s", " ".join(default_args))
2056
+ log.info("Exiting with status code 0 (SUCCESS)")
2057
+ sys.exit(0)
2058
+
2059
+
2060
+ def exit_failure(default_args: list[str]):
2061
+ log.error("Acquire FAILED")
2062
+ log.error("Arguments: %s", " ".join(sys.argv[1:]))
2063
+ log.error("Default Arguments: %s", " ".join(default_args))
2064
+ log.error("Exiting with status code 1 (FAILURE)")
2065
+ sys.exit(1)
2066
+
2067
+
2039
2068
  def main() -> None:
2040
2069
  parser = create_argument_parser(PROFILES, VOLATILE, MODULES)
2041
2070
  args, rest = parse_acquire_args(parser, config=CONFIG)
2042
2071
 
2043
- # start GUI if requested through CLI / config
2044
- flavour = None
2045
- if args.gui == "always" or (
2046
- args.gui == "depends" and os.environ.get("PYS_KEYSOURCE") == "prompt" and len(sys.argv) == 1
2047
- ):
2048
- flavour = platform.system()
2049
-
2050
- acquire_gui = GUI(flavour=flavour, upload_available=args.auto_upload)
2051
- args.output, args.auto_upload, cancel = acquire_gui.wait_for_start(args)
2052
-
2053
2072
  # Since output has a default value, set it to None when output_file is defined
2054
2073
  if args.output_file:
2055
2074
  args.output = None
2056
2075
 
2057
- if cancel:
2058
- parser.exit(0)
2059
-
2060
- # From here onwards, the GUI will be locked and cannot be closed because we're acquiring
2061
-
2062
2076
  try:
2063
2077
  check_and_set_log_args(args)
2064
2078
  except ValueError as err:
@@ -2075,65 +2089,118 @@ def main() -> None:
2075
2089
 
2076
2090
  setup_logging(log, log_file, args.verbose, delay=args.log_delay)
2077
2091
 
2078
- log.info(ACQUIRE_BANNER)
2079
- log.info("User: %s | Admin: %s", get_user_name(), is_user_admin())
2080
- log.info("Arguments: %s", " ".join(sys.argv[1:]))
2081
- log.info("Default Arguments: %s", " ".join(args.config.get("arguments")))
2082
- log.info("")
2092
+ acquire_successful = True
2093
+ files_to_upload = [log_file]
2094
+ acquire_gui = None
2095
+ try:
2096
+ log.info(ACQUIRE_BANNER)
2097
+ log.info("User: %s | Admin: %s", get_user_name(), is_user_admin())
2098
+ log.info("Arguments: %s", " ".join(sys.argv[1:]))
2099
+ log.info("Default Arguments: %s", " ".join(args.config.get("arguments")))
2100
+ log.info("")
2083
2101
 
2084
- plugins_to_load = [("cloud", MinIO)]
2085
- upload_plugins = UploaderRegistry("acquire.plugins", plugins_to_load)
2102
+ # start GUI if requested through CLI / config
2103
+ flavour = None
2104
+ if args.gui == "always" or (
2105
+ args.gui == "depends" and os.environ.get("PYS_KEYSOURCE") == "prompt" and len(sys.argv) == 1
2106
+ ):
2107
+ flavour = platform.system()
2108
+ acquire_gui = GUI(flavour=flavour, upload_available=args.auto_upload)
2109
+
2110
+ args.output, args.auto_upload, cancel = acquire_gui.wait_for_start(args)
2111
+ if cancel:
2112
+ log.info("Acquire cancelled")
2113
+ exit_success(args.config.get("arguments"))
2114
+ # From here onwards, the GUI will be locked and cannot be closed because we're acquiring
2115
+
2116
+ plugins_to_load = [("cloud", MinIO)]
2117
+ upload_plugins = UploaderRegistry("acquire.plugins", plugins_to_load)
2086
2118
 
2087
- try:
2088
2119
  check_and_set_acquire_args(args, upload_plugins)
2089
- except ValueError as err:
2090
- log.exception(err)
2091
- parser.exit(1)
2092
2120
 
2093
- if args.upload:
2121
+ if args.upload:
2122
+ try:
2123
+ upload_files(args.upload, args.upload_plugin, args.no_proxy)
2124
+ except Exception as err:
2125
+ acquire_gui.message("Failed to upload files")
2126
+ log.exception(err)
2127
+ exit_failure(args.config.get("arguments"))
2128
+ exit_success(args.config.get("arguments"))
2129
+
2130
+ target_paths = []
2131
+ for target_path in args.targets:
2132
+ target_path = args_to_uri([target_path], args.loader, rest)[0] if args.loader else target_path
2133
+ if target_path == "local":
2134
+ target_query = {}
2135
+ if args.force_fallback:
2136
+ target_query.update({"force-directory-fs": 1})
2137
+
2138
+ if args.fallback:
2139
+ target_query.update({"fallback-to-directory-fs": 1})
2140
+
2141
+ target_query = urllib.parse.urlencode(target_query)
2142
+ target_path = f"{target_path}?{target_query}"
2143
+ target_paths.append(target_path)
2144
+
2094
2145
  try:
2095
- upload_files(args.upload, args.upload_plugin, args.no_proxy)
2146
+ target_name = "Unknown" # just in case open_all already fails
2147
+ for target in Target.open_all(target_paths):
2148
+ target_name = "Unknown" # overwrite previous target name
2149
+ target_name = target.name
2150
+ log.info("Loading target %s", target_name)
2151
+ log.info(target)
2152
+ if target.os == "esxi" and target.name == "local":
2153
+ # Loader found that we are running on an esxi host
2154
+ # Perform operations to "enhance" memory
2155
+ with esxi_memory_context_manager():
2156
+ files_to_upload = acquire_children_and_targets(target, args)
2157
+ else:
2158
+ files_to_upload = acquire_children_and_targets(target, args)
2096
2159
  except Exception:
2097
- log.exception("Failed to upload files")
2098
- return
2099
-
2100
- target_paths = []
2101
- for target_path in args.targets:
2102
- target_path = args_to_uri([target_path], args.loader, rest)[0] if args.loader else target_path
2103
- if target_path == "local":
2104
- target_query = {}
2105
- if args.force_fallback:
2106
- target_query.update({"force-directory-fs": 1})
2160
+ log.error("Failed to acquire target: %s", target_name)
2161
+ if not is_user_admin():
2162
+ log.error("Try re-running as administrator/root")
2163
+ acquire_gui.message("This application must be run as administrator.")
2164
+ raise
2107
2165
 
2108
- if args.fallback:
2109
- target_query.update({"fallback-to-directory-fs": 1})
2166
+ files_to_upload = sort_files(files_to_upload)
2110
2167
 
2111
- target_query = urllib.parse.urlencode(target_query)
2112
- target_path = f"{target_path}?{target_query}"
2113
- target_paths.append(target_path)
2168
+ except Exception as err:
2169
+ log.error("Acquiring artifacts FAILED")
2170
+ log.exception(err)
2171
+ acquire_successful = False
2172
+ else:
2173
+ log.info("Acquiring artifacts succeeded")
2114
2174
 
2115
2175
  try:
2116
- target_name = "Unknown" # just in case open_all already fails
2117
- for target in Target.open_all(target_paths):
2118
- target_name = "Unknown" # overwrite previous target name
2119
- target_name = target.name
2120
- log.info("Loading target %s", target_name)
2121
- log.info(target)
2122
- if target.os == "esxi" and target.name == "local":
2123
- # Loader found that we are running on an esxi host
2124
- # Perform operations to "enhance" memory
2125
- with esxi_memory_context_manager():
2126
- acquire_children_and_targets(target, args)
2127
- else:
2128
- acquire_children_and_targets(target, args)
2129
- except Exception:
2130
- if not is_user_admin():
2131
- log.error("Failed to load target: %s, try re-running as administrator/root", target_name)
2132
- acquire_gui.message("This application must be run as administrator.")
2176
+ # The auto-upload of files is done at the very very end to make sure any
2177
+ # logged exceptions are written to the log file before uploading.
2178
+ # This means that any failures from this point on will not be part of the
2179
+ # uploaded log files, they will be written to the logfile on disk though.
2180
+ if args.auto_upload and args.upload_plugin and files_to_upload:
2181
+ try:
2182
+ log_file_handler = get_file_handler(log)
2183
+ if log_file_handler:
2184
+ log_file_handler.close()
2185
+
2186
+ upload_files(files_to_upload, args.upload_plugin)
2187
+ except Exception:
2188
+ if acquire_gui:
2189
+ acquire_gui.message("Failed to upload files")
2190
+ raise
2191
+
2192
+ if acquire_gui:
2193
+ acquire_gui.finish()
2133
2194
  acquire_gui.wait_for_quit()
2134
- parser.exit(1)
2135
- log.exception("Failed to load target: %s", target_name)
2136
- raise
2195
+
2196
+ except Exception as err:
2197
+ acquire_successful = False
2198
+ log.exception(err)
2199
+
2200
+ if acquire_successful:
2201
+ exit_success(args.config.get("arguments"))
2202
+ else:
2203
+ exit_failure(args.config.get("arguments"))
2137
2204
 
2138
2205
 
2139
2206
  def load_child(target: Target, child_path: Path) -> None:
@@ -2149,7 +2216,7 @@ def load_child(target: Target, child_path: Path) -> None:
2149
2216
  return child
2150
2217
 
2151
2218
 
2152
- def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> None:
2219
+ def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> list[str | Path]:
2153
2220
  if args.child:
2154
2221
  target = load_child(target, args.child)
2155
2222
 
@@ -2172,7 +2239,7 @@ def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> No
2172
2239
  files.extend(acquire_target(target, args, args.start_time))
2173
2240
 
2174
2241
  except Exception:
2175
- log.exception("Failed to acquire target")
2242
+ log.error("Failed to acquire main target")
2176
2243
  acquire_gui.message("Failed to acquire target")
2177
2244
  acquire_gui.wait_for_quit()
2178
2245
  raise
@@ -2192,29 +2259,11 @@ def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> No
2192
2259
  child_files = acquire_target(child_target, args)
2193
2260
  files.extend(child_files)
2194
2261
  except Exception:
2195
- log.exception("Failed to acquire child target")
2262
+ log.exception("Failed to acquire child target %s", child_target.name)
2196
2263
  acquire_gui.message("Failed to acquire child target")
2197
2264
  continue
2198
2265
 
2199
- files = sort_files(files)
2200
-
2201
- if args.auto_upload:
2202
- log_file_handler = get_file_handler(log)
2203
- if log_file_handler:
2204
- log_file_handler.close()
2205
-
2206
- log.info("")
2207
- try:
2208
- upload_files(files, args.upload_plugin)
2209
- acquire_gui.finish()
2210
- acquire_gui.wait_for_quit()
2211
- except Exception:
2212
- log.exception("Failed to upload files")
2213
- acquire_gui.message("Failed to upload files")
2214
- acquire_gui.wait_for_quit()
2215
- else:
2216
- acquire_gui.finish()
2217
- acquire_gui.wait_for_quit()
2266
+ return files
2218
2267
 
2219
2268
 
2220
2269
  def sort_files(files: list[Union[str, Path]]) -> list[Path]:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  from pathlib import Path
3
5
  from typing import Any, Optional
@@ -31,7 +33,7 @@ class UploaderPlugin:
31
33
 
32
34
 
33
35
  def upload_files_using_uploader(
34
- uploader: UploaderPlugin, paths: list[Path], proxies: Optional[dict[str, str]] = None
36
+ uploader: UploaderPlugin, paths: list[str | Path], proxies: Optional[dict[str, str]] = None
35
37
  ) -> None:
36
38
  """Uploads the files in ``paths`` to a destination.
37
39
 
@@ -278,8 +278,12 @@ def check_and_set_acquire_args(
278
278
  ValueError: When an invalid combination of arguments is found.
279
279
  """
280
280
  upload_plugin = None
281
+ setattr(args, "upload_plugin", upload_plugin)
281
282
 
282
283
  # check & set upload related configuration
284
+ if args.upload and args.auto_upload:
285
+ raise ValueError("only one of --upload or --auto-upload can be specified")
286
+
283
287
  if args.upload or args.auto_upload:
284
288
  upload_mode = args.config.get("upload", {}).get("mode")
285
289
  if not upload_mode:
@@ -291,8 +295,7 @@ def check_and_set_acquire_args(
291
295
 
292
296
  # If initialization of the plugin fails, a ValueError is raised
293
297
  upload_plugin = upload_plugin_cls(**args.config)
294
-
295
- setattr(args, "upload_plugin", upload_plugin)
298
+ setattr(args, "upload_plugin", upload_plugin)
296
299
 
297
300
  if not args.upload:
298
301
  # check output related configuration
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.15.dev5'
16
- __version_tuple__ = version_tuple = (3, 15, 'dev5')
15
+ __version__ = version = '3.16.dev2'
16
+ __version_tuple__ = version_tuple = (3, 16, 'dev2')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: acquire
3
- Version: 3.15.dev5
3
+ Version: 3.16.dev2
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes