diff --git a/.gitignore b/.gitignore index e43b0f9..876ddd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .DS_Store +BaseSystem.img +BaseSystem.dmg +BaseSystem.chunklist diff --git a/basic.sh b/basic.sh new file mode 100755 index 0000000..e0bcaca --- /dev/null +++ b/basic.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +VMDIR=$PWD +OVMF=$VMDIR/firmware +#export QEMU_AUDIO_DRV=pa +#QEMU_AUDIO_DRV=pa + +qemu-system-x86_64 \ + -enable-kvm \ + -m 2G \ + -machine q35,accel=kvm \ + -smp 4,cores=2 \ + -cpu Penryn,vendor=GenuineIntel,kvm=on,+sse3,+sse4.2,+aes,+xsave,+avx,+xsaveopt,+xsavec,+xgetbv1,+avx2,+bmi2,+smep,+bmi1,+fma,+movbe,+invtsc \ + -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \ + -smbios type=2 \ + -drive if=pflash,format=raw,readonly,file=$OVMF/OVMF_CODE.fd \ + -drive if=pflash,format=raw,file=$OVMF/OVMF_VARS-1024x768.fd \ + -vga qxl \ + -device ich9-intel-hda -device hda-output \ + -usb -device usb-kbd -device usb-tablet \ + -netdev user,id=net0 \ + -device e1000-82545em,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \ + -device ich9-ahci,id=sata \ + -drive id=ESP,if=none,format=qcow2,file=ESP.qcow2 \ + -device ide-hd,bus=sata.2,drive=ESP \ + -drive id=InstallMedia,if=none,file=BaseSystem.img \ + -device ide-hd,bus=sata.3,drive=InstallMedia \ diff --git a/firmware/OVMF_CODE.fd b/firmware/OVMF_CODE.fd new file mode 100644 index 0000000..dd85896 Binary files /dev/null and b/firmware/OVMF_CODE.fd differ diff --git a/firmware/OVMF_VARS-1024x768.fd b/firmware/OVMF_VARS-1024x768.fd new file mode 100644 index 0000000..fbc34ca Binary files /dev/null and b/firmware/OVMF_VARS-1024x768.fd differ diff --git a/jumpstart.sh b/jumpstart.sh new file mode 100755 index 0000000..080e5fe --- /dev/null +++ b/jumpstart.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# jumpstart.sh: Fetches BaseSystem and converts it to a viable format. + +TOOLS=$PWD/tools + +$TOOLS/FetchMacOS/fetch.sh +$TOOLS/dmg2img $TOOLS/FetchMacOS/BaseSystem/BaseSystem.dmg $PWD/BaseSystem.img diff --git a/tools/FetchMacOS/.idea/FetchMacOS.iml b/tools/FetchMacOS/.idea/FetchMacOS.iml new file mode 100644 index 0000000..6711606 --- /dev/null +++ b/tools/FetchMacOS/.idea/FetchMacOS.iml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tools/FetchMacOS/.idea/misc.xml b/tools/FetchMacOS/.idea/misc.xml new file mode 100644 index 0000000..a17801f --- /dev/null +++ b/tools/FetchMacOS/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tools/FetchMacOS/.idea/modules.xml b/tools/FetchMacOS/.idea/modules.xml new file mode 100644 index 0000000..679a9f7 --- /dev/null +++ b/tools/FetchMacOS/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tools/FetchMacOS/.idea/workspace.xml b/tools/FetchMacOS/.idea/workspace.xml new file mode 100644 index 0000000..22eee8a --- /dev/null +++ b/tools/FetchMacOS/.idea/workspace.xml @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1499116578112 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/FetchMacOS/fetch-macos.py b/tools/FetchMacOS/fetch-macos.py new file mode 100755 index 0000000..c6ea87c --- /dev/null +++ b/tools/FetchMacOS/fetch-macos.py @@ -0,0 +1,124 @@ +#!/usr/bin/python + +"""fetch-macos.py: Fetches macOS products from Apple's SoftwareUpdate service.""" + +import logging +import plistlib +import os +import errno +import click +import requests + +__author__ = "Foxlet" +__copyright__ = "Copyright 2018, FurCode Project" +__license__ = "GPLv3" +__version__ = "1.2" + +logging.basicConfig(format='%(asctime)-15s %(message)s', level=logging.INFO) +logger = logging.getLogger('webactivity') + + +class ClientMeta: + # Client used to connect to the Software CDN + osinstall = {"User-Agent":"osinstallersetupplaind (unknown version) CFNetwork/720.5.7 Darwin/14.5.0 (x86_64)"} + # Client used to connect to the Software Distribution service + swupdate = {"User-Agent":"Software%20Update (unknown version) CFNetwork/807.0.1 Darwin/16.0.0 (x86_64)"} + + +class Filesystem: + @staticmethod + def download_file(url, size, path): + label = url.split('/')[-1] + filename = os.path.join(path, label) + # Set to stream mode for large files + remote = requests.get(url, stream=True, headers=ClientMeta.osinstall) + + with open(filename, 'wb') as f: + with click.progressbar(remote.iter_content(1024), length=size/1024, label="Fetching {} ...".format(filename)) as stream: + for data in stream: + f.write(data) + return filename + + @staticmethod + def check_directory(path): + try: + os.makedirs(path) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + +class SoftwareService: + # macOS 10.14 ships in 4 different catalogs from SoftwareScan + catalogs = {"CustomerSeed":"https://swscan.apple.com/content/catalogs/others/index-10.14customerseed-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog", + "DeveloperSeed":"https://swscan.apple.com/content/catalogs/others/index-10.14seed-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog", + "PublicSeed":"https://swscan.apple.com/content/catalogs/others/index-10.14beta-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog", + "PublicRelease":"https://swscan.apple.com/content/catalogs/others/index-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"} + + def __init__(self, catalog_id): + self.catalog_url = self.catalogs.get(catalog_id, self.catalogs["PublicRelease"]) + self.catalog_data = "" + + def getcatalog(self): + logging.info("Network Request: %s", "Fetching {}".format(self.catalog_url)) + catalog_raw = requests.get(self.catalog_url, headers=ClientMeta.swupdate) + self.catalog_data = catalog_raw.text.encode('UTF-8') + return catalog_raw.text.encode('UTF-8') + + def getosinstall(self): + root = plistlib.readPlistFromString(self.catalog_data) + products = root['Products'] + for product in products: + if 'ExtendedMetaInfo' in products[product]: + IAMetaInfo = products[product]['ExtendedMetaInfo'] + if 'InstallAssistantPackageIdentifiers' in IAMetaInfo: + IAPackageID = IAMetaInfo['InstallAssistantPackageIdentifiers'] + if IAPackageID['OSInstall'] == 'com.apple.mpkg.OSInstall': + return product + + +class MacOSProduct: + def __init__(self, catalog, product_id): + root = plistlib.readPlistFromString(catalog) + products = root['Products'] + self.date = root['IndexDate'] + self.product = products[product_id] + + def fetchpackages(self, path): + Filesystem.check_directory(path) + packages = self.product['Packages'] + for item in packages: + if "BaseSystem" in item.get("URL"): + Filesystem.download_file(item.get("URL"), item.get("Size"), path) + + +@click.command() +@click.option('-o', '--output-dir', default="BaseSystem/", help="Target directory for package output.") +@click.option('-c', '--catalog-id', default="DeveloperSeed", help="Name of catalog.") +@click.option('-p', '--product-id', default="", help="Product ID (as seen in SoftwareUpdate).") +@click.option('-l', '--latest', is_flag=True, help="Get latest available macOS package.") +def fetchmacos(output_dir="BaseSystem/", catalog_id="DeveloperSeed", product_id="", latest=False): + # Get the remote catalog data + remote = SoftwareService(catalog_id) + catalog = remote.getcatalog() + + # Get the current macOS package + if latest: + product_id = remote.getosinstall() + else: + if product_id == "": + print "You must provide a Product ID (or pass the -l flag) to continue." + exit(1) + product_id = product_id + try: + update = MacOSProduct(catalog, product_id) + except KeyError: + print "Product ID {} could not be found.".format(product_id) + exit(1) + logging.info("Selected macOS Product: {}".format(product_id)) + + # Download package to disk + update.fetchpackages(output_dir) + +if __name__ == "__main__": + fetchmacos() diff --git a/tools/FetchMacOS/fetch.sh b/tools/FetchMacOS/fetch.sh new file mode 100755 index 0000000..9e278d0 --- /dev/null +++ b/tools/FetchMacOS/fetch.sh @@ -0,0 +1,7 @@ +set +x; +SCRIPTDIR="$(dirname "$0")"; +cd $SCRIPTDIR +sudo easy_install pip +sudo -H pip install -r requirements.txt +python fetch-macos.py -l +exit; diff --git a/tools/FetchMacOS/requirements.txt b/tools/FetchMacOS/requirements.txt new file mode 100644 index 0000000..f80ea3b --- /dev/null +++ b/tools/FetchMacOS/requirements.txt @@ -0,0 +1,2 @@ +requests +click diff --git a/tools/dmg2img b/tools/dmg2img new file mode 100755 index 0000000..a4aac3a Binary files /dev/null and b/tools/dmg2img differ