From 2029ffc8b78b19acf61124979cd07b7d94e7a69a Mon Sep 17 00:00:00 2001 From: platomav Date: Mon, 28 Mar 2022 00:58:07 +0300 Subject: [PATCH] Apple EFI Package Grabber v2.0 Parses user-provided (DB) list of Apple Software Update CatalogURL .sucatalog links and saves all newer (since last run) EFI firmware package links into a text file. It removes any xml formatting, ignores false positives, removes duplicate links and sorts them in alphabetical order for easy comparison afterwards. --- Apple EFI Package Grabber/Apple_EFI_Grab.dat | 4 + Apple EFI Package Grabber/Apple_EFI_Grab.py | 129 ++++++++++++++++++ .../Apple_EFI_Links.py | 71 ---------- README.md | 12 +- 4 files changed, 139 insertions(+), 77 deletions(-) create mode 100644 Apple EFI Package Grabber/Apple_EFI_Grab.dat create mode 100644 Apple EFI Package Grabber/Apple_EFI_Grab.py delete mode 100644 Apple EFI Sucatalog Link Grabber/Apple_EFI_Links.py diff --git a/Apple EFI Package Grabber/Apple_EFI_Grab.dat b/Apple EFI Package Grabber/Apple_EFI_Grab.dat new file mode 100644 index 0000000..5740129 --- /dev/null +++ b/Apple EFI Package Grabber/Apple_EFI_Grab.dat @@ -0,0 +1,4 @@ +2021-01-01 00:00:00 + + + diff --git a/Apple EFI Package Grabber/Apple_EFI_Grab.py b/Apple EFI Package Grabber/Apple_EFI_Grab.py new file mode 100644 index 0000000..c4d544b --- /dev/null +++ b/Apple EFI Package Grabber/Apple_EFI_Grab.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +#coding=utf-8 + +""" +Apple EFI Grab +Apple EFI Package Grabber +Copyright (C) 2018-2021 Plato Mavropoulos +""" + +title = 'Apple EFI Package Grabber v2.0' + +print('\n' + title) + +import sys + +sys_ver = sys.version_info +if sys_ver < (3,7): + sys.stdout.write('\n\nError: Python >= 3.7 required, not %d.%d!\n' % (sys_ver[0], sys_ver[1])) + (raw_input if sys_ver[0] <= 2 else input)('\nPress enter to exit') # pylint: disable=E0602 + sys.exit(1) + +import traceback + +def show_exception_and_exit(exc_type, exc_value, tb): + if exc_type is KeyboardInterrupt: + print('\nNote: Keyboard Interrupt!') + else: + print('\nError: %s crashed, please report the following:\n' % title) + traceback.print_exception(exc_type, exc_value, tb) + input('\nPress enter to exit') + + sys.exit(1) + +sys.excepthook = show_exception_and_exit + +import datetime +import urllib.request +from multiprocessing.pool import ThreadPool + +def fetch_cat_info(name): + url = cat_url[:-len('others/')] + name if name in ['index.sucatalog','index-1.sucatalog'] else cat_url + name + with urllib.request.urlopen(urllib.request.Request(url, method='HEAD')) as head : mod = head.headers['last-modified'] + + return name, url, mod + +def fetch_cat_links(cat_file): + cat_links = [] + + with urllib.request.urlopen(cat_file[1]) as link: fdata = link.readlines() + + cat_lines = [l.decode('utf-8').strip('\n') for l in fdata] + + for line in cat_lines: + if ('.pkg' in line or '.tar' in line) and ('FirmwareUpd' in line or '/BridgeOSUpdateCustomer' in line or 'EFIUpd' in line) \ + and 'Bluetooth' not in line and 'DPVGA' not in line and 'Thunderbolt' not in line and 'PMG5' not in line and 'HardDrive' not in line: + down_link = line[line.find('http'):(line.find('.pkg') if '.pkg' in line else line.find('.tar')) + 4] + down_link = down_link.replace('http:','https:') + cat_links.append(down_link) + + return cat_links + +dat_db = 'Apple_EFI_Grab.dat' +cat_url = 'https://swscan.apple.com/content/catalogs/others/' +apple_cat = [] +down_links = [] +svr_date = None +thread_num = 2 + +with open(dat_db, 'r', encoding='utf-8') as dat: db_lines = dat.readlines() +db_lines = [line.strip('\n') for line in db_lines] + +db_date = datetime.datetime.strptime(db_lines[0], '%Y-%m-%d %H:%M:%S') +db_links = set([line for line in db_lines if line.startswith('https')]) +db_sucat = [line for line in db_lines if line.startswith('index')] + +print('\nGetting Catalog Listing...') + +if not db_sucat: + input('\nError: Failed to retrieve Catalogs from DB!\n\nDone!') + sys.exit(1) + +apple_mod = ThreadPool(thread_num).imap_unordered(fetch_cat_info, db_sucat) + +for name, url, mod in apple_mod: + dt = datetime.datetime.strptime(mod, '%a, %d %b %Y %H:%M:%S %Z') + if not svr_date or dt > svr_date : svr_date = dt + + apple_cat.append((name, url, dt)) + +if not svr_date: + input('\nError: Failed to retrieve Current Catalog Datetime!\n\nDone!') + sys.exit(1) + +print('\n Previous Catalog Datetime :', db_date) +print(' Current Catalog Datetime :', svr_date) + +if svr_date <= db_date: + input('\nNothing new since %s!\n\nDone!' % db_date) + sys.exit() + +print('\nGetting Catalog Links...') + +down_links = ThreadPool(thread_num).imap_unordered(fetch_cat_links, apple_cat) +down_links = [item for sublist in down_links for item in sublist] + +if not down_links: + input('\nError: Failed to retrieve Catalog Links!\n\nDone!') + sys.exit(1) + +new_links = sorted(list(dict.fromkeys([link for link in down_links if link not in db_links]))) + +if new_links: + print('\nFound %d new link(s) between %s and %s!' % (len(new_links), db_date, svr_date)) + + cur_date = datetime.datetime.utcnow().isoformat(timespec='seconds').replace('-','').replace('T','').replace(':','') # Local UTC Unix + + with open('Apple_%s.txt' % cur_date, 'w', encoding='utf-8') as lout: lout.write('\n'.join(map(str, new_links))) +else: + print('\nThere are no new links between %s and %s!' % (db_date, svr_date)) + +new_db_sucat = '\n'.join(map(str, db_sucat)) + +new_db_links = '\n'.join(map(str, sorted(list(dict.fromkeys(down_links))))) + +new_db_lines = '%s\n\n%s\n\n%s' % (svr_date, new_db_sucat, new_db_links) + +with open(dat_db, 'w', encoding='utf-8') as dbout: dbout.write(new_db_lines) + +input('\nDone!') \ No newline at end of file diff --git a/Apple EFI Sucatalog Link Grabber/Apple_EFI_Links.py b/Apple EFI Sucatalog Link Grabber/Apple_EFI_Links.py deleted file mode 100644 index 9e9c124..0000000 --- a/Apple EFI Sucatalog Link Grabber/Apple_EFI_Links.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 - -""" -Apple EFI Links -Apple EFI Sucatalog Link Grabber -Copyright (C) 2018-2019 Plato Mavropoulos -""" - -print('Apple EFI Sucatalog Link Grabber v1.2\n') - -import os -import sys -import datetime - -# Remove previous output files -if os.path.isfile('OUT.txt') : os.remove('OUT.txt') - -# Get input catalog file paths -if len(sys.argv) >= 2 : - # Drag & Drop or CLI - catalogs = sys.argv[1:] -else : - # Working directory - catalogs = [] - for root, dirs, files in os.walk(os.getcwd()) : - for name in files : - if name.endswith('.sucatalog') : - catalogs.append(os.path.join(root, name)) - -print('Working...') - -# Parse each input xml file -for input_file in catalogs : - input_name,input_extension = os.path.splitext(os.path.basename(input_file)) - - print('\n%s%s' % (input_name, input_extension)) - - with open(input_file, 'r') as in_file : - for line in in_file : - # Find EFI Firmware package links - if ('.pkg' in line or '.tar' in line) and ('FirmwareUpd' in line or '/BridgeOSUpdateCustomer' in line or 'EFIUpd' in line) \ - and 'Bluetooth' not in line and 'DPVGA' not in line and 'Thunderbolt' not in line and 'PMG5' not in line and 'HardDrive' not in line : - if '.pkg' in line : link = line[line.find('http'):line.find('.pkg') + 4] # Remove xml formatting - else : link = line[line.find('http'):line.find('.tar') + 4] - - with open('OUT.txt', 'a') as out_file : out_file.write(link + '\n') # Store links in temporary output file - -# Parse temporary output file -if os.path.isfile('OUT.txt') : - with open('OUT.txt', 'r+') as out_file : - parsed_lines = [] - final_lines = [] - - for line in out_file : - if line not in parsed_lines : # Remove duplicate links - final_lines.append(line) - parsed_lines.append(line) - - final_lines = ''.join(map(str, sorted(final_lines))) - - current_datetime = datetime.datetime.utcnow().isoformat(timespec='seconds').replace('-','').replace('T','').replace(':','') - - output_file = 'EFI %s.txt' % current_datetime - - with open(output_file, 'w') as efi_file : efi_file.write(final_lines) # Save final output file - - print('\nStored %s!' % output_file) - - os.remove('OUT.txt') # Remove temporary output file - -input('\nDone!') \ No newline at end of file diff --git a/README.md b/README.md index 0f77960..e51f5ac 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ * [**Fujitsu UPC BIOS Extractor**](#fujitsu-upc-bios-extractor) * [**Fujitsu SFX BIOS Extractor**](#fujitsu-sfx-bios-extractor) * [**Award BIOS Module Extractor**](#award-bios-module-extractor) -* [**Apple EFI Sucatalog Link Grabber**](#apple-efi-sucatalog-link-grabber) +* [**Apple EFI Package Grabber**](#apple-efi-package-grabber) * [**Apple EFI File Renamer**](#apple-efi-file-renamer) * [**Apple EFI IM4P Splitter**](#apple-efi-im4p-splitter) * [**Apple EFI Package Extractor**](#apple-efi-package-extractor) @@ -384,17 +384,17 @@ Some Anti-Virus software may claim that the built/frozen/compiled executable con ![](https://i.imgur.com/EhCzMLk.png) -## **Apple EFI Sucatalog Link Grabber** +## **Apple EFI Package Grabber** -![](https://i.imgur.com/zTVFs4I.png) +![](https://i.imgur.com/BaHrjGi.png) #### **Description** -Parses Apple Software Update CatalogURL .sucatalog files and saves all EFI firmware package links into a text file. It removes any xml formatting, ignores false positives, removes duplicate links and sorts them in alphabetical order for easy comparison afterwards. +Parses user-provided (DB) list of Apple Software Update CatalogURL .sucatalog links and saves all newer (since last run) EFI firmware package links into a text file. It removes any xml formatting, ignores false positives, removes duplicate links and sorts them in alphabetical order for easy comparison afterwards. #### **Usage** -You can either Drag & Drop or let it automatically parse any .sucatalog files within its working directory. +First, you need to familiarize a bit with the DB (i.e. Apple_EFI_Grab.dat file). It consists of 3 sections: Last run DateTime (YYYY-MM-DD HH:MM:SS), Sucatalog links to check and EFI Package links which have been gathered so far across all runs. Before running the utility for the fist time, you need to insert the Sucatalog links into the DB, below the 1st line (DateTime). The Sucatalog links in the DB are stored in partial form, starting from "index" string. For example: "https://swscan.apple.com/content/catalogs/others/index-12.merged-1.sucatalog" must be stored as "index-12.merged-1.sucatalog" in the DB. The Sucatalog links are not pre-included in the DB but you can find them online (e.g. https://github.com/zhangyoufu/swscan.apple.com/blob/master/url.txt). #### **Download** @@ -422,7 +422,7 @@ PyInstaller can build/freeze/compile the utility at all three supported platform 3. Build/Freeze/Compile: -> pyinstaller --noupx --onefile Apple_EFI_Links.py +> pyinstaller --noupx --onefile Apple_EFI_Grab.py At dist folder you should find the final utility executable