-
Notifications
You must be signed in to change notification settings - Fork 96
Add macOS plugins #1727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add macOS plugins #1727
Changes from 27 commits
898b07a
1e8d95c
6db60f2
7e4c235
a33ccab
bc39d35
5a31d6e
4742b4a
4a9acd1
44c2850
57debef
f2fc33d
f5ee807
820629c
d1d73c8
ba73adb
175eda3
d12c07c
6ffa21e
2fd1cba
17da16e
285cd0b
188326d
ca71719
4c76edf
0c09f0b
90a832c
2488a8b
da8c071
cace2ae
220c262
0f2e889
4a4895c
20e87cc
4d85beb
3fa433a
2df67f5
ecc58e5
4930701
f9a0fe2
0c0890d
aa8757c
736ae05
e607bc6
7704d03
de3d9d3
52ff082
3658cb6
5709272
8eda477
057814b
4c99f9b
4603625
f059838
b033eb1
9ab673d
a52abc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import plistlib | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from dissect.target.exceptions import UnsupportedPluginError | ||
| from dissect.target.helpers.record import TargetRecordDescriptor | ||
| from dissect.target.plugin import Plugin, export | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Iterator | ||
|
|
||
| from dissect.target import Target | ||
|
|
||
| AirportPreferencesRecord = TargetRecordDescriptor( | ||
| "macos/airport_preferences", | ||
| [ | ||
| ("varint", "counter"), | ||
| ("string", "device_uuid"), | ||
| ("string", "version"), | ||
| ("string", "preferred_order"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| class AirportPreferencesPlugin(Plugin): | ||
| """macOS AirPort (WiFi) preferences plugin.""" | ||
|
|
||
| PATH = "/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist" | ||
|
|
||
| def __init__(self, target: Target): | ||
| super().__init__(target) | ||
| self.file = None | ||
| self._resolve_file() | ||
|
|
||
| def _resolve_file(self) -> None: | ||
| path = self.target.fs.path(self.PATH) | ||
| if path.exists(): | ||
| self.file = path | ||
|
|
||
| def check_compatible(self) -> None: | ||
| if not self.file: | ||
| raise UnsupportedPluginError("No com.apple.airport.preferences.plist file found") | ||
|
|
||
| @export(record=AirportPreferencesRecord) | ||
| def airport_preferences(self) -> Iterator[AirportPreferencesRecord]: | ||
| """Yield AirPort preference information.""" | ||
| plist = plistlib.load(self.file.open()) | ||
|
|
||
| counter = plist.get("Counter") | ||
| version = plist.get("Version") | ||
| device_uuid = plist.get("DeviceUUID") | ||
| preferred_order = plist.get("PreferredOrder") | ||
|
|
||
| yield AirportPreferencesRecord( | ||
| counter=counter, | ||
| device_uuid=device_uuid, | ||
| version=version, | ||
| preferred_order=preferred_order, | ||
| source=self.file, | ||
| _target=self.target, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| from dissect.target.exceptions import UnsupportedPluginError | ||
| from dissect.target.helpers.record import TargetRecordDescriptor | ||
| from dissect.target.plugin import Plugin, export | ||
| from dissect.target.plugins.os.unix.bsd.darwin.macos.helpers.build_records import build_sqlite_records | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Iterator | ||
|
|
||
| from dissect.target import Target | ||
|
|
||
| AuthorizationRulesRecord = TargetRecordDescriptor( | ||
| "macos/authorization_rules", | ||
| [ | ||
| ("string[]", "tables"), | ||
| ("varint", "rules_id"), | ||
| ("string", "rules_name"), | ||
| ("varint", "rules_type"), | ||
| ("varint", "rules_class"), | ||
| ("string", "rules_group"), | ||
| ("varint", "rules_kofn"), | ||
| ("varint", "rules_timeout"), | ||
| ("varint", "rules_flags"), | ||
| ("string", "rules_tries"), | ||
| ("varint", "rules_version"), | ||
| ("datetime", "rules_created"), | ||
| ("datetime", "rules_modified"), | ||
| ("string", "rules_hash"), | ||
| ("string", "rules_identifier"), | ||
| ("string", "rules_requirement"), | ||
| ("string", "rules_comment"), | ||
| ("string[]", "rules_delegates_map"), | ||
| ("datetime", "rules_history_timestamp"), | ||
| ("string", "rules_history_source"), | ||
| ("varint", "rules_history_operation"), | ||
| ("varint", "mechanisms_map_m_id"), | ||
| ("varint", "mechanisms_map_ord"), | ||
| ("string", "mechanisms_plugin"), | ||
| ("string", "mechanisms_param"), | ||
| ("varint", "mechanisms_privileged"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
| ConfigTableRecord = TargetRecordDescriptor( | ||
| "macos/authorization_rules/config", | ||
| [ | ||
| ("string", "table"), | ||
| ("string", "key"), | ||
| ("string", "value"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
| SQLiteSequenceTableRecord = TargetRecordDescriptor( | ||
| "macos/authorization_rules/sqlite_sequence", | ||
| [ | ||
| ("string", "table"), | ||
| ("string", "name"), | ||
| ("varint", "seq"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
| AuthorizationRulesRecords = ( | ||
| AuthorizationRulesRecord, | ||
| ConfigTableRecord, | ||
| SQLiteSequenceTableRecord, | ||
| ) | ||
|
|
||
| joins = ( | ||
| {"table1": "rules", "key1": "name", "table2": "rules_history", "key2": "rule", "join": "iterate"}, | ||
| {"table1": "rules", "key1": "version", "table2": "rules_history", "key2": "version", "join": "ignore"}, | ||
| {"table1": "rules", "key1": "id", "table2": "mechanisms_map", "key2": "r_id", "join": "iterate"}, | ||
| {"table1": "mechanisms_map", "key1": "m_id", "table2": "mechanisms", "key2": "id", "join": "iterate"}, | ||
| {"table1": "rules", "key1": "id", "table2": "delegates_map", "key2": "r_id", "join": "nested"}, | ||
| ) | ||
|
|
||
|
|
||
| class AuthorizationRulesPlugin(Plugin): | ||
| """macOS authorization rules plugin.""" | ||
|
|
||
| PATH = "/var/db/auth.db" | ||
|
|
||
| def __init__(self, target: Target): | ||
| super().__init__(target) | ||
| self.file = None | ||
| self._resolve_file() | ||
|
|
||
| def _resolve_file(self) -> None: | ||
| path = self.target.fs.path(self.PATH) | ||
| if path.exists(): | ||
| self.file = path | ||
|
|
||
| def check_compatible(self) -> None: | ||
| if not self.file: | ||
| raise UnsupportedPluginError("No auth.db file found") | ||
|
|
||
| @export(record=AuthorizationRulesRecords) | ||
| def authorization_rules(self) -> Iterator[AuthorizationRulesRecords]: | ||
| """Yield authorization rules information.""" | ||
| yield from build_sqlite_records(self, (self.file,), AuthorizationRulesRecords, joins) | ||
|
|
||
| # Still missing prompts & buttons tables |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import re | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from dissect.target.exceptions import UnsupportedPluginError | ||
| from dissect.target.helpers.record import TargetRecordDescriptor | ||
| from dissect.target.plugin import Plugin, export | ||
| from dissect.target.plugins.os.unix.bsd.darwin.macos.helpers.build_records import build_plist_records | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Iterator | ||
|
|
||
| from dissect.target.target import Target | ||
|
|
||
| re_illegal_characters = re.compile(r"[\(\): \.\-#\/\>\<]") | ||
|
|
||
| OmitRecord = TargetRecordDescriptor( | ||
| "macos/code_signature_coderesources/omit", | ||
| [ | ||
| ("boolean", "omit"), | ||
| ("varint", "weight"), | ||
| ("string", "plist_path"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
| NestedRecord = TargetRecordDescriptor( | ||
| "macos/code_signature_coderesources/nested", | ||
| [ | ||
| ("boolean", "nested"), | ||
| ("varint", "weight"), | ||
| ("string", "plist_path"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
| CDHashRecord = TargetRecordDescriptor( | ||
| "macos/code_signature_coderesources/cdhash", | ||
| [ | ||
| ("string", "cdhash"), | ||
| ("string", "requirement"), | ||
| ("string", "plist_path"), | ||
| ("path", "source"), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| CodeSignatureCodeResourcesRecords = ( | ||
| OmitRecord, | ||
| NestedRecord, | ||
| CDHashRecord, | ||
| ) | ||
|
|
||
|
|
||
| class CodeSignatureCodeResourcesPlugin(Plugin): | ||
| """macOS Code signature CodeResources plugin.""" | ||
|
|
||
| PATHS = ( | ||
| "/Applications/Utilities/*.app/Contents/_CodeSignature/CodeResources", | ||
|
EmperorValkorion marked this conversation as resolved.
Outdated
|
||
| "/System/Library/CoreServices/*.app/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Extensions/*.kext/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/PlugIns/*.plugin/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/Resources/*.bundle/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Extensions/*.kext/Contents/Resources/*.bundle/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Filesystems/*/*.kext/Contents/_CodeSignature/CodeResources", | ||
| "/System/Library/Filesystems/*/Encodings/*.kext/Contents/_CodeSignature/CodeResource", | ||
| "/System/Library/PrivateFrameworks/*.framework/Versions/A/Resources/*.kext/Contents/_CodeSignature/CodeResources", | ||
| ) | ||
|
|
||
| def __init__(self, target: Target): | ||
| super().__init__(target) | ||
| self.files = set() | ||
| self._find_files() | ||
|
|
||
| def _find_files(self) -> None: | ||
| for pattern in self.PATHS: | ||
| for path in self.target.fs.glob(pattern): | ||
| self.files.add(path) | ||
|
|
||
| def check_compatible(self) -> None: | ||
| if not (self.files): | ||
| raise UnsupportedPluginError("No code signature coderesources files found") | ||
|
|
||
| @export(record=CodeSignatureCodeResourcesRecords) | ||
| def code_signature_coderesources(self) -> Iterator[CodeSignatureCodeResourcesRecords]: | ||
| """Yield code signature coderesources information.""" | ||
| yield from build_plist_records(self, self.files, CodeSignatureCodeResourcesRecords) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import re | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from dissect.target.exceptions import UnsupportedPluginError | ||
| from dissect.target.helpers.record import DynamicDescriptor | ||
| from dissect.target.plugin import Plugin, export | ||
| from dissect.target.plugins.os.unix.bsd.darwin.macos.helpers.build_records import build_plist_records | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Iterator | ||
|
|
||
| from dissect.target.target import Target | ||
|
|
||
| re_illegal_characters = re.compile(r"[\(\): \.\-#\/\>\<]") | ||
|
EmperorValkorion marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| class ContentsInfoPlugin(Plugin): | ||
| """macOS contents info plugin.""" | ||
|
|
||
| PATHS = ( | ||
| "/Applications/*/*.app/Contents/Info.plist", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about apps in Might be nice to add a utility for iterating applications and kexts. It could yield paths for every I.e. search within certain paths for A Some suggestions:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a find_bundle_files helper function in macos/helpers/build_paths.py, but I'm not quite satisfied with the performance yet. |
||
| "/Applications/*/*.app/Contents/Resources/*.help/Contents/Info.plist", | ||
| "/System/Library/CoreServices/*.app/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/PlugIns/*.plugin/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/Contents/PlugIns/*.kext/Contents/Resources/*.bundle/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/Contents/Resources/*.bundle/Contents/Info.plist", | ||
| "/System/Library/Extensions/*.kext/PlugIns/*.kext/Info.plist", | ||
| "/System/Library/Filesystems/*/*.kext/Contents/Info.plist", | ||
| "/System/Library/Filesystems/*/Encodings/*.kext/Contents/Info.plist", | ||
| "/System/Library/Frameworks/*.framework/Versions/A/Resources/Info.plist", | ||
| "/System/Library/PrivateFrameworks/*.framework/Versions/A/Resources/*.kext/Contents/Info.plist", | ||
| ) | ||
|
|
||
| def __init__(self, target: Target): | ||
| super().__init__(target) | ||
| self.files = set() | ||
| self._find_files() | ||
|
|
||
| def _find_files(self) -> None: | ||
| for pattern in self.PATHS: | ||
| for path in self.target.fs.glob(pattern): | ||
| self.files.add(path) | ||
|
|
||
| def check_compatible(self) -> None: | ||
| if not (self.files): | ||
| raise UnsupportedPluginError("No contents info files found") | ||
|
|
||
| @export(record=DynamicDescriptor(["string"])) | ||
| def contents_info(self) -> Iterator[DynamicDescriptor]: | ||
| """Yield contents info information.""" | ||
| yield from build_plist_records(self, self.files, function_name="macos/contents_info") | ||
Uh oh!
There was an error while loading. Please reload this page.