dissect.target 3.18.dev9__py3-none-any.whl → 3.18.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/plugins/os/windows/_os.py +6 -3
- dissect/target/plugins/os/windows/defender.py +218 -1
- dissect/target/plugins/os/windows/defender_helpers/__init__.py +0 -0
- dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py +282 -0
- dissect/target/plugins/os/windows/defender_helpers/defender_records.py +191 -0
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/METADATA +1 -1
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/RECORD +12 -9
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/WHEEL +1 -1
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/LICENSE +0 -0
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.18.dev9.dist-info → dissect.target-3.18.dev11.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,7 @@ class WindowsPlugin(OSPlugin):
|
|
21
21
|
self.add_mounts()
|
22
22
|
|
23
23
|
target.props["sysvol_drive"] = next(
|
24
|
-
(mnt for mnt, fs in target.fs.mounts.items() if fs is target.fs.mounts
|
24
|
+
(mnt for mnt, fs in target.fs.mounts.items() if fs is target.fs.mounts.get("sysvol") and mnt != "sysvol"),
|
25
25
|
None,
|
26
26
|
)
|
27
27
|
|
@@ -78,13 +78,16 @@ class WindowsPlugin(OSPlugin):
|
|
78
78
|
self.target.log.warning("Failed to map drive letters")
|
79
79
|
self.target.log.debug("", exc_info=e)
|
80
80
|
|
81
|
+
sysvol_drive = self.target.fs.mounts.get("sysvol")
|
81
82
|
# Fallback mount the sysvol to C: if we didn't manage to mount it to any other drive letter
|
82
|
-
if operator.countOf(self.target.fs.mounts.values(),
|
83
|
+
if sysvol_drive and operator.countOf(self.target.fs.mounts.values(), sysvol_drive) == 1:
|
83
84
|
if "c:" not in self.target.fs.mounts:
|
84
85
|
self.target.log.debug("Unable to determine drive letter of sysvol, falling back to C:")
|
85
|
-
self.target.fs.mount("c:",
|
86
|
+
self.target.fs.mount("c:", sysvol_drive)
|
86
87
|
else:
|
87
88
|
self.target.log.warning("Unknown drive letter for sysvol")
|
89
|
+
else:
|
90
|
+
self.target.log.warning("No sysvol drive found")
|
88
91
|
|
89
92
|
@export(property=True)
|
90
93
|
def hostname(self) -> Optional[str]:
|
@@ -1,7 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
1
4
|
from datetime import datetime, timezone
|
2
5
|
from io import BytesIO
|
3
6
|
from pathlib import Path
|
4
|
-
from typing import Any, BinaryIO, Generator, Iterable, Iterator, Union
|
7
|
+
from typing import Any, BinaryIO, Generator, Iterable, Iterator, TextIO, Union
|
5
8
|
|
6
9
|
import dissect.util.ts as ts
|
7
10
|
from dissect.cstruct import Structure, cstruct
|
@@ -10,6 +13,27 @@ from flow.record import Record
|
|
10
13
|
from dissect.target import plugin
|
11
14
|
from dissect.target.exceptions import UnsupportedPluginError
|
12
15
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
16
|
+
from dissect.target.plugins.os.windows.defender_helpers.defender_patterns import (
|
17
|
+
DEFENDER_MPLOG_BLOCK_PATTERNS,
|
18
|
+
DEFENDER_MPLOG_LINE,
|
19
|
+
DEFENDER_MPLOG_PATTERNS,
|
20
|
+
)
|
21
|
+
from dissect.target.plugins.os.windows.defender_helpers.defender_records import (
|
22
|
+
DefenderMPLogBMTelemetryRecord,
|
23
|
+
DefenderMPLogDetectionAddRecord,
|
24
|
+
DefenderMPLogDetectionEventRecord,
|
25
|
+
DefenderMPLogEMSRecord,
|
26
|
+
DefenderMPLogExclusionRecord,
|
27
|
+
DefenderMPLogLowfiRecord,
|
28
|
+
DefenderMPLogMinFilBlockedFileRecord,
|
29
|
+
DefenderMPLogMinFilUSSRecord,
|
30
|
+
DefenderMPLogOriginalFileNameRecord,
|
31
|
+
DefenderMPLogProcessImageRecord,
|
32
|
+
DefenderMPLogResourceScanRecord,
|
33
|
+
DefenderMPLogRTPRecord,
|
34
|
+
DefenderMPLogThreatActionRecord,
|
35
|
+
DefenderMPLogThreatRecord,
|
36
|
+
)
|
13
37
|
|
14
38
|
DEFENDER_EVTX_FIELDS = [
|
15
39
|
("datetime", "ts"),
|
@@ -73,6 +97,7 @@ DEFENDER_LOG_FILENAME_GLOB = "Microsoft-Windows-Windows Defender*"
|
|
73
97
|
EVTX_PROVIDER_NAME = "Microsoft-Windows-Windows Defender"
|
74
98
|
|
75
99
|
DEFENDER_QUARANTINE_DIR = "sysvol/programdata/microsoft/windows defender/quarantine"
|
100
|
+
DEFENDER_MPLOG_DIR = "sysvol/programdata/microsoft/windows defender/support"
|
76
101
|
DEFENDER_KNOWN_DETECTION_TYPES = [b"internalbehavior", b"regkey", b"runkey"]
|
77
102
|
|
78
103
|
DEFENDER_EXCLUSION_KEY = "HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions"
|
@@ -494,6 +519,198 @@ class MicrosoftDefenderPlugin(plugin.Plugin):
|
|
494
519
|
value=exclusion_value,
|
495
520
|
)
|
496
521
|
|
522
|
+
def _mplog_processimage(self, data: dict) -> Iterator[DefenderMPLogProcessImageRecord]:
|
523
|
+
yield DefenderMPLogProcessImageRecord(**data)
|
524
|
+
|
525
|
+
def _mplog_minfiluss(self, data: dict) -> Iterator[DefenderMPLogMinFilUSSRecord]:
|
526
|
+
yield DefenderMPLogMinFilUSSRecord(**data)
|
527
|
+
|
528
|
+
def _mplog_blockedfile(self, data: dict) -> Iterator[DefenderMPLogMinFilBlockedFileRecord]:
|
529
|
+
yield DefenderMPLogMinFilBlockedFileRecord(**data)
|
530
|
+
|
531
|
+
def _mplog_bmtelemetry(self, data: dict) -> Iterator[DefenderMPLogBMTelemetryRecord]:
|
532
|
+
data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S")
|
533
|
+
yield DefenderMPLogBMTelemetryRecord(**data)
|
534
|
+
|
535
|
+
def _mplog_ems(self, data: dict) -> Iterator[DefenderMPLogEMSRecord]:
|
536
|
+
yield DefenderMPLogEMSRecord(**data)
|
537
|
+
|
538
|
+
def _mplog_originalfilename(self, data: dict) -> Iterator[DefenderMPLogOriginalFileNameRecord]:
|
539
|
+
yield DefenderMPLogOriginalFileNameRecord(**data)
|
540
|
+
|
541
|
+
def _mplog_exclusion(self, data: dict) -> Iterator[DefenderMPLogExclusionRecord]:
|
542
|
+
yield DefenderMPLogExclusionRecord(**data)
|
543
|
+
|
544
|
+
def _mplog_lowfi(self, data: dict) -> Iterator[DefenderMPLogLowfiRecord]:
|
545
|
+
yield DefenderMPLogLowfiRecord(**data)
|
546
|
+
|
547
|
+
def _mplog_detectionadd(self, data: dict) -> Iterator[DefenderMPLogDetectionAddRecord]:
|
548
|
+
yield DefenderMPLogDetectionAddRecord(**data)
|
549
|
+
|
550
|
+
def _mplog_threat(self, data: dict) -> Iterator[DefenderMPLogThreatRecord]:
|
551
|
+
yield DefenderMPLogThreatRecord(**data)
|
552
|
+
|
553
|
+
def _mplog_resourcescan(self, data: dict) -> Iterator[DefenderMPLogResourceScanRecord]:
|
554
|
+
data["start_time"] = datetime.strptime(data["start_time"], "%m-%d-%Y %H:%M:%S")
|
555
|
+
data["end_time"] = datetime.strptime(data["end_time"], "%m-%d-%Y %H:%M:%S")
|
556
|
+
data["ts"] = data["start_time"]
|
557
|
+
rest = data.pop("rest")
|
558
|
+
yield DefenderMPLogResourceScanRecord(
|
559
|
+
threats=re.findall("Threat Name:([^\n]+)", rest),
|
560
|
+
resources=re.findall("Resource Path:([^\n]+)", rest),
|
561
|
+
**data,
|
562
|
+
)
|
563
|
+
|
564
|
+
def _mplog_threataction(self, data: dict) -> Iterator[DefenderMPLogThreatActionRecord]:
|
565
|
+
data["ts"] = datetime.strptime(data["ts"], "%m-%d-%Y %H:%M:%S")
|
566
|
+
rest = data.pop("rest")
|
567
|
+
yield DefenderMPLogThreatActionRecord(
|
568
|
+
threats=re.findall("Threat Name:([^\n]+)", rest),
|
569
|
+
resources=re.findall("(?:Path|File Name):([^\n]+)", rest),
|
570
|
+
actions=re.findall("Action:([^\n]+)", rest),
|
571
|
+
**data,
|
572
|
+
)
|
573
|
+
|
574
|
+
def _mplog_rtp_log(self, data: dict) -> Iterator[DefenderMPLogRTPRecord]:
|
575
|
+
times = {}
|
576
|
+
for dtkey in ["ts", "last_perf", "first_rtp_scan"]:
|
577
|
+
try:
|
578
|
+
times[dtkey] = datetime.strptime(data[dtkey], "%m-%d-%Y %H:%M:%S")
|
579
|
+
except ValueError:
|
580
|
+
pass
|
581
|
+
|
582
|
+
yield DefenderMPLogRTPRecord(
|
583
|
+
_target=self.target,
|
584
|
+
source_log=data["source_log"],
|
585
|
+
**times,
|
586
|
+
plugin_states=re.findall(r"^\s+(.*)$", data["plugin_states"])[0],
|
587
|
+
process_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["process_exclusions"]),
|
588
|
+
path_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["path_exclusions"]),
|
589
|
+
ext_exclusions=re.findall(DEFENDER_MPLOG_LINE, data["ext_exclusions"]),
|
590
|
+
)
|
591
|
+
|
592
|
+
def _mplog_detectionevent(self, data: dict) -> Iterator[DefenderMPLogDetectionEventRecord]:
|
593
|
+
yield DefenderMPLogDetectionEventRecord(**data)
|
594
|
+
|
595
|
+
def _mplog_line(
|
596
|
+
self, mplog_line: str, source: Path
|
597
|
+
) -> Iterator[
|
598
|
+
DefenderMPLogProcessImageRecord
|
599
|
+
| DefenderMPLogMinFilUSSRecord
|
600
|
+
| DefenderMPLogMinFilBlockedFileRecord
|
601
|
+
| DefenderMPLogEMSRecord
|
602
|
+
| DefenderMPLogOriginalFileNameRecord
|
603
|
+
| DefenderMPLogExclusionRecord
|
604
|
+
| DefenderMPLogLowfiRecord
|
605
|
+
| DefenderMPLogDetectionAddRecord
|
606
|
+
| DefenderMPLogThreatRecord
|
607
|
+
| DefenderMPLogDetectionEventRecord
|
608
|
+
]:
|
609
|
+
for pattern, record in DEFENDER_MPLOG_PATTERNS:
|
610
|
+
if match := pattern.match(mplog_line):
|
611
|
+
data = match.groupdict()
|
612
|
+
data["_target"] = self.target
|
613
|
+
data["source_log"] = source
|
614
|
+
yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data)
|
615
|
+
|
616
|
+
def _mplog_block(
|
617
|
+
self, mplog_line: str, mplog: TextIO, source: Path
|
618
|
+
) -> Iterator[DefenderMPLogResourceScanRecord | DefenderMPLogThreatActionRecord | DefenderMPLogRTPRecord]:
|
619
|
+
block = ""
|
620
|
+
for prefix, suffix, pattern, record in DEFENDER_MPLOG_BLOCK_PATTERNS:
|
621
|
+
if prefix.search(mplog_line):
|
622
|
+
block += mplog_line
|
623
|
+
break
|
624
|
+
if block:
|
625
|
+
while mplog_line := mplog.readline():
|
626
|
+
block += mplog_line
|
627
|
+
if suffix.search(mplog_line):
|
628
|
+
break
|
629
|
+
match = pattern.match(block)
|
630
|
+
data = match.groupdict()
|
631
|
+
data["_target"] = self.target
|
632
|
+
data["source_log"] = source
|
633
|
+
yield from getattr(self, f"_mplog_{record.name.split('/')[-1:][0]}")(data)
|
634
|
+
|
635
|
+
def _mplog(
|
636
|
+
self, mplog: TextIO, source: Path
|
637
|
+
) -> Iterator[
|
638
|
+
DefenderMPLogProcessImageRecord
|
639
|
+
| DefenderMPLogMinFilUSSRecord
|
640
|
+
| DefenderMPLogMinFilBlockedFileRecord
|
641
|
+
| DefenderMPLogBMTelemetryRecord
|
642
|
+
| DefenderMPLogEMSRecord
|
643
|
+
| DefenderMPLogOriginalFileNameRecord
|
644
|
+
| DefenderMPLogExclusionRecord
|
645
|
+
| DefenderMPLogLowfiRecord
|
646
|
+
| DefenderMPLogDetectionAddRecord
|
647
|
+
| DefenderMPLogThreatRecord
|
648
|
+
| DefenderMPLogDetectionEventRecord
|
649
|
+
| DefenderMPLogResourceScanRecord
|
650
|
+
| DefenderMPLogThreatActionRecord
|
651
|
+
| DefenderMPLogRTPRecord
|
652
|
+
]:
|
653
|
+
while mplog_line := mplog.readline():
|
654
|
+
yield from self._mplog_line(mplog_line, source)
|
655
|
+
yield from self._mplog_block(mplog_line, mplog, source)
|
656
|
+
|
657
|
+
@plugin.export(
|
658
|
+
record=[
|
659
|
+
DefenderMPLogProcessImageRecord,
|
660
|
+
DefenderMPLogMinFilUSSRecord,
|
661
|
+
DefenderMPLogMinFilBlockedFileRecord,
|
662
|
+
DefenderMPLogBMTelemetryRecord,
|
663
|
+
DefenderMPLogEMSRecord,
|
664
|
+
DefenderMPLogOriginalFileNameRecord,
|
665
|
+
DefenderMPLogExclusionRecord,
|
666
|
+
DefenderMPLogLowfiRecord,
|
667
|
+
DefenderMPLogDetectionAddRecord,
|
668
|
+
DefenderMPLogThreatRecord,
|
669
|
+
DefenderMPLogDetectionEventRecord,
|
670
|
+
DefenderMPLogResourceScanRecord,
|
671
|
+
DefenderMPLogThreatActionRecord,
|
672
|
+
DefenderMPLogRTPRecord,
|
673
|
+
]
|
674
|
+
)
|
675
|
+
def mplog(
|
676
|
+
self,
|
677
|
+
) -> Iterator[
|
678
|
+
DefenderMPLogProcessImageRecord
|
679
|
+
| DefenderMPLogMinFilUSSRecord
|
680
|
+
| DefenderMPLogMinFilBlockedFileRecord
|
681
|
+
| DefenderMPLogBMTelemetryRecord
|
682
|
+
| DefenderMPLogEMSRecord
|
683
|
+
| DefenderMPLogOriginalFileNameRecord
|
684
|
+
| DefenderMPLogExclusionRecord
|
685
|
+
| DefenderMPLogLowfiRecord
|
686
|
+
| DefenderMPLogDetectionAddRecord
|
687
|
+
| DefenderMPLogThreatRecord
|
688
|
+
| DefenderMPLogDetectionEventRecord
|
689
|
+
| DefenderMPLogResourceScanRecord
|
690
|
+
| DefenderMPLogThreatActionRecord
|
691
|
+
| DefenderMPLogRTPRecord
|
692
|
+
]:
|
693
|
+
"""Return the contents of the Defender MPLog file.
|
694
|
+
|
695
|
+
References:
|
696
|
+
- https://www.crowdstrike.com/blog/how-to-use-microsoft-protection-logging-for-forensic-investigations/
|
697
|
+
- https://www.intrinsec.com/hunt-mplogs/
|
698
|
+
- https://github.com/Intrinsec/mplog_parser
|
699
|
+
"""
|
700
|
+
mplog_directory = self.target.fs.path(DEFENDER_MPLOG_DIR)
|
701
|
+
|
702
|
+
if not (mplog_directory.exists() and mplog_directory.is_dir()):
|
703
|
+
return
|
704
|
+
|
705
|
+
for mplog_file in mplog_directory.glob("MPLog-*"):
|
706
|
+
for encoding in ["UTF-16", "UTF-8"]:
|
707
|
+
try:
|
708
|
+
with mplog_file.open("rt", encoding=encoding) as mplog:
|
709
|
+
yield from self._mplog(mplog, self.target.fs.path(mplog_file))
|
710
|
+
break
|
711
|
+
except UnicodeError:
|
712
|
+
continue
|
713
|
+
|
497
714
|
@plugin.arg(
|
498
715
|
"--output",
|
499
716
|
"-o",
|
File without changes
|
@@ -0,0 +1,282 @@
|
|
1
|
+
import re
|
2
|
+
|
3
|
+
from dissect.target.plugins.os.windows.defender_helpers.defender_records import (
|
4
|
+
DefenderMPLogBMTelemetryRecord,
|
5
|
+
DefenderMPLogDetectionAddRecord,
|
6
|
+
DefenderMPLogDetectionEventRecord,
|
7
|
+
DefenderMPLogEMSRecord,
|
8
|
+
DefenderMPLogExclusionRecord,
|
9
|
+
DefenderMPLogLowfiRecord,
|
10
|
+
DefenderMPLogMinFilBlockedFileRecord,
|
11
|
+
DefenderMPLogMinFilUSSRecord,
|
12
|
+
DefenderMPLogOriginalFileNameRecord,
|
13
|
+
DefenderMPLogProcessImageRecord,
|
14
|
+
DefenderMPLogResourceScanRecord,
|
15
|
+
DefenderMPLogRTPRecord,
|
16
|
+
DefenderMPLogThreatActionRecord,
|
17
|
+
DefenderMPLogThreatRecord,
|
18
|
+
)
|
19
|
+
|
20
|
+
DEFENDER_MPLOG_TS_PATTERN = r"(?P<ts>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z) "
|
21
|
+
|
22
|
+
# Loosely based on https://github.com/Intrinsec/mplog_parser but feel free to add patterns
|
23
|
+
|
24
|
+
DEFENDER_MPLOG_PATTERNS = [
|
25
|
+
# Process Image
|
26
|
+
(
|
27
|
+
re.compile(
|
28
|
+
"".join(
|
29
|
+
[
|
30
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
31
|
+
r"ProcessImageName: (?P<process_image_name>.*), ",
|
32
|
+
r"Pid: (?P<pid>\d*), ",
|
33
|
+
r"TotalTime: (?P<total_time>\d*), ",
|
34
|
+
r"Count: (?P<count>\d*), ",
|
35
|
+
r"MaxTime: (?P<max_time>\d*), ",
|
36
|
+
r"MaxTimeFile: (?P<max_time_file>.*), ",
|
37
|
+
r"EstimatedImpact: (?P<estimated_impact>\d*)",
|
38
|
+
]
|
39
|
+
)
|
40
|
+
),
|
41
|
+
DefenderMPLogProcessImageRecord,
|
42
|
+
),
|
43
|
+
# Mini-filter Unsuccessful scan status
|
44
|
+
(
|
45
|
+
re.compile(
|
46
|
+
"".join(
|
47
|
+
[
|
48
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
49
|
+
r"\[Mini-filter\] (Unsuccessful scan status)[^:]*: (?P<path>.+) ",
|
50
|
+
r"Process: (?P<process>.+), ",
|
51
|
+
r"Status: (?P<status>.+), ",
|
52
|
+
r"State: (?P<state>.+), ",
|
53
|
+
r"ScanRequest (?P<scan_request>.+), ",
|
54
|
+
r"FileId: (?P<file_id>.+), ",
|
55
|
+
r"Reason: (?P<reason>.+), ",
|
56
|
+
r"IoStatusBlockForNewFile: (?P<io_status_block_for_new_file>.+), ",
|
57
|
+
r"DesiredAccess:(?P<desired_access>.+), ",
|
58
|
+
r"FileAttributes:(?P<file_attributes>.+), ",
|
59
|
+
r"ScanAttributes:(?P<scan_attributes>.+), ",
|
60
|
+
r"AccessStateFlags:(?P<access_state_flags>.+), ",
|
61
|
+
r"BackingFileInfo: (?P<backing_file_info>.+)",
|
62
|
+
]
|
63
|
+
)
|
64
|
+
),
|
65
|
+
DefenderMPLogMinFilUSSRecord,
|
66
|
+
),
|
67
|
+
# EMS Scan
|
68
|
+
(
|
69
|
+
re.compile(
|
70
|
+
"".join(
|
71
|
+
[
|
72
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
73
|
+
r".*",
|
74
|
+
r"process: (?P<process>\w*) ",
|
75
|
+
r"pid: (?P<pid>\d*), ",
|
76
|
+
r"sigseq: (?P<sigseq>\w*), ",
|
77
|
+
r"sendMemoryScanReport: (?P<send_memory_scan_report>\d*), ",
|
78
|
+
r"source: (?P<source>\d*)",
|
79
|
+
]
|
80
|
+
)
|
81
|
+
),
|
82
|
+
DefenderMPLogEMSRecord,
|
83
|
+
),
|
84
|
+
# Original filename
|
85
|
+
(
|
86
|
+
re.compile(
|
87
|
+
"".join(
|
88
|
+
[
|
89
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
90
|
+
r".*",
|
91
|
+
r"original file name \"(?P<original_file_name>.*)\" ",
|
92
|
+
r"for \"(?P<full_path>.*)\", ",
|
93
|
+
r"hr=(?P<hr>\w*)",
|
94
|
+
]
|
95
|
+
)
|
96
|
+
),
|
97
|
+
DefenderMPLogOriginalFileNameRecord,
|
98
|
+
),
|
99
|
+
# Mini-filter Blocked file
|
100
|
+
(
|
101
|
+
re.compile(
|
102
|
+
"".join(
|
103
|
+
[
|
104
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
105
|
+
r".*",
|
106
|
+
r"\[Mini-filter\] Blocked file: (?P<blocked_file>.+) ",
|
107
|
+
r"Process: (?P<process>.+), ",
|
108
|
+
r"Status: (?P<status>.+), ",
|
109
|
+
r"State: (?P<state>.+), ",
|
110
|
+
r"ScanRequest (?P<scan_request>.+), ",
|
111
|
+
r"FileId: (?P<file_id>.+), ",
|
112
|
+
r"Reason: (?P<reason>.+), ",
|
113
|
+
r"IoStatusBlockForNewFile: (?P<io_status_block_for_new_file>.+), ",
|
114
|
+
r"DesiredAccess:(?P<desired_access>.+), ",
|
115
|
+
r"FileAttributes:(?P<file_attributes>.+), ",
|
116
|
+
r"ScanAttributes:(?P<scan_attributes>.+), ",
|
117
|
+
r"AccessStateFlags:(?P<access_state_flags>.+), ",
|
118
|
+
r"BackingFileInfo: (?P<backing_file_info>.+)",
|
119
|
+
]
|
120
|
+
)
|
121
|
+
),
|
122
|
+
DefenderMPLogMinFilBlockedFileRecord,
|
123
|
+
),
|
124
|
+
# Exclusion
|
125
|
+
(
|
126
|
+
re.compile(
|
127
|
+
"".join(
|
128
|
+
[
|
129
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
130
|
+
r"\[Exclusion\] (?P<full_path_with_drive_letter>.+) ",
|
131
|
+
r"-> (?P<full_path_with_device_path>.+)",
|
132
|
+
]
|
133
|
+
)
|
134
|
+
),
|
135
|
+
DefenderMPLogExclusionRecord,
|
136
|
+
),
|
137
|
+
# Lowfi
|
138
|
+
(
|
139
|
+
re.compile(
|
140
|
+
"".join(
|
141
|
+
[
|
142
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
143
|
+
r".*",
|
144
|
+
r"lowfi: (?P<lowfi>.+)",
|
145
|
+
]
|
146
|
+
)
|
147
|
+
),
|
148
|
+
DefenderMPLogLowfiRecord,
|
149
|
+
),
|
150
|
+
# Detection add
|
151
|
+
(
|
152
|
+
re.compile(
|
153
|
+
"".join(
|
154
|
+
[
|
155
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
156
|
+
r".*",
|
157
|
+
r"DETECTION_ADD\S* (?P<detection>.*)",
|
158
|
+
]
|
159
|
+
)
|
160
|
+
),
|
161
|
+
DefenderMPLogDetectionAddRecord,
|
162
|
+
),
|
163
|
+
# Threat
|
164
|
+
(
|
165
|
+
re.compile(
|
166
|
+
"".join(
|
167
|
+
[
|
168
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
169
|
+
r".*",
|
170
|
+
r"threat: (?P<threat>.*)",
|
171
|
+
]
|
172
|
+
)
|
173
|
+
),
|
174
|
+
DefenderMPLogThreatRecord,
|
175
|
+
),
|
176
|
+
# Detection event
|
177
|
+
(
|
178
|
+
re.compile(
|
179
|
+
"".join(
|
180
|
+
[
|
181
|
+
DEFENDER_MPLOG_TS_PATTERN,
|
182
|
+
r".*",
|
183
|
+
r"DETECTIONEVENT MPSOURCE_\S+ HackTool:(?P<threat_type>.*) file:(?P<command>.*)",
|
184
|
+
]
|
185
|
+
)
|
186
|
+
),
|
187
|
+
DefenderMPLogDetectionEventRecord,
|
188
|
+
),
|
189
|
+
]
|
190
|
+
|
191
|
+
|
192
|
+
DEFENDER_MPLOG_BLOCK_PATTERNS = [
|
193
|
+
(
|
194
|
+
re.compile(r"Begin Resource Scan"),
|
195
|
+
re.compile(r"End Scan"),
|
196
|
+
re.compile(
|
197
|
+
"".join(
|
198
|
+
[
|
199
|
+
r"Begin Resource Scan.*\n",
|
200
|
+
r"Scan ID:(?P<scan_id>[^\n]+)\n",
|
201
|
+
r"Scan Source:(?P<scan_source>\d+)\n",
|
202
|
+
r"Start Time:(?P<start_time>[0-9\-\:\s]*)\n",
|
203
|
+
r"End Time:(?P<end_time>[0-9\-\:\s]*)\n",
|
204
|
+
r".*",
|
205
|
+
r"Resource Schema:(?P<resource_schema>[^\n]+)\n",
|
206
|
+
r"Resource Path:(?P<resource_path>[^\n]+)\n",
|
207
|
+
r"Result Count:(?P<result_count>\d+)\n",
|
208
|
+
r"(?P<rest>.*)\n",
|
209
|
+
r"End Scan",
|
210
|
+
]
|
211
|
+
),
|
212
|
+
re.MULTILINE | re.DOTALL,
|
213
|
+
),
|
214
|
+
DefenderMPLogResourceScanRecord,
|
215
|
+
),
|
216
|
+
# Threat actions
|
217
|
+
(
|
218
|
+
re.compile(r"Beginning threat actions"),
|
219
|
+
re.compile(r"Finished threat actions"),
|
220
|
+
re.compile(
|
221
|
+
"".join(
|
222
|
+
[
|
223
|
+
r"Beginning threat actions\n",
|
224
|
+
r"Start time:(?P<ts>[0-9\-\:\s]*)\n",
|
225
|
+
r"(?P<rest>.*)\n",
|
226
|
+
r"Finished threat actions",
|
227
|
+
]
|
228
|
+
),
|
229
|
+
re.MULTILINE | re.DOTALL,
|
230
|
+
),
|
231
|
+
DefenderMPLogThreatActionRecord,
|
232
|
+
),
|
233
|
+
# RTP
|
234
|
+
(
|
235
|
+
re.compile(r"\*\*RTP Perf Log\*\*"),
|
236
|
+
re.compile(r"\*\*END RTP Perf Log\*\*"),
|
237
|
+
re.compile(
|
238
|
+
"".join(
|
239
|
+
[
|
240
|
+
r"\*+RTP Perf Log\*+\n",
|
241
|
+
r"RTP Start:(?P<ts>.*)\n",
|
242
|
+
r"Last Perf:(?P<last_perf>.*)\n",
|
243
|
+
r"First RTP Scan:(?P<first_rtp_scan>.*)\n",
|
244
|
+
r"Plugin States:(?P<plugin_states>.*)\n",
|
245
|
+
r"Process Exclusions:\n(?P<process_exclusions>.*)",
|
246
|
+
r"Path Exclusions:\n(?P<path_exclusions>.*)",
|
247
|
+
r"Ext Exclusions:\n(?P<ext_exclusions>.*)",
|
248
|
+
r"Worker Threads",
|
249
|
+
]
|
250
|
+
),
|
251
|
+
re.MULTILINE | re.DOTALL,
|
252
|
+
),
|
253
|
+
DefenderMPLogRTPRecord,
|
254
|
+
),
|
255
|
+
# BM Telemetry (block)
|
256
|
+
(
|
257
|
+
re.compile(r"BEGIN BM telemetry"),
|
258
|
+
re.compile(r"END BM telemetry"),
|
259
|
+
re.compile(
|
260
|
+
"".join(
|
261
|
+
[
|
262
|
+
r"BEGIN BM telemetry\n",
|
263
|
+
r"(GUID):(?P<guid>.+)\n",
|
264
|
+
r"(SignatureID):(?P<signature_id>.+)\n",
|
265
|
+
r"(SigSha):(?P<sigsha>.+)\n",
|
266
|
+
r"(ThreatLevel):(?P<threat_level>.+)\n",
|
267
|
+
r"(ProcessID):(?P<process_id>.+)\n",
|
268
|
+
r"(ProcessCreationTime):(?P<process_creation_time>.+)\n",
|
269
|
+
r"(SessionID):(?P<session_id>.+)\n",
|
270
|
+
r"(CreationTime):(?P<ts>.+)\n",
|
271
|
+
r"(ImagePath):(?P<image_path>.+)\n",
|
272
|
+
r"(Taint Info):(?P<taint_info>.+)\n",
|
273
|
+
r"(Operations):(?P<operations>.+)\n",
|
274
|
+
r"END BM telemetry",
|
275
|
+
]
|
276
|
+
)
|
277
|
+
),
|
278
|
+
DefenderMPLogBMTelemetryRecord,
|
279
|
+
),
|
280
|
+
]
|
281
|
+
|
282
|
+
DEFENDER_MPLOG_LINE = re.compile(r"^\s+(.*)$", re.MULTILINE)
|
@@ -0,0 +1,191 @@
|
|
1
|
+
from dissect.target.helpers.record import TargetRecordDescriptor
|
2
|
+
|
3
|
+
DefenderMPLogProcessImageRecord = TargetRecordDescriptor(
|
4
|
+
"windows/defender/mplog/processimage",
|
5
|
+
[
|
6
|
+
("datetime", "ts"),
|
7
|
+
("path", "source_log"),
|
8
|
+
("string", "process_image_name"),
|
9
|
+
("varint", "pid"),
|
10
|
+
("varint", "total_time"),
|
11
|
+
("varint", "count"),
|
12
|
+
("varint", "max_time"),
|
13
|
+
("string", "max_time_file"),
|
14
|
+
("varint", "estimated_impact"),
|
15
|
+
],
|
16
|
+
)
|
17
|
+
|
18
|
+
DefenderMPLogMinFilUSSRecord = TargetRecordDescriptor(
|
19
|
+
"windows/defender/mplog/minfiluss",
|
20
|
+
[
|
21
|
+
("datetime", "ts"),
|
22
|
+
("path", "source_log"),
|
23
|
+
("path", "path"),
|
24
|
+
("string", "process"),
|
25
|
+
("string", "status"),
|
26
|
+
("string", "state"),
|
27
|
+
("string", "scan_request"),
|
28
|
+
("string", "file_id"),
|
29
|
+
("string", "reason"),
|
30
|
+
("string", "io_status_block_for_new_file"),
|
31
|
+
("string", "desired_access"),
|
32
|
+
("string", "file_attributes"),
|
33
|
+
("string", "scan_attributes"),
|
34
|
+
("string", "access_state_flags"),
|
35
|
+
("string", "backing_file_info"),
|
36
|
+
],
|
37
|
+
)
|
38
|
+
|
39
|
+
DefenderMPLogMinFilBlockedFileRecord = TargetRecordDescriptor(
|
40
|
+
"windows/defender/mplog/blockedfile",
|
41
|
+
[
|
42
|
+
("datetime", "ts"),
|
43
|
+
("path", "source_log"),
|
44
|
+
("string", "blocked_file"),
|
45
|
+
("string", "process"),
|
46
|
+
("string", "status"),
|
47
|
+
("string", "state"),
|
48
|
+
("string", "scan_request"),
|
49
|
+
("string", "file_id"),
|
50
|
+
("string", "reason"),
|
51
|
+
("string", "io_status_block_for_new_file"),
|
52
|
+
("string", "desired_access"),
|
53
|
+
("string", "file_attributes"),
|
54
|
+
("string", "scan_attributes"),
|
55
|
+
("string", "access_state_flags"),
|
56
|
+
("string", "backing_file_info"),
|
57
|
+
],
|
58
|
+
)
|
59
|
+
|
60
|
+
|
61
|
+
DefenderMPLogBMTelemetryRecord = TargetRecordDescriptor(
|
62
|
+
"windows/defender/mplog/bmtelemetry",
|
63
|
+
[
|
64
|
+
("datetime", "ts"),
|
65
|
+
("path", "source_log"),
|
66
|
+
("string", "guid"),
|
67
|
+
("varint", "signature_id"),
|
68
|
+
("string", "sigsha"),
|
69
|
+
("varint", "threat_level"),
|
70
|
+
("varint", "process_id"),
|
71
|
+
("varint", "process_creation_time"),
|
72
|
+
("varint", "session_id"),
|
73
|
+
("path", "image_path"),
|
74
|
+
("string", "taint_info"),
|
75
|
+
("string", "operations"),
|
76
|
+
],
|
77
|
+
)
|
78
|
+
|
79
|
+
DefenderMPLogEMSRecord = TargetRecordDescriptor(
|
80
|
+
"windows/defender/mplog/ems",
|
81
|
+
[
|
82
|
+
("datetime", "ts"),
|
83
|
+
("path", "source_log"),
|
84
|
+
("string", "process"),
|
85
|
+
("varint", "pid"),
|
86
|
+
("string", "sigseq"),
|
87
|
+
("varint", "send_memory_scan_report"),
|
88
|
+
("varint", "source"),
|
89
|
+
],
|
90
|
+
)
|
91
|
+
|
92
|
+
DefenderMPLogOriginalFileNameRecord = TargetRecordDescriptor(
|
93
|
+
"windows/defender/mplog/originalfilename",
|
94
|
+
[
|
95
|
+
("datetime", "ts"),
|
96
|
+
("path", "source_log"),
|
97
|
+
("string", "original_file_name"),
|
98
|
+
("path", "full_path"),
|
99
|
+
("string", "hr"),
|
100
|
+
],
|
101
|
+
)
|
102
|
+
|
103
|
+
DefenderMPLogExclusionRecord = TargetRecordDescriptor(
|
104
|
+
"windows/defender/mplog/exclusion",
|
105
|
+
[
|
106
|
+
("datetime", "ts"),
|
107
|
+
("path", "source_log"),
|
108
|
+
("path", "full_path_with_drive_letter"),
|
109
|
+
("path", "full_path_with_device_path"),
|
110
|
+
],
|
111
|
+
)
|
112
|
+
|
113
|
+
DefenderMPLogLowfiRecord = TargetRecordDescriptor(
|
114
|
+
"windows/defender/mplog/lowfi",
|
115
|
+
[
|
116
|
+
("datetime", "ts"),
|
117
|
+
("path", "source_log"),
|
118
|
+
("command", "lowfi"),
|
119
|
+
],
|
120
|
+
)
|
121
|
+
|
122
|
+
DefenderMPLogDetectionAddRecord = TargetRecordDescriptor(
|
123
|
+
"windows/defender/mplog/detectionadd",
|
124
|
+
[
|
125
|
+
("datetime", "ts"),
|
126
|
+
("path", "source_log"),
|
127
|
+
("string", "detection"),
|
128
|
+
],
|
129
|
+
)
|
130
|
+
|
131
|
+
|
132
|
+
DefenderMPLogThreatRecord = TargetRecordDescriptor(
|
133
|
+
"windows/defender/mplog/threat",
|
134
|
+
[
|
135
|
+
("datetime", "ts"),
|
136
|
+
("path", "source_log"),
|
137
|
+
("command", "threat"),
|
138
|
+
],
|
139
|
+
)
|
140
|
+
|
141
|
+
DefenderMPLogDetectionEventRecord = TargetRecordDescriptor(
|
142
|
+
"windows/defender/mplog/detectionevent",
|
143
|
+
[
|
144
|
+
("datetime", "ts"),
|
145
|
+
("path", "source_log"),
|
146
|
+
("string", "threat_type"),
|
147
|
+
("command", "command"),
|
148
|
+
],
|
149
|
+
)
|
150
|
+
|
151
|
+
DefenderMPLogResourceScanRecord = TargetRecordDescriptor(
|
152
|
+
"windows/defender/mplog/resourcescan",
|
153
|
+
[
|
154
|
+
("datetime", "ts"),
|
155
|
+
("path", "source_log"),
|
156
|
+
("string", "scan_id"),
|
157
|
+
("varint", "scan_source"),
|
158
|
+
("datetime", "start_time"),
|
159
|
+
("datetime", "end_time"),
|
160
|
+
("string", "resource_schema"),
|
161
|
+
("path", "resource_path"),
|
162
|
+
("varint", "result_count"),
|
163
|
+
("string[]", "threats"),
|
164
|
+
("path[]", "resources"),
|
165
|
+
],
|
166
|
+
)
|
167
|
+
|
168
|
+
DefenderMPLogThreatActionRecord = TargetRecordDescriptor(
|
169
|
+
"windows/defender/mplog/threataction",
|
170
|
+
[
|
171
|
+
("datetime", "ts"),
|
172
|
+
("path", "source_log"),
|
173
|
+
("string[]", "threats"),
|
174
|
+
("path[]", "resources"),
|
175
|
+
("string[]", "actions"),
|
176
|
+
],
|
177
|
+
)
|
178
|
+
|
179
|
+
DefenderMPLogRTPRecord = TargetRecordDescriptor(
|
180
|
+
"windows/defender/mplog/rtp_log",
|
181
|
+
[
|
182
|
+
("datetime", "ts"),
|
183
|
+
("path", "source_log"),
|
184
|
+
("datetime", "last_perf"),
|
185
|
+
("datetime", "first_rtp_scan"),
|
186
|
+
("string", "plugin_states"),
|
187
|
+
("path[]", "process_exclusions"),
|
188
|
+
("path[]", "path_exclusions"),
|
189
|
+
("string[]", "ext_exclusions"),
|
190
|
+
],
|
191
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.18.
|
3
|
+
Version: 3.18.dev11
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -253,7 +253,7 @@ dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wq89wRSFZSBsoKVCxjDofnC4yw9
|
|
253
253
|
dissect/target/plugins/os/unix/log/messages.py,sha256=CXA-SkMPLaCgnTQg9nzII-7tO8Il_ENQmuYvDxo33rI,4698
|
254
254
|
dissect/target/plugins/os/unix/log/utmp.py,sha256=1nPHIaBUHt_9z6PDrvyqg4huKLihUaWLrMmgMsbaeIo,7755
|
255
255
|
dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
256
|
-
dissect/target/plugins/os/windows/_os.py,sha256=
|
256
|
+
dissect/target/plugins/os/windows/_os.py,sha256=i9RPs99YD3KvZpAHAIuaQeGDy0nt5DIvfMbreqO_JIY,12723
|
257
257
|
dissect/target/plugins/os/windows/activitiescache.py,sha256=Q2aILnhJ2rp2AwEbWwyBuSLjMbGqaYJTsavSbfkcFKE,6741
|
258
258
|
dissect/target/plugins/os/windows/adpolicy.py,sha256=fULRFO_I_QxAn6G9SCwlLL-TLVliS13JEGnGotf7lSA,6983
|
259
259
|
dissect/target/plugins/os/windows/amcache.py,sha256=ZZNOs3bILTf0AGkDkhoatndl0j39DXkstN7oOyxJECU,27188
|
@@ -262,7 +262,7 @@ dissect/target/plugins/os/windows/cim.py,sha256=jsrpu6TZpBUh7VWI9AV2Ib5bebTwsvqO
|
|
262
262
|
dissect/target/plugins/os/windows/clfs.py,sha256=begVsZ-CY97Ksh6S1g03LjyBgu8ERY2hfNDWYPj0GXI,4872
|
263
263
|
dissect/target/plugins/os/windows/credhist.py,sha256=YSjuyd53Augdy_lKKzZHtx5Ozt0HzF6LDYIOb-8P1Pw,7058
|
264
264
|
dissect/target/plugins/os/windows/datetime.py,sha256=YKHUZU6lkKJocq15y0yCwvIIOb1Ej-kfvEBmHbrdIGw,9467
|
265
|
-
dissect/target/plugins/os/windows/defender.py,sha256=
|
265
|
+
dissect/target/plugins/os/windows/defender.py,sha256=VvcYsCc7gTJiz4LJotWqHU8k_4vo-mkCoBNx8bgtVEE,32608
|
266
266
|
dissect/target/plugins/os/windows/env.py,sha256=-u9F9xWy6PUbQmu5Tv_MDoVmy6YB-7CbHokIK_T3S44,13891
|
267
267
|
dissect/target/plugins/os/windows/generic.py,sha256=BSvDPfB9faU0uquMj0guw5tnR_97Nn0XAEE4k05BFSQ,22273
|
268
268
|
dissect/target/plugins/os/windows/lnk.py,sha256=On1k0PODYggQM1j514qFepBACCV2Z2u61Q4Ba6e3Y2c,8179
|
@@ -280,6 +280,9 @@ dissect/target/plugins/os/windows/tasks.py,sha256=8DRsIAuIJPaH_G18l8RYfnK_WkEqVx
|
|
280
280
|
dissect/target/plugins/os/windows/thumbcache.py,sha256=23YjOjTNoE7BYITmg8s9Zs8Wih2e73BkJJEaKlfotcI,4133
|
281
281
|
dissect/target/plugins/os/windows/ual.py,sha256=TYF-R46klEa_HHb86UJd6mPrXwHlAMOUTzC0pZ8uiq0,9787
|
282
282
|
dissect/target/plugins/os/windows/wer.py,sha256=ogecvKYxAvDXLptQj4cn0JLn1FxaXjeSuJWs4JgkoZs,8656
|
283
|
+
dissect/target/plugins/os/windows/defender_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
284
|
+
dissect/target/plugins/os/windows/defender_helpers/defender_patterns.py,sha256=xsZH0WqMX_mC1q55jgp4RDHBRh2UQBXZVhJD0DRiwZU,9329
|
285
|
+
dissect/target/plugins/os/windows/defender_helpers/defender_records.py,sha256=_azaY5Y1cH-WPmkA5k94PMktZGYXmWJG8addFQxQ554,5177
|
283
286
|
dissect/target/plugins/os/windows/dpapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
284
287
|
dissect/target/plugins/os/windows/dpapi/blob.py,sha256=j3MMROXroes7pr_VLt8Xv6WEpv19hlgDpOxOJyZMRvo,5044
|
285
288
|
dissect/target/plugins/os/windows/dpapi/crypto.py,sha256=_F1F2j1chQw-KLqfWvgL2mCkF3HSvdVnM78OZ0ph9hc,9337
|
@@ -340,10 +343,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
340
343
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
341
344
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
342
345
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
343
|
-
dissect.target-3.18.
|
344
|
-
dissect.target-3.18.
|
345
|
-
dissect.target-3.18.
|
346
|
-
dissect.target-3.18.
|
347
|
-
dissect.target-3.18.
|
348
|
-
dissect.target-3.18.
|
349
|
-
dissect.target-3.18.
|
346
|
+
dissect.target-3.18.dev11.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
347
|
+
dissect.target-3.18.dev11.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
348
|
+
dissect.target-3.18.dev11.dist-info/METADATA,sha256=mWlhvOaO7RcVbyJ0_fU7KHrnNaVL2vQ6a7uKhPvJ-kM,12723
|
349
|
+
dissect.target-3.18.dev11.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
|
350
|
+
dissect.target-3.18.dev11.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
351
|
+
dissect.target-3.18.dev11.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
352
|
+
dissect.target-3.18.dev11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|