dissect.target 3.20.dev64__py3-none-any.whl → 3.20.2.dev11__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/helpers/configutil.py +5 -5
- dissect/target/helpers/record.py +12 -6
- dissect/target/helpers/regutil.py +28 -11
- dissect/target/helpers/utils.py +20 -1
- dissect/target/loaders/itunes.py +5 -3
- dissect/target/plugins/apps/browser/iexplore.py +7 -3
- dissect/target/plugins/general/network.py +1 -1
- dissect/target/plugins/general/plugins.py +1 -1
- dissect/target/plugins/os/unix/_os.py +1 -1
- dissect/target/plugins/os/unix/bsd/osx/network.py +2 -1
- dissect/target/plugins/os/unix/esxi/_os.py +34 -32
- dissect/target/plugins/os/unix/etc/etc.py +12 -6
- dissect/target/plugins/os/unix/linux/fortios/_keys.py +7914 -1114
- dissect/target/plugins/os/unix/linux/fortios/_os.py +109 -22
- dissect/target/plugins/os/unix/linux/network.py +338 -0
- dissect/target/plugins/os/unix/linux/network_managers.py +1 -1
- dissect/target/plugins/os/unix/log/auth.py +6 -37
- dissect/target/plugins/os/unix/log/helpers.py +46 -0
- dissect/target/plugins/os/unix/log/messages.py +24 -15
- dissect/target/plugins/os/unix/trash.py +13 -2
- dissect/target/plugins/os/windows/activitiescache.py +32 -30
- dissect/target/plugins/os/windows/catroot.py +14 -5
- dissect/target/plugins/os/windows/lnk.py +13 -7
- dissect/target/plugins/os/windows/network.py +9 -5
- dissect/target/plugins/os/windows/notifications.py +40 -38
- dissect/target/plugins/os/windows/regf/cit.py +20 -7
- dissect/target/tools/diff.py +990 -0
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/METADATA +73 -73
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/RECORD +34 -31
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/WHEEL +1 -1
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/entry_points.txt +1 -0
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev64.dist-info → dissect.target-3.20.2.dev11.dist-info}/top_level.txt +0 -0
@@ -11,12 +11,18 @@ from dissect.target.helpers.fsutil import open_decompress
|
|
11
11
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
12
12
|
from dissect.target.helpers.utils import year_rollover_helper
|
13
13
|
from dissect.target.plugin import Plugin, alias, export
|
14
|
+
from dissect.target.plugins.os.unix.log.helpers import (
|
15
|
+
RE_LINE,
|
16
|
+
RE_TS,
|
17
|
+
is_iso_fmt,
|
18
|
+
iso_readlines,
|
19
|
+
)
|
14
20
|
|
15
21
|
MessagesRecord = TargetRecordDescriptor(
|
16
22
|
"linux/log/messages",
|
17
23
|
[
|
18
24
|
("datetime", "ts"),
|
19
|
-
("string", "
|
25
|
+
("string", "service"),
|
20
26
|
("varint", "pid"),
|
21
27
|
("string", "message"),
|
22
28
|
("path", "source"),
|
@@ -24,12 +30,8 @@ MessagesRecord = TargetRecordDescriptor(
|
|
24
30
|
)
|
25
31
|
|
26
32
|
DEFAULT_TS_LOG_FORMAT = "%b %d %H:%M:%S"
|
27
|
-
RE_TS = re.compile(r"(\w+\s{1,2}\d+\s\d{2}:\d{2}:\d{2})")
|
28
|
-
RE_DAEMON = re.compile(r"^[^:]+:\d+:\d+[^\[\]:]+\s([^\[:]+)[\[|:]{1}")
|
29
|
-
RE_PID = re.compile(r"\w\[(\d+)\]")
|
30
|
-
RE_MSG = re.compile(r"[^:]+:\d+:\d+[^:]+:\s(.*)$")
|
31
33
|
RE_CLOUD_INIT_LINE = re.compile(
|
32
|
-
r"^(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) - (?P<
|
34
|
+
r"^(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) - (?P<service>.*)\[(?P<log_level>\w+)\]\: (?P<message>.*)$"
|
33
35
|
)
|
34
36
|
|
35
37
|
|
@@ -56,7 +58,7 @@ class MessagesPlugin(Plugin):
|
|
56
58
|
def messages(self) -> Iterator[MessagesRecord]:
|
57
59
|
"""Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
|
58
60
|
|
59
|
-
Due to year rollover detection, the contents
|
61
|
+
Due to year rollover detection, the log contents could be returned in reversed or mixed chronological order.
|
60
62
|
|
61
63
|
The messages log file holds information about a variety of events such as the system error messages, system
|
62
64
|
startups and shutdowns, change in the network configuration, etc. Aims to store valuable, non-debug and
|
@@ -75,16 +77,23 @@ class MessagesPlugin(Plugin):
|
|
75
77
|
yield from self._parse_cloud_init_log(log_file, tzinfo)
|
76
78
|
continue
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
if is_iso_fmt(log_file):
|
81
|
+
iterable = iso_readlines(log_file)
|
82
|
+
|
83
|
+
else:
|
84
|
+
iterable = year_rollover_helper(log_file, RE_TS, DEFAULT_TS_LOG_FORMAT, tzinfo)
|
85
|
+
|
86
|
+
for ts, line in iterable:
|
87
|
+
match = RE_LINE.search(line)
|
88
|
+
|
89
|
+
if not match:
|
90
|
+
self.target.log.warning("Unable to parse message line in %s", log_file)
|
91
|
+
self.target.log.debug("Line %s", line)
|
92
|
+
continue
|
82
93
|
|
83
94
|
yield MessagesRecord(
|
84
95
|
ts=ts,
|
85
|
-
|
86
|
-
pid=pid,
|
87
|
-
message=message,
|
96
|
+
**match.groupdict(),
|
88
97
|
source=log_file,
|
89
98
|
_target=self.target,
|
90
99
|
)
|
@@ -134,7 +143,7 @@ class MessagesPlugin(Plugin):
|
|
134
143
|
|
135
144
|
yield MessagesRecord(
|
136
145
|
ts=ts,
|
137
|
-
|
146
|
+
service=values["service"],
|
138
147
|
pid=None,
|
139
148
|
message=values["message"],
|
140
149
|
source=log_file,
|
@@ -31,15 +31,25 @@ class GnomeTrashPlugin(Plugin):
|
|
31
31
|
|
32
32
|
def __init__(self, target: Target):
|
33
33
|
super().__init__(target)
|
34
|
-
self.trashes =
|
34
|
+
self.trashes = set(self._garbage_collector())
|
35
35
|
|
36
36
|
def _garbage_collector(self) -> Iterator[tuple[UserDetails, TargetPath]]:
|
37
37
|
"""it aint much, but its honest work"""
|
38
|
+
|
39
|
+
# home trash folders
|
38
40
|
for user_details in self.target.user_details.all_with_home():
|
39
41
|
for trash_path in self.PATHS:
|
40
42
|
if (path := user_details.home_path.joinpath(trash_path)).exists():
|
41
43
|
yield user_details, path
|
42
44
|
|
45
|
+
# mounted devices trash folders
|
46
|
+
for mount_path in list(self.target.fs.mounts) + ["/mnt", "/media"]:
|
47
|
+
if mount_path == "/":
|
48
|
+
continue
|
49
|
+
|
50
|
+
for mount_trash in self.target.fs.path(mount_path).rglob(".Trash-*"):
|
51
|
+
yield UserDetails(None, None), mount_trash
|
52
|
+
|
43
53
|
def check_compatible(self) -> None:
|
44
54
|
if not self.trashes:
|
45
55
|
raise UnsupportedPluginError("No Trash folder(s) found")
|
@@ -52,7 +62,8 @@ class GnomeTrashPlugin(Plugin):
|
|
52
62
|
Recovers deleted files and artifacts from ``$HOME/.local/share/Trash``.
|
53
63
|
Probably also works with other desktop interfaces as long as they follow the Trash specification from FreeDesktop.
|
54
64
|
|
55
|
-
|
65
|
+
Also parses media trash locations such as ``/media/$USER/$Label/.Trash-*``, ``/mnt/$Label/.Trash-*`` and other
|
66
|
+
locations as defined in ``/etc/fstab``.
|
56
67
|
|
57
68
|
Resources:
|
58
69
|
- https://specifications.freedesktop.org/trash-spec/latest/
|
@@ -116,36 +116,38 @@ class ActivitiesCachePlugin(Plugin):
|
|
116
116
|
for user, cache_file in self.cachefiles:
|
117
117
|
fh = cache_file.open()
|
118
118
|
db = sqlite3.SQLite3(fh)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
119
|
+
|
120
|
+
if table := db.table("Activity"):
|
121
|
+
for r in table.rows():
|
122
|
+
yield ActivitiesCacheRecord(
|
123
|
+
start_time=mkts(r["[StartTime]"]),
|
124
|
+
end_time=mkts(r["[EndTime]"]),
|
125
|
+
last_modified_time=mkts(r["[LastModifiedTime]"]),
|
126
|
+
last_modified_on_client=mkts(r["[LastModifiedOnClient]"]),
|
127
|
+
original_last_modified_on_client=mkts(r["[OriginalLastModifiedOnClient]"]),
|
128
|
+
expiration_time=mkts(r["[ExpirationTime]"]),
|
129
|
+
app_id=r["[AppId]"],
|
130
|
+
enterprise_id=r["[EnterpriseId]"],
|
131
|
+
app_activity_id=r["[AppActivityId]"],
|
132
|
+
group_app_activity_id=r["[GroupAppActivityId]"],
|
133
|
+
group=r["[Group]"],
|
134
|
+
activity_type=r["[ActivityType]"],
|
135
|
+
activity_status=r["[ActivityStatus]"],
|
136
|
+
priority=r["[Priority]"],
|
137
|
+
match_id=r["[MatchId]"],
|
138
|
+
etag=r["[ETag]"],
|
139
|
+
tag=r["[Tag]"],
|
140
|
+
is_local_only=r["[IsLocalOnly]"],
|
141
|
+
created_in_cloud=r["[CreatedInCloud]"],
|
142
|
+
platform_device_id=r["[PlatformDeviceId]"],
|
143
|
+
package_id_hash=r["[PackageIdHash]"],
|
144
|
+
id=r["[Id]"],
|
145
|
+
payload=r["[Payload]"],
|
146
|
+
original_payload=r["[OriginalPayload]"],
|
147
|
+
clipboard_payload=r["[ClipboardPayload]"],
|
148
|
+
_target=self.target,
|
149
|
+
_user=user,
|
150
|
+
)
|
149
151
|
|
150
152
|
|
151
153
|
def mkts(ts: int) -> datetime | None:
|
@@ -217,15 +217,24 @@ class CatrootPlugin(Plugin):
|
|
217
217
|
with ese_file.open("rb") as fh:
|
218
218
|
ese_db = EseDB(fh)
|
219
219
|
|
220
|
-
tables = [table.name for table in ese_db.tables()]
|
221
220
|
for hash_type, table_name in [("sha256", "HashCatNameTableSHA256"), ("sha1", "HashCatNameTableSHA1")]:
|
222
|
-
|
221
|
+
try:
|
222
|
+
table = ese_db.table(table_name)
|
223
|
+
except KeyError as e:
|
224
|
+
self.target.log.warning("EseDB %s has no table %s", ese_file, table_name)
|
225
|
+
self.target.log.debug("", exc_info=e)
|
223
226
|
continue
|
224
227
|
|
225
|
-
for record in
|
228
|
+
for record in table.records():
|
226
229
|
file_digest = digest()
|
227
|
-
|
228
|
-
|
230
|
+
|
231
|
+
try:
|
232
|
+
setattr(file_digest, hash_type, record.get("HashCatNameTable_HashCol").hex())
|
233
|
+
catroot_names = record.get("HashCatNameTable_CatNameCol").decode().rstrip("|").split("|")
|
234
|
+
except Exception as e:
|
235
|
+
self.target.log.warning("Unable to parse catroot names for %s in %s", record, ese_file)
|
236
|
+
self.target.log.debug("", exc_info=e)
|
237
|
+
continue
|
229
238
|
|
230
239
|
for catroot_name in catroot_names:
|
231
240
|
yield CatrootRecord(
|
@@ -1,4 +1,6 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Iterator
|
2
4
|
|
3
5
|
from dissect.shellitem.lnk import Lnk
|
4
6
|
from dissect.util import ts
|
@@ -34,7 +36,7 @@ LnkRecord = TargetRecordDescriptor(
|
|
34
36
|
)
|
35
37
|
|
36
38
|
|
37
|
-
def parse_lnk_file(target: Target, lnk_file: Lnk, lnk_path: TargetPath) ->
|
39
|
+
def parse_lnk_file(target: Target, lnk_file: Lnk, lnk_path: TargetPath) -> LnkRecord:
|
38
40
|
# we need to get the active codepage from the system to properly decode some values
|
39
41
|
codepage = target.codepage or "ascii"
|
40
42
|
|
@@ -132,7 +134,7 @@ class LnkPlugin(Plugin):
|
|
132
134
|
|
133
135
|
@arg("--path", "-p", dest="path", default=None, help="Path to directory or .lnk file in target")
|
134
136
|
@export(record=LnkRecord)
|
135
|
-
def lnk(self, path:
|
137
|
+
def lnk(self, path: str | None = None) -> Iterator[LnkRecord]:
|
136
138
|
"""Parse all .lnk files in /ProgramData, /Users, and /Windows or from a specified path in record format.
|
137
139
|
|
138
140
|
Yields a LnkRecord record with the following fields:
|
@@ -160,10 +162,14 @@ class LnkPlugin(Plugin):
|
|
160
162
|
"""
|
161
163
|
|
162
164
|
for entry in self.lnk_entries(path):
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
165
|
+
try:
|
166
|
+
lnk_file = Lnk(entry.open())
|
167
|
+
yield parse_lnk_file(self.target, lnk_file, entry)
|
168
|
+
except Exception as e:
|
169
|
+
self.target.log.warning("Failed to parse link file %s", lnk_file)
|
170
|
+
self.target.log.debug("", exc_info=e)
|
171
|
+
|
172
|
+
def lnk_entries(self, path: str | None = None) -> Iterator[TargetPath]:
|
167
173
|
if path:
|
168
174
|
target_path = self.target.fs.path(path)
|
169
175
|
if not target_path.exists():
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import re
|
3
4
|
from enum import IntEnum
|
4
5
|
from functools import lru_cache
|
5
6
|
from typing import Iterator
|
@@ -224,11 +225,13 @@ def _try_value(subkey: RegistryKey, value: str) -> str | list | None:
|
|
224
225
|
return None
|
225
226
|
|
226
227
|
|
227
|
-
def _get_config_value(key: RegistryKey, name: str) -> set:
|
228
|
+
def _get_config_value(key: RegistryKey, name: str, sep: str | None = None) -> set:
|
228
229
|
value = _try_value(key, name)
|
229
230
|
if not value or value in ("", "0.0.0.0", None, [], ["0.0.0.0"]):
|
230
231
|
return set()
|
231
|
-
|
232
|
+
if sep and isinstance(value, str):
|
233
|
+
re_sep = "|".join(map(re.escape, sep))
|
234
|
+
value = re.split(re_sep, value)
|
232
235
|
if isinstance(value, list):
|
233
236
|
return set(value)
|
234
237
|
|
@@ -281,7 +284,8 @@ class WindowsNetworkPlugin(NetworkPlugin):
|
|
281
284
|
pass
|
282
285
|
|
283
286
|
# Extract the rest of the device information
|
284
|
-
|
287
|
+
if mac_address := _try_value(subkey, "NetworkAddress"):
|
288
|
+
device_info["mac"] = [mac_address]
|
285
289
|
device_info["vlan"] = _try_value(subkey, "VlanID")
|
286
290
|
|
287
291
|
if timestamp := _try_value(subkey, "NetworkInterfaceInstallTimestamp"):
|
@@ -354,11 +358,11 @@ class WindowsNetworkPlugin(NetworkPlugin):
|
|
354
358
|
dhcp_config["ip"].update(_get_config_value(key, "DhcpIPAddress"))
|
355
359
|
dhcp_config["subnetmask"].update(_get_config_value(key, "DhcpSubnetMask"))
|
356
360
|
dhcp_config["search_domain"].update(_get_config_value(key, "DhcpDomain"))
|
357
|
-
dhcp_config["dns"].update(_get_config_value(key, "DhcpNameServer"))
|
361
|
+
dhcp_config["dns"].update(_get_config_value(key, "DhcpNameServer", " ,"))
|
358
362
|
|
359
363
|
# Extract static configuration from the registry
|
360
364
|
static_config["gateway"].update(_get_config_value(key, "DefaultGateway"))
|
361
|
-
static_config["dns"].update(_get_config_value(key, "NameServer"))
|
365
|
+
static_config["dns"].update(_get_config_value(key, "NameServer", " ,"))
|
362
366
|
static_config["search_domain"].update(_get_config_value(key, "Domain"))
|
363
367
|
static_config["ip"].update(_get_config_value(key, "IPAddress"))
|
364
368
|
static_config["subnetmask"].update(_get_config_value(key, "SubnetMask"))
|
@@ -442,43 +442,45 @@ class NotificationsPlugin(Plugin):
|
|
442
442
|
"""
|
443
443
|
for user, wpndatabase in self.wpndb_files:
|
444
444
|
db = sqlite3.SQLite3(wpndatabase.open())
|
445
|
-
|
446
445
|
handlers = {}
|
447
|
-
for row in db.table("NotificationHandler").rows():
|
448
|
-
handlers[row["[RecordId]"]] = WpnDatabaseNotificationHandlerRecord(
|
449
|
-
created_time=datetime.datetime.strptime(row["[CreatedTime]"], "%Y-%m-%d %H:%M:%S"),
|
450
|
-
modified_time=datetime.datetime.strptime(row["[ModifiedTime]"], "%Y-%m-%d %H:%M:%S"),
|
451
|
-
id=row["[RecordId]"],
|
452
|
-
primary_id=row["[PrimaryId]"],
|
453
|
-
wns_id=row["[WNSId]"],
|
454
|
-
handler_type=row["[HandlerType]"],
|
455
|
-
wnf_event_name=row["[WNFEventName]"],
|
456
|
-
system_data_property_set=row["[SystemDataPropertySet]"],
|
457
|
-
_target=self.target,
|
458
|
-
_user=user,
|
459
|
-
)
|
460
|
-
|
461
|
-
for row in db.table("Notification").rows():
|
462
|
-
record = WpnDatabaseNotificationRecord(
|
463
|
-
arrival_time=wintimestamp(row["[ArrivalTime]"]),
|
464
|
-
expiry_time=wintimestamp(row["[ExpiryTime]"]),
|
465
|
-
order=row["[Order]"],
|
466
|
-
id=row["[Id]"],
|
467
|
-
handler_id=row["[HandlerId]"],
|
468
|
-
activity_id=UUID(bytes=row["[ActivityId]"]),
|
469
|
-
type=row["[Type]"],
|
470
|
-
payload=row["[Payload]"],
|
471
|
-
payload_type=row["[PayloadType]"],
|
472
|
-
tag=row["[Tag]"],
|
473
|
-
group=row["[Group]"],
|
474
|
-
boot_id=row["[BootId]"],
|
475
|
-
expires_on_reboot=row["[ExpiresOnReboot]"] != "FALSE",
|
476
|
-
_target=self.target,
|
477
|
-
_user=user,
|
478
|
-
)
|
479
|
-
handler = handlers.get(row["[HandlerId]"])
|
480
446
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
447
|
+
if table := db.table("NotificationHandler"):
|
448
|
+
for row in table.rows():
|
449
|
+
handlers[row["[RecordId]"]] = WpnDatabaseNotificationHandlerRecord(
|
450
|
+
created_time=datetime.datetime.strptime(row["[CreatedTime]"], "%Y-%m-%d %H:%M:%S"),
|
451
|
+
modified_time=datetime.datetime.strptime(row["[ModifiedTime]"], "%Y-%m-%d %H:%M:%S"),
|
452
|
+
id=row["[RecordId]"],
|
453
|
+
primary_id=row["[PrimaryId]"],
|
454
|
+
wns_id=row["[WNSId]"],
|
455
|
+
handler_type=row["[HandlerType]"],
|
456
|
+
wnf_event_name=row["[WNFEventName]"],
|
457
|
+
system_data_property_set=row["[SystemDataPropertySet]"],
|
458
|
+
_target=self.target,
|
459
|
+
_user=user,
|
460
|
+
)
|
461
|
+
|
462
|
+
if table := db.table("Notification"):
|
463
|
+
for row in table.rows():
|
464
|
+
record = WpnDatabaseNotificationRecord(
|
465
|
+
arrival_time=wintimestamp(row["[ArrivalTime]"]),
|
466
|
+
expiry_time=wintimestamp(row["[ExpiryTime]"]),
|
467
|
+
order=row["[Order]"],
|
468
|
+
id=row["[Id]"],
|
469
|
+
handler_id=row["[HandlerId]"],
|
470
|
+
activity_id=UUID(bytes=row["[ActivityId]"]),
|
471
|
+
type=row["[Type]"],
|
472
|
+
payload=row["[Payload]"],
|
473
|
+
payload_type=row["[PayloadType]"],
|
474
|
+
tag=row["[Tag]"],
|
475
|
+
group=row["[Group]"],
|
476
|
+
boot_id=row["[BootId]"],
|
477
|
+
expires_on_reboot=row["[ExpiresOnReboot]"] != "FALSE",
|
478
|
+
_target=self.target,
|
479
|
+
_user=user,
|
480
|
+
)
|
481
|
+
handler = handlers.get(row["[HandlerId]"])
|
482
|
+
|
483
|
+
if handler:
|
484
|
+
yield GroupedRecord("windows/notification/wpndatabase/grouped", [record, handler])
|
485
|
+
else:
|
486
|
+
yield record
|
@@ -632,8 +632,8 @@ def local_wintimestamp(target, ts):
|
|
632
632
|
class CITPlugin(Plugin):
|
633
633
|
"""Plugin that parses CIT data from the registry.
|
634
634
|
|
635
|
-
|
636
|
-
|
635
|
+
References:
|
636
|
+
- https://dfir.ru/2018/12/02/the-cit-database-and-the-syscache-hive/
|
637
637
|
"""
|
638
638
|
|
639
639
|
__namespace__ = "cit"
|
@@ -641,7 +641,7 @@ class CITPlugin(Plugin):
|
|
641
641
|
KEY = "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\CIT"
|
642
642
|
|
643
643
|
def check_compatible(self) -> None:
|
644
|
-
if not
|
644
|
+
if not list(self.target.registry.keys(self.KEY)):
|
645
645
|
raise UnsupportedPluginError("No CIT registry key found")
|
646
646
|
|
647
647
|
@export(record=get_args(CITRecords))
|
@@ -770,8 +770,9 @@ class CITPlugin(Plugin):
|
|
770
770
|
yield from _yield_bitmap_records(
|
771
771
|
self.target, cit, entry.use_data.bitmaps.foreground, CITProgramBitmapForegroundRecord
|
772
772
|
)
|
773
|
-
except Exception:
|
774
|
-
self.target.log.
|
773
|
+
except Exception as e:
|
774
|
+
self.target.log.warning("Failed to parse CIT value: %s", value.name)
|
775
|
+
self.target.log.debug("", exc_info=e)
|
775
776
|
|
776
777
|
@export(record=CITPostUpdateUseInfoRecord)
|
777
778
|
def puu(self) -> Iterator[CITPostUpdateUseInfoRecord]:
|
@@ -788,10 +789,16 @@ class CITPlugin(Plugin):
|
|
788
789
|
for reg_key in keys:
|
789
790
|
for key in self.target.registry.keys(reg_key):
|
790
791
|
try:
|
791
|
-
|
792
|
+
key_value = key.value("PUUActive").value
|
793
|
+
puu = c_cit.CIT_POST_UPDATE_USE_INFO(key_value)
|
792
794
|
except RegistryValueNotFoundError:
|
793
795
|
continue
|
794
796
|
|
797
|
+
except EOFError as e:
|
798
|
+
self.target.log.warning("Exception reading CIT structure in key %s", key.path)
|
799
|
+
self.target.log.debug("Unable to parse value %s", key_value, exc_info=e)
|
800
|
+
continue
|
801
|
+
|
795
802
|
yield CITPostUpdateUseInfoRecord(
|
796
803
|
log_time_start=wintimestamp(puu.LogTimeStart),
|
797
804
|
update_key=puu.UpdateKey,
|
@@ -852,10 +859,16 @@ class CITPlugin(Plugin):
|
|
852
859
|
for reg_key in keys:
|
853
860
|
for key in self.target.registry.keys(reg_key):
|
854
861
|
try:
|
855
|
-
|
862
|
+
key_value = key.value("DP").value
|
863
|
+
dp = c_cit.CIT_DP_DATA(key_value)
|
856
864
|
except RegistryValueNotFoundError:
|
857
865
|
continue
|
858
866
|
|
867
|
+
except EOFError as e:
|
868
|
+
self.target.log.warning("Exception reading CIT structure in key %s", key.path)
|
869
|
+
self.target.log.debug("Unable to parse value %s", key_value, exc_info=e)
|
870
|
+
continue
|
871
|
+
|
859
872
|
user = self.target.registry.get_user(key)
|
860
873
|
log_time_start = wintimestamp(dp.LogTimeStart)
|
861
874
|
|