mirror of https://github.com/kholia/OSX-KVM.git
Partial support for macOS Sequoia (15.x)
This commit is contained in:
parent
182e2dd071
commit
ad213b6c80
|
@ -136,8 +136,9 @@ processors work just fine (even for macOS Sonoma).
|
||||||
5. Monterey (12.6)
|
5. Monterey (12.6)
|
||||||
6. Ventura (13) - RECOMMENDED
|
6. Ventura (13) - RECOMMENDED
|
||||||
7. Sonoma (14)
|
7. Sonoma (14)
|
||||||
|
8. Sequoia (15)
|
||||||
|
|
||||||
Choose a product to download (1-7): 6
|
Choose a product to download (1-8): 6
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Modern NVIDIA GPUs are supported on HighSierra but not on later
|
Note: Modern NVIDIA GPUs are supported on HighSierra but not on later
|
||||||
|
|
|
@ -16,28 +16,29 @@ pylint -> Your code has been rated at -0.08/10 ;(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import binascii
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import linecache
|
import linecache
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import struct
|
import struct
|
||||||
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.request import Request, HTTPError, urlopen
|
from urllib.request import Request, HTTPError, urlopen
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urllib2 import Request, HTTPError, urlopen
|
print('ERROR: Python 2 is not supported, please use Python 3')
|
||||||
from urlparse import urlparse
|
sys.exit(1)
|
||||||
|
|
||||||
SELF_DIR = os.path.dirname(os.path.realpath(__file__))
|
SELF_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
RECENT_MAC = 'Mac-7BA5B2D9E42DDD94'
|
# MacPro7,1
|
||||||
|
RECENT_MAC = 'Mac-27AD2F918AE68F61'
|
||||||
MLB_ZERO = '00000000000000000'
|
MLB_ZERO = '00000000000000000'
|
||||||
MLB_VALID = 'C02749200YGJ803AX'
|
MLB_VALID = 'F5K105303J9K3F71M'
|
||||||
MLB_PRODUCT = '00000000000J80300'
|
MLB_PRODUCT = 'F5K00000000K3F700'
|
||||||
|
|
||||||
TYPE_SID = 16
|
TYPE_SID = 16
|
||||||
TYPE_K = 64
|
TYPE_K = 64
|
||||||
|
@ -52,12 +53,12 @@ INFO_SIGN_HASH = 'CH'
|
||||||
INFO_SIGN_SESS = 'CT'
|
INFO_SIGN_SESS = 'CT'
|
||||||
INFO_REQURED = [INFO_PRODUCT, INFO_IMAGE_LINK, INFO_IMAGE_HASH, INFO_IMAGE_SESS, INFO_SIGN_LINK, INFO_SIGN_HASH, INFO_SIGN_SESS]
|
INFO_REQURED = [INFO_PRODUCT, INFO_IMAGE_LINK, INFO_IMAGE_HASH, INFO_IMAGE_SESS, INFO_SIGN_LINK, INFO_SIGN_HASH, INFO_SIGN_SESS]
|
||||||
|
|
||||||
|
# Use -2 for better resize stability on Windows
|
||||||
|
TERMINAL_MARGIN = 2
|
||||||
|
|
||||||
def run_query(url, headers, post=None, raw=False):
|
def run_query(url, headers, post=None, raw=False):
|
||||||
if post is not None:
|
if post is not None:
|
||||||
data = '\n'.join([entry + '=' + post[entry] for entry in post])
|
data = '\n'.join(entry + '=' + post[entry] for entry in post).encode()
|
||||||
if sys.version_info[0] >= 3:
|
|
||||||
data = data.encode('utf-8')
|
|
||||||
else:
|
else:
|
||||||
data = None
|
data = None
|
||||||
req = Request(url=url, headers=headers, data=data)
|
req = Request(url=url, headers=headers, data=data)
|
||||||
|
@ -72,12 +73,11 @@ def run_query(url, headers, post=None, raw=False):
|
||||||
|
|
||||||
|
|
||||||
def generate_id(id_type, id_value=None):
|
def generate_id(id_type, id_value=None):
|
||||||
valid_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
|
return id_value or ''.join(random.choices(string.hexdigits[:16].upper(), k=id_type))
|
||||||
return ''.join(random.choice(valid_chars) for i in range(id_type)) if not id_value else id_value
|
|
||||||
|
|
||||||
|
|
||||||
def product_mlb(mlb):
|
def product_mlb(mlb):
|
||||||
return '00000000000' + mlb[11] + mlb[12] + mlb[13] + mlb[14] + '00'
|
return '00000000000' + mlb[11:15] + '00'
|
||||||
|
|
||||||
|
|
||||||
def mlb_from_eeee(eeee):
|
def mlb_from_eeee(eeee):
|
||||||
|
@ -88,13 +88,6 @@ def mlb_from_eeee(eeee):
|
||||||
return f'00000000000{eeee}00'
|
return f'00000000000{eeee}00'
|
||||||
|
|
||||||
|
|
||||||
def int_from_unsigned_bytes(byte_list, byteorder):
|
|
||||||
if byteorder == 'little':
|
|
||||||
byte_list = byte_list[::-1]
|
|
||||||
encoded = binascii.hexlify(byte_list)
|
|
||||||
return int(encoded, 16)
|
|
||||||
|
|
||||||
|
|
||||||
# zhangyoufu https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7#gistcomment-3061078 2021-10-08
|
# zhangyoufu https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7#gistcomment-3061078 2021-10-08
|
||||||
Apple_EFI_ROM_public_key_1 = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF
|
Apple_EFI_ROM_public_key_1 = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF
|
||||||
|
|
||||||
|
@ -128,8 +121,8 @@ def verify_chunklist(cnkpath):
|
||||||
if signature_method == 1:
|
if signature_method == 1:
|
||||||
data = f.read(256)
|
data = f.read(256)
|
||||||
assert len(data) == 256
|
assert len(data) == 256
|
||||||
signature = int_from_unsigned_bytes(data, 'little')
|
signature = int.from_bytes(data, 'little')
|
||||||
plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int_from_unsigned_bytes(digest, 'big')
|
plaintext = int(f'0x1{"f"*404}003031300d060960864801650304020105000420{"0"*64}', 16) | int.from_bytes(digest, 'big')
|
||||||
assert pow(signature, 0x10001, Apple_EFI_ROM_public_key_1) == plaintext
|
assert pow(signature, 0x10001, Apple_EFI_ROM_public_key_1) == plaintext
|
||||||
elif signature_method == 2:
|
elif signature_method == 2:
|
||||||
data = f.read(32)
|
data = f.read(32)
|
||||||
|
@ -194,7 +187,9 @@ def get_image_info(session, bid, mlb=MLB_ZERO, diag=False, os_type='default', ci
|
||||||
try:
|
try:
|
||||||
key, value = line.split(': ')
|
key, value = line.split(': ')
|
||||||
info[key] = value
|
info[key] = value
|
||||||
except Exception:
|
except KeyError:
|
||||||
|
continue
|
||||||
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for k in INFO_REQURED:
|
for k in INFO_REQURED:
|
||||||
|
@ -214,34 +209,47 @@ def save_image(url, sess, filename='', directory=''):
|
||||||
}
|
}
|
||||||
|
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.mkdir(directory)
|
os.makedirs(directory)
|
||||||
|
|
||||||
if filename == '':
|
if filename == '':
|
||||||
filename = os.path.basename(purl.path)
|
filename = os.path.basename(purl.path)
|
||||||
if filename.find('/') >= 0 or filename == '':
|
if filename.find(os.sep) >= 0 or filename == '':
|
||||||
raise RuntimeError('Invalid save path ' + filename)
|
raise RuntimeError('Invalid save path ' + filename)
|
||||||
|
|
||||||
print(f'Saving {url} to {directory}/{filename}...')
|
print(f'Saving {url} to {directory}{os.sep}{filename}...')
|
||||||
|
|
||||||
with open(os.path.join(directory, filename), 'wb') as fh:
|
with open(os.path.join(directory, filename), 'wb') as fh:
|
||||||
response = run_query(url, headers, raw=True)
|
response = run_query(url, headers, raw=True)
|
||||||
total_size = int(response.headers['content-length']) / float(2 ** 20)
|
headers = dict(response.headers)
|
||||||
# print(total_size)
|
totalsize = -1
|
||||||
if total_size < 1:
|
for header in headers:
|
||||||
total_size = response.headers['content-length']
|
if header.lower() == 'content-length':
|
||||||
print("Note: The total download size is %s bytes" % total_size)
|
totalsize = int(headers[header])
|
||||||
else:
|
break
|
||||||
print("Note: The total download size is %0.2f MB" % total_size)
|
|
||||||
size = 0
|
size = 0
|
||||||
|
oldterminalsize = 0
|
||||||
while True:
|
while True:
|
||||||
chunk = response.read(2**20)
|
chunk = response.read(2**20)
|
||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
fh.write(chunk)
|
fh.write(chunk)
|
||||||
size += len(chunk)
|
size += len(chunk)
|
||||||
print(f'\r{size / (2**20)} MBs downloaded...', end='')
|
terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
|
||||||
|
if oldterminalsize != terminalsize:
|
||||||
|
print(f'\r{"":<{terminalsize}}', end='')
|
||||||
|
oldterminalsize = terminalsize
|
||||||
|
if totalsize > 0:
|
||||||
|
progress = size / totalsize
|
||||||
|
barwidth = terminalsize // 3
|
||||||
|
print(f'\r{size / (2**20):.1f}/{totalsize / (2**20):.1f} MB ', end='')
|
||||||
|
if terminalsize > 55:
|
||||||
|
print(f'|{"=" * int(barwidth * progress):<{barwidth}}|', end='')
|
||||||
|
print(f' {progress*100:.1f}% downloaded', end='')
|
||||||
|
else:
|
||||||
|
# Fallback if Content-Length isn't available
|
||||||
|
print(f'\r{size / (2**20)} MB downloaded...', end='')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
print('\rDownload complete!\t\t\t\t\t')
|
print('\nDownload complete!')
|
||||||
|
|
||||||
return os.path.join(directory, os.path.basename(filename))
|
return os.path.join(directory, os.path.basename(filename))
|
||||||
|
|
||||||
|
@ -250,10 +258,9 @@ def verify_image(dmgpath, cnkpath):
|
||||||
print('Verifying image with chunklist...')
|
print('Verifying image with chunklist...')
|
||||||
|
|
||||||
with open(dmgpath, 'rb') as dmgf:
|
with open(dmgpath, 'rb') as dmgf:
|
||||||
cnkcount = 0
|
for cnkcount, (cnksize, cnkhash) in enumerate(verify_chunklist(cnkpath), 1):
|
||||||
for cnksize, cnkhash in verify_chunklist(cnkpath):
|
terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
|
||||||
cnkcount += 1
|
print(f'\r{f"Chunk {cnkcount} ({cnksize} bytes)":<{terminalsize}}', end='')
|
||||||
print(f'\rChunk {cnkcount} ({cnksize} bytes)', end='')
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
cnk = dmgf.read(cnksize)
|
cnk = dmgf.read(cnksize)
|
||||||
if len(cnk) != cnksize:
|
if len(cnk) != cnksize:
|
||||||
|
@ -262,7 +269,7 @@ def verify_image(dmgpath, cnkpath):
|
||||||
raise RuntimeError(f'Invalid chunk {cnkcount}: hash mismatch')
|
raise RuntimeError(f'Invalid chunk {cnkcount}: hash mismatch')
|
||||||
if dmgf.read(1) != b'':
|
if dmgf.read(1) != b'':
|
||||||
raise RuntimeError('Invalid image: larger than chunklist')
|
raise RuntimeError('Invalid image: larger than chunklist')
|
||||||
print('\rImage verification complete!\t\t\t\t\t')
|
print('\nImage verification complete!')
|
||||||
|
|
||||||
|
|
||||||
def action_download(args):
|
def action_download(args):
|
||||||
|
@ -298,10 +305,10 @@ def action_download(args):
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
print(info)
|
print(info)
|
||||||
print(f'Downloading {info[INFO_PRODUCT]}...')
|
print(f'Downloading {info[INFO_PRODUCT]}...')
|
||||||
dmgname = '' if args.basename == '' else args.basename + '.dmg'
|
|
||||||
dmgpath = save_image(info[INFO_IMAGE_LINK], info[INFO_IMAGE_SESS], dmgname, args.outdir)
|
|
||||||
cnkname = '' if args.basename == '' else args.basename + '.chunklist'
|
cnkname = '' if args.basename == '' else args.basename + '.chunklist'
|
||||||
cnkpath = save_image(info[INFO_SIGN_LINK], info[INFO_SIGN_SESS], cnkname, args.outdir)
|
cnkpath = save_image(info[INFO_SIGN_LINK], info[INFO_SIGN_SESS], cnkname, args.outdir)
|
||||||
|
dmgname = '' if args.basename == '' else args.basename + '.dmg'
|
||||||
|
dmgpath = save_image(info[INFO_IMAGE_LINK], info[INFO_IMAGE_SESS], dmgname, args.outdir)
|
||||||
try:
|
try:
|
||||||
verify_image(dmgpath, cnkpath)
|
verify_image(dmgpath, cnkpath)
|
||||||
return 0
|
return 0
|
||||||
|
@ -374,7 +381,7 @@ def action_selfcheck(args):
|
||||||
if product_default[INFO_PRODUCT] != valid_default[INFO_PRODUCT]:
|
if product_default[INFO_PRODUCT] != valid_default[INFO_PRODUCT]:
|
||||||
# Product-only MLB can give the same value with valid default MLB.
|
# Product-only MLB can give the same value with valid default MLB.
|
||||||
# This is not an error for all models, but for our chosen code it is.
|
# This is not an error for all models, but for our chosen code it is.
|
||||||
print('ERROR: Valid and product MLB give mismatch, got {product_default[INFO_PRODUCT]} and {valid_default[INFO_PRODUCT]}')
|
print(f'ERROR: Valid and product MLB give mismatch, got {product_default[INFO_PRODUCT]} and {valid_default[INFO_PRODUCT]}')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
print('SUCCESS: Found no discrepancies with MLB validation algorithm!')
|
print('SUCCESS: Found no discrepancies with MLB validation algorithm!')
|
||||||
|
@ -523,6 +530,7 @@ def main():
|
||||||
|
|
||||||
# No action specified, so present a download menu instead
|
# No action specified, so present a download menu instead
|
||||||
# https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macrecovery/boards.json
|
# https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macrecovery/boards.json
|
||||||
|
# https://github.com/corpnewt/gibMacOS
|
||||||
products = [
|
products = [
|
||||||
{"name": "High Sierra (10.13)", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000J80300", "short": "high-sierra"},
|
{"name": "High Sierra (10.13)", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000J80300", "short": "high-sierra"},
|
||||||
{"name": "Mojave (10.14)", "b": "Mac-7BA5B2DFE22DDD8C", "m": "00000000000KXPG00", "short": "mojave"},
|
{"name": "Mojave (10.14)", "b": "Mac-7BA5B2DFE22DDD8C", "m": "00000000000KXPG00", "short": "mojave"},
|
||||||
|
@ -530,7 +538,8 @@ def main():
|
||||||
{"name": "Big Sur (11.7)", "b": "Mac-2BD1B31983FE1663", "m": "00000000000000000", "short": "big-sur"},
|
{"name": "Big Sur (11.7)", "b": "Mac-2BD1B31983FE1663", "m": "00000000000000000", "short": "big-sur"},
|
||||||
{"name": "Monterey (12.6)", "b": "Mac-B809C3757DA9BB8D", "m": "00000000000000000", "os_type": "latest", "short": "monterey"},
|
{"name": "Monterey (12.6)", "b": "Mac-B809C3757DA9BB8D", "m": "00000000000000000", "os_type": "latest", "short": "monterey"},
|
||||||
{"name": "Ventura (13) - RECOMMENDED", "b": "Mac-4B682C642B45593E", "m": "00000000000000000", "os_type": "latest", "short": "ventura"},
|
{"name": "Ventura (13) - RECOMMENDED", "b": "Mac-4B682C642B45593E", "m": "00000000000000000", "os_type": "latest", "short": "ventura"},
|
||||||
{"name": "Sonoma (14) ", "b": "Mac-A61BADE1FDAD7B05", "m": "00000000000000000", "short": "sonoma"}
|
{"name": "Sonoma (14) ", "b": "Mac-827FAC58A8FDFA22", "m": "00000000000000000", "short": "sonoma"},
|
||||||
|
{"name": "Sequoia (15) ", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000000000", "short": "Sequoia", "os_type": "latest"},
|
||||||
]
|
]
|
||||||
for index, product in enumerate(products):
|
for index, product in enumerate(products):
|
||||||
name = product["name"]
|
name = product["name"]
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
Loading…
Reference in New Issue