From 38aaeb6d8cb5870af3aa8982541a11f885866b63 Mon Sep 17 00:00:00 2001 From: doronz88 Date: Wed, 30 Oct 2024 14:11:28 +0200 Subject: [PATCH] cli: add `--pem-db` option --- ipsw_parser/__main__.py | 18 +++++++++++++----- ipsw_parser/build_identity.py | 23 ++++++++++++++++------- ipsw_parser/ipsw.py | 5 +++-- requirements.txt | 1 + 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/ipsw_parser/__main__.py b/ipsw_parser/__main__.py index 297e23a..0e6cf6a 100644 --- a/ipsw_parser/__main__.py +++ b/ipsw_parser/__main__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import logging from pathlib import Path -from typing import IO +from typing import IO, Optional from zipfile import ZipFile import click @@ -21,6 +21,12 @@ logger = logging.getLogger(__name__) +PEM_DB_ENV_VAR = 'IPSW_PARSER_PEM_DB' + +pem_db_option = click.option('--pem-db', envvar=PEM_DB_ENV_VAR, + help='Path DB file url (can be either a filesystem path or an HTTP URL). ' + 'Alternatively, use the IPSW_PARSER_PEM_DB envvar.') + @click.group() def cli() -> None: @@ -47,7 +53,8 @@ def info(file) -> None: @cli.command('extract') @click.argument('file', type=click.Path(exists=True, file_okay=True, dir_okay=False)) @click.argument('output', type=click.Path(exists=False)) -def extract(file: IO, output: str) -> None: +@pem_db_option +def extract(file: IO, output: str, pem_db: Optional[str]) -> None: """ Extract .ipsw into filesystem layout """ output = Path(output) @@ -55,16 +62,17 @@ def extract(file: IO, output: str) -> None: output.mkdir(parents=True, exist_ok=True) ipsw = IPSW(ZipFile(file)) - ipsw.build_manifest.build_identities[0].extract(output) + ipsw.build_manifest.build_identities[0].extract(output, pem_db=pem_db) ipsw.archive.extractall( path=output, members=[f for f in ipsw.archive.filelist if f.filename.startswith('Firmware')]) @cli.command('device-support') @click.argument('file', type=click.Path(exists=True, file_okay=True, dir_okay=False)) -def device_support(file: IO) -> None: +@pem_db_option +def device_support(file: IO, pem_db: Optional[str]) -> None: """ Create DeviceSupport directory """ - IPSW(ZipFile(file)).create_device_support() + IPSW(ZipFile(file)).create_device_support(pem_db=pem_db) if __name__ == '__main__': diff --git a/ipsw_parser/build_identity.py b/ipsw_parser/build_identity.py index dd95628..7ff9420 100644 --- a/ipsw_parser/build_identity.py +++ b/ipsw_parser/build_identity.py @@ -5,6 +5,7 @@ from tempfile import TemporaryDirectory from typing import Optional +import requests from cached_property import cached_property from plumbum import ProcessExecutionError, local from pyimg4 import IM4P @@ -14,7 +15,7 @@ logger = logging.getLogger(__name__) -def _extract_dmg(buf: bytes, output: Path, sub_path: Optional[Path] = None) -> None: +def _extract_dmg(buf: bytes, output: Path, sub_path: Optional[Path] = None, pem_db: Optional[str] = None) -> None: ipsw = local['ipsw'] hdiutil = local['hdiutil'] # darwin system statistically have problems cleaning up after detaching the mountpoint @@ -28,7 +29,15 @@ def _extract_dmg(buf: bytes, output: Path, sub_path: Optional[Path] = None) -> N logger.debug('Found Apple Encrypted Archive. Decrypting...') dmg_aea = Path(str(dmg) + '.aea') dmg_aea.write_bytes(buf) - ipsw('fw', 'aea', dmg_aea, '-o', temp_dir) + args = ['fw', 'aea', dmg_aea, '-o', temp_dir] + if pem_db is not None: + if '://' in pem_db: + # create a local file containing it + temp_pem_db = temp_dir / 'pem-db.json' + temp_pem_db.write_text(requests.get(pem_db, verify=False).text) + pem_db = temp_pem_db + args += ['--pem-db', pem_db] + ipsw(args) else: dmg.write_bytes(buf) @@ -137,7 +146,7 @@ def populate_tss_request_parameters(self, parameters: dict, additional_keys: Opt if requires_uid_mode is not None: parameters['RequiresUIDMode'] = requires_uid_mode - def extract_dsc(self, output: Path) -> None: + def extract_dsc(self, output: Path, pem_db: Optional[str] = None) -> None: build_identity = self.build_manifest.build_identities[0] if not build_identity.has_component('Cryptex1,SystemOS'): return @@ -146,16 +155,16 @@ def extract_dsc(self, output: Path) -> None: device_support_symbols_path.mkdir(parents=True, exist_ok=True) _extract_dmg(build_identity.get_component('Cryptex1,SystemOS').data, device_support_symbols_path, - sub_path=Path('System')) + sub_path=Path('System'), pem_db=pem_db) _split_dsc(output) - def extract(self, output: Path) -> None: + def extract(self, output: Path, pem_db: Optional[str] = None) -> None: logger.info(f'extracting into: {output}') build_identity = self.build_manifest.build_identities[0] logger.info(f'extracting OS into: {output}') - _extract_dmg(build_identity.get_component('OS').data, output) + _extract_dmg(build_identity.get_component('OS').data, output, pem_db=pem_db) kernel_component = build_identity.get_component('KernelCache') kernel_path = Path(kernel_component.path) @@ -185,6 +194,6 @@ def extract(self, output: Path) -> None: cryptex_path.mkdir(parents=True, exist_ok=True) logger.info(f'extracting {name} into: {cryptex_path}') - _extract_dmg(build_identity.get_component(name).data, cryptex_path) + _extract_dmg(build_identity.get_component(name).data, cryptex_path, pem_db=pem_db) _split_dsc(output) diff --git a/ipsw_parser/ipsw.py b/ipsw_parser/ipsw.py index 95f95f7..490ac84 100644 --- a/ipsw_parser/ipsw.py +++ b/ipsw_parser/ipsw.py @@ -4,6 +4,7 @@ from contextlib import contextmanager from datetime import datetime from pathlib import Path +from typing import Optional from cached_property import cached_property from construct import Const, Default, PaddedString, Struct @@ -112,13 +113,13 @@ def get_development_files(self) -> list[str]: result.append(entry) return result - def create_device_support(self) -> None: + def create_device_support(self, pem_db: Optional[str] = None) -> None: device_support_path = Path('~/Library/Developer/Xcode/iOS DeviceSupport').expanduser() device_support_path /= (f'{self.build_manifest.supported_product_types[0]} ' f'{self.build_manifest.product_version} ({self.build_manifest.product_build_version})') build_identity = self.build_manifest.build_identities[0] symbols_path = device_support_path / 'Symbols' - build_identity.extract_dsc(symbols_path) + build_identity.extract_dsc(symbols_path, pem_db=pem_db) for file in (symbols_path / 'private/preboot/Cryptexes/OS/System/Library/Caches/com.apple.dyld').iterdir(): file.unlink() (device_support_path / 'Info.plist').write_bytes(plistlib.dumps({ diff --git a/requirements.txt b/requirements.txt index 5d59b93..2fe8ee8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ coloredlogs cached_property plumbum pyimg4>=0.8.6 +requests \ No newline at end of file