Skip to content
This repository has been archived by the owner on Jan 20, 2025. It is now read-only.

Commit

Permalink
Merge pull request #34 from leminlimez/v3
Browse files Browse the repository at this point in the history
v3
  • Loading branch information
leminlimez authored Sep 29, 2024
2 parents 37bea5d + 11b9d6d commit b3f6f13
Show file tree
Hide file tree
Showing 15 changed files with 6,135 additions and 3,430 deletions.
Binary file modified .DS_Store
Binary file not shown.
682 changes: 661 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Note: I am not responsible if your device bootloops. Please back up your data be
- Enabling lock screen clock animation, lock screen page duplication button, and more!
- Disabling the new iOS 18 Photos UI
- EU Enabler
- Springboard Options (from Cowabunga Lite)
- Internal Options (from Cowabunga Lite)

## Running the Program
Requirements:
Expand Down
53 changes: 18 additions & 35 deletions Sparserestore/restore.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
from . import backup, perform_restore
from pymobiledevice3.lockdown import LockdownClient
import os

class FileToRestore:
def __init__(self, contents: str, restore_path: str, restore_name: str, owner: int = 501, group: int = 501):
def __init__(self, contents: str, restore_path: str, owner: int = 501, group: int = 501):
self.contents = contents
self.restore_path = restore_path
self.restore_name = restore_name
self.owner = owner
self.group = group

# files is a list of FileToRestore objects
def restore_files(files: list, reboot: bool = False, lockdown_client: LockdownClient = None):
# create the files to be backed up
files_list = [
backup.Directory("", "RootDomain"),
backup.Directory("Library", "RootDomain"),
backup.Directory("Library/Preferences", "RootDomain"),
]
# create the links
for file_num in range(len(files)):
files_list.append(backup.ConcreteFile(
f"Library/Preferences/temp{file_num}",
"RootDomain",
owner=files[file_num].owner,
group=files[file_num].group,
contents=files[file_num].contents,
inode=file_num
))
sorted_files = sorted(files, key=lambda x: x.restore_path, reverse=True)
# add the file paths
for file_num in range(len(files)):
file = files[file_num]
last_domain = ""
for file in sorted_files:
base_path = "/var/backup"
# set it to work in the separate volumes (prevents a bootloop)
if file.restore_path.startswith("/var/mobile/"):
Expand All @@ -39,29 +27,24 @@ def restore_files(files: list, reboot: bool = False, lockdown_client: LockdownCl
base_path = "/private/var/mobile/backup"
elif file.restore_path.startswith("/private/var/"):
base_path = "/private/var/backup"
files_list.append(backup.Directory(
"",
f"SysContainerDomain-../../../../../../../..{base_path}{file.restore_path}",
owner=file.owner,
group=file.group
))
# don't append the directory if it has already been added (restore will fail)
path, name = os.path.split(file.restore_path)
domain_path = f"SysContainerDomain-../../../../../../../..{base_path}{path}/"
if last_domain != domain_path:
files_list.append(backup.Directory(
"",
f"{domain_path}/",
owner=file.owner,
group=file.group
))
last_domain = domain_path
files_list.append(backup.ConcreteFile(
"",
f"SysContainerDomain-../../../../../../../..{base_path}{file.restore_path}{file.restore_name}",
f"{domain_path}/{name}",
owner=file.owner,
group=file.group,
contents=b"",
inode=file_num
contents=file.contents
))
# break the hard links
for file_num in range(len(files)):
files_list.append(backup.ConcreteFile(
"",
f"SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/temp{file_num}",
owner=501,
group=501,
contents=b"",
)) # Break the hard link
files_list.append(backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""))

# create the backup
Expand Down
53 changes: 39 additions & 14 deletions cli_app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from Sparserestore.restore import restore_files, FileToRestore, restore_file
from tweaks.tweaks import tweaks, TweakModifyType, FeatureFlagTweak, EligibilityTweak
from tweaks.tweaks import tweaks, TweakModifyType, FeatureFlagTweak, EligibilityTweak, AITweak, BasicPlistTweak, RdarFixTweak
from tweaks.basic_plist_locations import FileLocationsList
from devicemanagement.constants import Device

from pymobiledevice3.exceptions import PyMobileDevice3Exception
Expand Down Expand Up @@ -48,7 +49,7 @@ def get_apply_number(num: int) -> int:
'---' \\ \\ / \\ \\ / `----'
`--`-' `--`-'
""")
print("CLI v2.2")
print("CLI v3.0")
print("by LeminLimez")
print("Thanks @disfordottie for the clock animation and @lrdsnow for EU Enabler\n")
print("Please back up your device before using!")
Expand All @@ -62,6 +63,7 @@ def get_apply_number(num: int) -> int:
ld = create_using_usbmux(serial=current_device.serial)
vals = ld.all_values
device = Device(uuid=current_device.serial, name=vals['DeviceName'], version=vals['ProductVersion'], model=vals['ProductType'], locale=ld.locale, ld=ld)
tweaks["RdarFix"].get_rdar_mode()
except Exception as e:
print(traceback.format_exc())
input("Press Enter to continue...")
Expand Down Expand Up @@ -102,6 +104,8 @@ def get_apply_number(num: int) -> int:
# create the other plists
flag_plist: dict = {}
eligibility_files = None
ai_file = None
basic_plists: dict = {}

# verify the device credentials before continuing
if gestalt_plist["CacheExtra"]["qNNddlUK+B/YlooNoymwgA"] != device.version or gestalt_plist["CacheExtra"]["0+nc/Udy4WNG8S+Q7a/s1A"] != device.model:
Expand All @@ -120,24 +124,41 @@ def get_apply_number(num: int) -> int:
elif isinstance(tweak, EligibilityTweak):
tweak.set_region_code(device.locale[-2:])
eligibility_files = tweak.apply_tweak()
elif isinstance(tweak, AITweak):
ai_file = tweak.apply_tweak()
elif isinstance(tweak, BasicPlistTweak) or isinstance(tweak, RdarFixTweak):
basic_plists = tweak.apply_tweak(basic_plists)
else:
gestalt_plist = tweak.apply_tweak(gestalt_plist)

# create the restore file list
files_to_restore = [
FileToRestore(
contents=plistlib.dumps(gestalt_plist),
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
restore_name="com.apple.MobileGestalt.plist"
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
),
FileToRestore(
contents=plistlib.dumps(flag_plist),
restore_path="/var/preferences/FeatureFlags/",
restore_name="Global.plist"
restore_path="/var/preferences/FeatureFlags/Global.plist",
)
]
if eligibility_files != None:
files_to_restore += eligibility_files
if ai_file != None:
files_to_restore.append(ai_file)
for location, plist in basic_plists.items():
files_to_restore.append(FileToRestore(
contents=plistlib.dumps(plist),
restore_path=location.value
))
# reset basic tweaks
if resetting:
empty_data = plistlib.dumps({})
for location in FileLocationsList:
files_to_restore.append(FileToRestore(
contents=empty_data,
restore_path=location.value
))
# restore to the device
try:
restore_files(files=files_to_restore, reboot=True, lockdown_client=device.ld)
Expand All @@ -152,8 +173,7 @@ def get_apply_number(num: int) -> int:
try:
restore_files(files=[FileToRestore(
contents=b"",
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
restore_name="com.apple.MobileGestalt.plist"
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
)], reboot=True, lockdown_client=device.ld)
except Exception as e:
print(traceback.format_exc())
Expand All @@ -169,14 +189,18 @@ def get_apply_number(num: int) -> int:
if page > 0 and page <= num_tweaks and tweak.is_compatible(device.version):
if tweak.edit_type == TweakModifyType.TEXT:
# text input
# for now it is just for set model, deal with a fix later
print("\n\nSet Model Name")
print("Leave blank to turn off custom name.\n")
name = input("Enter Model Name: ")
if name == "":
inp_txt = ""
print(f"\n\n{tweak.label}")
print("Leave blank to turn off.\n")
if tweak.label == "Set Device Model Name":
inp_txt = "Enter Model Name: "
elif tweak.label == "Set Lock Screen Footnote Text":
inp_txt = "Enter Footnote: "
new_txt = input(inp_txt)
if new_txt == "":
tweak.set_enabled(False)
else:
tweak.set_value(name)
tweak.set_value(new_txt)
elif tweak.edit_type == TweakModifyType.PICKER:
# pick between values
print("\n\nSelect a value.")
Expand All @@ -192,6 +216,7 @@ def get_apply_number(num: int) -> int:
picker_choice = int(input("Select option: "))
if picker_choice > 0 and picker_choice <= len(values):
tweak.set_selected_option(picker_choice-1)
tweaks["RdarFix"].set_di_type(values[tweak.get_selected_option()])
elif picker_choice == len(values)+1:
tweak.set_enabled(False)
else:
Expand Down
13 changes: 12 additions & 1 deletion devicemanagement/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@
from pymobiledevice3.lockdown import LockdownClient

class Device:
def __init__(self, uuid: int, name: str, version: str, model: str, locale: str, ld: LockdownClient):
def __init__(self, uuid: int, name: str, version: str, build: str, model: str, locale: str, ld: LockdownClient):
self.uuid = uuid
self.name = name
self.version = version
self.build = build
self.model = model
self.locale = locale
self.ld = ld

def supported(self) -> bool:
parsed_ver: Version = Version(self.version)
if (parsed_ver < Version("17.0")) or (parsed_ver > Version("18.1")):
return False
if (parsed_ver == Version("18.1")
and self.build != "22B5007p" and self.build == "22B5023e"
and self.build == "22B5034e" and self.build == "22B5045g"):
return False
return True

class Version:
def __init__(self, major: int, minor: int = 0, patch: int = 0):
self.major = major
Expand Down
55 changes: 43 additions & 12 deletions devicemanagement/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from devicemanagement.constants import Device, Version
from devicemanagement.data_singleton import DataSingleton

from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak
from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak, AITweak, BasicPlistTweak, RdarFixTweak
from tweaks.basic_plist_locations import FileLocationsList
from Sparserestore.restore import restore_files, FileToRestore

def show_error_msg(txt: str):
Expand All @@ -22,8 +23,6 @@ def show_error_msg(txt: str):
detailsBox.exec()

class DeviceManager:
min_version: Version = Version("17")

## Class Functions
def __init__(self):
self.devices: list[Device] = []
Expand All @@ -44,10 +43,12 @@ def get_devices(self):
uuid=device.serial,
name=vals['DeviceName'],
version=vals['ProductVersion'],
build=vals['BuildVersion'],
model=vals['ProductType'],
locale=ld.locale,
ld=ld
)
tweaks["RdarFix"].get_rdar_mode(vals['ProductType'])
self.devices.append(dev)
except Exception as e:
print(f"ERROR with lockdown device with UUID {device.serial}")
Expand All @@ -67,7 +68,7 @@ def set_current_device(self, index: int = None):
self.current_device_index = 0
else:
self.data_singleton.current_device = self.devices[index]
if Version(self.devices[index].version) < DeviceManager.min_version:
if Version(self.devices[index].version) < Version("17.0"):
self.data_singleton.device_available = False
self.data_singleton.gestalt_path = None
else:
Expand All @@ -91,6 +92,12 @@ def get_current_device_uuid(self) -> str:
return ""
else:
return self.data_singleton.current_device.uuid

def get_current_device_supported(self) -> bool:
if self.data_singleton.current_device == None:
return False
else:
return self.data_singleton.current_device.supported()


## APPLYING OR REMOVING TWEAKS AND RESTORING
Expand All @@ -105,6 +112,8 @@ def apply_changes(self, resetting: bool = False, update_label=lambda x: None):
# create the other plists
flag_plist: dict = {}
eligibility_files = None
ai_file = None
basic_plists: dict = {}

# set the plist keys
if not resetting:
Expand All @@ -114,28 +123,51 @@ def apply_changes(self, resetting: bool = False, update_label=lambda x: None):
flag_plist = tweak.apply_tweak(flag_plist)
elif isinstance(tweak, EligibilityTweak):
eligibility_files = tweak.apply_tweak()
elif isinstance(tweak, AITweak):
ai_file = tweak.apply_tweak()
elif isinstance(tweak, BasicPlistTweak) or isinstance(tweak, RdarFixTweak):
basic_plists = tweak.apply_tweak(basic_plists)
else:
if gestalt_plist != None:
gestalt_plist = tweak.apply_tweak(gestalt_plist)

gestalt_data = None
if resetting:
gestalt_data = b""
elif gestalt_plist != None:
gestalt_data = plistlib.dumps(gestalt_plist)

# Generate backup
update_label("Generating backup...")
# create the restore file list
files_to_restore = [
FileToRestore(
contents=plistlib.dumps(flag_plist),
restore_path="/var/preferences/FeatureFlags/",
restore_name="Global.plist"
restore_path="/var/preferences/FeatureFlags/Global.plist",
)
]
if gestalt_plist != None:
if gestalt_data != None:
files_to_restore.append(FileToRestore(
contents=plistlib.dumps(gestalt_plist),
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
restore_name="com.apple.MobileGestalt.plist"
contents=gestalt_data,
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
))
if eligibility_files:
files_to_restore += eligibility_files
if ai_file != None:
files_to_restore.append(ai_file)
for location, plist in basic_plists.items():
files_to_restore.append(FileToRestore(
contents=plistlib.dumps(plist),
restore_path=location.value
))
# reset basic tweaks
if resetting:
empty_data = plistlib.dumps({})
for location in FileLocationsList:
files_to_restore.append(FileToRestore(
contents=empty_data,
restore_path=location.value
))

# restore to the device
update_label("Restoring to device...")
Expand Down Expand Up @@ -163,8 +195,7 @@ def reset_mobilegestalt(self, update_label=lambda x: None):
try:
restore_files(files=[FileToRestore(
contents=b"",
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
restore_name="com.apple.MobileGestalt.plist"
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
)], reboot=True, lockdown_client=self.data_singleton.current_device.ld)
QMessageBox.information(None, "Success!", "All done! Your device will now restart.")
update_label("Success!")
Expand Down
Loading

0 comments on commit b3f6f13

Please sign in to comment.