From da32e9d4e37c0ed74ddffe7aaa7c07656da9682c Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Sat, 15 May 2021 18:29:42 -0600 Subject: [PATCH 01/18] Test Recovery picker --- OpenCore-Patcher.command | 2 +- Resources/SysPatch.py | 83 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/OpenCore-Patcher.command b/OpenCore-Patcher.command index 627741908..7398dc896 100755 --- a/OpenCore-Patcher.command +++ b/OpenCore-Patcher.command @@ -237,7 +237,7 @@ B. Exit response = menu.start() - if getattr(sys, "frozen", False): + if getattr(sys, "frozen", False) and self.constants.recovery_status is False: subprocess.run("""osascript -e 'tell application "Terminal" to close first window' & exit""", shell=True) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index a863036aa..028c33300 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -51,9 +51,88 @@ class PatchSysVolume: else: self.sip_patch_status = True + def recovery_root_mount(self): + def human_fmt(num): + for unit in ["B", "KB", "MB", "GB", "TB", "PB"]: + if abs(num) < 1000.0: + return "%3.1f %s" % (num, unit) + num /= 1000.0 + return "%.1f %s" % (num, "EB") + print("- Starting Root Volume Picker") + # Planned logic: + # Load "diskutil list -plist" + # Find all APFSVolumes entries where VolumeName is not named Update, VM, Recovery or Preboot + # Omit any VolumeName entries containing "- Data" + # Parse remianing options for macOS 11.x with /Volumes/$disk/System/Library/CoreServices/SystemVersion.plist + # List remaining drives as user options + all_disks = {} + disks = plistlib.loads(subprocess.run("diskutil list -plist".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + for disk in disks["AllDisksAndPartitions"]: + disk_info = plistlib.loads(subprocess.run(f"diskutil info -plist {disk['DeviceIdentifier']}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + try: + all_disks[disk["DeviceIdentifier"]] = {"identifier": disk_info["DeviceNode"], "name": disk_info["MediaName"], "size": disk_info["TotalSize"], "partitions": {}} + for partition in disk["Partitions"]: + partition_info = plistlib.loads(subprocess.run(f"diskutil info -plist {partition['DeviceIdentifier']}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + all_disks[disk["DeviceIdentifier"]]["partitions"][partition["DeviceIdentifier"]] = { + "fs": partition_info.get("FilesystemType", partition_info["Content"]), + "type": partition_info["Content"], + "name": partition_info.get("VolumeName", ""), + "size": partition_info["TotalSize"], + } + except KeyError: + # Avoid crashing with CDs installed + continue + menu = Utilities.TUIMenu( + ["Select Disk"], + "Please select the disk you would like to Patch: ", + in_between=["Missing disks? Ensure they have a macOS Big Sur install present."], + return_number_instead_of_direct_call=True, + loop=True, + ) + for disk in all_disks: + if not any(all_disks[disk]["partitions"][partition]["fs"] in ("apfs") for partition in all_disks[disk]["partitions"]) and \ + any(all_disks[disk]["partitions"][partition]["name"] not in (ModelArray.RecoveryIgnore) for partition in all_disks[disk]["partitions"]): + + continue + menu.add_menu_option(f"{disk}: {all_disks[disk]['name']} ({human_fmt(all_disks[disk]['size'])})", key=disk[4:]) + + response = menu.start() + + if response == -1: + return + + disk_identifier = "disk" + response + selected_disk = all_disks[disk_identifier] + + menu = Utilities.TUIMenu( + ["Select Partition"], + "Please select the partition you would like to install OpenCore to: ", + return_number_instead_of_direct_call=True, + loop=True, + in_between=["Missing disks? Ensure they have a macOS Big Sur install present.", "", "* denotes likely candidate."], + ) + for partition in selected_disk["partitions"]: + if selected_disk["partitions"][partition]["fs"] not in ("apfs"): + continue + text = f"{partition}: {selected_disk['partitions'][partition]['name']} ({human_fmt(selected_disk['partitions'][partition]['size'])})" + if selected_disk["partitions"][partition]["type"] not in ModelArray.RecoveryIgnore: + text += " *" + menu.add_menu_option(text, key=partition[len(disk_identifier) + 1:]) + + response = menu.start() + + if response == -1: + return + else: + return f"{disk_identifier}s{response}" + def find_mount_root_vol(self, patch): - root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) - self.root_mount_path = root_partition_info["DeviceIdentifier"] + + if self.constants.recovery_status is True: + print("- Running RecoveryOS logic") + else: + root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + self.root_mount_path = root_partition_info["DeviceIdentifier"] self.mount_location = "/System/Volumes/Update/mnt1" self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" self.mount_frameworks = f"{self.mount_location}/System/Library/Frameworks" From 771186e6cfe59b700ea4a51851b8f4a2201d5116 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Sat, 15 May 2021 18:58:25 -0600 Subject: [PATCH 02/18] Avoid using sudo and fdesetup calls in Recovery --- Resources/SysPatch.py | 99 ++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 028c33300..859fe9503 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -150,7 +150,10 @@ class PatchSysVolume: self.unpatch_root_vol() else: print("- Mounting drive as writable") - subprocess.run(["sudo", "mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() if Path(self.mount_extensions).exists(): print("- Successfully mounted the Root Volume") if patch is True: @@ -167,7 +170,10 @@ class PatchSysVolume: delete_path = Path(self.mount_extensions) / Path(delete_current_kext) if Path(delete_path).exists(): print(f"- Deleting {delete_current_kext}") - subprocess.run(["sudo", "rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() else: print(f"- Couldn't find {delete_current_kext}, skipping") @@ -176,19 +182,32 @@ class PatchSysVolume: existing_path = Path(self.mount_extensions) / Path(add_current_kext) if Path(existing_path).exists(): print(f"- Found conflicting kext, Deleting Root Volume's {add_current_kext}") - subprocess.run(["sudo", "rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() print(f"- Adding {add_current_kext}") - subprocess.run(["sudo", "cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def add_brightness_patch(self): print("- Merging legacy Brightness Control Patches") self.delete_old_binaries(ModelArray.DeleteBrightness) self.add_new_binaries(ModelArray.AddBrightness, self.constants.legacy_brightness) - subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def gpu_accel_patches_11(self): igpu_vendor,igpu_device,igpu_acpi = DeviceProbe.pci_probe().gpu_probe("IGPU") @@ -239,7 +258,10 @@ class PatchSysVolume: # Frameworks print("- Merging legacy Frameworks") - subprocess.run(["sudo", "ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() if self.model in ModelArray.LegacyBrightness: self.add_brightness_patch() @@ -247,16 +269,26 @@ class PatchSysVolume: # LaunchDaemons if Path(self.mount_lauchd / Path("HiddHack.plist")).exists(): print("- Removing legacy HiddHack") - subprocess.run(["sudo", "rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() print("- Adding IOHID-Fixup.plist") - subprocess.run(["sudo", "ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["sudo", "chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + subprocess.run(["chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() # PrivateFrameworks print("- Merging legacy PrivateFrameworks") - subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - + if self.constants.recovery_status is False: + subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() # Sets AppKit to Catalina Window Drawing codepath # Disabled upon ASentientBot request #print("- Enabling NSDefenestratorModeEnabled") @@ -270,7 +302,10 @@ class PatchSysVolume: # Perhaps a basic py2 script to run in recovery to restore # Ensures no .DS_Stores got in print("- Preparing Files") - subprocess.run(["sudo", "find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() if self.model in ModelArray.LegacyGPU or self.constants.assume_legacy is True: dgpu_vendor,dgpu_device,dgpu_acpi = DeviceProbe.pci_probe().gpu_probe("GFX0") @@ -294,13 +329,19 @@ class PatchSysVolume: def unpatch_root_vol(self): print("- Reverting to last signed APFS snapshot") - subprocess.run(["sudo", "bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def rebuild_snapshot(self): if self.constants.gui_mode is False: input("Press [ENTER] to continue with cache rebuild") print("- Rebuilding Kernel Cache (This may take some time)") - result = subprocess.run(["sudo", "kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if self.constants.recovery_status is False: + result = subprocess.run(["sudo", "kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + else: + result = subprocess.run(["kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if result.returncode != 0: self.success_status = False @@ -315,11 +356,17 @@ class PatchSysVolume: if self.constants.gui_mode is False: input("Press [ENTER] to continue with snapshotting") print("- Creating new APFS snapshot") - subprocess.run(["sudo", "bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def unmount_drive(self): print("- Unmounting Root Volume (Don't worry if this fails)") - subprocess.run(["sudo", "diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is False: + subprocess.run(["sudo", "diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + subprocess.run(["diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() def check_status(self): nvram_dump = plistlib.loads(subprocess.run("nvram -x -p".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) @@ -338,11 +385,13 @@ class PatchSysVolume: else: self.smb_status = False self.fv_status = True - self.fv_status: str = subprocess.run("fdesetup status".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() - if self.fv_status.startswith("FileVault is Off"): - self.fv_status = False + if self.constants.recovery_status == False: + self.fv_status: str = subprocess.run("fdesetup status".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() + if self.fv_status.startswith("FileVault is Off"): + self.fv_status = False else: - self.fv_status = True + # Assume FileVault is off for Recovery purposes + self.fv_status = False self.sip_patch_status = True self.csr_decode(self.sip_status, False) From 8f13906d2c68004dbdebce22f54741b396e31844 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Sat, 15 May 2021 19:09:25 -0600 Subject: [PATCH 03/18] Add disk picker --- Resources/SysPatch.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 859fe9503..b8ddff6e0 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -130,6 +130,11 @@ class PatchSysVolume: if self.constants.recovery_status is True: print("- Running RecoveryOS logic") + self.root_mount_path = self.recovery_root_mount() + print(f"- Root Mount Path: {self.root_mount_path}") + if not Path(self.mount_location).exists(): + print("- Creating mnt1 folder") + subprocess.run(["mkdir", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() else: root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) self.root_mount_path = root_partition_info["DeviceIdentifier"] From dd968a3244c5839aa068d44651a32059047bc0ba Mon Sep 17 00:00:00 2001 From: Dhinak G <17605561+dhinakg@users.noreply.github.com> Date: Wed, 19 May 2021 09:58:01 -0400 Subject: [PATCH 04/18] Fixes --- Resources/Build.py | 18 ++-- Resources/Constants.py | 24 ++--- Resources/DeviceProbe.py | 16 +-- Resources/SysPatch.py | 224 ++++++++++++++++++--------------------- Resources/Utilities.py | 34 +++--- 5 files changed, 142 insertions(+), 174 deletions(-) diff --git a/Resources/Build.py b/Resources/Build.py index c3555d58d..484c575f8 100644 --- a/Resources/Build.py +++ b/Resources/Build.py @@ -35,12 +35,6 @@ class BuildOpenCore: self.config = None self.constants: Constants.Constants = versions - def hexswap(self, input_hex: str): - hex_pairs = [input_hex[i:i + 2] for i in range(0, len(input_hex), 2)] - hex_rev = hex_pairs[::-1] - hex_str = "".join(["".join(x) for x in hex_rev]) - return hex_str.upper() - def build_efi(self): Utilities.cls() if not self.constants.custom_model: @@ -119,8 +113,8 @@ class BuildOpenCore: try: x = 1 for i in storage_devices: - storage_vendor = self.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) - storage_device = self.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) + storage_vendor = Utilities.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) + storage_device = Utilities.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) print(f'- Fixing PCIe Storage Controller ({x}) reporting') try: storage_path = [line.strip().split("= ", 1)[1] for line in storage_path_gfx.split("\n") if f'{storage_vendor}:{storage_device}'.lower() in line.strip()][0] @@ -141,8 +135,8 @@ class BuildOpenCore: try: x = 1 for i in nvme_devices: - nvme_vendor = self.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) - nvme_device = self.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) + nvme_vendor = Utilities.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) + nvme_device = Utilities.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) print(f'- Found 3rd Party NVMe SSD ({x}): {nvme_vendor}:{nvme_device}') nvme_aspm = i["pci-aspm-default"] try: @@ -435,8 +429,8 @@ class BuildOpenCore: try: x = 1 for i in mp_dgpu_devices: - mp_dgpu_vendor = self.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) - mp_dgpu_device = self.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) + mp_dgpu_vendor = Utilities.hexswap(binascii.hexlify(i["vendor-id"]).decode()[:4]) + mp_dgpu_device = Utilities.hexswap(binascii.hexlify(i["device-id"]).decode()[:4]) print(f'- Found dGPU ({x}): {mp_dgpu_vendor}:{mp_dgpu_device}') self.config["#Revision"][f"Hardware-MacPro-dGPU-{x}"] = f'{mp_dgpu_vendor}:{mp_dgpu_device}' diff --git a/Resources/Constants.py b/Resources/Constants.py index 130ecf1cb..599b7e24b 100644 --- a/Resources/Constants.py +++ b/Resources/Constants.py @@ -335,18 +335,18 @@ class Constants: def skylight_path(self): return self.payload_apple_private_frameworks_path_accel / Path("SkyLight.framework") csr_values = { - "CSR_ALLOW_UNTRUSTED_KEXTS ": False, # 0x1 - Introduced in El Capitan - "CSR_ALLOW_UNRESTRICTED_FS ": False, # 0x2 - Introduced in El Capitan - "CSR_ALLOW_TASK_FOR_PID ": False, # 0x4 - Introduced in El Capitan - "CSR_ALLOW_KERNEL_DEBUGGER ": False, # 0x8 - Introduced in El Capitan - "CSR_ALLOW_APPLE_INTERNAL ": False, # 0x10 - Introduced in El Capitan - "CSR_ALLOW_UNRESTRICTED_DTRACE ": False, # 0x20 - Introduced in El Capitan - "CSR_ALLOW_UNRESTRICTED_NVRAM ": False, # 0x40 - Introduced in El Capitan - "CSR_ALLOW_DEVICE_CONFIGURATION ": False, # 0x80 - Introduced in El Capitan - "CSR_ALLOW_ANY_RECOVERY_OS ": False, # 0x100 - Introduced in Sierra - "CSR_ALLOW_UNAPPROVED_KEXTS ": False, # 0x200 - Introduced in High Sierra - "CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE": False, # 0x400 - Introduced in Mojave - "CSR_ALLOW_UNAUTHENTICATED_ROOT ": False, # 0x800 - Introduced in Big Sur + "CSR_ALLOW_UNTRUSTED_KEXTS": False, # 0x1 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_UNRESTRICTED_FS": False, # 0x2 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_TASK_FOR_PID": False, # 0x4 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_KERNEL_DEBUGGER": False, # 0x8 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_APPLE_INTERNAL": False, # 0x10 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_UNRESTRICTED_DTRACE": False, # 0x20 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_UNRESTRICTED_NVRAM": False, # 0x40 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_DEVICE_CONFIGURATION": False, # 0x80 - Introduced in El Capitan # noqa: E241 + "CSR_ALLOW_ANY_RECOVERY_OS": False, # 0x100 - Introduced in Sierra # noqa: E241 + "CSR_ALLOW_UNAPPROVED_KEXTS": False, # 0x200 - Introduced in High Sierra # noqa: E241 + "CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE": False, # 0x400 - Introduced in Mojave # noqa: E241 + "CSR_ALLOW_UNAUTHENTICATED_ROOT": False, # 0x800 - Introduced in Big Sur # noqa: E241 } sbm_values = [ diff --git a/Resources/DeviceProbe.py b/Resources/DeviceProbe.py index f401d81ee..952eb6190 100644 --- a/Resources/DeviceProbe.py +++ b/Resources/DeviceProbe.py @@ -12,12 +12,6 @@ class pci_probe: def __init__(self): self.constants = Constants.Constants() - def hexswap(self, input_hex: str): - hex_pairs = [input_hex[i:i + 2] for i in range(0, len(input_hex), 2)] - hex_rev = hex_pairs[::-1] - hex_str = "".join(["".join(x) for x in hex_rev]) - return hex_str.upper() - # Converts given device IDs to DeviceProperty pathing, requires ACPI pathing as DeviceProperties shouldn't be used otherwise def deviceproperty_probe(self, vendor_id, device_id, acpi_path): gfxutil_output: str = subprocess.run([self.constants.gfxutil_path] + f"-v".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() @@ -55,8 +49,8 @@ class pci_probe: def gpu_probe(self, gpu_type): try: devices = plistlib.loads(subprocess.run(f"ioreg -r -n {gpu_type} -a".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) - vendor_id = self.hexswap(binascii.hexlify(devices[0]["vendor-id"]).decode()[:4]) - device_id = self.hexswap(binascii.hexlify(devices[0]["device-id"]).decode()[:4]) + vendor_id = Utilities.hexswap(binascii.hexlify(devices[0]["vendor-id"]).decode()[:4]) + device_id = Utilities.hexswap(binascii.hexlify(devices[0]["device-id"]).decode()[:4]) try: acpi_path = devices[0]["acpi-path"] acpi_path = self.acpi_strip(acpi_path) @@ -69,14 +63,14 @@ class pci_probe: return "", "", "" except IndexError: print(f"- No IOService entry found for {gpu_type} (I)") - return "", "", "", "" + return "", "", "" def wifi_probe(self): devices = plistlib.loads(subprocess.run("ioreg -c IOPCIDevice -r -d2 -a".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) try: devices = [i for i in devices if i["class-code"] == binascii.unhexlify(self.constants.classcode_wifi)] - vendor_id = self.hexswap(binascii.hexlify(devices[0]["vendor-id"]).decode()[:4]) - device_id = self.hexswap(binascii.hexlify(devices[0]["device-id"]).decode()[:4]) + vendor_id = Utilities.hexswap(binascii.hexlify(devices[0]["vendor-id"]).decode()[:4]) + device_id = Utilities.hexswap(binascii.hexlify(devices[0]["device-id"]).decode()[:4]) ioname = devices[0]["IOName"] try: acpi_path = devices[0]["acpi-path"] diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index b8ddff6e0..52fb4ab90 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -4,31 +4,41 @@ # - Full System/Library Snapshotting (need to research how Apple achieves this) # - Temporary Work-around: sudo bless --mount /System/Volumes/Update/mnt1 --bootefi --last-sealed-snapshot # - Work-around battery throttling on laptops with no battery (IOPlatformPluginFamily.kext/Contents/PlugIns/ACPI_SMC_PlatformPlugin.kext/Contents/Resources/) -from __future__ import print_function +import os import plistlib import shutil import subprocess import zipfile -import os from pathlib import Path +from typing import Any -from Resources import Constants, ModelArray, PCIIDArray, Utilities, DeviceProbe +from Resources import Constants, DeviceProbe, ModelArray, PCIIDArray, Utilities class PatchSysVolume: def __init__(self, model, versions): self.model = model self.constants: Constants.Constants = versions + self.sip_patch_status = True + self.root_mount_path = None + self.sip_status = None - def hexswap(self, input_hex: str): - hex_pairs = [input_hex[i:i + 2] for i in range(0, len(input_hex), 2)] - hex_rev = hex_pairs[::-1] - hex_str = "".join(["".join(x) for x in hex_rev]) - return hex_str.upper() + # TODO: Put this in a better place + self.mount_location = "/System/Volumes/Update/mnt1" + self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" + self.mount_frameworks = f"{self.mount_location}/System/Library/Frameworks" + self.mount_lauchd = f"{self.mount_location}/System/Library/LaunchDaemons" + self.mount_private_frameworks = f"{self.mount_location}/System/Library/PrivateFrameworks" - def csr_decode(self, sip_raw, print_status): - sip_int = int.from_bytes(sip_raw, byteorder='little') + def elevated(self, *args, **kwargs) -> subprocess.CompletedProcess[Any]: + if os.getuid() == 0: + return subprocess.run(*args, **kwargs) + else: + return subprocess.run(["sudo"] + args[0] + args[1:], **kwargs) + + def csr_decode(self, print_status): + sip_int = int.from_bytes(self.sip_status, byteorder="little") i = 0 for current_sip_bit in self.constants.csr_values: if sip_int & (1 << i): @@ -39,17 +49,20 @@ class PatchSysVolume: if print_status is True: print(f"- {current_sip_bit}\t {temp}") i = i + 1 - if ((self.constants.csr_values["CSR_ALLOW_UNTRUSTED_KEXTS "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_UNRESTRICTED_FS "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_UNRESTRICTED_DTRACE "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_UNRESTRICTED_NVRAM "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_DEVICE_CONFIGURATION "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_UNAPPROVED_KEXTS "] is True) \ - and (self.constants.csr_values["CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE"] is True) \ - and (self.constants.csr_values["CSR_ALLOW_UNAUTHENTICATED_ROOT "] is True)): - self.sip_patch_status = False - else: - self.sip_patch_status = True + + self.sip_patch_status = all( + self.constants.csr_values[i] + for i in [ + "CSR_ALLOW_UNTRUSTED_KEXTS", + "CSR_ALLOW_UNRESTRICTED_FS", + "CSR_ALLOW_UNRESTRICTED_DTRACE", + "CSR_ALLOW_UNRESTRICTED_NVRAM", + "CSR_ALLOW_DEVICE_CONFIGURATION", + "CSR_ALLOW_UNAPPROVED_KEXTS", + "CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE", + "CSR_ALLOW_UNAUTHENTICATED_ROOT", + ] + ) def recovery_root_mount(self): def human_fmt(num): @@ -58,6 +71,7 @@ class PatchSysVolume: return "%3.1f %s" % (num, unit) num /= 1000.0 return "%.1f %s" % (num, "EB") + print("- Starting Root Volume Picker") # Planned logic: # Load "diskutil list -plist" @@ -71,28 +85,27 @@ class PatchSysVolume: disk_info = plistlib.loads(subprocess.run(f"diskutil info -plist {disk['DeviceIdentifier']}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) try: all_disks[disk["DeviceIdentifier"]] = {"identifier": disk_info["DeviceNode"], "name": disk_info["MediaName"], "size": disk_info["TotalSize"], "partitions": {}} - for partition in disk["Partitions"]: + for partition in disk["Partitions"] + disk.get("APFSVolumes", []): partition_info = plistlib.loads(subprocess.run(f"diskutil info -plist {partition['DeviceIdentifier']}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) all_disks[disk["DeviceIdentifier"]]["partitions"][partition["DeviceIdentifier"]] = { "fs": partition_info.get("FilesystemType", partition_info["Content"]), "type": partition_info["Content"], "name": partition_info.get("VolumeName", ""), "size": partition_info["TotalSize"], + "sealed": partition_info.get("Sealed", "No"), } except KeyError: # Avoid crashing with CDs installed continue menu = Utilities.TUIMenu( ["Select Disk"], - "Please select the disk you would like to Patch: ", + "Please select the disk you would like to patch: ", in_between=["Missing disks? Ensure they have a macOS Big Sur install present."], return_number_instead_of_direct_call=True, loop=True, ) for disk in all_disks: - if not any(all_disks[disk]["partitions"][partition]["fs"] in ("apfs") for partition in all_disks[disk]["partitions"]) and \ - any(all_disks[disk]["partitions"][partition]["name"] not in (ModelArray.RecoveryIgnore) for partition in all_disks[disk]["partitions"]): - + if not any(all_disks[disk]["partitions"][partition]["fs"] == "apfs" for partition in all_disks[disk]["partitions"]): continue menu.add_menu_option(f"{disk}: {all_disks[disk]['name']} ({human_fmt(all_disks[disk]['size'])})", key=disk[4:]) @@ -111,13 +124,14 @@ class PatchSysVolume: loop=True, in_between=["Missing disks? Ensure they have a macOS Big Sur install present.", "", "* denotes likely candidate."], ) + # TODO: check if Big Sur, when macOS 12 comes out for partition in selected_disk["partitions"]: - if selected_disk["partitions"][partition]["fs"] not in ("apfs"): + if selected_disk["partitions"][partition]["fs"] != "apfs": continue text = f"{partition}: {selected_disk['partitions'][partition]['name']} ({human_fmt(selected_disk['partitions'][partition]['size'])})" - if selected_disk["partitions"][partition]["type"] not in ModelArray.RecoveryIgnore: + if selected_disk["partitions"][partition]["sealed"] != "No": text += " *" - menu.add_menu_option(text, key=partition[len(disk_identifier) + 1:]) + menu.add_menu_option(text, key=partition[len(disk_identifier) + 1 :]) response = menu.start() @@ -127,10 +141,11 @@ class PatchSysVolume: return f"{disk_identifier}s{response}" def find_mount_root_vol(self, patch): - if self.constants.recovery_status is True: print("- Running RecoveryOS logic") self.root_mount_path = self.recovery_root_mount() + if not self.root_mount_path: + return print(f"- Root Mount Path: {self.root_mount_path}") if not Path(self.mount_location).exists(): print("- Creating mnt1 folder") @@ -138,14 +153,9 @@ class PatchSysVolume: else: root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) self.root_mount_path = root_partition_info["DeviceIdentifier"] - self.mount_location = "/System/Volumes/Update/mnt1" - self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" - self.mount_frameworks = f"{self.mount_location}/System/Library/Frameworks" - self.mount_lauchd = f"{self.mount_location}/System/Library/LaunchDaemons" - self.mount_private_frameworks = f"{self.mount_location}/System/Library/PrivateFrameworks" if self.root_mount_path.startswith("disk"): - self.root_mount_path = self.root_mount_path[:-2] if self.root_mount_path.endswith('s1') else self.root_mount_path + self.root_mount_path = self.root_mount_path[:-2] if self.root_mount_path.count("s") > 1 else self.root_mount_path print(f"- Found Root Volume at: {self.root_mount_path}") if Path(self.mount_extensions).exists(): print("- Root Volume is already mounted") @@ -155,10 +165,7 @@ class PatchSysVolume: self.unpatch_root_vol() else: print("- Mounting drive as writable") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() if Path(self.mount_extensions).exists(): print("- Successfully mounted the Root Volume") if patch is True: @@ -175,10 +182,7 @@ class PatchSysVolume: delete_path = Path(self.mount_extensions) / Path(delete_current_kext) if Path(delete_path).exists(): print(f"- Deleting {delete_current_kext}") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["sudo", "rm", "-R", delete_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() else: print(f"- Couldn't find {delete_current_kext}, skipping") @@ -187,36 +191,23 @@ class PatchSysVolume: existing_path = Path(self.mount_extensions) / Path(add_current_kext) if Path(existing_path).exists(): print(f"- Found conflicting kext, Deleting Root Volume's {add_current_kext}") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["rm", "-R", existing_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() print(f"- Adding {add_current_kext}") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def add_brightness_patch(self): print("- Merging legacy Brightness Control Patches") self.delete_old_binaries(ModelArray.DeleteBrightness) self.add_new_binaries(ModelArray.AddBrightness, self.constants.legacy_brightness) - if self.constants.recovery_status is False: - subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["ditto", self.constants.payload_apple_private_frameworks_path_brightness, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def gpu_accel_patches_11(self): - igpu_vendor,igpu_device,igpu_acpi = DeviceProbe.pci_probe().gpu_probe("IGPU") - dgpu_vendor,dgpu_device,dgpu_acpi = DeviceProbe.pci_probe().gpu_probe("GFX0") + igpu_vendor, igpu_device, igpu_acpi = DeviceProbe.pci_probe().gpu_probe("IGPU") + dgpu_vendor, dgpu_device, dgpu_acpi = DeviceProbe.pci_probe().gpu_probe("GFX0") if dgpu_vendor: print(f"- Found GFX0: {dgpu_vendor}:{dgpu_device}") if dgpu_vendor == self.constants.pci_nvidia: @@ -226,7 +217,7 @@ class PatchSysVolume: self.add_new_binaries(ModelArray.AddGeneralAccel, self.constants.legacy_general_path) self.add_new_binaries(ModelArray.AddNvidiaAccel11, self.constants.legacy_nvidia_path) # TODO: Enable below code if macOS 12 drops support - #elif dgpu_device in PCIIDArray.nvidia_ids().kepler_ids and self.constants.detected_os > self.constants.big_sur: + # elif dgpu_device in PCIIDArray.nvidia_ids().kepler_ids and self.constants.detected_os > self.constants.big_sur: # print("- Merging legacy Nvidia Kepler Kexts and Bundles") # self.add_new_binaries(ModelArray.AddNvidiaKeplerAccel11, self.constants.legacy_nvidia_kepler_path) elif dgpu_vendor == self.constants.pci_amd_ati: @@ -250,7 +241,7 @@ class PatchSysVolume: self.add_new_binaries(ModelArray.AddIntelGen2Accel, self.constants.legacy_intel_gen2_path) # TODO: Enable below code if macOS 12 drops support - #elif igpu_device in PCIIDArray.intel_ids().ivy_ids: + # elif igpu_device in PCIIDArray.intel_ids().ivy_ids: # print("- Merging legacy Intel 3rd Gen Kexts and Bundles") # self.add_new_binaries(ModelArray.AddIntelGen3Accel, self.constants.legacy_intel_gen3_path) elif igpu_vendor == self.constants.pci_nvidia: @@ -263,10 +254,7 @@ class PatchSysVolume: # Frameworks print("- Merging legacy Frameworks") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["ditto", self.constants.payload_apple_frameworks_path_accel, self.mount_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() if self.model in ModelArray.LegacyBrightness: self.add_brightness_patch() @@ -274,30 +262,19 @@ class PatchSysVolume: # LaunchDaemons if Path(self.mount_lauchd / Path("HiddHack.plist")).exists(): print("- Removing legacy HiddHack") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["rm", f"{self.mount_lauchd}/HiddHack.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() print("- Adding IOHID-Fixup.plist") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["sudo", "chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - subprocess.run(["chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["ditto", self.constants.payload_apple_lauchd_path_accel, self.mount_lauchd], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chmod", "755", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["chown", "root:wheel", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE).stdout.decode().strip().encode() # PrivateFrameworks print("- Merging legacy PrivateFrameworks") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["ditto", self.constants.payload_apple_private_frameworks_path_accel, self.mount_private_frameworks], stdout=subprocess.PIPE).stdout.decode().strip().encode() # Sets AppKit to Catalina Window Drawing codepath # Disabled upon ASentientBot request - #print("- Enabling NSDefenestratorModeEnabled") - #subprocess.run("defaults write -g NSDefenestratorModeEnabled -bool true".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode() + # print("- Enabling NSDefenestratorModeEnabled") + # subprocess.run("defaults write -g NSDefenestratorModeEnabled -bool true".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode() def patch_root_vol(self): print(f"- Detecting patches for {self.model}") @@ -307,14 +284,20 @@ class PatchSysVolume: # Perhaps a basic py2 script to run in recovery to restore # Ensures no .DS_Stores got in print("- Preparing Files") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["find", self.constants.payload_apple_root_path, "-name", "'.DS_Store'", "-delete"], stdout=subprocess.PIPE).stdout.decode().strip().encode() if self.model in ModelArray.LegacyGPU or self.constants.assume_legacy is True: - dgpu_vendor,dgpu_device,dgpu_acpi = DeviceProbe.pci_probe().gpu_probe("GFX0") - if dgpu_vendor and dgpu_vendor == self.constants.pci_amd_ati and (dgpu_device in PCIIDArray.amd_ids().polaris_ids or dgpu_device in PCIIDArray.amd_ids().vega_ids or dgpu_device in PCIIDArray.amd_ids().navi_ids or dgpu_device in PCIIDArray.amd_ids().legacy_gcn_ids): + dgpu_vendor, dgpu_device, dgpu_acpi = DeviceProbe.pci_probe().gpu_probe("GFX0") + if ( + dgpu_vendor + and dgpu_vendor == self.constants.pci_amd_ati + and ( + dgpu_device in PCIIDArray.amd_ids().polaris_ids + or dgpu_device in PCIIDArray.amd_ids().vega_ids + or dgpu_device in PCIIDArray.amd_ids().navi_ids + or dgpu_device in PCIIDArray.amd_ids().legacy_gcn_ids + ) + ): print("- Detected Metal-based AMD GPU, skipping legacy patches") elif dgpu_vendor and dgpu_vendor == self.constants.pci_nvidia and dgpu_device in PCIIDArray.nvidia_ids().kepler_ids: print("- Detected Metal-based Nvidia GPU, skipping legacy patches") @@ -334,19 +317,13 @@ class PatchSysVolume: def unpatch_root_vol(self): print("- Reverting to last signed APFS snapshot") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def rebuild_snapshot(self): if self.constants.gui_mode is False: input("Press [ENTER] to continue with cache rebuild") print("- Rebuilding Kernel Cache (This may take some time)") - if self.constants.recovery_status is False: - result = subprocess.run(["sudo", "kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - else: - result = subprocess.run(["kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + result = self.elevated(["kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if result.returncode != 0: self.success_status = False @@ -361,24 +338,18 @@ class PatchSysVolume: if self.constants.gui_mode is False: input("Press [ENTER] to continue with snapshotting") print("- Creating new APFS snapshot") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["bless", "--folder", f"{self.mount_location}/System/Library/CoreServices", "--bootefi", "--create-snapshot"], stdout=subprocess.PIPE).stdout.decode().strip().encode() def unmount_drive(self): print("- Unmounting Root Volume (Don't worry if this fails)") - if self.constants.recovery_status is False: - subprocess.run(["sudo", "diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() - else: - subprocess.run(["diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() + self.elevated(["diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode() def check_status(self): nvram_dump = plistlib.loads(subprocess.run("nvram -x -p".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) try: self.sip_status = nvram_dump["csr-active-config"] except KeyError: - self.sip_status = b'\x00\x00\x00\x00' + self.sip_status = b"\x00\x00\x00\x00" self.smb_model: str = subprocess.run("nvram 94B73556-2197-4702-82A8-3E1337DAFBFB:HardwareModel ".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() if not self.smb_model.startswith("nvram: Error getting variable"): @@ -397,8 +368,7 @@ class PatchSysVolume: else: # Assume FileVault is off for Recovery purposes self.fv_status = False - self.sip_patch_status = True - self.csr_decode(self.sip_status, False) + self.csr_decode(False) def check_files(self): if Path(self.constants.payload_apple_root_path).exists(): @@ -417,7 +387,13 @@ class PatchSysVolume: def download_files(self): Utilities.cls() print("- Downloading Apple binaries") - popen_oclp = subprocess.Popen(["curl", "-S", "-L", f"{self.constants.url_apple_binaries}{self.constants.payload_version}.zip", "--output", self.constants.payload_apple_root_path_zip], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + popen_oclp = subprocess.Popen( + ["curl", "-S", "-L", f"{self.constants.url_apple_binaries}{self.constants.payload_version}.zip", "--output", self.constants.payload_apple_root_path_zip], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) for stdout_line in iter(popen_oclp.stdout.readline, ""): print(stdout_line, end="") popen_oclp.stdout.close() @@ -449,17 +425,18 @@ class PatchSysVolume: elif self.model not in ModelArray.SupportedSMBIOS11 and self.constants.assume_legacy is False: print("Cannot run on this machine, model is unsupported!") elif self.constants.detected_os < self.constants.big_sur: - print(f"Cannot run on this OS, requires macOS 11!") + print("Cannot run on this OS, requires macOS 11!") else: self.check_status() Utilities.cls() - if (self.sip_patch_status is False) and (self.smb_status is False): + if True or (self.sip_patch_status is False) and (self.smb_status is False): print("- Detected SIP and SecureBootModel are disabled, continuing") if self.constants.gui_mode is False: input("\nPress [ENTER] to continue") self.check_files() if self.constants.payload_apple_root_path.exists(): - self.find_mount_root_vol(True) + if not self.find_mount_root_vol(True): + return self.unmount_drive() print("- Patching complete") if self.success_status is True: @@ -469,7 +446,7 @@ class PatchSysVolume: if self.sip_patch_status is True: print("SIP set incorrectly, cannot patch on this machine!") print("Please disable SIP and SecureBootModel in Patcher Settings") - self.csr_decode(self.sip_status, True) + self.csr_decode(True) print("") if self.smb_status is True: print("SecureBootModel set incorrectly, unable to patch!") @@ -486,7 +463,7 @@ class PatchSysVolume: if self.constants.custom_model is not None: print("Unpatching must be done on target machine!") elif self.constants.detected_os < self.constants.big_sur: - print(f"Cannot run on this OS, requires macOS 11!") + print("Cannot run on this OS, requires macOS 11!") else: self.check_status() Utilities.cls() @@ -494,14 +471,15 @@ class PatchSysVolume: print("- Detected SIP and SecureBootModel are disabled, continuing") if self.constants.gui_mode is False: input("\nPress [ENTER] to continue") - self.find_mount_root_vol(False) + if not self.find_mount_root_vol(False): + return self.unmount_drive() print("- Unpatching complete") print("\nPlease reboot the machine for patches to take effect") if self.sip_patch_status is True: print("SIP set incorrectly, cannot unpatch on this machine!") print("Please disable SIP and SecureBootModel in Patcher Settings") - self.csr_decode(self.sip_status, True) + self.csr_decode(True) print("") if self.smb_status is True: print("SecureBootModel set incorrectly, unable to unpatch!") diff --git a/Resources/Utilities.py b/Resources/Utilities.py index ba602aebc..27086d81f 100644 --- a/Resources/Utilities.py +++ b/Resources/Utilities.py @@ -2,36 +2,39 @@ from __future__ import print_function import os -import math as m +import math import plistlib import subprocess + +def hexswap(input_hex: str): + hex_pairs = [input_hex[i : i + 2] for i in range(0, len(input_hex), 2)] + hex_rev = hex_pairs[::-1] + hex_str = "".join(["".join(x) for x in hex_rev]) + return hex_str.upper() + + def header(lines): lines = [i for i in lines if i is not None] total_length = len(max(lines, key=len)) + 4 print("#" * (total_length)) for line in lines: - left_side = m.floor(((total_length - 2 - len(line.strip())) / 2)) + left_side = math.floor(((total_length - 2 - len(line.strip())) / 2)) print("#" + " " * left_side + line.strip() + " " * (total_length - len("#" + " " * left_side + line.strip()) - 1) + "#") print("#" * total_length) + def check_recovery(): root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) - if root_partition_info["VolumeName"] == "macOS Base System" and \ - root_partition_info["FilesystemType"] == "apfs" and \ - root_partition_info["BusProtocol"] == "Disk Image": + if root_partition_info["VolumeName"] == "macOS Base System" and root_partition_info["FilesystemType"] == "apfs" and root_partition_info["BusProtocol"] == "Disk Image": return True else: return False + def cls(): - # RecoveryOS doesn't support terminal clearing - if check_recovery() == False: - os.system('cls' if os.name == 'nt' else 'clear') - else: - # Default terminal window is 24 lines tall - for i in range(24): - print("") + # We only support macOS, so + print("\u001Bc") # def menu(title, prompt, menu_options, add_quit=True, auto_number=False, in_between=[], top_level=False): # return_option = ["Q", "Quit", None] if top_level else ["B", "Back", None] @@ -61,7 +64,7 @@ def cls(): # menu_options[keys.index(selected.upper())][2]() if menu_options[keys.index(selected.upper())][2] else None -class TUIMenu(): +class TUIMenu: def __init__(self, title, prompt, options=None, return_number_instead_of_direct_call=False, add_quit=True, auto_number=False, in_between=None, top_level=False, loop=False): self.title = title self.prompt = prompt @@ -80,8 +83,7 @@ class TUIMenu(): def start(self): return_option = ["Q", "Quit"] if self.top_level else ["B", "Back"] if self.add_quit and not self.added_quit: - self.add_menu_option( - return_option[1], function=None, key=return_option[0]) + self.add_menu_option(return_option[1], function=None, key=return_option[0]) self.added_quit = True while True: @@ -120,7 +122,7 @@ class TUIMenu(): return -class TUIOnlyPrint(): +class TUIOnlyPrint: def __init__(self, title, prompt, in_between=None): self.title = title self.prompt = prompt From 88903e8edf1a79a05858e9c6b568efafa369b747 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 09:07:18 -0600 Subject: [PATCH 05/18] Fix import --- Resources/DeviceProbe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/DeviceProbe.py b/Resources/DeviceProbe.py index 952eb6190..d37a37efe 100644 --- a/Resources/DeviceProbe.py +++ b/Resources/DeviceProbe.py @@ -6,7 +6,7 @@ import binascii import plistlib import subprocess -from Resources import Constants +from Resources import Constants, Utilities class pci_probe: def __init__(self): @@ -84,4 +84,4 @@ class pci_probe: return "", "", "", "" except IndexError: print(f"- No IOService entry found for Wireless Card (I)") - return "", "", "", "" \ No newline at end of file + return "", "", "", "" From db39757655fd28dca9f84284c984224fe91ac9b5 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 09:16:02 -0600 Subject: [PATCH 06/18] Fix elevation crash --- Resources/SysPatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 52fb4ab90..6bdb10a17 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -35,7 +35,7 @@ class PatchSysVolume: if os.getuid() == 0: return subprocess.run(*args, **kwargs) else: - return subprocess.run(["sudo"] + args[0] + args[1:], **kwargs) + return subprocess.run(["sudo"] + [args[0][0]] + args[0][1:], **kwargs) def csr_decode(self, print_status): sip_int = int.from_bytes(self.sip_status, byteorder="little") From 9b316bbbca4881fadb3c8528b20366e84d8cc35a Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 09:28:20 -0600 Subject: [PATCH 07/18] Fix crash on APFS snapshotting --- Resources/SysPatch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 6bdb10a17..274715343 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -161,8 +161,10 @@ class PatchSysVolume: print("- Root Volume is already mounted") if patch is True: self.patch_root_vol() + return True else: self.unpatch_root_vol() + return True else: print("- Mounting drive as writable") self.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() @@ -170,8 +172,10 @@ class PatchSysVolume: print("- Successfully mounted the Root Volume") if patch is True: self.patch_root_vol() + return True else: self.unpatch_root_vol() + return True else: print("- Failed to mount the Root Volume") else: From f7f66cd732e104d955b96be182d8ef691e3d6de5 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 09:51:30 -0600 Subject: [PATCH 08/18] Fix SIP parsing --- Resources/SysPatch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 274715343..c0fd70af7 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -50,7 +50,7 @@ class PatchSysVolume: print(f"- {current_sip_bit}\t {temp}") i = i + 1 - self.sip_patch_status = all( + self.sip_needs_change = all( self.constants.csr_values[i] for i in [ "CSR_ALLOW_UNTRUSTED_KEXTS", @@ -63,6 +63,10 @@ class PatchSysVolume: "CSR_ALLOW_UNAUTHENTICATED_ROOT", ] ) + if sip_needs_change is True: + self.sip_patch_status = False + else: + self.sip_patch_status = True def recovery_root_mount(self): def human_fmt(num): @@ -433,7 +437,7 @@ class PatchSysVolume: else: self.check_status() Utilities.cls() - if True or (self.sip_patch_status is False) and (self.smb_status is False): + if (self.sip_patch_status is False) and (self.smb_status is False): print("- Detected SIP and SecureBootModel are disabled, continuing") if self.constants.gui_mode is False: input("\nPress [ENTER] to continue") From a6ccd2867149c6772a5495fc555e3ac166d24563 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 09:58:37 -0600 Subject: [PATCH 09/18] Update SysPatch.py --- Resources/SysPatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index c0fd70af7..4191f8bef 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -49,8 +49,8 @@ class PatchSysVolume: if print_status is True: print(f"- {current_sip_bit}\t {temp}") i = i + 1 - - self.sip_needs_change = all( + + sip_needs_change = all( self.constants.csr_values[i] for i in [ "CSR_ALLOW_UNTRUSTED_KEXTS", From 9a68ce67eb0dd91e05df9b4306239727ddd9f78c Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 10:48:10 -0600 Subject: [PATCH 10/18] Update SysPatch.py --- Resources/SysPatch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 4191f8bef..a713c3c7b 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -151,9 +151,10 @@ class PatchSysVolume: if not self.root_mount_path: return print(f"- Root Mount Path: {self.root_mount_path}") - if not Path(self.mount_location).exists(): + if not Path(self.constants.payload_mnt1_path).exists(): print("- Creating mnt1 folder") - subprocess.run(["mkdir", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + Path(self.constants.payload_mnt1_path).mkdir() + self.mount_location = self.constants.payload_mnt1_path else: root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) self.root_mount_path = root_partition_info["DeviceIdentifier"] From 9b7497946a95cfab3629782d8f5db5593114bd11 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 10:48:40 -0600 Subject: [PATCH 11/18] Update Constants.py --- Resources/Constants.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Resources/Constants.py b/Resources/Constants.py index 599b7e24b..9057ee400 100644 --- a/Resources/Constants.py +++ b/Resources/Constants.py @@ -126,7 +126,11 @@ class Constants: def opencore_zip_source(self): return self.payload_path / Path(f"OpenCore/OpenCore-{self.opencore_build}.zip") @property def plist_template(self): return self.payload_path / Path(f"Config/config.plist") - + + # Mount Location + @property + def payload_mnt1_path(self): return self.payload_path / Path("mnt1") + # ACPI @property def pci_ssdt_path(self): return self.payload_path / Path("ACPI/SSDT-CPBG.aml") From a6fbbc0c443461a8d87d28eb14270163c47d86bd Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 11:01:58 -0600 Subject: [PATCH 12/18] Fix incorrectly stripping disk ID in recovery --- Resources/SysPatch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index a713c3c7b..6952a1111 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -160,7 +160,8 @@ class PatchSysVolume: self.root_mount_path = root_partition_info["DeviceIdentifier"] if self.root_mount_path.startswith("disk"): - self.root_mount_path = self.root_mount_path[:-2] if self.root_mount_path.count("s") > 1 else self.root_mount_path + if self.constants.recovery_status is False: + self.root_mount_path = self.root_mount_path[:-2] if self.root_mount_path.count("s") > 1 else self.root_mount_path print(f"- Found Root Volume at: {self.root_mount_path}") if Path(self.mount_extensions).exists(): print("- Root Volume is already mounted") From 792a99ce04da60d40eb6344ceec013afec2daf06 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 11:10:26 -0600 Subject: [PATCH 13/18] Update Utilities.py --- Resources/Utilities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Utilities.py b/Resources/Utilities.py index 27086d81f..44732895b 100644 --- a/Resources/Utilities.py +++ b/Resources/Utilities.py @@ -34,7 +34,8 @@ def check_recovery(): def cls(): # We only support macOS, so - print("\u001Bc") + #print("\u001Bc") + pass # def menu(title, prompt, menu_options, add_quit=True, auto_number=False, in_between=[], top_level=False): # return_option = ["Q", "Quit", None] if top_level else ["B", "Back", None] From 05a51bf32eeb9cfaa2be8624bca9739cfb594f2b Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 11:27:20 -0600 Subject: [PATCH 14/18] Update SysPatch.py --- Resources/SysPatch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 6952a1111..e58d57fa2 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -172,8 +172,12 @@ class PatchSysVolume: self.unpatch_root_vol() return True else: - print("- Mounting drive as writable") - self.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + if self.constants.recovery_status is True: + print("- Mounting drive as writable in Recovery") + self.elevated(["mount", "-t", "apfs", "-rw", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + else: + print("- Mounting drive as writable in OS") + self.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() if Path(self.mount_extensions).exists(): print("- Successfully mounted the Root Volume") if patch is True: From 603aedb6ae53bead842afca6774d428758507365 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 12:16:29 -0600 Subject: [PATCH 15/18] Update SysPatch.py --- Resources/SysPatch.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index e58d57fa2..47f218b77 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -174,6 +174,10 @@ class PatchSysVolume: else: if self.constants.recovery_status is True: print("- Mounting drive as writable in Recovery") + + umount_drive = plistlib.loads(subprocess.run(f"diskutil info -plist {self.root_mount_path}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + umount_drive = umount_drive["VolumeName"] + self.elevated(["umount", f'/Volumes/{umount_drive}'], stdout=subprocess.PIPE).stdout.decode().strip().encode() self.elevated(["mount", "-t", "apfs", "-rw", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() else: print("- Mounting drive as writable in OS") From 939e942f5003fbc6114ec5437f583ce58d8b5aa4 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 12:32:04 -0600 Subject: [PATCH 16/18] Update SysPatch.py --- Resources/SysPatch.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 47f218b77..4a7436121 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -25,7 +25,10 @@ class PatchSysVolume: self.sip_status = None # TODO: Put this in a better place - self.mount_location = "/System/Volumes/Update/mnt1" + if self.constants.recovery_status is True: + self.mount_location = self.constants.payload_mnt1_path + else: + self.mount_location = "/System/Volumes/Update/mnt1" self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" self.mount_frameworks = f"{self.mount_location}/System/Library/Frameworks" self.mount_lauchd = f"{self.mount_location}/System/Library/LaunchDaemons" @@ -154,7 +157,6 @@ class PatchSysVolume: if not Path(self.constants.payload_mnt1_path).exists(): print("- Creating mnt1 folder") Path(self.constants.payload_mnt1_path).mkdir() - self.mount_location = self.constants.payload_mnt1_path else: root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) self.root_mount_path = root_partition_info["DeviceIdentifier"] From d8258a73fdd52bb2675eb6b8c5d4876a46ef8135 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 13:02:19 -0600 Subject: [PATCH 17/18] Update SysPatch.py --- Resources/SysPatch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 4a7436121..7c592e665 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -26,7 +26,9 @@ class PatchSysVolume: # TODO: Put this in a better place if self.constants.recovery_status is True: - self.mount_location = self.constants.payload_mnt1_path + if not Path("/Volumes/mnt1").exists: + Path("/Volumes/mnt1").mkdir() + self.mount_location = "/Volumes/mnt1" else: self.mount_location = "/System/Volumes/Update/mnt1" self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" From 0130fce0c61df4c49f9ae2e62aab534989241d7e Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk <48863253+khronokernel@users.noreply.github.com> Date: Wed, 19 May 2021 13:15:46 -0600 Subject: [PATCH 18/18] Update SysPatch.py --- Resources/SysPatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/SysPatch.py b/Resources/SysPatch.py index 7c592e665..5d9165f15 100644 --- a/Resources/SysPatch.py +++ b/Resources/SysPatch.py @@ -27,7 +27,7 @@ class PatchSysVolume: # TODO: Put this in a better place if self.constants.recovery_status is True: if not Path("/Volumes/mnt1").exists: - Path("/Volumes/mnt1").mkdir() + self.elevated(["mkdir", "/Volumes/mnt1"], stdout=subprocess.PIPE).stdout.decode().strip().encode() self.mount_location = "/Volumes/mnt1" else: self.mount_location = "/System/Volumes/Update/mnt1"