forked from mineek/sunst0rm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sunstorm.py
374 lines (317 loc) · 16.4 KB
/
sunstorm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
import argparse
import os
import sys
import zipfile
from utils.manifest import Manifest
import utils.api
import subprocess
import shutil
# os.chdir(os.path.dirname(sys.argv[0]))
def prep_restore(ipsw, blob, board, kpp, legacy, skip_baseband):
# getting lowercase board to avoid errors
board = board.lower()
# check if ./work directory already exists, and deleting if it does
if os.path.exists('./work'):
shutil.rmtree('./work')
# extract the IPSW to the work directory
print('[*] Extracting IPSW')
with zipfile.ZipFile(ipsw, 'r') as z:
z.extractall('work')
# make a directory in the work directory called ramdisk
os.mkdir('work/ramdisk')
# read manifest from work/BuildManifest.plist
with open('work/BuildManifest.plist', 'rb') as f:
manifest = Manifest(f.read())
# get the ramdisk name
ramdisk_path = manifest.get_comp(board, 'RestoreRamDisk')
if ramdisk_path == None:
print("[!] Error: BoardConfig was not recognized")
sys.exit(1)
# extract it using img4
print('[*] Extracting ramdisk')
subprocess.run(['./bin/img4', '-i', 'work/' +
ramdisk_path, '-o', 'work/ramdisk.dmg'])
# mount it using hdiutil
print('[*] Mounting ramdisk')
subprocess.run(['/usr/bin/hdiutil', 'attach',
'work/ramdisk.dmg', '-mountpoint', 'work/ramdisk'])
# patch asr into the ramdisk
print('[*] Patching ASR in the ramdisk')
subprocess.run(['./bin/asr64_patcher',
'work/ramdisk/usr/sbin/asr', 'work/patched_asr'])
# extract the ents and save it to work/asr_ents.plist like: subprocess.run(['./bin/ldid', '-e', 'work/ramdisk/usr/sbin/asr', '>', 'work/asr.plist'])
print('[*] Extracting ASR entitlements')
with open('work/asr.plist', 'wb') as f:
subprocess.run(['./bin/ldid', '-e',
'work/ramdisk/usr/sbin/asr'], stdout=f)
# resign it using ldid
print('[*] Resigning ASR')
subprocess.run(
['./bin/ldid', '-Swork/asr.plist', 'work/patched_asr'])
# chmod 755 the new asr
print('[*] Chmoding ASR')
subprocess.run(['/bin/chmod', '-R', '755', 'work/patched_asr'])
# copy the patched asr back to the ramdisk
print('[*] Copying patched ASR back to the ramdisk')
subprocess.run(['/bin/cp', 'work/patched_asr',
'work/ramdisk/usr/sbin/asr'])
if not legacy:
# patch restored_external
print('[*] Patching restored_external')
subprocess.run(['./bin/restored_external64_patcher',
'work/ramdisk/usr/local/bin/restored_external', 'work/restored_external_patched'])
# resign it using ldid
print('[*] Extracting restored_external Ents')
with open('work/restored_external.plist', 'wb') as f:
subprocess.run(['./bin/ldid', '-e',
'work/ramdisk/usr/local/bin/restored_external'], stdout=f)
# resign it using ldid
print('[*] Resigning restored_external')
subprocess.run(['./bin/ldid', '-Swork/restored_external.plist',
'work/restored_external_patched'])
# chmod 755 the new restored_external
print('[*] Chmoding restored_external')
subprocess.run(['/bin/chmod', '-R', '755',
'work/restored_external_patched'])
# copy the patched restored_external back to the ramdisk
print('[*] Copying patched restored_external back to the ramdisk')
subprocess.run(['/bin/cp', 'work/restored_external_patched',
'work/ramdisk/usr/local/bin/restored_external'])
else:
print('[*] Legacy mode, skipping restored_external')
# detach the ramdisk
print('[*] Detaching ramdisk')
subprocess.run(['/usr/bin/hdiutil', 'detach', 'work/ramdisk'])
# create the ramdisk using pyimg4
print('[*] Creating ramdisk')
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'create',
'-i', 'work/ramdisk.dmg', '-o', 'work/ramdisk.im4p', '-f', 'rdsk'])
# get kernelcache name from manifest
kernelcache = manifest.get_comp(board, 'RestoreKernelCache')
# extract the kernel using pyimg4 like this: pyimg4 im4p extract -i kernelcache -o kcache.raw --extra kpp.bin
print('[*] Extracting ramdisk')
if kpp:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'extract', '-i',
'work/' + kernelcache, '-o', 'work/kcache.raw', '--extra', 'work/kpp.bin'])
else:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'extract',
'-i', 'work/' + kernelcache, '-o', 'work/kcache.raw'])
# patch the kernel using kernel64patcher like this: Kernel64Patcher kcache.raw krnl.patched -f -a
print('[*] Patching kernel')
subprocess.run(['./bin/kernel64patcher',
'work/kcache.raw', 'work/krnl.patched', '-f', '-a'])
# rebuild the kernel like this: pyimg4 im4p create -i krnl.patched -o krnl.im4p --extra kpp.bin -f rkrn --lzss (leave out --extra kpp.bin if you dont have kpp)
print('[*] Rebuilding kernel')
if kpp:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'create', '-i', 'work/krnl.patched',
'-o', 'work/krnl.im4p', '--extra', 'work/kpp.bin', '-f', 'rkrn', '--lzss'])
else:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'create', '-i',
'work/krnl.patched', '-o', 'work/krnl.im4p', '-f', 'rkrn', '--lzss'])
# done!
print('[*] Done!')
# ask user if they want to restore the device
print('[?] Do you want to restore the device? (y/n)')
if input() == 'y':
# ask user if they are in pwndfu with sigchecks removed
print('[?] Are you in pwndfu with sigchecks removed? (y/n)')
if input() == 'y':
# restore the device using futurestore like this: futurerestore -t blob --use-pwndfu --skip-blob --rdsk ramdisk.im4p --rkrn krnl.im4p --latest-sep --latest-baseband ipsw.ipsw
print('[*] Restoring Device')
if skip_baseband:
subprocess.run(['./bin/futurerestore', '-t', blob, '--use-pwndfu', '--skip-blob', '--rdsk',
'work/ramdisk.im4p', '--rkrn', 'work/krnl.im4p', '--latest-sep', '--no-baseband', ipsw])
else:
subprocess.run(['./bin/futurerestore', '-t', blob, '--use-pwndfu', '--skip-blob', '--rdsk',
'work/ramdisk.im4p', '--rkrn', 'work/krnl.im4p', '--latest-sep', '--latest-baseband', ipsw])
# exit
print('[*] Done!')
# clean
print('[*] Cleaning')
subprocess.run(['/bin/rm', '-rf', 'work'])
print('[*] Done!')
sys.exit(0)
else:
# dont restore device but tell user to enter pwndfu
print('[!] You need to enter pwndfu')
# tell the user how they can restore the device later
if skip_baseband:
print('[!] You can restore the device later using futurestore like this: futurerestore -t blob --use-pwndfu --skip-blob --rdsk work/ramdisk.im4p --rkrn work/krnl.im4p --latest-sep --no-baseband ipsw.ipsw')
else:
print('[!] You can restore the device later using futurestore like this: futurerestore -t blob --use-pwndfu --skip-blob --rdsk work/ramdisk.im4p --rkrn work/krnl.im4p --latest-sep --latest-baseband ipsw.ipsw')
# exit
sys.exit(0)
else:
print('[*] Exiting')
# clean up
subprocess.run(['/bin/rm', '-rf', 'work'])
# exit
sys.exit(0)
def prep_boot(ipsw, blob, board, kpp, identifier, legacy):
# getting lowercase board to avoid errors
board = board.lower()
# checking if work and boot direcotries exist, and deleting if they do
if os.path.exists('./work'):
shutil.rmtree('./work')
if os.path.exists('./boot'):
shutil.rmtree('./boot')
# create a working directory
print('[*] Creating working directory')
subprocess.run(['/bin/mkdir', 'work'])
# unzip the ipsw
print('[*] Unzipping IPSW')
with zipfile.ZipFile(ipsw, 'r') as z:
z.extractall('work')
with open('work/BuildManifest.plist', 'rb') as f:
manifest = Manifest(f.read())
# get ProductBuildVersion from manifest
print('[*] Getting ProductBuildVersion')
productbuildversion = manifest.getProductBuildVersion()
ibss_iv, ibss_key, ibec_iv, ibec_key = utils.api.get_keys(
identifier, board, productbuildversion)
# get ibec and ibss from manifest
print('[*] Getting IBSS and IBEC')
ibss = manifest.get_comp(board, 'iBSS')
ibec = manifest.get_comp(board, 'iBEC')
# decrypt ibss like this: img4 -i ibss -o ibss.dmg -k ivkey
print('[*] Decrypting IBSS')
subprocess.run(['./bin/img4', '-i', 'work/' + ibss,
'-o', 'work/ibss.dmg', '-k', ibss_iv + ibss_key])
# decrypt ibec like this: img4 -i ibec -o ibec.dmg -k ivkey
print('[*] Decrypting IBEC')
subprocess.run(['./bin/img4', '-i', 'work/' + ibec,
'-o', 'work/ibec.dmg', '-k', ibec_iv + ibec_key])
# patch ibss like this: iBoot64Patcher ibss.dmg ibss.patched
print('[*] Patching IBSS')
subprocess.run(['./bin/iBoot64Patcher',
'work/ibss.dmg', 'work/ibss.patched'])
# patch ibec like this: iBoot64Patcher ibec.dmg ibec.patched -b "-v"
print('[*] Patching IBEC')
subprocess.run(['./bin/iBoot64Patcher',
'work/ibec.dmg', 'work/ibec.patched', '-b', '-v'])
# convert blob into im4m like this: img4tool -e -s blob -m IM4M
print('[*] Converting BLOB to IM4M')
subprocess.run(['./bin/img4tool', '-e', '-s', blob, '-m', 'IM4M'])
# convert ibss into img4 like this: img4 -i ibss.patched -o ibss.img4 -M IM4M -A -T ibss
print('[*] Converting IBSS to IMG4')
subprocess.run(['./bin/img4', '-i', 'work/ibss.patched',
'-o', 'work/ibss.img4', '-M', 'IM4M', '-A', '-T', 'ibss'])
# convert ibec into img4 like this: img4 -i ibec.patched -o ibec.img4 -M IM4M -A -T ibec
print('[*] Converting IBEC to IMG4')
subprocess.run(['./bin/img4', '-i', 'work/ibec.patched',
'-o', 'work/ibec.img4', '-M', 'IM4M', '-A', '-T', 'ibec'])
# get the names of the devicetree and trustcache
print('[*] Getting DeviceTree and TrustCache')
# read manifest from work/BuildManifest.plist
if legacy:
devicetree = manifest.get_comp(board, 'DeviceTree')
else:
trustcache = manifest.get_comp(board, 'StaticTrustCache')
devicetree = manifest.get_comp(board, 'DeviceTree')
# sign them like this img4 -i devicetree -o devicetree.img4 -M IM4M -T rdtr
print('[*] Signing DeviceTree')
subprocess.run(['./bin/img4', '-i', 'work/' + devicetree,
'-o', 'work/devicetree.img4', '-M', 'IM4M', '-T', 'rdtr'])
# sign them like this img4 -i trustcache -o trustcache.img4 -M IM4M -T rtsc
print('[*] Signing TrustCache')
if not legacy:
subprocess.run(['./bin/img4', '-i', 'work/' + trustcache,
'-o', 'work/trustcache.img4', '-M', 'IM4M', '-T', 'rtsc'])
# grab kernelcache from manifest
print('[*] Getting KernelCache')
kernelcache = manifest.get_comp(board, 'KernelCache')
# extract the kernel like this: pyimg4 im4p extract -i kernelcache -o kcache.raw --extra kpp.bin
print('[*] Extracting kernel')
if kpp:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'extract', '-i',
'work/' + kernelcache, '-o', 'work/kcache.raw', '--extra', 'work/kpp.bin'])
else:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'extract',
'-i', 'work/' + kernelcache, '-o', 'work/kcache.raw'])
# patch it like this: Kernel64Patcher kcache.raw krnlboot.patched -f
print('[*] Patching kernel')
subprocess.run(['./bin/Kernel64Patcher',
'work/kcache.raw', 'work/krnlboot.patched', '-f'])
# convert it like this: pyimg4 im4p create -i krnlboot.patched -o krnlboot.im4p --extra kpp.bin -f rkrn --lzss
print('[*] Converting kernel')
if kpp:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'create', '-i', 'work/krnlboot.patched',
'-o', 'work/krnlboot.im4p', '--extra', 'work/kpp.bin', '-f', 'rkrn', '--lzss'])
else:
subprocess.run([sys.executable, '-m', 'pyimg4', 'im4p', 'create', '-i',
'work/krnlboot.patched', '-o', 'work/krnlboot.im4p', '-f', 'rkrn', '--lzss'])
# sign it like this: pyimg4 img4 create -p krnlboot.im4p -o krnlboot.img4 -m IM4M
print('[*] Signing kernel')
subprocess.run([sys.executable, '-m', 'pyimg4', 'img4', 'create', '-p',
'work/krnlboot.im4p', '-o', 'work/krnlboot.img4', '-m', 'IM4M'])
subprocess.run(['./bin/img4', '-i', 'other/bootlogo.im4p',
'-o', 'work/bootlogo.img4', '-m', 'IM4M', '-A', '-T', 'rlgo'])
print('[*] Creating boot directory')
subprocess.run(['mkdir', 'boot'])
# copy ibss, ibec, trustcache, devicetree, and krnlboot, and the bootlogo to the boot directory
print('[*] Copying files to boot directory')
subprocess.run(['cp', 'work/ibss.img4', 'boot/ibss.img4'])
subprocess.run(['cp', 'work/ibec.img4', 'boot/ibec.img4'])
if not legacy:
subprocess.run(['cp', 'work/trustcache.img4', 'boot/trustcache.img4'])
subprocess.run(['cp', 'work/devicetree.img4', 'boot/devicetree.img4'])
subprocess.run(['cp', 'work/krnlboot.img4', 'boot/krnlboot.img4'])
subprocess.run(['cp', 'work/bootlogo.img4', 'boot/bootlogo.img4'])
# clean up
print('[*] Cleaning up')
subprocess.run(['rm', '-rf', 'work'])
# done
print('[*] Done!')
print('[*] Boot using: boot.sh')
sys.exit(0)
def main():
if os.path.exists('.deps-installed') == False:
print('[*] Doing the last steps to configure your dependencies...')
print('[*] Downloading futurerestore...')
subprocess.run(['curl', '-sLo', 'futurerestore-macOS-RELEASE.zip', 'https://nightly.link/futurerestore/futurerestore/workflows/ci/main/futurerestore-macOS-RELEASE.zip'])
print('[*] Unzipping futurerestore...')
subprocess.run(['unzip', 'futurerestore-macOS-RELEASE.zip'])
subprocess.run('tar Jxfv futurerestore-*.xz', shell=True)
print('[*] Moving futurerestore to the bin folder...')
subprocess.run(['mv', 'futurerestore', 'bin/'])
subprocess.run('rm -rf futurerestore-*', shell=True)
print('[*] Adding executable flag to the binaries...')
subprocess.run(['chmod', '-R', '+x', 'bin'])
with open('.deps-installed', 'w') as file:
file.write('ok')
file.close()
print('[*] Dependencies configuration done! Running better-sunst0rm...')
parser = argparse.ArgumentParser(description='iOS Tethered IPSW Restore')
parser.add_argument('-i', '--ipsw', help='IPSW to restore', required=True)
parser.add_argument('-t', '--blob', help='Blob to use', required=True)
parser.add_argument('-r', '--restore', help='Restore Mode',
required=False, action='store_true')
parser.add_argument('-b', '--boot', help='Boot Mode',
required=False, action='store_true')
parser.add_argument('-d', '--boardconfig',
help='BoardConfig to use', required=True)
parser.add_argument('-kpp', '--kpp', help='Use KPP',
required=False, action='store_true')
parser.add_argument('-id', '--identifier',
help='Identifier to use', required=False)
parser.add_argument('--legacy', help='Use Legacy Mode (ios 11 or lower)',
required=False, action='store_true')
parser.add_argument('--skip-baseband', help='Skip Baseband',
required=False, action='store_true')
args = parser.parse_args()
if args.restore:
prep_restore(args.ipsw, args.blob, args.boardconfig,
args.kpp, args.legacy, args.skip_baseband)
elif args.boot:
if args.identifier == None:
print('[!] You need to specify an identifier')
sys.exit(0)
prep_boot(args.ipsw, args.blob, args.boardconfig,
args.kpp, args.identifier, args.legacy)
else:
print('[!] Please specify a mode')
sys.exit(0)
if __name__ == '__main__':
print("better-sunst0rm")
print("Made by mineek | Some code by m1n1exploit | Improved by rastiqdev\n")
main()