dissect.target 3.8.dev30__py3-none-any.whl → 3.8.dev32__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.
- dissect/target/plugins/browsers/browser.py +69 -14
- dissect/target/plugins/browsers/chrome.py +15 -4
- dissect/target/plugins/browsers/chromium.py +77 -7
- dissect/target/plugins/browsers/edge.py +15 -4
- dissect/target/plugins/browsers/firefox.py +113 -10
- dissect/target/plugins/browsers/iexplore.py +90 -15
- dissect/target/plugins/os/unix/bsd/_os.py +41 -0
- dissect/target/plugins/os/unix/bsd/freebsd/__init__.py +0 -0
- dissect/target/plugins/os/unix/bsd/freebsd/_os.py +26 -0
- dissect/target/plugins/os/unix/bsd/ios/__init__.py +0 -0
- dissect/target/plugins/os/unix/bsd/ios/_os.py +45 -0
- dissect/target/plugins/os/unix/bsd/openbsd/__init__.py +0 -0
- dissect/target/plugins/os/unix/bsd/openbsd/_os.py +30 -0
- dissect/target/plugins/os/unix/bsd/osx/__init__.py +0 -0
- dissect/target/plugins/os/unix/bsd/osx/_os.py +65 -0
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/METADATA +5 -4
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/RECORD +23 -14
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/WHEEL +1 -1
- {dissect.target-3.8.dev30.data → dissect/target}/data/autocompletion/target_bash_completion.sh +0 -0
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/LICENSE +0 -0
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.8.dev30.dist-info → dissect.target-3.8.dev32.dist-info}/top_level.txt +0 -0
@@ -20,11 +20,26 @@ GENERIC_HISTORY_RECORD_FIELDS = [
|
|
20
20
|
("uri", "from_url"),
|
21
21
|
("path", "source"),
|
22
22
|
]
|
23
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS = [
|
24
|
+
("datetime", "ts_start"),
|
25
|
+
("datetime", "ts_end"),
|
26
|
+
("string", "browser"),
|
27
|
+
("varint", "id"),
|
28
|
+
("path", "path"),
|
29
|
+
("uri", "url"),
|
30
|
+
("filesize", "size"),
|
31
|
+
("varint", "state"),
|
32
|
+
("path", "source"),
|
33
|
+
]
|
23
34
|
|
24
35
|
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
25
36
|
"browser/history", GENERIC_HISTORY_RECORD_FIELDS
|
26
37
|
)
|
27
38
|
|
39
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
40
|
+
"browser/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
41
|
+
)
|
42
|
+
|
28
43
|
|
29
44
|
class BrowserPlugin(Plugin):
|
30
45
|
"""General browser plugin.
|
@@ -51,28 +66,42 @@ class BrowserPlugin(Plugin):
|
|
51
66
|
for entry in self.BROWSERS:
|
52
67
|
try:
|
53
68
|
self._plugins.append(getattr(self.target, entry))
|
54
|
-
except Exception:
|
69
|
+
except Exception:
|
55
70
|
target.log.exception("Failed to load browser plugin: %s", entry)
|
56
71
|
|
57
|
-
def check_compatible(self):
|
72
|
+
def check_compatible(self) -> bool:
|
73
|
+
"""Perform a compatibility check with the target.
|
74
|
+
This function checks if any of the supported browser plugins
|
75
|
+
can be used. Otherwise it should raise an ``UnsupportedPluginError``.
|
76
|
+
Raises:
|
77
|
+
UnsupportedPluginError: If the plugin could not be loaded.
|
78
|
+
"""
|
58
79
|
if not len(self._plugins):
|
59
80
|
raise UnsupportedPluginError("No compatible browser plugins found")
|
60
81
|
|
61
|
-
def _func(self,
|
62
|
-
|
82
|
+
def _func(self, func_name: str):
|
83
|
+
"""Return the supported browser plugin records.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
func_name: Exported function of the browser plugin to find.
|
87
|
+
|
88
|
+
Yields:
|
89
|
+
Record from the browser function.
|
90
|
+
"""
|
91
|
+
for plugin_name in self._plugins:
|
63
92
|
try:
|
64
|
-
for entry in getattr(
|
93
|
+
for entry in getattr(plugin_name, func_name)():
|
65
94
|
yield entry
|
66
95
|
except Exception:
|
67
|
-
self.target.log.exception("Failed to execute browser plugin: %s.%s",
|
96
|
+
self.target.log.exception("Failed to execute browser plugin: %s.%s", plugin_name._name, func_name)
|
68
97
|
|
69
98
|
@export(record=BrowserHistoryRecord)
|
70
99
|
def history(self):
|
71
|
-
"""Return browser history records from all browsers installed.
|
100
|
+
"""Return browser history records from all browsers installed and supported.
|
72
101
|
|
73
102
|
Historical browser records for Chrome, Chromium, Edge (Chromium), Firefox, and Internet Explorer are returned.
|
74
103
|
|
75
|
-
Yields
|
104
|
+
Yields BrowserHistoryRecord with the following fields:
|
76
105
|
hostname (string): The target hostname.
|
77
106
|
domain (string): The target domain.
|
78
107
|
ts (datetime): Visit timestamp.
|
@@ -91,12 +120,38 @@ class BrowserPlugin(Plugin):
|
|
91
120
|
from_url (uri): URL of the "from" visit.
|
92
121
|
source: (path): The source file of the history record.
|
93
122
|
"""
|
94
|
-
|
95
|
-
yield e
|
123
|
+
yield from self._func("history")
|
96
124
|
|
125
|
+
@export(record=BrowserDownloadRecord)
|
126
|
+
def downloads(self):
|
127
|
+
"""Return browser download records from all browsers installed and supported.
|
97
128
|
|
98
|
-
|
129
|
+
Yields:
|
130
|
+
BrowserDownloadRecord with the following fieds:
|
131
|
+
hostname (string): The target hostname.
|
132
|
+
domain (string): The target domain.
|
133
|
+
ts_start (datetime): Download start timestamp.
|
134
|
+
ts_end (datetime): Download end timestamp.
|
135
|
+
browser (string): The browser from which the records are generated from.
|
136
|
+
id (string): Record ID.
|
137
|
+
path (string): Download path.
|
138
|
+
url (uri): Download URL.
|
139
|
+
size (varint): Download file size.
|
140
|
+
state (varint): Download state number.
|
141
|
+
source: (path): The source file of the download record.
|
142
|
+
"""
|
143
|
+
yield from self._func("downloads")
|
144
|
+
|
145
|
+
|
146
|
+
def try_idna(url: str) -> bytes:
|
147
|
+
"""Attempts to convert a possible Unicode url to ASCII using the IDNA standard.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
url: A String containing the url to be converted.
|
151
|
+
|
152
|
+
Returns: Bytes object with the ASCII version of the url.
|
153
|
+
"""
|
99
154
|
try:
|
100
|
-
return
|
101
|
-
except Exception:
|
102
|
-
return
|
155
|
+
return url.encode("idna")
|
156
|
+
except Exception:
|
157
|
+
return url
|
@@ -1,7 +1,10 @@
|
|
1
1
|
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
2
2
|
from dissect.target.helpers.record import create_extended_descriptor
|
3
3
|
from dissect.target.plugin import Plugin, export
|
4
|
-
from dissect.target.plugins.browsers.browser import
|
4
|
+
from dissect.target.plugins.browsers.browser import (
|
5
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS,
|
6
|
+
GENERIC_HISTORY_RECORD_FIELDS,
|
7
|
+
)
|
5
8
|
from dissect.target.plugins.browsers.chromium import ChromiumMixin
|
6
9
|
|
7
10
|
|
@@ -20,11 +23,19 @@ class ChromePlugin(ChromiumMixin, Plugin):
|
|
20
23
|
# Macos
|
21
24
|
"Library/Application Support/Google/Chrome/Default",
|
22
25
|
]
|
23
|
-
|
26
|
+
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
24
27
|
"browser/chrome/history", GENERIC_HISTORY_RECORD_FIELDS
|
25
28
|
)
|
29
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
30
|
+
"browser/chrome/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
31
|
+
)
|
26
32
|
|
27
|
-
@export(record=
|
33
|
+
@export(record=BrowserHistoryRecord)
|
28
34
|
def history(self):
|
29
35
|
"""Return browser history records for Google Chrome."""
|
30
|
-
yield from
|
36
|
+
yield from super().history("chrome")
|
37
|
+
|
38
|
+
@export(record=BrowserDownloadRecord)
|
39
|
+
def downloads(self):
|
40
|
+
"""Return browser download records for Google Chrome."""
|
41
|
+
yield from super().downloads("chrome")
|
@@ -1,10 +1,11 @@
|
|
1
|
+
from collections import defaultdict
|
1
2
|
from typing import Iterator
|
2
3
|
|
3
4
|
from dissect.sql import sqlite3
|
4
5
|
from dissect.sql.exceptions import Error as SQLError
|
5
6
|
from dissect.sql.sqlite3 import SQLite3
|
6
7
|
from dissect.util.ts import webkittimestamp
|
7
|
-
from flow.record import
|
8
|
+
from flow.record.fieldtypes import path
|
8
9
|
|
9
10
|
from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
|
10
11
|
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
@@ -12,6 +13,7 @@ from dissect.target.helpers.fsutil import TargetPath
|
|
12
13
|
from dissect.target.helpers.record import create_extended_descriptor
|
13
14
|
from dissect.target.plugin import Plugin, export
|
14
15
|
from dissect.target.plugins.browsers.browser import (
|
16
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS,
|
15
17
|
GENERIC_HISTORY_RECORD_FIELDS,
|
16
18
|
try_idna,
|
17
19
|
)
|
@@ -21,11 +23,14 @@ class ChromiumMixin:
|
|
21
23
|
"""Mixin class with methods for Chromium-based browsers."""
|
22
24
|
|
23
25
|
DIRS = []
|
24
|
-
|
26
|
+
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
25
27
|
"browser/chromium/history", GENERIC_HISTORY_RECORD_FIELDS
|
26
28
|
)
|
29
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
30
|
+
"browser/chromium/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
31
|
+
)
|
27
32
|
|
28
|
-
def history(self, browser_name: str = None) -> Iterator[
|
33
|
+
def history(self, browser_name: str = None) -> Iterator[BrowserHistoryRecord]:
|
29
34
|
"""Return browser history records from supported Chromium-based browsers.
|
30
35
|
|
31
36
|
Args:
|
@@ -55,7 +60,7 @@ class ChromiumMixin:
|
|
55
60
|
for user, db_file, db in self._iter_db("History"):
|
56
61
|
try:
|
57
62
|
urls = {row.id: row for row in db.table("urls").rows()}
|
58
|
-
visits
|
63
|
+
visits = {}
|
59
64
|
|
60
65
|
for row in db.table("visits").rows():
|
61
66
|
visits[row.id] = row
|
@@ -67,7 +72,7 @@ class ChromiumMixin:
|
|
67
72
|
else:
|
68
73
|
from_visit, from_url = None, None
|
69
74
|
|
70
|
-
yield self.
|
75
|
+
yield self.BrowserHistoryRecord(
|
71
76
|
ts=webkittimestamp(row.visit_time),
|
72
77
|
browser=browser_name,
|
73
78
|
id=row.id,
|
@@ -89,6 +94,66 @@ class ChromiumMixin:
|
|
89
94
|
except SQLError as e:
|
90
95
|
self.target.log.warning("Error processing history file: %s", db_file, exc_info=e)
|
91
96
|
|
97
|
+
def downloads(self, browser_name: str = None) -> Iterator[BrowserDownloadRecord]:
|
98
|
+
"""Return browser download records from supported Chromium-based browsers.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
browser_name: The name of the browser as a string.
|
102
|
+
Yields:
|
103
|
+
Records with the following fields:
|
104
|
+
hostname (string): The target hostname.
|
105
|
+
domain (string): The target domain.
|
106
|
+
ts_start (datetime): Download start timestamp.
|
107
|
+
ts_ed (datetime): Download end timestamp.
|
108
|
+
browser (string): The browser from which the records are generated from.
|
109
|
+
id (string): Record ID.
|
110
|
+
path (string): Download path.
|
111
|
+
url (uri): Download URL.
|
112
|
+
size (varint): Download file size.
|
113
|
+
state (varint): Download state number.
|
114
|
+
source: (path): The source file of the download record.
|
115
|
+
Raises:
|
116
|
+
SQLError: If the history file could not be processed.
|
117
|
+
"""
|
118
|
+
for user, db_file, db in self._iter_db("History"):
|
119
|
+
try:
|
120
|
+
download_chains = defaultdict(list)
|
121
|
+
for row in db.table("downloads_url_chains"):
|
122
|
+
download_chains[row.id].append(row)
|
123
|
+
|
124
|
+
for chain in download_chains.values():
|
125
|
+
chain.sort(key=lambda row: row.chain_index)
|
126
|
+
|
127
|
+
for row in db.table("downloads").rows():
|
128
|
+
download_path = row.target_path
|
129
|
+
if download_path and self.target.os == "windows":
|
130
|
+
download_path = path.from_windows(download_path)
|
131
|
+
elif download_path:
|
132
|
+
download_path = path.from_posix()(download_path)
|
133
|
+
|
134
|
+
url = None
|
135
|
+
download_chain = download_chains.get(row.id)
|
136
|
+
|
137
|
+
if download_chain:
|
138
|
+
url = download_chain[-1].url
|
139
|
+
url = try_idna(url)
|
140
|
+
|
141
|
+
yield self.BrowserDownloadRecord(
|
142
|
+
ts_start=webkittimestamp(row.start_time),
|
143
|
+
ts_end=webkittimestamp(row.end_time) if row.end_time else None,
|
144
|
+
browser=browser_name,
|
145
|
+
id=row.get("id"),
|
146
|
+
path=download_path,
|
147
|
+
url=url,
|
148
|
+
size=row.get("total_bytes"),
|
149
|
+
state=row.get("state"),
|
150
|
+
source=str(db_file),
|
151
|
+
_target=self.target,
|
152
|
+
_user=user,
|
153
|
+
)
|
154
|
+
except SQLError as e:
|
155
|
+
self.target.log.warning("Error processing history file: %s", db_file, exc_info=e)
|
156
|
+
|
92
157
|
def _iter_db(self, filename: str) -> Iterator[SQLite3]:
|
93
158
|
"""Generate a connection to a sqlite history database files.
|
94
159
|
|
@@ -151,7 +216,12 @@ class ChromiumPlugin(ChromiumMixin, Plugin):
|
|
151
216
|
"AppData/Local/Chromium/User Data/Default",
|
152
217
|
]
|
153
218
|
|
154
|
-
@export(record=ChromiumMixin.
|
219
|
+
@export(record=ChromiumMixin.BrowserHistoryRecord)
|
155
220
|
def history(self):
|
156
221
|
"""Return browser history records for Chromium browser."""
|
157
|
-
yield from
|
222
|
+
yield from super().history("chromium")
|
223
|
+
|
224
|
+
@export(record=ChromiumMixin.BrowserDownloadRecord)
|
225
|
+
def downloads(self):
|
226
|
+
"""Return browser download records for Chromium browser."""
|
227
|
+
yield from super().downloads("chromium")
|
@@ -1,7 +1,10 @@
|
|
1
1
|
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
2
2
|
from dissect.target.helpers.record import create_extended_descriptor
|
3
3
|
from dissect.target.plugin import Plugin, export
|
4
|
-
from dissect.target.plugins.browsers.browser import
|
4
|
+
from dissect.target.plugins.browsers.browser import (
|
5
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS,
|
6
|
+
GENERIC_HISTORY_RECORD_FIELDS,
|
7
|
+
)
|
5
8
|
from dissect.target.plugins.browsers.chromium import ChromiumMixin
|
6
9
|
|
7
10
|
|
@@ -16,11 +19,19 @@ class EdgePlugin(ChromiumMixin, Plugin):
|
|
16
19
|
# Macos
|
17
20
|
"Library/Application Support/Microsoft Edge/Default",
|
18
21
|
]
|
19
|
-
|
22
|
+
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
20
23
|
"browser/edge/history", GENERIC_HISTORY_RECORD_FIELDS
|
21
24
|
)
|
25
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
26
|
+
"browser/edge/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
27
|
+
)
|
22
28
|
|
23
|
-
@export(record=
|
29
|
+
@export(record=BrowserHistoryRecord)
|
24
30
|
def history(self):
|
25
31
|
"""Return browser history records for Microsoft Edge."""
|
26
|
-
yield from
|
32
|
+
yield from super().history("edge")
|
33
|
+
|
34
|
+
@export(record=BrowserDownloadRecord)
|
35
|
+
def downloads(self):
|
36
|
+
"""Return browser download records for Microsoft Edge."""
|
37
|
+
yield from super().downloads("edge")
|
@@ -1,20 +1,22 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Iterator
|
3
|
+
|
1
4
|
from dissect.sql import sqlite3
|
2
5
|
from dissect.sql.exceptions import Error as SQLError
|
3
|
-
from dissect.
|
6
|
+
from dissect.sql.sqlite3 import SQLite3
|
7
|
+
from dissect.util.ts import from_unix_ms, from_unix_us
|
8
|
+
from flow.record.fieldtypes import path
|
4
9
|
|
5
10
|
from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
|
6
11
|
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
|
7
12
|
from dissect.target.helpers.record import create_extended_descriptor
|
8
13
|
from dissect.target.plugin import Plugin, export
|
9
14
|
from dissect.target.plugins.browsers.browser import (
|
15
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS,
|
10
16
|
GENERIC_HISTORY_RECORD_FIELDS,
|
11
17
|
try_idna,
|
12
18
|
)
|
13
19
|
|
14
|
-
FirefoxBrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
15
|
-
"browser/firefox/history", GENERIC_HISTORY_RECORD_FIELDS
|
16
|
-
)
|
17
|
-
|
18
20
|
|
19
21
|
class FirefoxPlugin(Plugin):
|
20
22
|
"""Firefox browser plugin."""
|
@@ -27,6 +29,12 @@ class FirefoxPlugin(Plugin):
|
|
27
29
|
".mozilla/firefox",
|
28
30
|
"snap/firefox/common/.mozilla/firefox",
|
29
31
|
]
|
32
|
+
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
33
|
+
"browser/firefox/history", GENERIC_HISTORY_RECORD_FIELDS
|
34
|
+
)
|
35
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
36
|
+
"browser/firefox/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
37
|
+
)
|
30
38
|
|
31
39
|
def __init__(self, target):
|
32
40
|
super().__init__(target)
|
@@ -43,7 +51,14 @@ class FirefoxPlugin(Plugin):
|
|
43
51
|
if not len(self.users_dirs):
|
44
52
|
raise UnsupportedPluginError("No Firefox directories found")
|
45
53
|
|
46
|
-
def _iter_db(self, filename):
|
54
|
+
def _iter_db(self, filename: str) -> Iterator[SQLite3]:
|
55
|
+
"""Yield opened history database files of all users.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
filename: The filename of the database.
|
59
|
+
Yields:
|
60
|
+
Opened SQLite3 databases.
|
61
|
+
"""
|
47
62
|
for user, cur_dir in self.users_dirs:
|
48
63
|
for profile_dir in cur_dir.iterdir():
|
49
64
|
if profile_dir.is_dir():
|
@@ -55,11 +70,11 @@ class FirefoxPlugin(Plugin):
|
|
55
70
|
except SQLError as e:
|
56
71
|
self.target.log.warning("Could not open %s file: %s", filename, db_file, exc_info=e)
|
57
72
|
|
58
|
-
@export(record=
|
59
|
-
def history(self):
|
73
|
+
@export(record=BrowserHistoryRecord)
|
74
|
+
def history(self) -> Iterator[BrowserHistoryRecord]:
|
60
75
|
"""Return browser history records from Firefox.
|
61
76
|
|
62
|
-
Yields
|
77
|
+
Yields BrowserHistoryRecord with the following fields:
|
63
78
|
hostname (string): The target hostname.
|
64
79
|
domain (string): The target domain.
|
65
80
|
ts (datetime): Visit timestamp.
|
@@ -93,7 +108,7 @@ class FirefoxPlugin(Plugin):
|
|
93
108
|
else:
|
94
109
|
from_visit, from_place = None, None
|
95
110
|
|
96
|
-
yield
|
111
|
+
yield self.BrowserHistoryRecord(
|
97
112
|
ts=from_unix_us(row.visit_date),
|
98
113
|
browser="firefox",
|
99
114
|
id=row.id,
|
@@ -114,3 +129,91 @@ class FirefoxPlugin(Plugin):
|
|
114
129
|
)
|
115
130
|
except SQLError as e:
|
116
131
|
self.target.log.warning("Error processing history file: %s", db_file, exc_info=e)
|
132
|
+
|
133
|
+
@export(record=BrowserDownloadRecord)
|
134
|
+
def downloads(self) -> Iterator[BrowserDownloadRecord]:
|
135
|
+
"""Return browser download records from Firefox.
|
136
|
+
|
137
|
+
Yields BrowserDownloadRecord with the following fields:
|
138
|
+
hostname (string): The target hostname.
|
139
|
+
domain (string): The target domain.
|
140
|
+
ts_start (datetime): Download start timestamp.
|
141
|
+
ts_end (datetime): Download end timestamp.
|
142
|
+
browser (string): The browser from which the records are generated from.
|
143
|
+
id (string): Record ID.
|
144
|
+
path (string): Download path.
|
145
|
+
url (uri): Download URL.
|
146
|
+
size (varint): Download file size.
|
147
|
+
state (varint): Download state number.
|
148
|
+
source: (path): The source file of the download record.
|
149
|
+
"""
|
150
|
+
for user, db_file, db in self._iter_db("places.sqlite"):
|
151
|
+
try:
|
152
|
+
places = {row.id: row for row in db.table("moz_places").rows()}
|
153
|
+
attributes = {row.id: row.name for row in db.table("moz_anno_attributes").rows()}
|
154
|
+
annotations = {}
|
155
|
+
|
156
|
+
for row in db.table("moz_annos"):
|
157
|
+
attribute_name = attributes.get(row.anno_attribute_id, row.anno_attribute_id)
|
158
|
+
|
159
|
+
if attribute_name == "downloads/metaData":
|
160
|
+
content = json.loads(row.content)
|
161
|
+
else:
|
162
|
+
content = row.content
|
163
|
+
|
164
|
+
if row.place_id not in annotations:
|
165
|
+
annotations[row.place_id] = {"id": row.id}
|
166
|
+
|
167
|
+
annotations[row.place_id][attribute_name] = {
|
168
|
+
"content": content,
|
169
|
+
"flags": row.flags,
|
170
|
+
"expiration": row.expiration,
|
171
|
+
"type": row.type,
|
172
|
+
"date_added": from_unix_us(row.dateAdded),
|
173
|
+
"last_modified": from_unix_us(row.lastModified),
|
174
|
+
}
|
175
|
+
|
176
|
+
for place_id, annotation in annotations.items():
|
177
|
+
if "downloads/metaData" not in annotation:
|
178
|
+
continue
|
179
|
+
|
180
|
+
metadata = annotation.get("downloads/metaData", {})
|
181
|
+
|
182
|
+
ts_end = None
|
183
|
+
size = None
|
184
|
+
state = None
|
185
|
+
|
186
|
+
content = metadata.get("content")
|
187
|
+
if content:
|
188
|
+
ts_end = metadata.get("content").get("endTime")
|
189
|
+
ts_end = from_unix_ms(ts_end) if ts_end else None
|
190
|
+
|
191
|
+
size = content.get("fileSize")
|
192
|
+
state = content.get("state")
|
193
|
+
|
194
|
+
dest_file_info = annotation.get("downloads/destinationFileURI", {})
|
195
|
+
download_path = dest_file_info.get("content")
|
196
|
+
|
197
|
+
if download_path and self.target.os == "windows":
|
198
|
+
download_path = path.from_windows(download_path)
|
199
|
+
elif download_path:
|
200
|
+
download_path = path.from_posix(download_path)
|
201
|
+
|
202
|
+
place = places.get(place_id)
|
203
|
+
url = place.get("url")
|
204
|
+
url = try_idna(url) if url else None
|
205
|
+
|
206
|
+
yield self.BrowserDownloadRecord(
|
207
|
+
ts_start=dest_file_info.get("date_added"),
|
208
|
+
ts_end=ts_end,
|
209
|
+
browser="firefox",
|
210
|
+
id=annotation.get("id"),
|
211
|
+
path=download_path,
|
212
|
+
url=url,
|
213
|
+
size=size,
|
214
|
+
state=state,
|
215
|
+
source=str(db_file),
|
216
|
+
_target=self.target,
|
217
|
+
)
|
218
|
+
except SQLError as e:
|
219
|
+
self.target.log.warning("Error processing history file: %s", db_file, exc_info=e)
|
@@ -9,23 +9,30 @@ from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExt
|
|
9
9
|
from dissect.target.helpers.record import create_extended_descriptor
|
10
10
|
from dissect.target.plugin import Plugin, export
|
11
11
|
from dissect.target.plugins.browsers.browser import (
|
12
|
+
GENERIC_DOWNLOAD_RECORD_FIELDS,
|
12
13
|
GENERIC_HISTORY_RECORD_FIELDS,
|
13
14
|
try_idna,
|
14
15
|
)
|
15
16
|
from dissect.target.plugins.general.users import UserDetails
|
16
17
|
from dissect.target.target import Target
|
17
18
|
|
18
|
-
IEBrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
19
|
-
"browser/ie/history", GENERIC_HISTORY_RECORD_FIELDS
|
20
|
-
)
|
21
|
-
|
22
19
|
|
23
20
|
class WebCache:
|
21
|
+
"""Class for opening and pre-processing IE WebCache file."""
|
22
|
+
|
24
23
|
def __init__(self, target: Target, fh: BinaryIO):
|
25
24
|
self.target = target
|
26
25
|
self.db = esedb.EseDB(fh)
|
27
26
|
|
28
27
|
def find_containers(self, name: str) -> table.Table:
|
28
|
+
"""Look up all ``ContainerId`` values for a given container name.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
name: The container name to look up all container IDs of.
|
32
|
+
|
33
|
+
Yields:
|
34
|
+
All ``ContainerId`` values for the requested container name.
|
35
|
+
"""
|
29
36
|
try:
|
30
37
|
for container_record in self.db.table("Containers").records():
|
31
38
|
if record_name := container_record.get("Name"):
|
@@ -37,6 +44,14 @@ class WebCache:
|
|
37
44
|
pass
|
38
45
|
|
39
46
|
def _iter_records(self, name: str) -> Iterator[record.Record]:
|
47
|
+
"""Yield records from a Webcache container.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
name: The container name.
|
51
|
+
|
52
|
+
Yields:
|
53
|
+
Records from specified Webcache container.
|
54
|
+
"""
|
40
55
|
for container in self.find_containers(name):
|
41
56
|
try:
|
42
57
|
yield from container.records()
|
@@ -45,8 +60,13 @@ class WebCache:
|
|
45
60
|
continue
|
46
61
|
|
47
62
|
def history(self) -> Iterator[record.Record]:
|
63
|
+
"""Yield records from the history webcache container."""
|
48
64
|
yield from self._iter_records("history")
|
49
65
|
|
66
|
+
def downloads(self) -> Iterator[record.Record]:
|
67
|
+
"""Yield records from the iedownload webcache container."""
|
68
|
+
yield from self._iter_records("iedownload")
|
69
|
+
|
50
70
|
|
51
71
|
class InternetExplorerPlugin(Plugin):
|
52
72
|
"""Internet explorer browser plugin."""
|
@@ -56,8 +76,13 @@ class InternetExplorerPlugin(Plugin):
|
|
56
76
|
DIRS = [
|
57
77
|
"AppData/Local/Microsoft/Windows/WebCache",
|
58
78
|
]
|
59
|
-
|
60
79
|
CACHE_FILENAME = "WebCacheV01.dat"
|
80
|
+
BrowserDownloadRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
81
|
+
"browser/ie/download", GENERIC_DOWNLOAD_RECORD_FIELDS
|
82
|
+
)
|
83
|
+
BrowserHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
|
84
|
+
"browser/ie/history", GENERIC_HISTORY_RECORD_FIELDS
|
85
|
+
)
|
61
86
|
|
62
87
|
def __init__(self, target: Target):
|
63
88
|
super().__init__(target)
|
@@ -74,11 +99,30 @@ class InternetExplorerPlugin(Plugin):
|
|
74
99
|
if not len(self.users_dirs):
|
75
100
|
raise UnsupportedPluginError("No Internet Explorer directories found")
|
76
101
|
|
77
|
-
|
78
|
-
|
102
|
+
def _iter_cache(self) -> Iterator[WebCache]:
|
103
|
+
"""Yield open IE Webcache files.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
filename: Name of the Webcache file.
|
107
|
+
|
108
|
+
Yields:
|
109
|
+
Open Webcache file.
|
110
|
+
|
111
|
+
Raises:
|
112
|
+
FileNoteFoundError: If the webcache file could not be found.
|
113
|
+
"""
|
114
|
+
for user, cdir in self.users_dirs:
|
115
|
+
cache_file = cdir.joinpath(self.CACHE_FILENAME)
|
116
|
+
try:
|
117
|
+
yield user, cache_file, WebCache(self.target, cache_file.open())
|
118
|
+
except FileNotFoundError:
|
119
|
+
self.target.log.warning("Could not find %s file: %s", self.CACHE_FILENAME, cache_file)
|
120
|
+
|
121
|
+
@export(record=BrowserHistoryRecord)
|
122
|
+
def history(self) -> Iterator[BrowserHistoryRecord]:
|
79
123
|
"""Return browser history records from Internet Explorer.
|
80
124
|
|
81
|
-
Yields
|
125
|
+
Yields BrowserHistoryRecord with the following fields:
|
82
126
|
hostname (string): The target hostname.
|
83
127
|
domain (string): The target domain.
|
84
128
|
ts (datetime): Visit timestamp.
|
@@ -97,12 +141,7 @@ class InternetExplorerPlugin(Plugin):
|
|
97
141
|
from_url (uri): URL of the "from" visit.
|
98
142
|
source: (path): The source file of the history record.
|
99
143
|
"""
|
100
|
-
for user,
|
101
|
-
cache_file = cdir.joinpath(self.CACHE_FILENAME)
|
102
|
-
if not cache_file.exists():
|
103
|
-
continue
|
104
|
-
|
105
|
-
cache = WebCache(self.target, cache_file.open())
|
144
|
+
for user, cache_file, cache in self._iter_cache():
|
106
145
|
for container_record in cache.history():
|
107
146
|
if not container_record.get("Url"):
|
108
147
|
continue
|
@@ -113,7 +152,7 @@ class InternetExplorerPlugin(Plugin):
|
|
113
152
|
if accessed_time := container_record.get("AccessedTime"):
|
114
153
|
ts = wintimestamp(accessed_time)
|
115
154
|
|
116
|
-
yield
|
155
|
+
yield self.BrowserHistoryRecord(
|
117
156
|
ts=ts,
|
118
157
|
browser="iexplore",
|
119
158
|
id=container_record.get("EntryId"),
|
@@ -132,3 +171,39 @@ class InternetExplorerPlugin(Plugin):
|
|
132
171
|
_target=self.target,
|
133
172
|
_user=user,
|
134
173
|
)
|
174
|
+
|
175
|
+
@export(record=BrowserDownloadRecord)
|
176
|
+
def downloads(self) -> Iterator[BrowserDownloadRecord]:
|
177
|
+
"""Return browser downloads records from Internet Explorer.
|
178
|
+
|
179
|
+
Yields BrowserDownloadRecord with the following fields:
|
180
|
+
hostname (string): The target hostname.
|
181
|
+
domain (string): The target domain.
|
182
|
+
ts_start (datetime): Download start timestamp.
|
183
|
+
ts_end (datetime): Download end timestamp.
|
184
|
+
browser (string): The browser from which the records are generated from.
|
185
|
+
id (string): Record ID.
|
186
|
+
path (string): Download path.
|
187
|
+
url (uri): Download URL.
|
188
|
+
size (varint): Download file size.
|
189
|
+
state (varint): Download state number.
|
190
|
+
source: (path): The source file of the download record.
|
191
|
+
"""
|
192
|
+
for user, cache_file, cache in self._iter_cache():
|
193
|
+
for container_record in cache.downloads():
|
194
|
+
response_headers = container_record.ResponseHeaders.decode("utf-16-le", errors="ignore")
|
195
|
+
ref_url, mime_type, temp_download_path, down_url, down_path = response_headers.split("\x00")[-6:-1]
|
196
|
+
|
197
|
+
yield self.BrowserDownloadRecord(
|
198
|
+
ts_start=None,
|
199
|
+
ts_end=wintimestamp(container_record.AccessedTime),
|
200
|
+
browser="iexplore",
|
201
|
+
id=container_record.EntryId,
|
202
|
+
path=down_path,
|
203
|
+
url=down_url,
|
204
|
+
size=None,
|
205
|
+
state=None,
|
206
|
+
source=str(cache_file),
|
207
|
+
_target=self.target,
|
208
|
+
_user=user,
|
209
|
+
)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
from dissect.target.filesystem import Filesystem
|
6
|
+
from dissect.target.plugin import OperatingSystem, export
|
7
|
+
from dissect.target.plugins.os.unix._os import UnixPlugin
|
8
|
+
from dissect.target.target import Target
|
9
|
+
|
10
|
+
|
11
|
+
class BsdPlugin(UnixPlugin):
|
12
|
+
def __init__(self, target: Target):
|
13
|
+
super().__init__(target)
|
14
|
+
|
15
|
+
@classmethod
|
16
|
+
def detect(cls, target: Target) -> Optional[Filesystem]:
|
17
|
+
for fs in target.filesystems:
|
18
|
+
# checking the existence of /var/authpf for free- and openbsd
|
19
|
+
# checking the existence of /var/at for net- and freebsd
|
20
|
+
if fs.exists("/usr/obj") or fs.exists("/altroot") or fs.exists("/etc/pf.conf"):
|
21
|
+
return fs
|
22
|
+
|
23
|
+
return None
|
24
|
+
|
25
|
+
@export(property=True)
|
26
|
+
def os(self) -> str:
|
27
|
+
return OperatingSystem.BSD.value
|
28
|
+
|
29
|
+
@export(property=True)
|
30
|
+
def hostname(self) -> Optional[str]:
|
31
|
+
fh = self.target.fs.path("/etc/rc.conf")
|
32
|
+
|
33
|
+
for line in fh.open("rt").readlines():
|
34
|
+
if line.startswith("hostname"):
|
35
|
+
hostname = line.rstrip().split("=", maxsplit=1)[1].replace('"', "")
|
36
|
+
return hostname
|
37
|
+
|
38
|
+
@export(property=True)
|
39
|
+
def ips(self) -> Optional[List[str]]:
|
40
|
+
self.target.log.error(f"ips plugin not implemented for {self.__class__}")
|
41
|
+
return None
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from dissect.target.filesystem import Filesystem
|
6
|
+
from dissect.target.plugin import export
|
7
|
+
from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
|
8
|
+
from dissect.target.target import Target
|
9
|
+
|
10
|
+
|
11
|
+
class FreeBsdPlugin(BsdPlugin):
|
12
|
+
def __init__(self, target: Target):
|
13
|
+
super().__init__(target)
|
14
|
+
self._os_release = self._parse_os_release("/bin/freebsd-version*")
|
15
|
+
|
16
|
+
@classmethod
|
17
|
+
def detect(cls, target: Target) -> Optional[Filesystem]:
|
18
|
+
for fs in target.filesystems:
|
19
|
+
if fs.exists("/net") or fs.exists("/.sujournal"):
|
20
|
+
return fs
|
21
|
+
|
22
|
+
return None
|
23
|
+
|
24
|
+
@export(property=True)
|
25
|
+
def version(self) -> Optional[str]:
|
26
|
+
return self._os_release.get("USERLAND_VERSION")
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import plistlib
|
2
|
+
|
3
|
+
from dissect.target.plugin import OperatingSystem, export
|
4
|
+
from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
|
5
|
+
|
6
|
+
|
7
|
+
class IOSPlugin(BsdPlugin):
|
8
|
+
@classmethod
|
9
|
+
def detect(cls, target):
|
10
|
+
for fs in target.filesystems:
|
11
|
+
if fs.exists("/private/var/preferences"):
|
12
|
+
return fs
|
13
|
+
|
14
|
+
return None
|
15
|
+
|
16
|
+
@classmethod
|
17
|
+
def create(cls, target, sysvol):
|
18
|
+
target.fs.mount("/", sysvol)
|
19
|
+
return cls(target)
|
20
|
+
|
21
|
+
@export(property=True)
|
22
|
+
def hostname(self):
|
23
|
+
path = self.target.fs.path("/private/var/preferences/SystemConfiguration/preferences.plist")
|
24
|
+
|
25
|
+
if not path.exists():
|
26
|
+
return None
|
27
|
+
|
28
|
+
preferences = plistlib.load(path.open())
|
29
|
+
return preferences["System"]["System"]["ComputerName"]
|
30
|
+
|
31
|
+
@export(property=True)
|
32
|
+
def ips(self):
|
33
|
+
raise NotImplementedError
|
34
|
+
|
35
|
+
@export(property=True)
|
36
|
+
def version(self):
|
37
|
+
raise NotImplementedError
|
38
|
+
|
39
|
+
@export(property=True)
|
40
|
+
def users(self):
|
41
|
+
raise NotImplementedError
|
42
|
+
|
43
|
+
@export(property=True)
|
44
|
+
def os(self) -> str:
|
45
|
+
return OperatingSystem.IOS.value
|
File without changes
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from dissect.target.filesystem import Filesystem
|
6
|
+
from dissect.target.plugin import export
|
7
|
+
from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
|
8
|
+
from dissect.target.target import Target
|
9
|
+
|
10
|
+
|
11
|
+
class OpenBsdPlugin(BsdPlugin):
|
12
|
+
def __init__(self, target: Target):
|
13
|
+
super().__init__(target)
|
14
|
+
self._hostname_dict = self._parse_hostname_string(["/etc/myname"])
|
15
|
+
|
16
|
+
@classmethod
|
17
|
+
def detect(cls, target: Target) -> Optional[Filesystem]:
|
18
|
+
for fs in target.filesystems:
|
19
|
+
if fs.exists("/bsd") or fs.exists("/bsd.rd") or fs.exists("/bsd.mp") or fs.exists("/bsd.mp"):
|
20
|
+
return fs
|
21
|
+
|
22
|
+
return None
|
23
|
+
|
24
|
+
@export(property=True)
|
25
|
+
def version(self) -> Optional[str]:
|
26
|
+
return None
|
27
|
+
|
28
|
+
@export(property=True)
|
29
|
+
def hostname(self) -> Optional[str]:
|
30
|
+
return self._hostname_dict.get("hostname", None)
|
File without changes
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import plistlib
|
4
|
+
from typing import Iterator, Optional
|
5
|
+
|
6
|
+
from dissect.target.filesystem import Filesystem
|
7
|
+
from dissect.target.helpers.record import UnixUserRecord
|
8
|
+
from dissect.target.plugin import OperatingSystem, export
|
9
|
+
from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
|
10
|
+
from dissect.target.target import Target
|
11
|
+
|
12
|
+
|
13
|
+
class MacPlugin(BsdPlugin):
|
14
|
+
VERSION = "/System/Library/CoreServices/SystemVersion.plist"
|
15
|
+
GLOBAL = "/Library/Preferences/.GlobalPreferences.plist"
|
16
|
+
SYSTEM = "/Library/Preferences/SystemConfiguration/preferences.plist"
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def detect(cls, target: Target) -> Optional[Filesystem]:
|
20
|
+
for fs in target.filesystems:
|
21
|
+
if fs.exists("/Library") and fs.exists("/Applications"):
|
22
|
+
return fs
|
23
|
+
|
24
|
+
return None
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def create(cls, target: Target, sysvol: Filesystem) -> MacPlugin:
|
28
|
+
target.fs.mount("/", sysvol)
|
29
|
+
return cls(target)
|
30
|
+
|
31
|
+
@export(property=True)
|
32
|
+
def hostname(self) -> Optional[str]:
|
33
|
+
for path in ["/Library/Preferences/SystemConfiguration/preferences.plist"]:
|
34
|
+
try:
|
35
|
+
preferencesPlist = self.target.fs.open(path).read().rstrip()
|
36
|
+
preferences = plistlib.loads(preferencesPlist)
|
37
|
+
return preferences["System"]["System"]["ComputerName"]
|
38
|
+
|
39
|
+
except FileNotFoundError:
|
40
|
+
pass
|
41
|
+
|
42
|
+
@export(property=True)
|
43
|
+
def ips(self) -> Optional[list[str]]:
|
44
|
+
raise NotImplementedError
|
45
|
+
|
46
|
+
@export(property=True)
|
47
|
+
def version(self) -> Optional[str]:
|
48
|
+
for path in ["/System/Library/CoreServices/SystemVersion.plist"]:
|
49
|
+
try:
|
50
|
+
systemVersionPlist = self.target.fs.open(path).read().rstrip()
|
51
|
+
systemVersion = plistlib.loads(systemVersionPlist)
|
52
|
+
productName = systemVersion["ProductName"]
|
53
|
+
productUserVisibleVersion = systemVersion["ProductUserVisibleVersion"]
|
54
|
+
productBuildVersion = systemVersion["ProductBuildVersion"]
|
55
|
+
return f"{productName} {productUserVisibleVersion} ({productBuildVersion})"
|
56
|
+
except FileNotFoundError:
|
57
|
+
pass
|
58
|
+
|
59
|
+
@export(record=UnixUserRecord)
|
60
|
+
def users(self) -> Iterator[UnixUserRecord]:
|
61
|
+
raise NotImplementedError
|
62
|
+
|
63
|
+
@export(property=True)
|
64
|
+
def os(self) -> str:
|
65
|
+
return OperatingSystem.OSX.value
|
@@ -1,11 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.8.
|
3
|
+
Version: 3.8.dev32
|
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
|
-
|
6
|
-
Author: Dissect Team
|
7
|
-
Author-email: dissect@fox-it.com
|
5
|
+
Author-email: Dissect Team <dissect@fox-it.com>
|
8
6
|
License: Affero General Public License v3
|
7
|
+
Project-URL: homepage, https://dissect.tools
|
8
|
+
Project-URL: documentation, https://docs.dissect.tools/en/latest/projects/dissect.target
|
9
|
+
Project-URL: repository, https://github.com/fox-it/dissect.target
|
9
10
|
Classifier: Programming Language :: Python :: 3
|
10
11
|
Requires-Python: ~=3.9
|
11
12
|
Description-Content-Type: text/markdown
|
@@ -17,6 +17,7 @@ dissect/target/containers/vdi.py,sha256=_kRUu8jlHSHVWUaE6xkwBR8UTtRKOcGMdfW3FP8O
|
|
17
17
|
dissect/target/containers/vhd.py,sha256=sWhtwG6HRQgerP2PODRSxkJb14khamZj1UWWLa0KEHY,1104
|
18
18
|
dissect/target/containers/vhdx.py,sha256=NYZpGPhEj_P2_yAZe233Uuml_0fqIlHrh-fs6idRPJY,992
|
19
19
|
dissect/target/containers/vmdk.py,sha256=RSoS9jSazrKVCqjyG36ahpPjY7MgJAQ3cgE7lqDtOZs,1032
|
20
|
+
dissect/target/data/autocompletion/target_bash_completion.sh,sha256=7iHF2JMXtJJNCtbR2fnIP2BnRUSJxKeKW9zpyX-vJLo,3569
|
20
21
|
dissect/target/filesystems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
22
|
dissect/target/filesystems/ad1.py,sha256=YlNWF1wvjrEe2Y4H45CtdgeLdJZ5tUeS_XE1rTWpq7A,2257
|
22
23
|
dissect/target/filesystems/cb.py,sha256=FBjJhI9Q-l-0G8eVA4NhF4IyNkEhTP3wzsD7gLmqBvo,3594
|
@@ -92,12 +93,12 @@ dissect/target/plugins/apps/webservers/iis.py,sha256=RIS_1w9hcQSfF3-83MsRsT8EJeY
|
|
92
93
|
dissect/target/plugins/apps/webservers/nginx.py,sha256=P6lkAJwf4U8pyaIHt37DKyGYhJ2a-Mb7I-msZNRuuG8,4006
|
93
94
|
dissect/target/plugins/apps/webservers/webservers.py,sha256=00a0GWWK_9CLuZnXJEyHitHxHaGK-HUrmHOK6jpeiXg,2156
|
94
95
|
dissect/target/plugins/browsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
95
|
-
dissect/target/plugins/browsers/browser.py,sha256=
|
96
|
-
dissect/target/plugins/browsers/chrome.py,sha256=
|
97
|
-
dissect/target/plugins/browsers/chromium.py,sha256=
|
98
|
-
dissect/target/plugins/browsers/edge.py,sha256=
|
99
|
-
dissect/target/plugins/browsers/firefox.py,sha256=
|
100
|
-
dissect/target/plugins/browsers/iexplore.py,sha256=
|
96
|
+
dissect/target/plugins/browsers/browser.py,sha256=Bm-MuRkEi-hyaQnB02t2bz3EghRoTPOtkW5jtrw6LWU,5491
|
97
|
+
dissect/target/plugins/browsers/chrome.py,sha256=0qy_IvQ6hFxslClpfmlyTH3VOokB6VP8fnukXrbNDU4,1559
|
98
|
+
dissect/target/plugins/browsers/chromium.py,sha256=3lVEbJl1jmd3kjioWzrYkfHUNzdpzX9KZ7nr1BNJLrg,9570
|
99
|
+
dissect/target/plugins/browsers/edge.py,sha256=uVJqHN5CD0oLuJ8D4PIfIao4iui4HEekbs5el8CDwx8,1342
|
100
|
+
dissect/target/plugins/browsers/firefox.py,sha256=BPC8M4OWkw9-bWz1LYEdVS78XfQQugi1OoA8WiL6r84,9347
|
101
|
+
dissect/target/plugins/browsers/iexplore.py,sha256=pzboThhidWubxiR5d82wheINT1bMfa-66jxEbDlw66g,8251
|
101
102
|
dissect/target/plugins/child/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
102
103
|
dissect/target/plugins/child/esxi.py,sha256=RzfrnFWPYL-y67zU-vRJzzTGYT1MBNWrFh4so0L7dR0,554
|
103
104
|
dissect/target/plugins/child/hyperv.py,sha256=lmhLQD1JSn-EpGKc7RNUAzESmfosz7RwGCm2JHWK-pU,2185
|
@@ -138,6 +139,15 @@ dissect/target/plugins/os/unix/packagemanager.py,sha256=-mxNhDjvj497-wBvu3z22316
|
|
138
139
|
dissect/target/plugins/os/unix/services.py,sha256=kL50akbOTb0sjmrTNZx3PmEhXe_jodrIgsQ39NhOalc,2887
|
139
140
|
dissect/target/plugins/os/unix/shadow.py,sha256=7ztW_fYLihxNjS2opFToF-xKZngYDGcTEbZKnodRkc8,3409
|
140
141
|
dissect/target/plugins/os/unix/ssh.py,sha256=HUcl73hF8d43s7hpyvnewErWdz4jeO8tmD13x7fgSvw,8207
|
142
|
+
dissect/target/plugins/os/unix/bsd/_os.py,sha256=e5rttTOFOmd7e2HqP9ZZFMEiPLBr-8rfH0XH1IIeroQ,1372
|
143
|
+
dissect/target/plugins/os/unix/bsd/freebsd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
144
|
+
dissect/target/plugins/os/unix/bsd/freebsd/_os.py,sha256=Vqiyn08kv1IioNUwpgtBJ9SToCFhLCsJdpVhl5E7COM,789
|
145
|
+
dissect/target/plugins/os/unix/bsd/ios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
146
|
+
dissect/target/plugins/os/unix/bsd/ios/_os.py,sha256=Sw34LFMtIRkA9YHc0CSgGmecIJTz34vyPoZD9kJDqqA,1134
|
147
|
+
dissect/target/plugins/os/unix/bsd/openbsd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
148
|
+
dissect/target/plugins/os/unix/bsd/openbsd/_os.py,sha256=9npz-osM-wHmjOACUqof5N5HJeps7J8KuyenUS5MZDs,923
|
149
|
+
dissect/target/plugins/os/unix/bsd/osx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
150
|
+
dissect/target/plugins/os/unix/bsd/osx/_os.py,sha256=W19r0vSb10jwQsiduibF_pJx6VjPw-OIm2Xy0ok31Lg,2407
|
141
151
|
dissect/target/plugins/os/unix/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
142
152
|
dissect/target/plugins/os/unix/linux/_os.py,sha256=BFq1PB1QA9YU8lMZiAv5tZJhmRqjzW0ibXGxl0llY5M,2467
|
143
153
|
dissect/target/plugins/os/unix/linux/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -241,11 +251,10 @@ dissect/target/volumes/bde.py,sha256=gYGg5yF9MNARwNzEkrEfZmKkxyZW4rhLkpdnPJCbhGk
|
|
241
251
|
dissect/target/volumes/disk.py,sha256=95grSsPt1BLVpKwTclwQYzPFGKTkFFqapIk0RoGWf38,968
|
242
252
|
dissect/target/volumes/lvm.py,sha256=zXAfszxNR6tOGrKAtAa_E-JhjI-sXQyR4VYLXD-kqCw,1616
|
243
253
|
dissect/target/volumes/vmfs.py,sha256=mlAJ8278tYaoRjk1u6tFFlCaDQUrVu5ZZE4ikiFvxi8,1707
|
244
|
-
dissect.target-3.8.
|
245
|
-
dissect.target-3.8.
|
246
|
-
dissect.target-3.8.
|
247
|
-
dissect.target-3.8.
|
248
|
-
dissect.target-3.8.
|
249
|
-
dissect.target-3.8.
|
250
|
-
dissect.target-3.8.
|
251
|
-
dissect.target-3.8.dev30.dist-info/RECORD,,
|
254
|
+
dissect.target-3.8.dev32.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
255
|
+
dissect.target-3.8.dev32.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
256
|
+
dissect.target-3.8.dev32.dist-info/METADATA,sha256=Xxk_a-9Tb-0_dvcelF-C5KTFviYGegIYrD76c9l0JVA,9752
|
257
|
+
dissect.target-3.8.dev32.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
258
|
+
dissect.target-3.8.dev32.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
259
|
+
dissect.target-3.8.dev32.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
260
|
+
dissect.target-3.8.dev32.dist-info/RECORD,,
|
{dissect.target-3.8.dev30.data → dissect/target}/data/autocompletion/target_bash_completion.sh
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|