From 95bbfd385faa88a09678ad9df341284468665a7b Mon Sep 17 00:00:00 2001 From: JSCU-CNI <121175071+JSCU-CNI@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:19:50 +0200 Subject: [PATCH 1/5] add parallels child detection --- dissect/target/plugins/child/parallels.py | 67 +++++++++++++++++++ .../plugins/os/unix/bsd/osx/_os/dissect.plist | 4 +- tests/conftest.py | 2 + tests/plugins/child/test_parallels.py | 25 +++++++ tests/plugins/os/unix/bsd/osx/test__os.py | 2 +- 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 dissect/target/plugins/child/parallels.py create mode 100644 tests/plugins/child/test_parallels.py diff --git a/dissect/target/plugins/child/parallels.py b/dissect/target/plugins/child/parallels.py new file mode 100644 index 000000000..6e4503b9b --- /dev/null +++ b/dissect/target/plugins/child/parallels.py @@ -0,0 +1,67 @@ +from typing import Iterator + +from dissect.target.exceptions import UnsupportedPluginError +from dissect.target.helpers.fsutil import TargetPath +from dissect.target.helpers.record import ChildTargetRecord +from dissect.target.plugin import ChildTargetPlugin +from dissect.target.target import Target + +PARALLELS_USER_PATHS = [ + "Parallels", + "Documents/Parallels", + "Library/Group Containers/*.com.parallels.desktop.appstore/Shared/Parallels", +] + +PARALLELS_SYSTEM_PATHS = [ + "/Users/Shared/Parallels", +] + + +def find_pvms(target: Target) -> Iterator[TargetPath]: + """Finds virtual machines located in default folders on a MacOS target. + + Resources: + - https://kb.parallels.com/117333 + """ + for user_details in target.user_details.all_with_home(): + for parallels_path in PARALLELS_SYSTEM_PATHS: + if (path := target.fs.path(parallels_path)).exists(): + yield from iter_vms(path) + + for parallels_path in PARALLELS_USER_PATHS: + if "*" in parallels_path: + start_path, pattern = parallels_path.split("*", 1) + for path in user_details.home_path.joinpath(start_path).rglob("*" + pattern): + yield from iter_vms(path) + else: + if (path := user_details.home_path.joinpath(parallels_path)).exists(): + yield from iter_vms(path) + + +def iter_vms(path: TargetPath) -> Iterator[TargetPath]: + """Glob for .pvm folders in the provided folder.""" + for file in path.rglob("*.pvm"): + if file.is_dir(): + yield file + + +class ParallelsChildTargetPlugin(ChildTargetPlugin): + """Child target plugin that yields Parallels Desktop VM files.""" + + __type__ = "parallels" + + def __init__(self, target: Target): + super().__init__(target) + self.pvms = list(find_pvms(target)) + + def check_compatible(self) -> None: + if not self.pvms: + raise UnsupportedPluginError("No Parallels pvm file(s) found") + + def list_children(self): + for pvm in self.pvms: + yield ChildTargetRecord( + type=self.__type__, + path=pvm, + _target=self.target, + ) diff --git a/tests/_data/plugins/os/unix/bsd/osx/_os/dissect.plist b/tests/_data/plugins/os/unix/bsd/osx/_os/dissect.plist index a85d673c3..d6b861ad7 100644 --- a/tests/_data/plugins/os/unix/bsd/osx/_os/dissect.plist +++ b/tests/_data/plugins/os/unix/bsd/osx/_os/dissect.plist @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b89fdb2ba4488e5afcbe22693322f93e95cdbbb85cd02c42c8bed965bdf9a08 -size 1255 +oid sha256:16b94cb1bdee735b833a56cb3e09ad23f7f55f61fb169b208c83832e9aa7d5a0 +size 1259 diff --git a/tests/conftest.py b/tests/conftest.py index 16501eaff..53b8a295f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -455,6 +455,8 @@ def target_osx_users(target_osx: Target, fs_osx: VirtualFilesystem) -> Iterator[ test = absolute_path("_data/plugins/os/unix/bsd/osx/_os/test.plist") fs_osx.map_file("/var/db/dslocal/nodes/Default/users/_test.plist", test) + fs_osx.makedirs("/Users/dissect") + yield target_osx diff --git a/tests/plugins/child/test_parallels.py b/tests/plugins/child/test_parallels.py new file mode 100644 index 000000000..3c1fc28aa --- /dev/null +++ b/tests/plugins/child/test_parallels.py @@ -0,0 +1,25 @@ +from dissect.target.filesystem import VirtualFilesystem +from dissect.target.plugins.child.parallels import ParallelsChildTargetPlugin +from dissect.target.target import Target + + +def test_parallels_child_detection(target_osx_users: Target, fs_osx: VirtualFilesystem) -> None: + """test if we correctly find Parallels child VMs on MacOS targets.""" + + fs_osx.makedirs("/Users/dissect/Parallels/Windows 11.pvm") + fs_osx.makedirs("/Users/dissect/Documents/Parallels/Windows 10.pvm") + fs_osx.makedirs( + "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm" + ) + fs_osx.makedirs("/Users/Shared/Parallels/Windows 7.pvm") + + target_osx_users.add_plugin(ParallelsChildTargetPlugin) + children = list(target_osx_users.list_children()) + + assert len(children) == 4 + assert [c.path for c in children] == [ + "/Users/Shared/Parallels/Windows 7.pvm", + "/Users/dissect/Parallels/Windows 11.pvm", + "/Users/dissect/Documents/Parallels/Windows 10.pvm", + "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm", + ] diff --git a/tests/plugins/os/unix/bsd/osx/test__os.py b/tests/plugins/os/unix/bsd/osx/test__os.py index e4202aa45..c645efcf7 100644 --- a/tests/plugins/os/unix/bsd/osx/test__os.py +++ b/tests/plugins/os/unix/bsd/osx/test__os.py @@ -25,7 +25,7 @@ def test_unix_bsd_osx_os(target_osx_users, fs_osx): assert dissect_user.name == "_dissect" assert dissect_user.passwd == "*" - assert dissect_user.home == "/var/empty" + assert dissect_user.home == "/Users/dissect" assert dissect_user.shell == "/usr/bin/false" assert dissect_user.source == "/var/db/dslocal/nodes/Default/users/_dissect.plist" From d66ff39900d3c32af238eba08fb9d9cfc901cf86 Mon Sep 17 00:00:00 2001 From: Computer Network Investigation <121175071+JSCU-CNI@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:22:21 +0200 Subject: [PATCH 2/5] Update dissect/target/plugins/child/parallels.py Co-authored-by: Erik Schamper <1254028+Schamper@users.noreply.github.com> --- dissect/target/plugins/child/parallels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/target/plugins/child/parallels.py b/dissect/target/plugins/child/parallels.py index 6e4503b9b..cbb4ca8fe 100644 --- a/dissect/target/plugins/child/parallels.py +++ b/dissect/target/plugins/child/parallels.py @@ -18,7 +18,7 @@ def find_pvms(target: Target) -> Iterator[TargetPath]: - """Finds virtual machines located in default folders on a MacOS target. + """Finds virtual machines located in default folders on a macOS target. Resources: - https://kb.parallels.com/117333 From 6727aa2cb0c4f0e74c2f96f6b69326ba75dfad5d Mon Sep 17 00:00:00 2001 From: Computer Network Investigation <121175071+JSCU-CNI@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:53:46 +0200 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Erik Schamper <1254028+Schamper@users.noreply.github.com> --- dissect/target/plugins/child/parallels.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dissect/target/plugins/child/parallels.py b/dissect/target/plugins/child/parallels.py index cbb4ca8fe..c24530324 100644 --- a/dissect/target/plugins/child/parallels.py +++ b/dissect/target/plugins/child/parallels.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import Iterator from dissect.target.exceptions import UnsupportedPluginError @@ -38,7 +39,7 @@ def find_pvms(target: Target) -> Iterator[TargetPath]: yield from iter_vms(path) -def iter_vms(path: TargetPath) -> Iterator[TargetPath]: +def iter_vms(path: Path) -> Iterator[TargetPath]: """Glob for .pvm folders in the provided folder.""" for file in path.rglob("*.pvm"): if file.is_dir(): @@ -58,7 +59,7 @@ def check_compatible(self) -> None: if not self.pvms: raise UnsupportedPluginError("No Parallels pvm file(s) found") - def list_children(self): + def list_children(self) -> Iterator[ChildTargetRecord]: for pvm in self.pvms: yield ChildTargetRecord( type=self.__type__, From 009cbb393086c5cae02177c2b1d0730732aa931d Mon Sep 17 00:00:00 2001 From: JSCU-CNI <121175071+JSCU-CNI@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:12:00 +0200 Subject: [PATCH 4/5] fix linter --- tests/plugins/child/test_parallels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/child/test_parallels.py b/tests/plugins/child/test_parallels.py index 3c1fc28aa..887fe369e 100644 --- a/tests/plugins/child/test_parallels.py +++ b/tests/plugins/child/test_parallels.py @@ -9,7 +9,7 @@ def test_parallels_child_detection(target_osx_users: Target, fs_osx: VirtualFile fs_osx.makedirs("/Users/dissect/Parallels/Windows 11.pvm") fs_osx.makedirs("/Users/dissect/Documents/Parallels/Windows 10.pvm") fs_osx.makedirs( - "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm" + "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm" # noqa: E501 ) fs_osx.makedirs("/Users/Shared/Parallels/Windows 7.pvm") @@ -21,5 +21,5 @@ def test_parallels_child_detection(target_osx_users: Target, fs_osx: VirtualFile "/Users/Shared/Parallels/Windows 7.pvm", "/Users/dissect/Parallels/Windows 11.pvm", "/Users/dissect/Documents/Parallels/Windows 10.pvm", - "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm", + "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm", # noqa: E501 ] From 10243556118b0b9aa0d4453c8c750def02fd4165 Mon Sep 17 00:00:00 2001 From: JSCU-CNI <121175071+JSCU-CNI@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:04:05 +0200 Subject: [PATCH 5/5] attempt to fix test on windows --- tests/plugins/child/test_parallels.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/plugins/child/test_parallels.py b/tests/plugins/child/test_parallels.py index 887fe369e..2a1f17d21 100644 --- a/tests/plugins/child/test_parallels.py +++ b/tests/plugins/child/test_parallels.py @@ -6,12 +6,12 @@ def test_parallels_child_detection(target_osx_users: Target, fs_osx: VirtualFilesystem) -> None: """test if we correctly find Parallels child VMs on MacOS targets.""" - fs_osx.makedirs("/Users/dissect/Parallels/Windows 11.pvm") - fs_osx.makedirs("/Users/dissect/Documents/Parallels/Windows 10.pvm") + fs_osx.makedirs("Users/dissect/Parallels/Windows 11.pvm") + fs_osx.makedirs("Users/dissect/Documents/Parallels/Windows 10.pvm") fs_osx.makedirs( - "/Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm" # noqa: E501 + "Users/dissect/Library/Group Containers/someversionnumber.com.parallels.desktop.appstore/Shared/Parallels/Windows 8.pvm" # noqa: E501 ) - fs_osx.makedirs("/Users/Shared/Parallels/Windows 7.pvm") + fs_osx.makedirs("Users/Shared/Parallels/Windows 7.pvm") target_osx_users.add_plugin(ParallelsChildTargetPlugin) children = list(target_osx_users.list_children())