GUI: Add root patching support

This commit is contained in:
Mykola Grymalyuk
2023-05-07 12:05:58 -06:00
parent f8b2b5a759
commit 1204daa330
12 changed files with 367 additions and 53 deletions
+1
View File
@@ -34,3 +34,4 @@ __pycache__/
/payloads/KDK.dmg /payloads/KDK.dmg
*.log *.log
/Universal-Binaries.dmg /Universal-Binaries.dmg
/payloads/KDKInfo.plist
-3
View File
@@ -7,7 +7,6 @@ import plistlib
import shutil import shutil
import zipfile import zipfile
import logging import logging
import time
from pathlib import Path from pathlib import Path
from datetime import date from datetime import date
@@ -146,8 +145,6 @@ class BuildOpenCore:
support.BuildSupport(self.model, self.constants, self.config).cleanup() support.BuildSupport(self.model, self.constants, self.config).cleanup()
self._save_config() self._save_config()
time.sleep(5)
# Post-build handling # Post-build handling
support.BuildSupport(self.model, self.constants, self.config).sign_files() support.BuildSupport(self.model, self.constants, self.config).sign_files()
support.BuildSupport(self.model, self.constants, self.config).validate_pathing() support.BuildSupport(self.model, self.constants, self.config).validate_pathing()
+1
View File
@@ -110,6 +110,7 @@ class tui_disk_installation:
if result.returncode != 0: if result.returncode != 0:
if "execution error" in result.stderr.decode() and result.stderr.decode().strip()[-5:-1] == "-128": if "execution error" in result.stderr.decode() and result.stderr.decode().strip()[-5:-1] == "-128":
# cancelled prompt # cancelled prompt
logging.info("- Mount cancelled by user")
return return
else: else:
logging.info("An error occurred!") logging.info("An error occurred!")
+3 -1
View File
@@ -538,10 +538,12 @@ class KernelDebugKitObject:
logging.info(f"- {msg}") logging.info(f"- {msg}")
self.error_msg = msg self.error_msg = msg
return False
self._remove_unused_kdks() self._remove_unused_kdks()
self.success = True self.success = True
logging.info("- Kernel Debug Kit checksum verified")
return True
class KernelDebugKitUtilities: class KernelDebugKitUtilities:
-2
View File
@@ -910,8 +910,6 @@ class PatchSysVolume:
elif self.constants.gui_mode is False: elif self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu: ") input("\nPress [ENTER] to return to the main menu: ")
else:
logging.info("- Returning to main menu")
def start_unpatch(self) -> None: def start_unpatch(self) -> None:
""" """
+3 -3
View File
@@ -8,7 +8,7 @@ from pathlib import Path
from resources import utilities, updates, global_settings, network_handler, constants from resources import utilities, updates, global_settings, network_handler, constants
from resources.sys_patch import sys_patch_detect from resources.sys_patch import sys_patch_detect
from resources.gui import gui_main from resources.wx_gui import gui_entry
class AutomaticSysPatch: class AutomaticSysPatch:
@@ -170,7 +170,7 @@ class AutomaticSysPatch:
if output.returncode == 0: if output.returncode == 0:
logging.info("- Launching GUI's Build/Install menu") logging.info("- Launching GUI's Build/Install menu")
self.constants.start_build_install = True self.constants.start_build_install = True
gui_main.wx_python_gui(self.constants).main_menu(None) gui_entry.EntryPoint(self.constants).start(entry=gui_entry.SupportedEntryPoints.BUILD_OC)
return False return False
@@ -245,7 +245,7 @@ class AutomaticSysPatch:
if output.returncode == 0: if output.returncode == 0:
logging.info("- Launching GUI's Build/Install menu") logging.info("- Launching GUI's Build/Install menu")
self.constants.start_build_install = True self.constants.start_build_install = True
gui_main.wx_python_gui(self.constants).main_menu(None) gui_entry.EntryPoint(self.constants).start(entry=gui_entry.SupportedEntryPoints.BUILD_OC)
except KeyError: except KeyError:
logging.info("- Unable to determine if boot disk is removable, skipping prompt") logging.info("- Unable to determine if boot disk is removable, skipping prompt")
+22 -5
View File
@@ -1,6 +1,7 @@
import wx import wx
import logging import logging
import threading import threading
import traceback
from resources.wx_gui import gui_main_menu, gui_install_oc, gui_support from resources.wx_gui import gui_main_menu, gui_install_oc, gui_support
from resources.build import build from resources.build import build
@@ -33,7 +34,7 @@ class BuildFrame(wx.Frame):
self._invoke_build() self._invoke_build()
def _generate_elements(self, frame=None) -> None: def _generate_elements(self, frame: wx.Frame = None) -> None:
""" """
Generate UI elements for build frame Generate UI elements for build frame
@@ -70,6 +71,8 @@ class BuildFrame(wx.Frame):
return_button = wx.Button(frame, label="Return to Main Menu", pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 5), size=(200, 30)) return_button = wx.Button(frame, label="Return to Main Menu", pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 5), size=(200, 30))
return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
return_button.Center(wx.HORIZONTAL) return_button.Center(wx.HORIZONTAL)
return_button.Disable()
self.return_button = return_button
# Adjust window size to fit all elements # Adjust window size to fit all elements
frame.SetSize((-1, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40)) frame.SetSize((-1, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
@@ -82,7 +85,17 @@ class BuildFrame(wx.Frame):
while thread.is_alive(): while thread.is_alive():
wx.Yield() wx.Yield()
self.install_button.Enable() self.return_button.Enable()
dialog = wx.MessageDialog(
parent=self,
message=f"Would you like to install OpenCore now?",
caption="Finished building your OpenCore configuration!",
style=wx.YES_NO | wx.ICON_QUESTION
)
dialog.SetYesNoLabels("Install to disk", "View build log")
self.on_install() if dialog.ShowModal() == wx.ID_YES else self.install_button.Enable()
def _build(self): def _build(self):
@@ -91,11 +104,15 @@ class BuildFrame(wx.Frame):
""" """
logger = logging.getLogger() logger = logging.getLogger()
logger.addHandler(gui_support.ThreadHandler(self.text_box)) logger.addHandler(gui_support.ThreadHandler(self.text_box))
build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants) try:
build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants)
except:
logging.error("- An internal error occurred while building:\n")
logging.error(traceback.format_exc())
logger.removeHandler(logger.handlers[2]) logger.removeHandler(logger.handlers[2])
def on_return_to_main_menu(self, event): def on_return_to_main_menu(self, event: wx.Event = None):
self.frame_modal.Hide() self.frame_modal.Hide()
main_menu_frame = gui_main_menu.MainMenu( main_menu_frame = gui_main_menu.MainMenu(
None, None,
@@ -108,7 +125,7 @@ class BuildFrame(wx.Frame):
self.Destroy() self.Destroy()
def on_install(self, event): def on_install(self, event: wx.Event = None):
self.frame_modal.Destroy() self.frame_modal.Destroy()
self.Destroy() self.Destroy()
install_oc_frame = gui_install_oc.InstallOCFrame( install_oc_frame = gui_install_oc.InstallOCFrame(
+16 -5
View File
@@ -8,8 +8,10 @@ from resources.wx_gui import (
gui_main_menu, gui_main_menu,
gui_build, gui_build,
gui_menubar, gui_menubar,
gui_install_oc gui_install_oc,
gui_sys_patch
) )
from resources.sys_patch import sys_patch_detect
class SupportedEntryPoints: class SupportedEntryPoints:
""" """
@@ -38,19 +40,30 @@ class EntryPoint:
Launches entry point for the wxPython GUI Launches entry point for the wxPython GUI
""" """
self._generate_base_data() self._generate_base_data()
if "--gui_patch" in sys.argv or "--gui_unpatch" in sys.argv:
entry = gui_sys_patch.SysPatchMenu
patches = sys_patch_detect.DetectRootPatch(self.constants.computer.real_model, self.constants).detect_patch_set()
self.frame: wx.Frame = entry( self.frame: wx.Frame = entry(
None, None,
title=f"{self.constants.patcher_name} ({self.constants.patcher_version})", title=f"{self.constants.patcher_name} ({self.constants.patcher_version})",
global_constants=self.constants, global_constants=self.constants,
screen_location=None screen_location=None,
**({"patches": patches} if "--gui_patch" in sys.argv or "--gui_unpatch" in sys.argv else {})
) )
self.frame.SetMenuBar(gui_menubar.GenerateMenubar().generate()) self.frame.SetMenuBar(gui_menubar.GenerateMenubar().generate())
atexit.register(self.OnCloseFrame) atexit.register(self.OnCloseFrame)
if "--gui_patch" in sys.argv:
self.frame.start_root_patching(patches)
elif "--gui_unpatch" in sys.argv:
self.frame.revert_root_patching(patches)
self.app.MainLoop() self.app.MainLoop()
def OnCloseFrame(self, event=None): def OnCloseFrame(self, event: wx.Event = None):
""" """
Closes the wxPython GUI Closes the wxPython GUI
""" """
@@ -66,5 +79,3 @@ class EntryPoint:
self.frame.DestroyChildren() self.frame.DestroyChildren()
self.frame.Destroy() self.frame.Destroy()
self.app.ExitMainLoop() self.app.ExitMainLoop()
sys.exit()
+19 -10
View File
@@ -1,6 +1,7 @@
import wx import wx
import threading import threading
import logging import logging
import traceback
from resources.wx_gui import gui_main_menu, gui_support from resources.wx_gui import gui_main_menu, gui_support
from resources import constants, install from resources import constants, install
@@ -20,6 +21,8 @@ class InstallOCFrame(wx.Frame):
self.available_disks: dict = None self.available_disks: dict = None
self.stock_output = logging.getLogger().handlers[0].stream self.stock_output = logging.getLogger().handlers[0].stream
self.hyperlink_colour = (25, 179, 231)
self._generate_elements() self._generate_elements()
self.SetPosition(screen_location) if screen_location else self.Centre() self.SetPosition(screen_location) if screen_location else self.Centre()
@@ -106,16 +109,19 @@ class InstallOCFrame(wx.Frame):
# disk6s1 -> disk6 # disk6s1 -> disk6
disk_root = self.constants.booted_oc_disk.strip("disk") disk_root = self.constants.booted_oc_disk.strip("disk")
disk_root = "disk" + disk_root.split("s")[0] disk_root = "disk" + disk_root.split("s")[0]
logging.info(f"- Checking if booted disk is present: {disk_root}")
# Add buttons for each disk # Add buttons for each disk
for disk in self.available_disks: for disk in self.available_disks:
# Create a button for each disk # Create a button for each disk
logging.info(f"- {self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}") logging.info(f"- {self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}")
disk_button = wx.Button(dialog, label=f"{self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}", size=(300,30), pos=(-1, gpt_note.GetPosition()[1] + gpt_note.GetSize()[1] + 5)) disk_button = wx.Button(dialog, label=f"{self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}", size=(350,30), pos=(-1, gpt_note.GetPosition()[1] + gpt_note.GetSize()[1] + 5))
disk_button.Center(wx.HORIZONTAL) disk_button.Center(wx.HORIZONTAL)
disk_button.Bind(wx.EVT_BUTTON, lambda event, disk=disk: self._display_volumes(disk, self.available_disks)) disk_button.Bind(wx.EVT_BUTTON, lambda event, disk=disk: self._display_volumes(disk, self.available_disks))
logging.info(f"- Comparing {disk_root} to {self.available_disks[disk]['disk']}")
if disk_root == self.available_disks[disk]['disk']: if disk_root == self.available_disks[disk]['disk']:
disk_button.SetForegroundColour((25, 179, 231)) logging.info("- Found match")
disk_button.SetForegroundColour(self.hyperlink_colour)
if disk_root: if disk_root:
@@ -130,12 +136,12 @@ class InstallOCFrame(wx.Frame):
# Add button: Search for disks again # Add button: Search for disks again
search_button = wx.Button(dialog, label="Search for disks again", size=(200,30), pos=(-1, disk_label.GetPosition()[1] + disk_label.GetSize()[1] + 5)) search_button = wx.Button(dialog, label="Search for disks again", size=(200,30), pos=(-1, disk_label.GetPosition()[1] + disk_label.GetSize()[1] + 5))
search_button.Center(wx.HORIZONTAL) search_button.Center(wx.HORIZONTAL)
search_button.Bind(wx.EVT_BUTTON, self._reload_frame) search_button.Bind(wx.EVT_BUTTON, self.on_reload_frame)
# Add button: Return to main menu # Add button: Return to main menu
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, search_button.GetPosition()[1] + 25)) return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, search_button.GetPosition()[1] + 25))
return_button.Center(wx.HORIZONTAL) return_button.Center(wx.HORIZONTAL)
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
# Set size # Set size
dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40)) dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
@@ -173,7 +179,7 @@ class InstallOCFrame(wx.Frame):
# Add button: Return to main menu # Add button: Return to main menu
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, disk_button.GetPosition()[1] + disk_button.GetSize()[1])) return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, disk_button.GetPosition()[1] + disk_button.GetSize()[1]))
return_button.Center(wx.HORIZONTAL) return_button.Center(wx.HORIZONTAL)
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
# Set size # Set size
dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40)) dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
@@ -210,7 +216,7 @@ class InstallOCFrame(wx.Frame):
# Add button: Return to main menu # Add button: Return to main menu
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 10)) return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 10))
return_button.Center(wx.HORIZONTAL) return_button.Center(wx.HORIZONTAL)
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
return_button.Disable() return_button.Disable()
# Set size # Set size
@@ -230,7 +236,6 @@ class InstallOCFrame(wx.Frame):
thread.start() thread.start()
while thread.is_alive(): while thread.is_alive():
# wx.Yield()
wx.GetApp().Yield() wx.GetApp().Yield()
if self.result is True: if self.result is True:
@@ -253,11 +258,15 @@ class InstallOCFrame(wx.Frame):
logger = logging.getLogger() logger = logging.getLogger()
logger.addHandler(gui_support.ThreadHandler(self.text_box)) logger.addHandler(gui_support.ThreadHandler(self.text_box))
self.result = install.tui_disk_installation(self.constants).install_opencore(partition) try:
self.result = install.tui_disk_installation(self.constants).install_opencore(partition)
except:
logging.error("- An internal error occurred while installing:\n")
logging.error(traceback.format_exc())
logger.removeHandler(logger.handlers[2]) logger.removeHandler(logger.handlers[2])
def _reload_frame(self, event) -> None: def on_reload_frame(self, event: wx.Event = None) -> None:
""" """
Reload frame Reload frame
""" """
@@ -271,7 +280,7 @@ class InstallOCFrame(wx.Frame):
frame.Show() frame.Show()
def _return_to_main_menu(self, event: wx.Event) -> None: def on_return_to_main_menu(self, event: wx.Event = None) -> None:
""" """
Return to main menu Return to main menu
""" """
+7 -5
View File
@@ -69,7 +69,8 @@ class MainMenu(wx.Frame):
self.SetSize((350, copy_label.GetPosition()[1] + 50)) self.SetSize((350, copy_label.GetPosition()[1] + 50))
def on_build_and_install(self, event): def on_build_and_install(self, event: wx.Event = None):
self.Hide()
gui_build.BuildFrame( gui_build.BuildFrame(
parent=None, parent=None,
title=self.title, title=self.title,
@@ -79,7 +80,8 @@ class MainMenu(wx.Frame):
self.Destroy() self.Destroy()
def on_post_install_root_patch(self, event): def on_post_install_root_patch(self, event: wx.Event = None):
self.Hide()
gui_sys_patch.SysPatchMenu( gui_sys_patch.SysPatchMenu(
parent=None, parent=None,
title=self.title, title=self.title,
@@ -89,11 +91,11 @@ class MainMenu(wx.Frame):
self.Destroy() self.Destroy()
def on_create_macos_installer(self, event): def on_create_macos_installer(self, event: wx.Event = None):
pass pass
def on_settings(self, event): def on_settings(self, event: wx.Event = None):
pass pass
def on_help(self, event): def on_help(self, event: wx.Event = None):
pass pass
+1 -1
View File
@@ -45,7 +45,7 @@ class RestartHost:
Restarts the host machine Restarts the host machine
""" """
def __init__(self, frame) -> None: def __init__(self, frame: wx.Frame) -> None:
self.frame: wx.Frame = frame self.frame: wx.Frame = frame
+294 -18
View File
@@ -1,41 +1,148 @@
import wx import wx
import os
import logging import logging
import plistlib import plistlib
import threading
import subprocess
import time
import sys
import traceback
from pathlib import Path from pathlib import Path
from resources import constants from resources import constants, kdk_handler, utilities, network_handler
from data import os_data
from resources.sys_patch import ( from resources.sys_patch import (
sys_patch, sys_patch,
sys_patch_detect sys_patch_detect
) )
from resources.wx_gui import ( from resources.wx_gui import (
gui_main_menu gui_main_menu,
gui_support,
) )
class SysPatchMenu(wx.Frame): class SysPatchMenu(wx.Frame):
""" """
Create a frame for building OpenCore Create a frame for root patching
Uses a Modal Dialog for smoother transition from other frames Uses a Modal Dialog for smoother transition from other frames
""" """
def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None): def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None, patches: dict = {}):
super(SysPatchMenu, self).__init__(parent, title=title, size=(350, 260)) super(SysPatchMenu, self).__init__(parent, title=title, size=(350, 260))
self.title = title self.title = title
self.constants: constants.Constants = global_constants self.constants: constants.Constants = global_constants
self.frame_modal: wx.Dialog = None self.frame_modal: wx.Dialog = None
self.return_button: wx.Button = None
self.frame_modal = wx.Dialog(self, title=title, size=(360, 200)) self.frame_modal = wx.Dialog(self, title=title, size=(360, 200))
self._generate_elements(self.frame_modal) if patches:
return
self._generate_elements_display_patches(self.frame_modal, patches)
self.SetPosition(screen_location) if screen_location else self.Centre() self.SetPosition(screen_location) if screen_location else self.Centre()
self.frame_modal.ShowWindowModal() self.frame_modal.ShowWindowModal()
def _kdk_download(self, frame: wx.Frame = None) -> bool:
frame = self if not frame else frame
def _generate_elements(self, frame=None) -> None: logging.info("KDK missing, generating KDK download frame")
header = wx.StaticText(frame, label="Downloading Kernel Debug Kit", pos=(-1,5))
header.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
header.Center(wx.HORIZONTAL)
subheader = wx.StaticText(frame, label="Fetching KDK database...", pos=(-1, header.GetPosition()[1] + header.GetSize()[1] + 5))
subheader.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
subheader.Center(wx.HORIZONTAL)
progress_bar = wx.Gauge(frame, range=100, pos=(-1, subheader.GetPosition()[1] + subheader.GetSize()[1] + 5), size=(250, 20))
progress_bar.Center(wx.HORIZONTAL)
progress_bar.Pulse()
# Set size of frame
frame.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 35))
frame.Show()
# Generate KDK object
kdk_result = False
self.kdk_obj: kdk_handler.KernelDebugKitObject = None
def kdk_thread_spawn():
self.kdk_obj = kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version)
kdk_thread = threading.Thread(target=kdk_thread_spawn)
kdk_thread.start()
while kdk_thread.is_alive():
progress_bar.Pulse()
wx.GetApp().Yield()
if self.kdk_obj.success is False:
progress_bar.SetValue(0)
wx.MessageBox(f"KDK download failed: {self.kdk_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR)
return False
kdk_download_obj = self.kdk_obj.retrieve_download()
if not kdk_download_obj:
# KDK is already downloaded
return True
kdk_download_obj.download()
header.SetLabel(f"Downloading KDK Build: {self.kdk_obj.kdk_url_build}")
header.Center(wx.HORIZONTAL)
progress_bar.SetValue(0)
# Set below developer note
progress_bar.SetPosition(
wx.Point(
subheader.GetPosition().x,
subheader.GetPosition().y + subheader.GetSize().height + 30
)
)
progress_bar.Center(wx.HORIZONTAL)
progress_bar.Show()
developer_note = wx.StaticText(frame, label="Starting shortly", pos=(-1, progress_bar.GetPosition().y - 23))
developer_note.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
developer_note.Center(wx.HORIZONTAL)
frame.SetSize(-1, progress_bar.GetPosition().y + progress_bar.GetSize().height + 40)
while kdk_download_obj.is_active():
subheader.SetLabel(f"{utilities.human_fmt(kdk_download_obj.downloaded_file_size)} downloaded of {utilities.human_fmt(kdk_download_obj.total_file_size)} ({kdk_download_obj.get_percent():.2f}%)")
subheader.Center(wx.HORIZONTAL)
developer_note.SetLabel(
f"Average download speed: {utilities.human_fmt(kdk_download_obj.get_speed())}/s"
)
developer_note.Center(wx.HORIZONTAL)
progress_bar.SetValue(int(kdk_download_obj.get_percent()))
wx.GetApp().Yield()
if kdk_download_obj.download_complete is False:
logging.info("Failed to download KDK")
logging.info(kdk_download_obj.error_msg)
# wx.MessageBox(f"KDK download failed: {kdk_download_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR)
msg = wx.MessageDialog(frame, f"KDK download failed: {kdk_download_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR)
msg.ShowModal()
return False
if self.kdk_obj.validate_kdk_checksum() is False:
logging.error("KDK checksum validation failed")
logging.error(self.kdk_obj.error_msg)
msg = wx.MessageDialog(frame, f"KDK checksum validation failed: {self.kdk_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR)
msg.ShowModal()
return False
logging.info("KDK download complete")
return True
def _generate_elements_display_patches(self, frame: wx.Frame = None, patches: dict = {}) -> None:
""" """
Generate UI elements for root patching frame Generate UI elements for root patching frame
@@ -59,7 +166,7 @@ class SysPatchMenu(wx.Frame):
available_label.Center(wx.HORIZONTAL) available_label.Center(wx.HORIZONTAL)
# Labels: {patch name} # Labels: {patch name}
patches: dict = sys_patch_detect.DetectRootPatch(self.constants.computer.real_model, self.constants).detect_patch_set() patches: dict = sys_patch_detect.DetectRootPatch(self.constants.computer.real_model, self.constants).detect_patch_set() if not patches else patches
can_unpatch: bool = patches["Validation: Unpatching Possible"] can_unpatch: bool = patches["Validation: Unpatching Possible"]
if not any(not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True for patch in patches): if not any(not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True for patch in patches):
@@ -72,7 +179,7 @@ class SysPatchMenu(wx.Frame):
if not patches: if not patches:
# Prompt user with no patches found # Prompt user with no patches found
patch_label = wx.StaticText(frame, label="No patches needed", pos=(-1, available_label.GetPosition()[1] + 20)) patch_label = wx.StaticText(frame, label="No patches required", pos=(-1, available_label.GetPosition()[1] + 20))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
patch_label.Center(wx.HORIZONTAL) patch_label.Center(wx.HORIZONTAL)
@@ -80,7 +187,7 @@ class SysPatchMenu(wx.Frame):
# Add Label for each patch # Add Label for each patch
i = 0 i = 0
if no_new_patches is True: if no_new_patches is True:
patch_label = wx.StaticText(frame, label="No new patches needed", pos=(-1, 50)) patch_label = wx.StaticText(frame, label="All applicable patches already installed", pos=(-1, 50))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
patch_label.Center(wx.HORIZONTAL) patch_label.Center(wx.HORIZONTAL)
i = i + 10 i = i + 10
@@ -112,18 +219,31 @@ class SysPatchMenu(wx.Frame):
patch_label = wx.StaticText(frame, label=f"- {patch.split('Validation: ')[1]}", pos=(available_label.GetPosition().x - 10, patch_label.GetPosition().y + 20)) patch_label = wx.StaticText(frame, label=f"- {patch.split('Validation: ')[1]}", pos=(available_label.GetPosition().x - 10, patch_label.GetPosition().y + 20))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
# patch_label.Center(wx.HORIZONTAL) else:
if self.constants.computer.oclp_sys_version and self.constants.computer.oclp_sys_date:
date = self.constants.computer.oclp_sys_date.split(" @")
date = date[0] if len(date) == 2 else ""
patch_text = f"{self.constants.computer.oclp_sys_version}, {date}"
patch_label = wx.StaticText(frame, label="Root Volume last patched:", pos=(-1, patch_label.GetPosition().y + i + 20))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
patch_label.Center(wx.HORIZONTAL)
patch_label = wx.StaticText(frame, label=patch_text, pos=(available_label.GetPosition().x - 10, patch_label.GetPosition().y + 20))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
patch_label.Center(wx.HORIZONTAL)
# Button: Start Root Patching # Button: Start Root Patching
start_button = wx.Button(frame, label="Start Root Patching", pos=(10, patch_label.GetPosition().y + 20), size=(170, 30)) start_button = wx.Button(frame, label="Start Root Patching", pos=(10, patch_label.GetPosition().y + 20), size=(170, 30))
start_button.Bind(wx.EVT_BUTTON, lambda event: self._start_root_patching(frame, patches, no_new_patches)) start_button.Bind(wx.EVT_BUTTON, lambda event: self.start_root_patching(frame, patches, no_new_patches))
start_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) start_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
start_button.Center(wx.HORIZONTAL) start_button.Center(wx.HORIZONTAL)
# Button: Revert Root Patches # Button: Revert Root Patches
revert_button = wx.Button(frame, label="Revert Root Patches", pos=(10, start_button.GetPosition().y + start_button.GetSize().height - 5), size=(170, 30)) revert_button = wx.Button(frame, label="Revert Root Patches", pos=(10, start_button.GetPosition().y + start_button.GetSize().height - 5), size=(170, 30))
revert_button.Bind(wx.EVT_BUTTON, lambda event: self._revert_root_patching(frame, patches, can_unpatch)) revert_button.Bind(wx.EVT_BUTTON, lambda event: self.revert_root_patching(frame, patches, can_unpatch))
revert_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) revert_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
revert_button.Center(wx.HORIZONTAL) revert_button.Center(wx.HORIZONTAL)
@@ -132,24 +252,144 @@ class SysPatchMenu(wx.Frame):
return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
return_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) return_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
return_button.Center(wx.HORIZONTAL) return_button.Center(wx.HORIZONTAL)
self.return_button = return_button
# Disable buttons if unsupported
if not patches: if not patches:
start_button.Disable() start_button.Disable()
revert_button.Disable() else:
if patches["Validation: Patching Possible"] is False:
start_button.Disable()
if patches["Validation: Unpatching Possible"] is False:
revert_button.Disable()
# Relaunch as root if not root
uid = os.geteuid()
if uid != 0:
start_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
revert_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
# Set frame size # Set frame size
frame.SetSize((-1, return_button.GetPosition().y + return_button.GetSize().height + 35)) frame.SetSize((-1, return_button.GetPosition().y + return_button.GetSize().height + 35))
def _generate_modal(self, patches: dict = {}, variant: str = "Root Patching"):
"""
Create UI for root patching/unpatching
"""
supported_variants = ["Root Patching", "Revert Root Patches"]
if variant not in supported_variants:
logging.error(f"Unsupported variant: {variant}")
return
def _start_root_patching(self, frame: wx.Frame, patches: dict, no_new_patches: bool): self.frame_modal.Close()
pass
dialog = wx.Dialog(self, title=self.title, size=(400, 200))
# Title
title = wx.StaticText(dialog, label=variant, pos=(-1, 10))
title.SetFont(wx.Font(15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
title.Center(wx.HORIZONTAL)
if variant == "Root Patching":
# Label
label = wx.StaticText(dialog, label="Root Patching will patch the following:", pos=(-1, title.GetPosition()[1] + 30))
label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
label.Center(wx.HORIZONTAL)
# Labels
i = 0
for patch in patches:
if (not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True):
logging.info(f"- Adding patch: {patch} - {patches[patch]}")
patch_label = wx.StaticText(dialog, label=f"- {patch}", pos=(label.GetPosition()[0], label.GetPosition()[1] + 20 + i))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
i = i + 5
if i == 0:
patch_label = wx.StaticText(dialog, label="- No patches to apply", pos=(label.GetPosition()[0], label.GetPosition()[1] + 20))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
else:
patch_label = wx.StaticText(dialog, label="Reverting to last sealed snapshot", pos=(-1, title.GetPosition()[1] + 30))
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
patch_label.Center(wx.HORIZONTAL)
def _revert_root_patching(self, frame: wx.Frame, patches: dict, can_unpatch: bool): # Text box
pass text_box = wx.TextCtrl(dialog, pos=(10, patch_label.GetPosition()[1] + 20), size=(400, 400), style=wx.TE_READONLY | wx.TE_MULTILINE | wx.TE_RICH2)
text_box.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
text_box.Center(wx.HORIZONTAL)
self.text_box = text_box
def on_return_to_main_menu(self, event): # Button: Return to Main Menu
return_button = wx.Button(dialog, label="Return to Main Menu", pos=(10, text_box.GetPosition()[1] + text_box.GetSize()[1] + 5), size=(150, 30))
return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
return_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
return_button.Center(wx.HORIZONTAL)
self.return_button = return_button
# Set frame size
dialog.SetSize((-1, return_button.GetPosition().y + return_button.GetSize().height + 33))
dialog.ShowWindowModal()
def start_root_patching(self, patches: dict):
logging.info("Starting root patching")
if patches["Settings: Kernel Debug Kit missing"] is True:
if self._kdk_download(self) is False:
self.on_return_to_main_menu()
return
self._generate_modal(patches, "Root Patching")
thread = threading.Thread(target=self._start_root_patching, args=(patches,))
thread.start()
while thread.is_alive():
wx.GetApp().Yield()
self._post_patch()
self.return_button.Enable()
def _start_root_patching(self, patches: dict):
logger = logging.getLogger()
logger.addHandler(gui_support.ThreadHandler(self.text_box))
try:
sys_patch.PatchSysVolume(self.constants.computer.real_model, self.constants, patches).start_patch()
except:
logging.error("- An internal error occurred while running the Root Patcher:\n")
logging.error(traceback.format_exc())
logger.removeHandler(logger.handlers[2])
def revert_root_patching(self, patches: dict):
logging.info("Reverting root patches")
self._generate_modal(patches, "Revert Root Patches")
thread = threading.Thread(target=self._revert_root_patching, args=(patches,))
thread.start()
while thread.is_alive():
wx.GetApp().Yield()
self._post_patch()
self.return_button.Enable()
def _revert_root_patching(self, patches: dict):
logger = logging.getLogger()
logger.addHandler(gui_support.ThreadHandler(self.text_box))
try:
sys_patch.PatchSysVolume(self.constants.computer.real_model, self.constants, patches).start_unpatch()
except:
logging.error("- An internal error occurred while running the Root Patcher:\n")
logging.error(traceback.format_exc())
logger.removeHandler(logger.handlers[2])
def on_return_to_main_menu(self, event: wx.Event = None):
self.frame_modal.Hide() self.frame_modal.Hide()
main_menu_frame = gui_main_menu.MainMenu( main_menu_frame = gui_main_menu.MainMenu(
None, None,
@@ -162,6 +402,42 @@ class SysPatchMenu(wx.Frame):
self.Destroy() self.Destroy()
def _post_patch(self):
if self.constants.root_patcher_succeeded is False:
return
if self.constants.needs_to_open_preferences is False:
gui_support.RestartHost(self).restart(message="Root Patcher finished successfully!\n\nWould you like to reboot now?")
return
if self.constants.detected_os >= os_data.os_data.ventura:
gui_support.RestartHost(self).restart(message="Root Patcher finished successfully!\nIf you were prompted to open System Settings to authorize new kexts, this can be ignored. Your system is ready once restarted.\n\nWould you like to reboot now?")
return
# Create dialog box to open System Preferences -> Security and Privacy
self.popup = wx.MessageDialog(
self.frame_modal,
"We just finished installing the patches to your Root Volume!\n\nHowever, Apple requires users to manually approve the kernel extensions installed before they can be used next reboot.\n\nWould you like to open System Preferences?",
"Open System Preferences?",
wx.YES_NO | wx.ICON_INFORMATION
)
self.popup.SetYesNoLabels("Open System Preferences", "Ignore")
answer = self.popup.ShowModal()
if answer == wx.ID_YES:
output =subprocess.run(
[
"osascript", "-e",
'tell app "System Preferences" to activate',
"-e", 'tell app "System Preferences" to reveal anchor "General" of pane id "com.apple.preference.security"',
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
if output.returncode != 0:
# Some form of fallback if unaccelerated state errors out
subprocess.run(["open", "-a", "System Preferences"])
time.sleep(5)
sys.exit(0)
def _check_if_new_patches_needed(self, patches: dict) -> bool: def _check_if_new_patches_needed(self, patches: dict) -> bool: