Skip to content

Commit

Permalink
cli: add --pem-db option
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Oct 30, 2024
1 parent 9fb57a1 commit 38aaeb6
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 14 deletions.
18 changes: 13 additions & 5 deletions ipsw_parser/__main__.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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:
Expand All @@ -47,24 +53,26 @@ 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)

if not output.exists():
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__':
Expand Down
23 changes: 16 additions & 7 deletions ipsw_parser/build_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
5 changes: 3 additions & 2 deletions ipsw_parser/ipsw.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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({
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ coloredlogs
cached_property
plumbum
pyimg4>=0.8.6
requests

0 comments on commit 38aaeb6

Please sign in to comment.