Add support for KDK downloads

This commit is contained in:
Dhinak G
2022-09-25 00:54:01 -04:00
parent 653692f898
commit 407ed9c4cd
5 changed files with 75 additions and 21 deletions

View File

@@ -600,6 +600,10 @@ class Constants:
@property
def payload_local_binaries_root_path_zip(self):
return self.payload_path / Path("Universal-Binaries.zip")
@property
def kdk_download_path(self):
return self.payload_path / Path("KDK.dmg")
sbm_values = [

View File

@@ -26,7 +26,7 @@ class OpenCoreLegacyPatcher:
def generate_base_data(self):
self.constants.detected_os = os_probe.detect_kernel_major()
self.constants.detected_os_minor = os_probe.detect_kernel_minor()
self.constants.detected_os_build = os_probe.detect_kernel_build()
self.constants.detected_os_build = os_probe.detect_os_build()
self.constants.computer = device_probe.Computer.probe()
self.constants.recovery_status = utilities.check_recovery()
self.computer = self.constants.computer

View File

@@ -16,7 +16,13 @@ def detect_kernel_minor():
return int(platform.uname().release.partition(".")[2].partition(".")[0])
def detect_kernel_build():
def detect_os_version():
# Return OS version
# Example Output: 12.0 (string)
return subprocess.run("sw_vers -productVersion".split(), stdout=subprocess.PIPE).stdout.decode().strip()
def detect_os_build():
# Return OS build
# Example Output: 21A5522h (string)
return subprocess.run("sw_vers -buildVersion".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode().split("\n")[0]
return subprocess.run("sw_vers -buildVersion".split(), stdout=subprocess.PIPE).stdout.decode().strip()

View File

@@ -1,13 +1,16 @@
# Additional support functions for sys_patch.py
# Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk
import subprocess
import tempfile
from data import os_data
from resources import generate_smbios
from resources import generate_smbios, utilities
from pathlib import Path
from datetime import datetime
import plistlib
import os
from resources import constants
class sys_patch_helpers:
@@ -67,6 +70,24 @@ class sys_patch_helpers:
return True
return False
def download_and_install_kdk(self, version: str, build: str):
# Note: cannot do lazy matching as we don't store old version/build numbers nor can we enumerate KDKs from the portal
URL_TEMPLATE = f"https://download.developer.apple.com/macOS/Kernel_Debug_Kit_{version}_build_{build}/Kernel_Debug_Kit_{version}_build_{build}.dmg"
print(f"- Downloading Apple KDK for macOS {version} build {build}")
if utilities.download_apple_developer_portal(URL_TEMPLATE, self.constants.kdk_download_path):
print("- Successfully downloaded KDK, installing")
# Set up a new temporary directory to mount the KDK to
with tempfile.TemporaryDirectory() as mount_point:
utilities.process_status(subprocess.run(["hdiutil", "attach", self.constants.kdk_download_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
# Install the KDK
utilities.process_status(utilities.elevated(["installer", "-pkg", f"{mount_point}/KDK.pkg", "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
subprocess.run(["hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # Do not really care if this fails
print("- Successfully installed KDK")
return True
else:
print("- Failed to download KDK")
return False
def determine_kdk_present(self, match_closest=False):
# Check if KDK is present
@@ -98,4 +119,4 @@ class sys_patch_helpers:
# Verify that the KDK is valid
if (kdk_folder / Path("System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern")).exists():
return kdk_folder
return None
return None

View File

@@ -14,6 +14,7 @@ import time
import atexit
import requests
import shutil
import urllib.parse
from resources import constants, ioreg, amfi_detect
from data import sip_data, os_data
@@ -376,7 +377,7 @@ def get_firmware_vendor(*, decode: bool = False):
def verify_network_connection(url):
try:
response = SESSION.head(url, timeout=5)
response = SESSION.head(url, timeout=5, allow_redirects=True)
return True
except (requests.exceptions.Timeout, requests.exceptions.TooManyRedirects, requests.exceptions.ConnectionError, requests.exceptions.HTTPError):
return False
@@ -384,33 +385,31 @@ def verify_network_connection(url):
def download_file(link, location, is_gui=None, verify_checksum=False):
if verify_network_connection(link):
disable_sleep_while_running()
short_link = os.path.basename(link)
base_name = Path(link).name
if Path(location).exists():
Path(location).unlink()
header = SESSION.head(link).headers
try:
# Try to get true file
# ex. Github's release links provides a "fake" header
# Thus need to resolve to the real link
link = SESSION.head(link).headers["location"]
header = SESSION.head(link).headers
except KeyError:
pass
head_response = SESSION.head(link, allow_redirects=True)
try:
# Handle cases where Content-Length has garbage or is missing
total_file_size = int(SESSION.head(link).headers['Content-Length'])
total_file_size = int(head_response.headers['Content-Length'])
except KeyError:
total_file_size = 0
if total_file_size > 1024:
file_size_rounded = round(total_file_size / 1024 / 1024, 2)
file_size_string = f" of {file_size_rounded}MB"
else:
file_size_string = ""
response = SESSION.get(link, stream=True)
# SU Catalog's link is quite long, strip to make it bearable
if "sucatalog.gz" in short_link:
short_link = "sucatalog.gz"
header = f"# Downloading: {short_link} #"
if "sucatalog.gz" in base_name:
base_name = "sucatalog.gz"
header = f"# Downloading: {base_name} #"
box_length = len(header)
box_string = "#" * box_length
dl = 0
@@ -463,6 +462,30 @@ def download_file(link, location, is_gui=None, verify_checksum=False):
print(link)
return None
def download_apple_developer_portal(link, location, is_gui=None, verify_checksum=False):
TOKEN_URL_BASE = "https://developerservices2.apple.com/services/download?path="
remote_path = urllib.parse.urlparse(link).path
token_url = urllib.parse.urlunparse(urllib.parse.urlparse(TOKEN_URL_BASE)._replace(query=urllib.parse.urlencode({"path": remote_path})))
try:
response = SESSION.get(token_url, timeout=5)
except (requests.exceptions.Timeout, requests.exceptions.TooManyRedirects, requests.exceptions.ConnectionError):
print(" - Could not contact Apple download servers")
return None
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
if response.status_code == 400 and "The path specified is invalid" in response.text:
print(" - File does not exist on Apple download servers")
else:
print(" - Could not request download authorization from Apple download servers")
return None
return download_file(link, location, is_gui, verify_checksum)
def dump_constants(constants):
with open(os.path.join(os.path.expanduser('~'), 'Desktop', 'internal_data.txt'), 'w') as f:
f.write(str(vars(constants)))
@@ -550,7 +573,7 @@ def monitor_disk_output(disk):
def validate_link(link):
# Check if link is 404
try:
response = SESSION.head(link, timeout=5)
response = SESSION.head(link, timeout=5, allow_redirects=True)
if response.status_code == 404:
return False
else: