├── .DS_Store ├── .gitattributes ├── .gitignore ├── CODENAME Nugget.spec ├── LICENSE ├── README.md ├── compile.py ├── exploit ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── backup.cpython-312.pyc │ ├── mbdb.cpython-312.pyc │ └── restore.cpython-312.pyc ├── backup.py ├── mbdb.py └── restore.py ├── main_app.py ├── main_app.spec └── requirements.txt /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qbap/Nugget/3c9f3a80cb929ecf18959d01c620480ad62b0ce1/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | build 3 | com.apple.MobileGestalt.plist 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /CODENAME Nugget.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | a = Analysis( 5 | ['main_app.py'], 6 | pathex=[], 7 | binaries=[], 8 | datas=[], 9 | hiddenimports=['zeroconf', 'zeroconf._utils.ipaddress', 'zeroconf._handlers.answers'], 10 | hookspath=[], 11 | hooksconfig={}, 12 | runtime_hooks=[], 13 | excludes=[], 14 | noarchive=False, 15 | optimize=0, 16 | ) 17 | pyz = PYZ(a.pure) 18 | 19 | exe = EXE( 20 | pyz, 21 | a.scripts, 22 | [], 23 | exclude_binaries=True, 24 | name='CODENAME Nugget', 25 | debug=False, 26 | bootloader_ignore_signals=False, 27 | strip=False, 28 | upx=True, 29 | console=True, 30 | disable_windowed_traceback=False, 31 | argv_emulation=False, 32 | target_arch=None, 33 | codesign_identity=None, 34 | entitlements_file=None, 35 | ) 36 | coll = COLLECT( 37 | exe, 38 | a.binaries, 39 | a.datas, 40 | strip=False, 41 | upx=True, 42 | upx_exclude=[], 43 | name='CODENAME Nugget', 44 | ) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 leminlimez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CODENAME Nugget 2 | Unlock your device's full potential! Works on all versions iOS 16.0+. More info about [Nugget](https://onejailbreak.com/blog/nugget/). 3 | 4 | Note: I am not responsible if your device bootloops. Please back up your data before using. 5 | 6 | ## Features 7 | - Enable Dynamic Island on any device 8 | - Change Device Model Name 9 | - Enable Boot Chime 10 | - Enable Charge Limit 11 | - Enable Stage Manager 12 | 13 | ## Running the Program 14 | Requirements: 15 | - pymobiledevice3 16 | 17 | Note: It is highly recommended to use a virtual environment: 18 | ``` 19 | python -m venv .env # only needed once 20 | source .env/bin/activate 21 | pip install -r requirements.txt # only needed once 22 | python main_app.py 23 | ``` 24 | 25 | ## Getting the File 26 | You need to get the mobilegestalt file that is specific to your device. To do that, follow these steps: 27 | 1. Install the `Shortcuts` app from the iOS app store. 28 | 2. Download this shortcut: https://www.icloud.com/shortcuts/d6f0a136ddda4714a80750512911c53b 29 | 3. Save the file and share it to your computer. 30 | 4. Place it in the same folder as the python file (or specify the path in the program) 31 | 32 | ## Credits 33 | - [JJTech](https://github.com/JJTech0130) for Sparserestore/[TrollRestore](https://github.com/JJTech0130/TrollRestore) 34 | - [pymobiledevice3](https://github.com/doronz88/pymobiledevice3) 35 | 36 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | from sys import platform 2 | 3 | import PyInstaller.__main__ 4 | 5 | args = [ 6 | 'main_app.py', 7 | # '--hidden-import=ipsw_parser', 8 | '--hidden-import=zeroconf', 9 | '--hidden-import=zeroconf._utils.ipaddress', 10 | '--hidden-import=zeroconf._handlers.answers', 11 | '--onedir', 12 | '--name=CODENAME Nugget', 13 | ] 14 | 15 | PyInstaller.__main__.run(args) -------------------------------------------------------------------------------- /exploit/__init__.py: -------------------------------------------------------------------------------- 1 | from . import backup -------------------------------------------------------------------------------- /exploit/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qbap/Nugget/3c9f3a80cb929ecf18959d01c620480ad62b0ce1/exploit/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /exploit/__pycache__/backup.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qbap/Nugget/3c9f3a80cb929ecf18959d01c620480ad62b0ce1/exploit/__pycache__/backup.cpython-312.pyc -------------------------------------------------------------------------------- /exploit/__pycache__/mbdb.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qbap/Nugget/3c9f3a80cb929ecf18959d01c620480ad62b0ce1/exploit/__pycache__/mbdb.cpython-312.pyc -------------------------------------------------------------------------------- /exploit/__pycache__/restore.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qbap/Nugget/3c9f3a80cb929ecf18959d01c620480ad62b0ce1/exploit/__pycache__/restore.cpython-312.pyc -------------------------------------------------------------------------------- /exploit/backup.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from datetime import datetime 3 | import plistlib 4 | from pathlib import Path 5 | from base64 import b64decode 6 | from hashlib import sha1 7 | from . import mbdb 8 | from .mbdb import _FileMode 9 | from random import randbytes 10 | from typing import Optional 11 | 12 | # RWX:RX:RX 13 | DEFAULT = _FileMode.S_IRUSR | _FileMode.S_IWUSR | _FileMode.S_IXUSR | _FileMode.S_IRGRP | _FileMode.S_IXGRP | _FileMode.S_IROTH | _FileMode.S_IXOTH 14 | 15 | @dataclass 16 | class BackupFile: 17 | path: str 18 | domain: str 19 | 20 | def to_record(self) -> mbdb.MbdbRecord: 21 | raise NotImplementedError() 22 | 23 | @dataclass 24 | class ConcreteFile(BackupFile): 25 | contents: bytes 26 | owner: int = 0 27 | group: int = 0 28 | inode: Optional[int] = None 29 | mode: _FileMode = DEFAULT 30 | 31 | def to_record(self) -> mbdb.MbdbRecord: 32 | if self.inode is None: 33 | self.inode = int.from_bytes(randbytes(8), "big") 34 | return mbdb.MbdbRecord( 35 | domain=self.domain, 36 | filename=self.path, 37 | link="", 38 | hash=sha1(self.contents).digest(), 39 | key=b"", 40 | mode=self.mode | _FileMode.S_IFREG, 41 | #unknown2=0, 42 | #unknown3=0, 43 | inode=self.inode, 44 | user_id=self.owner, 45 | group_id=self.group, 46 | mtime=int(datetime.now().timestamp()), 47 | atime=int(datetime.now().timestamp()), 48 | ctime=int(datetime.now().timestamp()), 49 | size=len(self.contents), 50 | flags=4, 51 | properties=[] 52 | ) 53 | 54 | @dataclass 55 | class Directory(BackupFile): 56 | owner: int = 0 57 | group: int = 0 58 | mode: _FileMode = DEFAULT 59 | 60 | def to_record(self) -> mbdb.MbdbRecord: 61 | return mbdb.MbdbRecord( 62 | domain=self.domain, 63 | filename=self.path, 64 | link="", 65 | hash=b"", 66 | key=b"", 67 | mode=self.mode | _FileMode.S_IFDIR, 68 | #unknown2=0, 69 | #unknown3=0, 70 | inode=0, # inode is not respected for directories 71 | user_id=self.owner, 72 | group_id=self.group, 73 | mtime=int(datetime.now().timestamp()), 74 | atime=int(datetime.now().timestamp()), 75 | ctime=int(datetime.now().timestamp()), 76 | size=0, 77 | flags=4, 78 | properties=[] 79 | ) 80 | 81 | @dataclass 82 | class SymbolicLink(BackupFile): 83 | target: str 84 | owner: int = 0 85 | group: int = 0 86 | inode: Optional[int] = None 87 | mode: _FileMode = DEFAULT 88 | 89 | def to_record(self) -> mbdb.MbdbRecord: 90 | if self.inode is None: 91 | self.inode = int.from_bytes(randbytes(8), "big") 92 | return mbdb.MbdbRecord( 93 | domain=self.domain, 94 | filename=self.path, 95 | link=self.target, 96 | hash=b"", 97 | key=b"", 98 | mode=self.mode | _FileMode.S_IFLNK, 99 | #unknown2=0, 100 | #unknown3=0, 101 | inode=self.inode, 102 | user_id=self.owner, 103 | group_id=self.group, 104 | mtime=int(datetime.now().timestamp()), 105 | atime=int(datetime.now().timestamp()), 106 | ctime=int(datetime.now().timestamp()), 107 | size=0, 108 | flags=4, 109 | properties=[] 110 | ) 111 | 112 | @dataclass 113 | class Backup: 114 | files: list[BackupFile] 115 | 116 | def write_to_directory(self, directory: Path): 117 | for file in self.files: 118 | if isinstance(file, ConcreteFile): 119 | #print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex()) 120 | with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f: 121 | f.write(file.contents) 122 | 123 | with open(directory / "Manifest.mbdb", "wb") as f: 124 | f.write(self.generate_manifest_db().to_bytes()) 125 | 126 | with open(directory / "Status.plist", "wb") as f: 127 | f.write(self.generate_status()) 128 | 129 | with open(directory / "Manifest.plist", "wb") as f: 130 | f.write(self.generate_manifest()) 131 | 132 | with open(directory / "Info.plist", "wb") as f: 133 | f.write(plistlib.dumps({})) 134 | 135 | 136 | def generate_manifest_db(self): # Manifest.mbdb 137 | records = [] 138 | for file in self.files: 139 | records.append(file.to_record()) 140 | return mbdb.Mbdb(records=records) 141 | 142 | def generate_status(self) -> bytes: # Status.plist 143 | return plistlib.dumps({ 144 | "BackupState": "new", 145 | "Date": datetime.fromisoformat("1970-01-01T00:00:00+00:00"), 146 | "IsFullBackup": False, 147 | "SnapshotState": "finished", 148 | "UUID": "00000000-0000-0000-0000-000000000000", 149 | "Version": "2.4" 150 | }) 151 | 152 | def generate_manifest(self) -> bytes: # Manifest.plist 153 | return plistlib.dumps({ 154 | "BackupKeyBag": b64decode(""" 155 | VkVSUwAAAAQAAAAFVFlQRQAAAAQAAAABVVVJRAAAABDud41d1b9NBICR1BH9JfVtSE1D 156 | SwAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV1JBUAAA 157 | AAQAAAAAU0FMVAAAABRY5Ne2bthGQ5rf4O3gikep1e6tZUlURVIAAAAEAAAnEFVVSUQA 158 | AAAQB7R8awiGR9aba1UuVahGPENMQVMAAAAEAAAAAVdSQVAAAAAEAAAAAktUWVAAAAAE 159 | AAAAAFdQS1kAAAAoN3kQAJloFg+ukEUY+v5P+dhc/Welw/oucsyS40UBh67ZHef5ZMk9 160 | UVVVSUQAAAAQgd0cg0hSTgaxR3PVUbcEkUNMQVMAAAAEAAAAAldSQVAAAAAEAAAAAktU 161 | WVAAAAAEAAAAAFdQS1kAAAAoMiQTXx0SJlyrGJzdKZQ+SfL124w+2Tf/3d1R2i9yNj9z 162 | ZCHNJhnorVVVSUQAAAAQf7JFQiBOS12JDD7qwKNTSkNMQVMAAAAEAAAAA1dSQVAAAAAE 163 | AAAAAktUWVAAAAAEAAAAAFdQS1kAAAAoSEelorROJA46ZUdwDHhMKiRguQyqHukotrxh 164 | jIfqiZ5ESBXX9txi51VVSUQAAAAQfF0G/837QLq01xH9+66vx0NMQVMAAAAEAAAABFdS 165 | QVAAAAAEAAAAAktUWVAAAAAEAAAAAFdQS1kAAAAol0BvFhd5bu4Hr75XqzNf4g0fMqZA 166 | ie6OxI+x/pgm6Y95XW17N+ZIDVVVSUQAAAAQimkT2dp1QeadMu1KhJKNTUNMQVMAAAAE 167 | AAAABVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo2N2DZarQ6GPoWRgTiy/t 168 | djKArOqTaH0tPSG9KLbIjGTOcLodhx23xFVVSUQAAAAQQV37JVZHQFiKpoNiGmT6+ENM 169 | QVMAAAAEAAAABldSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAofe2QSvDC2cV7 170 | Etk4fSBbgqDx5ne/z1VHwmJ6NdVrTyWi80Sy869DM1VVSUQAAAAQFzkdH+VgSOmTj3yE 171 | cfWmMUNMQVMAAAAEAAAAB1dSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kAAAAo7kLY 172 | PQ/DnHBERGpaz37eyntIX/XzovsS0mpHW3SoHvrb9RBgOB+WblVVSUQAAAAQEBpgKOz9 173 | Tni8F9kmSXd0sENMQVMAAAAEAAAACFdSQVAAAAAEAAAAA0tUWVAAAAAEAAAAAFdQS1kA 174 | AAAo5mxVoyNFgPMzphYhm1VG8Fhsin/xX+r6mCd9gByF5SxeolAIT/ICF1VVSUQAAAAQ 175 | rfKB2uPSQtWh82yx6w4BoUNMQVMAAAAEAAAACVdSQVAAAAAEAAAAA0tUWVAAAAAEAAAA 176 | AFdQS1kAAAAo5iayZBwcRa1c1MMx7vh6lOYux3oDI/bdxFCW1WHCQR/Ub1MOv+QaYFVV 177 | SUQAAAAQiLXvK3qvQza/mea5inss/0NMQVMAAAAEAAAACldSQVAAAAAEAAAAA0tUWVAA 178 | AAAEAAAAAFdQS1kAAAAoD2wHX7KriEe1E31z7SQ7/+AVymcpARMYnQgegtZD0Mq2U55u 179 | xwNr2FVVSUQAAAAQ/Q9feZxLS++qSe/a4emRRENMQVMAAAAEAAAAC1dSQVAAAAAEAAAA 180 | A0tUWVAAAAAEAAAAAFdQS1kAAAAocYda2jyYzzSKggRPw/qgh6QPESlkZedgDUKpTr4Z 181 | Z8FDgd7YoALY1g=="""), 182 | "Lockdown": {}, 183 | "SystemDomainsVersion": "20.0", 184 | "Version": "9.1" 185 | }) -------------------------------------------------------------------------------- /exploit/mbdb.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from io import BytesIO 3 | 4 | # Mode bitfield 5 | from enum import IntFlag 6 | class _FileMode(IntFlag): 7 | S_IFMT = 0o0170000 8 | S_IFIFO = 0o0010000 9 | S_IFCHR = 0o0020000 10 | S_IFDIR = 0o0040000 11 | S_IFBLK = 0o0060000 12 | S_IFREG = 0o0100000 13 | S_IFLNK = 0o0120000 14 | S_IFSOCK = 0o0140000 15 | 16 | #S_IRWXU = 0o0000700 17 | S_IRUSR = 0o0000400 18 | S_IWUSR = 0o0000200 19 | S_IXUSR = 0o0000100 20 | 21 | #S_IRWXG = 0o0000070 22 | S_IRGRP = 0o0000040 23 | S_IWGRP = 0o0000020 24 | S_IXGRP = 0o0000010 25 | 26 | #S_IRWXO = 0o0000007 27 | S_IROTH = 0o0000004 28 | S_IWOTH = 0o0000002 29 | S_IXOTH = 0o0000001 30 | 31 | S_ISUID = 0o0004000 32 | S_ISGID = 0o0002000 33 | S_ISVTX = 0o0001000 34 | 35 | @dataclass 36 | class MbdbRecord: 37 | domain: str 38 | filename: str 39 | link: str 40 | hash: bytes 41 | key: bytes 42 | mode: _FileMode 43 | inode: int 44 | user_id: int 45 | group_id: int 46 | mtime: int 47 | atime: int 48 | ctime: int 49 | size: int 50 | flags: int 51 | properties: list 52 | 53 | @classmethod 54 | def from_stream(cls, d: BytesIO): 55 | #d = BytesIO(data) 56 | 57 | domain_len = int.from_bytes(d.read(2), "big") 58 | domain = d.read(domain_len).decode("utf-8") 59 | 60 | filename_len = int.from_bytes(d.read(2), "big") 61 | filename = d.read(filename_len).decode("utf-8") 62 | 63 | link_len = int.from_bytes(d.read(2), "big") 64 | link = d.read(link_len).decode("utf-8") if link_len != 0xffff else "" 65 | 66 | hash_len = int.from_bytes(d.read(2), "big") 67 | hash = d.read(hash_len) if hash_len != 0xffff else b"" 68 | 69 | key_len = int.from_bytes(d.read(2), "big") 70 | key = d.read(key_len) if key_len != 0xffff else b"" 71 | 72 | mode = _FileMode(int.from_bytes(d.read(2), "big")) 73 | #unknown2 = int.from_bytes(d.read(4), "big") 74 | #unknown3 = int.from_bytes(d.read(4), "big") 75 | inode = int.from_bytes(d.read(8), "big") 76 | user_id = int.from_bytes(d.read(4), "big") 77 | group_id = int.from_bytes(d.read(4), "big") 78 | mtime = int.from_bytes(d.read(4), "big") 79 | atime = int.from_bytes(d.read(4), "big") 80 | ctime = int.from_bytes(d.read(4), "big") 81 | size = int.from_bytes(d.read(8), "big") 82 | flags = int.from_bytes(d.read(1), "big") 83 | 84 | properties_count = int.from_bytes(d.read(1), "big") 85 | properties = [] 86 | 87 | for _ in range(properties_count): 88 | name_len = int.from_bytes(d.read(2), "big") 89 | name = d.read(name_len).decode("utf-8") if name_len != 0xffff else "" 90 | 91 | value_len = int.from_bytes(d.read(2), "big") 92 | value = d.read(value_len).decode("utf-8") if value_len != 0xffff else "" 93 | 94 | properties.append((name, value)) 95 | 96 | return cls(domain, filename, link, hash, key, mode, inode, user_id, group_id, mtime, atime, ctime, size, flags, properties) 97 | 98 | def to_bytes(self) -> bytes: 99 | d = BytesIO() 100 | 101 | d.write(len(self.domain).to_bytes(2, "big")) 102 | d.write(self.domain.encode("utf-8")) 103 | 104 | d.write(len(self.filename).to_bytes(2, "big")) 105 | d.write(self.filename.encode("utf-8")) 106 | 107 | d.write(len(self.link).to_bytes(2, "big")) 108 | d.write(self.link.encode("utf-8")) 109 | 110 | d.write(len(self.hash).to_bytes(2, "big")) 111 | d.write(self.hash) 112 | 113 | d.write(len(self.key).to_bytes(2, "big")) 114 | d.write(self.key) 115 | 116 | d.write(self.mode.to_bytes(2, "big")) 117 | #d.write(self.unknown2.to_bytes(4, "big")) 118 | #d.write(self.unknown3.to_bytes(4, "big")) 119 | d.write(self.inode.to_bytes(8, "big")) 120 | d.write(self.user_id.to_bytes(4, "big")) 121 | d.write(self.group_id.to_bytes(4, "big")) 122 | d.write(self.mtime.to_bytes(4, "big")) 123 | d.write(self.atime.to_bytes(4, "big")) 124 | d.write(self.ctime.to_bytes(4, "big")) 125 | d.write(self.size.to_bytes(8, "big")) 126 | d.write(self.flags.to_bytes(1, "big")) 127 | 128 | d.write(len(self.properties).to_bytes(1, "big")) 129 | 130 | for name, value in self.properties: 131 | d.write(len(name).to_bytes(2, "big")) 132 | d.write(name.encode("utf-8")) 133 | 134 | d.write(len(value).to_bytes(2, "big")) 135 | d.write(value.encode("utf-8")) 136 | 137 | return d.getvalue() 138 | 139 | @dataclass 140 | class Mbdb: 141 | records: list[MbdbRecord] 142 | 143 | @classmethod 144 | def from_bytes(cls, data: bytes): 145 | d = BytesIO(data) 146 | 147 | if d.read(4) != b"mbdb": 148 | raise ValueError("Invalid MBDB file") 149 | 150 | if d.read(2) != b"\x05\x00": 151 | raise ValueError("Invalid MBDB version") 152 | 153 | records = [] 154 | while d.tell() < len(data): 155 | records.append(MbdbRecord.from_stream(d)) 156 | 157 | return cls(records) 158 | 159 | def to_bytes(self) -> bytes: 160 | d = BytesIO() 161 | 162 | d.write(b"mbdb") 163 | d.write(b"\x05\x00") 164 | 165 | for record in self.records: 166 | d.write(record.to_bytes()) 167 | 168 | return d.getvalue() -------------------------------------------------------------------------------- /exploit/restore.py: -------------------------------------------------------------------------------- 1 | from exploit import backup 2 | from pymobiledevice3.lockdown import create_using_usbmux 3 | from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service 4 | from tempfile import TemporaryDirectory 5 | from pathlib import Path 6 | 7 | def restore_file(fp: str, restore_path: str, restore_name: str): 8 | # open the file and read the contents 9 | contents = open(fp, "rb").read() 10 | 11 | # create the backup 12 | back = backup.Backup(files=[ 13 | backup.Directory("", "RootDomain", owner=501, group=501), 14 | backup.Directory("Library", "RootDomain", owner=501, group=501), 15 | backup.Directory("Library/Preferences", "RootDomain", owner=501, group=501), 16 | backup.ConcreteFile("Library/Preferences/Hello", "RootDomain", owner=501, group=501, contents=contents), 17 | backup.Directory("", f"SysContainerDomain-../../../../../../../../var/.backup.i{restore_path}", owner=501, group=501), 18 | backup.ConcreteFile("", f"SysContainerDomain-../../../../../../../../var/.backup.i{restore_path}{restore_name}", owner=501, group=501, contents=contents), 19 | backup.Directory("", "SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/Hello", owner=501, group=501), 20 | ]) 21 | 22 | # get a temporary dir to store the backup 23 | with TemporaryDirectory() as backup_dir: 24 | backup_dir_path = Path(backup_dir) 25 | back.write_to_directory(backup_dir_path) 26 | print(f"Backup written to {backup_dir}") 27 | input("Press Enter to continue...") 28 | 29 | lockdown = create_using_usbmux() 30 | with Mobilebackup2Service(lockdown) as mb: 31 | mb.restore(backup_dir, system=True, reboot=False, copy=False, source=".") -------------------------------------------------------------------------------- /main_app.py: -------------------------------------------------------------------------------- 1 | from exploit.restore import restore_file 2 | from pathlib import Path 3 | import plistlib 4 | import traceback 5 | 6 | running = True 7 | passed_check = False 8 | # tweaks 9 | dynamic_island_enabled = False 10 | current_model_name = "" 11 | boot_chime_enabled = False 12 | charge_limit_enabled = False 13 | stage_manager_enabled = False 14 | 15 | gestalt_path = Path.joinpath(Path.cwd(), "com.apple.MobileGestalt.plist") 16 | 17 | while running: 18 | print("""\n\n\n\n 19 | 20 | ,--. 21 | ,--.'| ___ 22 | ,--,: : | ,--.'|_ 23 | ,`--.'`| ' : ,--, | | :,' 24 | | : : | | ,'_ /| ,----._,. ,----._,. : : ' : 25 | : | \\ | : .--. | | : / / ' / / / ' / ,---. .;__,' / 26 | | : ' '; |,'_ /| : . || : || : | / \\ | | | 27 | ' ' ;. ;| ' | | . .| | .\\ .| | .\\ . / / |:__,'| : 28 | | | | \\ || | ' | | |. ; '; |. ; '; |. ' / | ' : |__ 29 | ' : | ; .': | : ; ; |' . . |' . . |' ; /| | | '.'| 30 | | | '`--' ' : `--' \\`---`-'| | `---`-'| |' | / | ; : ; 31 | ' : | : , .-./.'__/\\_: | .'__/\\_: || : | | , / 32 | ; |.' `--`----' | : : | : : \\ \\ / ---`-' 33 | '---' \\ \\ / \\ \\ / `----' 34 | `--`-' `--`-' 35 | """) 36 | print("by LeminLimez") 37 | print("v1.0\n\n") 38 | 39 | if not passed_check and Path.exists(gestalt_path) and Path.is_file(gestalt_path): 40 | passed_check = True 41 | 42 | if passed_check: 43 | print(f"1. {"[Y] " if dynamic_island_enabled else ""}Toggle Dynamic Island") 44 | print(f"2. {"[Y] " if current_model_name != "" else ""}Set Device Model Name") 45 | print(f"3. {"[Y] " if boot_chime_enabled else ""}Toggle Boot Chime") 46 | print(f"4. {"[Y] " if charge_limit_enabled else ""}Toggle Charge Limit") 47 | print(f"5. {"[Y] " if stage_manager_enabled else ""}Toggle Stage Manager Supported") 48 | print("\n9. Apply") 49 | print("0. Exit\n") 50 | page = int(input("Enter a number: ")) 51 | if page == 1: 52 | dynamic_island_enabled = not dynamic_island_enabled 53 | elif page == 2: 54 | print("\n\nSet Model Name") 55 | print("Leave blank to turn off custom name.\n") 56 | name = input("Enter Model Name: ") 57 | current_model_name = name 58 | elif page == 3: 59 | boot_chime_enabled = not boot_chime_enabled 60 | elif page == 4: 61 | charge_limit_enabled = not charge_limit_enabled 62 | elif page == 5: 63 | stage_manager_enabled = not stage_manager_enabled 64 | elif page == 9: 65 | print() 66 | # set the tweaks and apply 67 | # first open the file in read mode 68 | with open(gestalt_path, 'rb') as in_fp: 69 | plist = plistlib.load(in_fp) 70 | 71 | # set the plist keys 72 | if dynamic_island_enabled: 73 | plist["CacheExtra"]["oPeik/9e8lQWMszEjbPzng"]["ArtworkDeviceSubType"] = 2556 74 | if current_model_name != "": 75 | plist["CacheExtra"]["oPeik/9e8lQWMszEjbPzng"]["ArtworkDeviceProductDescription"] = current_model_name 76 | if boot_chime_enabled: 77 | plist["CacheExtra"]["QHxt+hGLaBPbQJbXiUJX3w"] = True 78 | if charge_limit_enabled: 79 | plist["CacheExtra"]["37NVydb//GP/GrhuTN+exg"] = True 80 | if stage_manager_enabled: 81 | plist["CacheExtra"]["qeaj75wk3HF4DwQ8qbIi7g"] = 1 82 | 83 | # write back to the file 84 | with open(gestalt_path, 'wb') as out_fp: 85 | plistlib.dump(plist, out_fp) 86 | # restore to the device 87 | try: 88 | restore_file(fp=gestalt_path, restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/", restore_name="com.apple.MobileGestalt.plist") 89 | input("Success! Reboot your device to see the changes.") 90 | except Exception as e: 91 | print(traceback.format_exc()) 92 | input("Press Enter to continue...") 93 | running = False 94 | elif page == 0: 95 | # exit the panel 96 | print("Goodbye!") 97 | running = False 98 | else: 99 | print("No MobileGestalt file found!") 100 | print(f"Please place the file in \'{Path.cwd()}\' with the name \'com.apple.MobileGestalt.plist\'") 101 | print("Remember to make a backup of the file!!\n") 102 | print("1. Retry") 103 | print("2. Enter path\n") 104 | choice = int(input("Enter number: ")) 105 | if choice == 2: 106 | new_path = input("Enter new path to file: ") 107 | gestalt_path = Path(new_path) -------------------------------------------------------------------------------- /main_app.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | a = Analysis( 5 | ['main_app.py'], 6 | pathex=[], 7 | binaries=[], 8 | datas=[], 9 | hiddenimports=[], 10 | hookspath=[], 11 | hooksconfig={}, 12 | runtime_hooks=[], 13 | excludes=[], 14 | noarchive=False, 15 | ) 16 | pyz = PYZ(a.pure) 17 | 18 | exe = EXE( 19 | pyz, 20 | a.scripts, 21 | [], 22 | exclude_binaries=True, 23 | name='main_app', 24 | debug=False, 25 | bootloader_ignore_signals=False, 26 | strip=False, 27 | upx=True, 28 | console=True, 29 | disable_windowed_traceback=False, 30 | argv_emulation=False, 31 | target_arch=None, 32 | codesign_identity=None, 33 | entitlements_file=None, 34 | ) 35 | coll = COLLECT( 36 | exe, 37 | a.binaries, 38 | a.datas, 39 | strip=False, 40 | upx=True, 41 | upx_exclude=[], 42 | name='main_app', 43 | ) 44 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pymobiledevice3 --------------------------------------------------------------------------------