From 0d38bc0edf668290645d9cd8531399cb4db386c2 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Wed, 8 Feb 2023 12:23:25 -0700 Subject: [PATCH] kdk_handler.py: Add extra error handling to failed KDK install --- resources/gui/gui_main.py | 8 +-- resources/kdk_handler.py | 21 ++++-- resources/sys_patch/sys_patch.py | 87 ++++++++++++------------ resources/sys_patch/sys_patch_detect.py | 4 +- resources/sys_patch/sys_patch_helpers.py | 27 -------- 5 files changed, 62 insertions(+), 85 deletions(-) diff --git a/resources/gui/gui_main.py b/resources/gui/gui_main.py index 5cc273b4e..9f2b1051a 100644 --- a/resources/gui/gui_main.py +++ b/resources/gui/gui_main.py @@ -1350,12 +1350,12 @@ class wx_python_gui: logging.error(kdk_download_obj.error_msg) error_msg = kdk_download_obj.error_msg else: - kdk_result = kdk_obj.validate_kdk_checksum() - error_msg = kdk_obj.error_msg + kdk_result = self.kdk_obj.validate_kdk_checksum() + error_msg = self.kdk_obj.error_msg else: logging.error("Failed to download KDK") - logging.error(kdk_obj.error_msg) - error_msg = kdk_obj.error_msg + logging.error(self.kdk_obj.error_msg) + error_msg = self.kdk_obj.error_msg if kdk_result is False: # Create popup window to inform user of error diff --git a/resources/kdk_handler.py b/resources/kdk_handler.py index 4ce9ace24..3133876e0 100644 --- a/resources/kdk_handler.py +++ b/resources/kdk_handler.py @@ -256,7 +256,7 @@ class KernelDebugKitObject: self.success = True kdk_download_path = self.constants.kdk_download_path if override_path == "" else Path(override_path) - kdk_plist_path = Path(f"{kdk_download_path}/{KDK_INFO_PLIST}") if override_path == "" else Path(f"{Path(override_path).parent}/{KDK_INFO_PLIST}") + kdk_plist_path = Path(f"{kdk_download_path.parent}/{KDK_INFO_PLIST}") if override_path == "" else Path(f"{Path(override_path).parent}/{KDK_INFO_PLIST}") self._generate_kdk_info_plist(kdk_plist_path) return network_handler.DownloadObject(self.kdk_url, kdk_download_path) @@ -278,6 +278,7 @@ class KernelDebugKitObject: } try: + plist_path.touch() plistlib.dump(kdk_dict, plist_path.open("wb"), sort_keys=False) except Exception as e: logging.error(f"- Failed to generate KDK Info.plist: {e}") @@ -503,6 +504,9 @@ class KernelDebugKitUtilities: logging.warning("- Cannot install KDK, not running as root") return False + logging.info(f"- Installing KDK package: {kdk_path.name}") + logging.info(f" - This may take a while...") + result = utilities.elevated(["installer", "-pkg", kdk_path, "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if result.returncode != 0: logging.info("- Failed to install KDK:") @@ -529,7 +533,7 @@ class KernelDebugKitUtilities: logging.warning("- Cannot install KDK, not running as root") return False - logging.info(f"- Installing downloaded KDK (this may take a while)") + logging.info(f"- Extracting downloaded KDK disk image") with tempfile.TemporaryDirectory() as mount_point: result = subprocess.run(["hdiutil", "attach", kdk_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if result.returncode != 0: @@ -541,22 +545,27 @@ class KernelDebugKitUtilities: if not kdk_pkg_path.exists(): logging.warning("- Failed to find KDK package in DMG, likely corrupted!!!") + self._unmount_disk_image(mount_point) return False if self.install_kdk_pkg(kdk_pkg_path) is False: + self._unmount_disk_image(mount_point) return False self._create_backup(kdk_pkg_path, Path(f"{kdk_path.parent}/{KDK_INFO_PLIST}")) result = subprocess.run(["hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if result.returncode != 0: - # Non-fatal error - logging.info("- Failed to unmount KDK:") - logging.info(result.stdout.decode('utf-8')) + self._unmount_disk_image(mount_point) logging.info("- Successfully installed KDK") return True + def _unmount_disk_image(self, mount_point): + result = subprocess.run(["hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if result.returncode != 0: + logging.info("- Failed to unmount KDK:") + logging.info(result.stdout.decode('utf-8')) + def _create_backup(self, kdk_path: Path, kdk_info_plist: Path): """ diff --git a/resources/sys_patch/sys_patch.py b/resources/sys_patch/sys_patch.py index 6fe251afc..4b2c85cb4 100644 --- a/resources/sys_patch/sys_patch.py +++ b/resources/sys_patch/sys_patch.py @@ -113,56 +113,53 @@ class PatchSysVolume: return False - def invoke_kdk_handler(self): - # If we're invoked, there is no KDK installed (or something went wrong) - kdk_result = False - error_msg = "" - - kdk_obj = kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version) - - if kdk_obj.success is False: - error_msg = kdk_obj.error_msg - return kdk_result, error_msg, None - - kdk_download_obj = kdk_obj.retrieve_download() - - # We didn't get a download object, something's wrong - if not kdk_download_obj: - if kdk_obj.kdk_already_installed is True: - error_msg = "KDK already installed, function should not have been invoked" - return kdk_result, error_msg, None - else: - error_msg = "Could not retrieve KDK" - return kdk_result, error_msg, None - - # Hold thread until download is complete - kdk_download_obj.download(spawn_thread=False) - - if kdk_download_obj.download_complete is False: - error_msg = kdk_download_obj.error_msg - return kdk_result, error_msg, None - - kdk_result = kdk_obj.validate_kdk_checksum() - downloaded_kdk = self.constants.kdk_download_path - - return kdk_result, error_msg, downloaded_kdk - - def merge_kdk_with_root(self, save_hid_cs=False): if self.skip_root_kmutil_requirement is True: return if self.constants.detected_os < os_data.os_data.ventura: return - downloaded_kdk = None - kdk_path = sys_patch_helpers.sys_patch_helpers(self.constants).determine_kdk_present(match_closest=False) - if kdk_path is None: - if not self.constants.kdk_download_path.exists(): - kdk_result, error_msg, downloaded_kdk = self.invoke_kdk_handler() - if kdk_result is False: - raise Exception(f"Unable to download KDK: {error_msg}") - sys_patch_helpers.sys_patch_helpers(self.constants).install_kdk() - kdk_path = sys_patch_helpers.sys_patch_helpers(self.constants).determine_kdk_present(match_closest=True, override_build=downloaded_kdk) + if self.constants.kdk_download_path.exists(): + if kdk_handler.KernelDebugKitUtilities().install_kdk_dmg(self.constants.kdk_download_path) is False: + logging.info("Failed to install KDK") + raise Exception("Failed to install KDK") + + kdk_obj = kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version) + if kdk_obj.success is False: + logging.info(f"Unable to get KDK info: {kdk_obj.error_msg}") + raise Exception(f"Unable to get KDK info: {kdk_obj.error_msg}") + + if kdk_obj.kdk_already_installed is False: + + kdk_download_obj = kdk_obj.retrieve_download() + if not kdk_download_obj: + logging.info(f"Could not retrieve KDK: {kdk_obj.error_msg}") + + # Hold thread until download is complete + kdk_download_obj.download(spawn_thread=False) + + if kdk_download_obj.download_complete is False: + error_msg = kdk_download_obj.error_msg + logging.info(f"Could not download KDK: {error_msg}") + raise Exception(f"Could not download KDK: {error_msg}") + + if kdk_obj.validate_kdk_checksum() is False: + logging.info(f"KDK checksum validation failed: {kdk_obj.error_msg}") + raise Exception(f"KDK checksum validation failed: {kdk_obj.error_msg}") + + kdk_handler.KernelDebugKitUtilities().install_kdk_dmg(self.constants.kdk_download_path) + # re-init kdk_obj to get the new kdk_installed_path + kdk_obj = kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version) + if kdk_obj.success is False: + logging.info(f"Unable to get KDK info: {kdk_obj.error_msg}") + raise Exception(f"Unable to get KDK info: {kdk_obj.error_msg}") + + if kdk_obj.kdk_already_installed is False: + # We shouldn't get here, but just in case + logging.warning(f"KDK was not installed, but should have been: {kdk_obj.error_msg}") + raise Exception("KDK was not installed, but should have been: {kdk_obj.error_msg}") + + kdk_path = Path(kdk_obj.kdk_installed_path) if kdk_obj.kdk_installed_path != "" else None oclp_plist = Path("/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist") if (Path(self.mount_location) / Path("System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern")).exists() and oclp_plist.exists(): @@ -178,7 +175,7 @@ class PatchSysVolume: pass if kdk_path is None: - logging.info(f"- Unable to find Kernel Debug Kit: {downloaded_kdk}") + logging.info(f"- Unable to find Kernel Debug Kit") raise Exception("Unable to find Kernel Debug Kit") self.kdk_path = kdk_path logging.info(f"- Found KDK at: {kdk_path}") diff --git a/resources/sys_patch/sys_patch_detect.py b/resources/sys_patch/sys_patch_detect.py index c8886d752..a3a28a791 100644 --- a/resources/sys_patch/sys_patch_detect.py +++ b/resources/sys_patch/sys_patch_detect.py @@ -337,9 +337,7 @@ class detect_root_patch: return utilities.check_kext_loaded("WhateverGreen", self.constants.detected_os) def check_kdk(self): - if kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version, passive=True).kdk_already_installed == "": - return False - return True + return kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version, passive=True).kdk_already_installed def check_sip(self): if self.constants.detected_os > os_data.os_data.catalina: diff --git a/resources/sys_patch/sys_patch_helpers.py b/resources/sys_patch/sys_patch_helpers.py index a987172d2..9cbfe043c 100644 --- a/resources/sys_patch/sys_patch_helpers.py +++ b/resources/sys_patch/sys_patch_helpers.py @@ -77,33 +77,6 @@ class sys_patch_helpers: return True return False - def install_kdk(self): - if not self.constants.kdk_download_path.exists(): - return - - logging.info(f"- Installing downloaded KDK (this may take a while)") - with tempfile.TemporaryDirectory() as mount_point: - utilities.process_status(subprocess.run(["hdiutil", "attach", self.constants.kdk_download_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - # Due to a permissions bug in macOS, sometimes the OS will fail on a Read-only file system error - # We don't actually need to write inside the KDK DMG, however macOS will do whatever it wants - # Thus move the KDK to another location, and run the installer from there - kdk_dst_path = Path(f"{self.constants.payload_path}/KernelDebugKit.pkg") - if kdk_dst_path.exists(): - utilities.process_status(utilities.elevated(["rm", kdk_dst_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(subprocess.run(["cp", f"{mount_point}/KernelDebugKit.pkg", self.constants.payload_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - result = utilities.elevated(["installer", "-pkg", kdk_dst_path, "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if result.returncode != 0: - logging.info("- Failed to install KDK:") - logging.info(result.stdout.decode('utf-8')) - if result.stderr: - logging.info(result.stderr.decode('utf-8')) - utilities.elevated(["hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - raise Exception("Failed to install KDK") - utilities.process_status(utilities.elevated(["rm", kdk_dst_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.elevated(["hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - logging.info("- Successfully installed KDK") - - def disable_window_server_caching(self): # On legacy GCN GPUs, the WindowServer cache generated creates