mirror of
https://github.com/tihmstar/futurerestore.git
synced 2024-12-22 17:35:29 +00:00
Changing locale and ReadMe for latest Apple names
This commit is contained in:
parent
29680274ae
commit
e5792d8123
|
@ -1,3 +1,3 @@
|
||||||
AUTOMAKE_OPTIONS = foreign
|
AUTOMAKE_OPTIONS = foreign
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
SUBDIRS = external/idevicerestore external/tsschecker futurerestore
|
SUBDIRS = external/idevicerestore external/tsschecker futurerestore
|
||||||
|
|
244
README.md
244
README.md
|
@ -1,80 +1,109 @@
|
||||||
# futurerestore
|
# futurerestore
|
||||||
_futurerestore is a hacked up idevicerestore wrapper, which allows manually specifying SEP and Baseband for restoring_
|
_It is a hacked up idevicerestore wrapper, which allows manually specifying SEP and Baseband for restoring._
|
||||||
|
|
||||||
Latest compiled version can be found here:
|
Latest compiled version can be found [here](https://github.com/tihmstar/futurerestore/releases).
|
||||||
(MacOS & Linux)
|
|
||||||
http://api.tihmstar.net/builds/futurerestore/futurerestore-latest.zip
|
__Only use if you are sure what you're doing.__
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
* Supports the following downgrade methods
|
* Supports the following downgrade methods:
|
||||||
* Prometheus 64bit devices (generator and nonce collision mode)
|
* Prometheus 64-bit devices (generator and ApNonce collision mode)
|
||||||
* Odysseus for 32bit devices
|
* Odysseus for 32-bit / 64-bit devices
|
||||||
* Re-restoring 32bit devices to iOS 9 with @alitek123's no-nonce method
|
* Re-restoring 32-bit devices to iOS 9.x with [alitek123](https://github.com/alitek12)'s no-ApNonce method (alternative — [idevicererestore](https://downgrade.party)).
|
||||||
* Allows restoring any nonmatching signed iOS/Sep/Baseband
|
* Allows restoring to non-matching firmware with custom SEP+baseband
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
* ## Bundled Libs
|
|
||||||
Those don't need to be installed manually
|
|
||||||
* jsmn
|
|
||||||
* ## External Libs
|
* ## External Libs
|
||||||
Make sure these are installed
|
Make sure these are installed
|
||||||
* libzip
|
* [libzip](https://github.com/nih-at/libzip);
|
||||||
* libcurl
|
* [libcurl](https://github.com/curl/curl);
|
||||||
* openssl (or CommonCrypto on OSX)
|
* [openssl](https://github.com/openssl/openssl) (or CommonCrypto on macOS/OS X);
|
||||||
* [libplist](https://github.com/libimobiledevice/libplist)
|
* [libplist](https://github.com/libimobiledevice/libplist);
|
||||||
|
* [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd);
|
||||||
|
* [libirecovery](https://github.com/libimobiledevice/libirecovery);
|
||||||
|
* [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice);
|
||||||
|
* [img4tool](https://github.com/tihmstar/img4tool);
|
||||||
|
* [liboffsetfinder64](https://github.com/tihmstar/liboffsetfinder64)
|
||||||
|
* [libipatcher](https://github.com/tihmstar/libipatcher)
|
||||||
|
|
||||||
* ## Submodules
|
* ## Submodules
|
||||||
Make sure these projects compile on your system (install it's dependencies)
|
Make sure these projects compile on your system (install it's dependencies):
|
||||||
* [tsschecker](https://github.com/tihmstar/tsschecker)
|
* [jssy](https://github.com/tihmstar/jssy);
|
||||||
* [img4tool](https://github.com/tihmstar/img4tool)
|
* [tsschecker](https://github.com/tihmstar/tsschecker);
|
||||||
* [idevicerestore](https://github.com/tihmstar/idevicerestore)
|
* [idevicerestore](https://github.com/tihmstar/idevicerestore)
|
||||||
|
|
||||||
|
## Report an issue
|
||||||
|
You can do it [here](https://github.com/tihmstar/futurerestore/issues).
|
||||||
|
|
||||||
|
### Restoring on Windows 10
|
||||||
|
1. Try to restore the device, error `-8` occurs;
|
||||||
|
2. Leave the device plugged in, it'll stay on the Recovery screen;
|
||||||
|
3. Head over to device manager under control panel in Windows;
|
||||||
|
4. Locate "Apple Recovery (iBoot) USB Composite Device" (at the bottom);
|
||||||
|
5. Right click and choose "Uninstall device".
|
||||||
|
You may see a tick box that allows you to uninstall the driver software as well, tick that (all the three Apple mobile device entries under USB devices will disappear);
|
||||||
|
6. Unplug the device and re-plug it in;
|
||||||
|
7. Go back to futurerestore and send the restore command again (just press the up arrow to get it back, then enter).
|
||||||
|
Error `-8` is now fixed, but the process will fail again after the screen of your device has turned green;
|
||||||
|
8. Go back to device manager and repeat the driver uninstall process as described above (step 4 to 6);
|
||||||
|
9. Go back to futurerestore once again and repeat the restore process;
|
||||||
|
10. The device will reboot and error `-10` will also be solved;
|
||||||
|
11. The restore will now proceed and succeed.
|
||||||
|
|
||||||
|
### Some about [cURL](https://github.com/curl/curl)
|
||||||
|
* Linux: Follow [this guide](https://dev.to/jake/using-libcurl3-and-libcurl4-on-ubuntu-1804-bionic-184g) to use tsschecker on Ubuntu 18.04 (Bionic) as it requires libcurl3 which cannot coexist with libcurl4 on this OS.
|
||||||
|
|
||||||
# Help
|
# Help
|
||||||
_(might become outdated):_
|
_(might become outdated):_
|
||||||
|
|
||||||
Usage: `futurerestore [OPTIONS] IPSW`
|
Usage: `futurerestore [OPTIONS] iPSW`
|
||||||
|
|
||||||
|
| option (short) | option (long) | description |
|
||||||
| option (short) | option (long) | description |
|
|----------------|------------------------------------------|-----------------------------------------------------------------------------------|
|
||||||
|----------------|---------------------------|-----------------------------------------------------------------------------------|
|
| ` -t ` | ` --apticket PATH ` | Signing tickets used for restoring |
|
||||||
| -t | --apticket | PATH Apticket used for restoring |
|
| ` -u ` | ` --update ` | Update instead of erase install (requires appropriate APTicket) |
|
||||||
| -b | --baseband | PATH Baseband to be flashed |
|
| | | DO NOT use this parameter, if you update from jailbroken firmware! |
|
||||||
| -p | --baseband-manifest | PATH Buildmanifest for requesting baseband ticket |
|
| ` -w ` | ` --wait ` | Keep rebooting until ApNonce matches APTicket (ApNonce collision, unreliable) |
|
||||||
| -s | --sep PATH | Sep to be flashed |
|
| ` -d ` | ` --debug ` | Show all code, use to save a log for debug testing |
|
||||||
| -m | --sep-manifest PATH | Buildmanifest for requesting sep ticket |
|
| | ` --exit-recovery ` | Exit recovery mode and quit |
|
||||||
| -w | --wait | keep rebooting until nonce matches APTicket |
|
| | ` --use-pwndfu ` | Restoring devices with Odysseus method. Device needs to be in pwned DFU mode already |
|
||||||
| -u | --update | update instead of erase install |
|
| | ` --just-boot "-v" ` | Tethered booting the device from pwned DFU mode. You can optionally set ` boot-args ` |
|
||||||
| -d | --debug | show all code, use to save a log for debug testing |
|
| | ` --latest-sep ` | Use latest signed sep instead of manually specifying one (may cause bad restore) |
|
||||||
| |--latest-sep | use latest signed sep instead of manually specifying one(may cause bad restore) |
|
| ` -s ` | ` --sep PATH ` | SEP to be flashed |
|
||||||
| | --latest-baseband | use latest signed baseband instead of manually specifying one(may cause bad restore) |
|
| ` -m ` | ` --sep-manifest PATH ` | BuildManifest for requesting SEP ticket |
|
||||||
| | --no-baseband | skip checks and don't flash baseband. WARNING: only use this for device without baseband (eg iPod or some wifi only iPads) |
|
| | ` --latest-baseband ` | Use latest signed baseband instead of manually specifying one (may cause bad restore) |
|
||||||
|
| ` -b ` | ` --baseband PATH ` | Baseband to be flashed |
|
||||||
|
| ` -p ` | ` --baseband-manifest PATH ` | Buildmanifest for requesting baseband ticket |
|
||||||
|
| | ` --no-baseband ` | Skip checks and don't flash baseband |
|
||||||
|
| | | Only use this for device without a baseband (eg. iPod touch or some Wi-Fi only iPads) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0) What futurerestore can do
|
## 0) What futurerestore can do
|
||||||
**Downgrade/Upgrade/Re-restore same iOS.**
|
**Downgrade/Upgrade/Re-restore same mobile firmware version.**
|
||||||
Whenever you read "downgrade" nowadays it means you can also upgrade and re-restore if you're on the same iOS. Basically this allows restoring an iOS and the installed iOS doesn't matter.
|
Whenever you read "downgrade" nowadays it means you can also upgrade and re-restore if you're on the same firmware version. Basically this allows restoring an firmware version and the installed firmware version doesn't matter.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1) Prometheus (64bit device) - generator method
|
## 1) Prometheus (64-bit device) - generator method
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
- Jailbreak
|
- Jailbreak
|
||||||
- SHSH2 files with a generator
|
- signing ticket files (`.shsh`, `.shsh2`, `.plist`) with a generator
|
||||||
- nonceEnabler patch enabled
|
- nonceEnabler patch enabled
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
You can downgrade if the destination iOS is compatible with the latest signed SEP and if you have shsh2 files with a generator for that iOS.
|
You can downgrade, if the destination firmware version is compatible with the **latest signed SEP and baseband** and if you **have a signing tickets files with a generator for that firmware version**.
|
||||||
|
|
||||||
### How to use
|
### How to use
|
||||||
1. Device must be jailbroken and nonceEnabler patch must be active
|
1. Device must be jailbroken and nonceEnabler patch must be active
|
||||||
2. Open shsh file and look up the generator
|
2. Open signing ticket file and look up the generator
|
||||||
* Looks like this: `<key>generator</key><string>0xde3318d224cf14a1</string>`
|
* Looks like this: `<key>generator</key><string>0xde3318d224cf14a1</string>`
|
||||||
3. Write the generator to device's NVRAM
|
3. Write the generator to device's NVRAM
|
||||||
* SSH into the device and run `nvram com.apple.System.boot-nonce=0xde3318d224cf14a1` to set the generator *0xde3318d224cf14a1*
|
* Connect with SSH into the device and run `nvram com.apple.System.boot-nonce=0xde3318d224cf14a1` to set the generator *0xde3318d224cf14a1*
|
||||||
* verify with `nvram -p`
|
* verify it with `nvram -p`
|
||||||
4. Connect your device in normal mode to computer
|
4. Connect your device in normal mode to computer
|
||||||
5. On the computer run `futurerestore -t ticket.shsh --latest-baseband --latest-sep ios.ipsw`
|
5. On the computer run `futurerestore -t ticket.shsh --latest-baseband --latest-sep ios.ipsw`
|
||||||
|
|
||||||
|
@ -83,82 +112,143 @@ You can downgrade if the destination iOS is compatible with the latest signed SE
|
||||||
*Prometheus*
|
*Prometheus*
|
||||||
|
|
||||||
<a href="http://www.youtube.com/watch?feature=player_embedded&v=UXxpUH71-s4" target="_blank"><img src="http://img.youtube.com/vi/UXxpUH71-s4/0.jpg" alt="Prometheus" width="240" height="180"/></a>
|
<a href="http://www.youtube.com/watch?feature=player_embedded&v=UXxpUH71-s4" target="_blank"><img src="http://img.youtube.com/vi/UXxpUH71-s4/0.jpg" alt="Prometheus" width="240" height="180"/></a>
|
||||||
*NonceEnabler*
|
*nonceEnabler*
|
||||||
|
|
||||||
### Recommended method to active nonceEnabler patch
|
### Recommended methods to activate nonceEnabler patch
|
||||||
1. Get nvpatch https://github.com/Siguza/ios-kern-utils/releases/
|
#### Method 1: ios-kern-utils (iOS 7.x-10.x):
|
||||||
2. Run on the device `nvpatch com.apple.System.boot-nonce`
|
1. Install DEB-file of [ios-kern-utils](https://github.com/Siguza/ios-kern-utils/releases/) on device;
|
||||||
|
2. Run on the device `nvpatch com.apple.System.boot-nonce`.
|
||||||
|
|
||||||
|
#### Method 2: Using special applications
|
||||||
|
Use utilities for setting boot-nonce generator:
|
||||||
|
1. [PhœnixNonce](https://github.com/Siguza/PhoenixNonce) for iOS 9.x;
|
||||||
|
2. [v0rtexnonce](https://github.com/arx8x/v0rtexnonce) for iOS 10.x;
|
||||||
|
3. [Nonceset1112](https://github.com/julioverne/NonceSet112) for iOS 11.0-11.1.2;
|
||||||
|
4. [noncereboot1131UI](https://github.com/s0uthwest/noncereboot1131UI) for iOS 11.0-11.4b3;
|
||||||
|
5. [NonceReboot12xx](https://github.com/ur0/NonceReboot12XX) for iOS 12.0-12.1.2.
|
||||||
|
|
||||||
|
#### Method 3: Using jailbreak tools
|
||||||
|
Use jailbreak tools for setting boot-nonce generator:
|
||||||
|
1. [Meridian](https://meridian.sparkes.zone) for iOS 10.x;
|
||||||
|
2. [backr00m](https://nito.tv) or greeng0blin for tvOS 10.2-11.1;
|
||||||
|
3. [Electra and ElectraTV](https://coolstar.org/electra) for iOS and tvOS 11.x;
|
||||||
|
4. [unc0ver](https://unc0ver.dev) for iOS 11.0-12.2, 12.4.x;
|
||||||
|
5. [Chimera and ChimeraTV](https://chimera.sh) for iOS 12.0-12.2, 12.4 and tvOS 12.0-12.2, 12.4.
|
||||||
|
|
||||||
### Activate tfp0 if jailbreak doesn't allow it
|
### Activate tfp0 if jailbreak doesn't allow it
|
||||||
#### Method 1 (if jailbroken on 9.3.x)
|
#### Method 1 (if jailbroken on iOS 9.2-9.3.x)
|
||||||
* reboot
|
* reboot;
|
||||||
* reactivate jailbreak with https://jbme.qwertyoruiop.com/
|
* reactivate jailbreak with [Luca Todesco](https://github.com/kpwn)'s [JailbreakMe](https://jbme.qwertyoruiop.com/);
|
||||||
* done
|
* done.
|
||||||
|
|
||||||
#### Method 2
|
#### Method 2 (if jailbroken on iOS 8.0-8.1 with [Pangu8](https://en.8.pangu.io))
|
||||||
* Use cl0ver (https://github.com/Siguza/cl0ver)
|
* install this [untether DEB-file](http://apt.saurik.com/beta/pangu8-tfp0/io.pangu.xuanyuansword8_0.5_iphoneos-arm.deb) with included tfp0 patch
|
||||||
|
|
||||||
|
#### Method 3 (if jailbroken on iOS 7.x with [Pangu7](https://en.7.pangu.io))
|
||||||
|
* install this [untether DEB-file](http://apt.saurik.com/debs/io.pangu.axe7_0.3_iphoneos-arm.deb) with included tfp0 patch
|
||||||
|
|
||||||
|
#### Method 4
|
||||||
|
* Use [cl0ver](https://github.com/Siguza/cl0ver) for iOS 9.x.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2) Prometheus (64bit device) - nonce collision method
|
## 2) Prometheus (64-bit device) - ApNonce collision method (Recovery mode);
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
- iPhone5s or iPad Air on iOS 9.1 - 10.2
|
- Device with A7 chip on iOS 9.1 - 10.2 or iOS 10.3 beta 1;
|
||||||
- No Jailbreak required
|
- Jailbreak doesn't required;
|
||||||
- SHSH files with customly chosen APNonce
|
- Signing ticket files (`.shsh`, `.shsh2`, `.plist`) with a customly chosen APNonce;
|
||||||
- The shsh file needs to have one of the nonces, which the device generates a lot
|
- Signing ticket files needs to have one of the ApNonces, which the device generates a lot;
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
You can downgrade if the destination iOS is compatible with the latest signed SEP. You also need to have special shsh files. If you don't know what this is, you probably can **NOT** use this method!
|
You can downgrade if the destination firmware version, if it is compatible with the **latest signed SEP and baseband**. You also need to have **special signing ticket files**. If you don't know what this is, you probably can **NOT** use this method!
|
||||||
|
|
||||||
### How to use
|
### How to use
|
||||||
1. Connect your device in normal mode or recovery mode
|
1. Connect your device in normal or recovery mode;
|
||||||
2. On the computer run `futurerestore -w -t ticket.shsh --latest-baseband --latest-sep ios.ipsw`
|
2. On the computer run `futurerestore -w -t ticket.shsh --latest-baseband --latest-sep ios.ipsw`
|
||||||
* If you have saved multiple tickets with different nonces you can specify more than
|
* If you have saved multiple signing tickets with different nonces you can specify more than
|
||||||
one to speed up the process: `futurerestore -w -t t1.shsh -t t2.shsh -t t3.shsh -t t4.shsh --latest-baseband --latest-sep ios.ipsw`
|
one to speed up the process: `futurerestore -w -t t1.shsh -t t2.shsh -t t3.shsh -t t4.shsh --latest-baseband --latest-sep ios.ipsw`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3) Odysseus (32bit devices)
|
## 3) Prometheus (64-bit device) - ApNonce collision method (DFU mode);
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
- futurerestore compiled with libipatcher (odysseus support)
|
- __Devices with A7 (iPhone 5s, iPad Air, iPad mini 2), A8 (iPhone 6 [+], iPad mini [2,3,4], iPod touch [6th generation]) and A8X (iPad Air 2) chips on all firmwares;__
|
||||||
- Jailbreak or bootrom exploit (limera1n)
|
- __Devices have been released after ~September, 2015 {PROBABLY};__
|
||||||
- Firmware keys for the device/destination iOS must be public (check ipsw.me)
|
- Jailbreak doesn't required;
|
||||||
- SHSH files for the destination iOS (OTA blobs work too!)
|
- Signing ticket files (`.shsh`, `.shsh2`, `.plist`) with a customly chosen APNonce;
|
||||||
|
- Signing ticket files needs to have one of the ApNonces, which the device generates a lot;
|
||||||
|
- __[img4tool](https://github.com/tihmstar/img4tool) can't be used for Windows [problem with signing iBSS/iBEC], now it's TO-DO;__
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
If you have a jailbroken 32bit device you can downgrade to any iOS you have blobs for. You can still get OTA blobs for iOS 6.1.3 and 8.4.1 for some devices and use those.
|
You can downgrade if the destination firmware version, if it is compatible with the **latest signed SEP and baseband**. You also need to have **special signing ticket files**. If you don't know what this is, you probably can **NOT** use this method!
|
||||||
|
|
||||||
|
### How to use
|
||||||
|
1. Connect your device in DFU mode;
|
||||||
|
2. Use [irecovery](https://github.com/libimobiledevice/libirecovery) for checking ApNonce, which booted in DFU;
|
||||||
|
3. Extract iBSS/iBEC from target firmware for downgrade (unsigned);
|
||||||
|
4. Check DFU-collisioned ApNonces with [irecovery](https://github.com/libimobiledevice/libirecovery), which booted in DFU.
|
||||||
|
You can't automatically collision DFU ApNonces.
|
||||||
|
|
||||||
|
__If ApNonce is not collisioned, "use hands" for DFU booting.__
|
||||||
|
|
||||||
|
__If ApNonce is successfully coliisioned, use this SHSH2 for sign iBSS/iBEC.__
|
||||||
|
5. Use img4tool for sign iBSS:
|
||||||
|
`img4tool -s ticket.shsh -c iBSS.signed -p <original_iBSS>`;
|
||||||
|
6. Use img4tool for sign iBEC:
|
||||||
|
`img4tool -s ticket.shsh -c iBEC.signed -p <original_iBEC>`;
|
||||||
|
7. So, after signing we can boot into Recovery with irecovery.
|
||||||
|
|
||||||
|
`irecovery -f iBSS.signed` - loading iBSS;
|
||||||
|
|
||||||
|
`irecovery -f iBEC.signed` - loading iBEC;
|
||||||
|
8. So good! On the computer run `futurerestore -t ticket.shsh --latest-baseband --latest-sep -w ios.ipsw`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4) Odysseus (32-bit / 64-bit devices)
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
- futurerestore compiled with libipatcher;
|
||||||
|
- Jailbreak or bootrom exploit (limera1n, checkm8);
|
||||||
|
- **32-bit**: firmware keys for the device/destination firmware version must be public (check ipsw.me);
|
||||||
|
- **64-bit**: devices with **A12** and **A13** chips is **NOT** compatible with this method;
|
||||||
|
- Signing ticket files (`.shsh`, `.shsh2`, `.plist`) from by destination firmware (OTA blobs work too!).
|
||||||
|
|
||||||
|
### Info
|
||||||
|
If you have a jailbroken device, you can downgrade to **any** firmware version you have blobs for. You can still get OTA blobs for iOS 6.1.3, 8.4.1 or 10.3.3 for some devices and use those.
|
||||||
|
|
||||||
### How to use
|
### How to use
|
||||||
1. Get device into kDFU/pwnDFU
|
1. Get device into kDFU/pwnDFU
|
||||||
* Pre-iPhone4s (limera1n devices):
|
* Pre-iPhone4s (limera1n devices):
|
||||||
* Enter pwndfu mode with redsn0w or any other tool
|
* Enter to pwnDFU mode with redsn0w or any other tool
|
||||||
* iPhone4s and later:
|
* iPhone 4s and later:
|
||||||
* Jailbreak required!
|
* Enter to kDFU mode with kDFU app (cydia: repo.tihmstar.net) or by loading a pwniBSS from any existing odysseus bundle
|
||||||
* Enter kDFU mode with kDFU app (cydia: repo.tihmstar.net) or by loading a pwniBSS from any existing odysseus bundle.
|
or
|
||||||
|
* Enter to pwnDFU mode with [ipwndfu](https://github.com/axi0mx/ipwndfu) or use futurerestore for it;
|
||||||
2. Connect your device to computer in kDFU mode (or pwnDFU mode)
|
2. Connect your device to computer in kDFU mode (or pwnDFU mode)
|
||||||
3. On the computer run `futurerestore --use-pwndfu -t ticket.shsh --latest-baseband ios.ipsw`
|
3. On the computer run `futurerestore --use-pwndfu -t ticket.shsh --latest-baseband ios.ipsw`
|
||||||
|
|
||||||
### Youtube
|
### Youtube
|
||||||
<a href="http://www.youtube.com/watch?feature=player_embedded&v=FQfcybsEWmM" target="_blank"><img src="http://img.youtube.com/vi/FQfcybsEWmM/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
<a href="http://www.youtube.com/watch?feature=player_embedded&v=FQfcybsEWmM" target="_blank"><img src="http://img.youtube.com/vi/FQfcybsEWmM/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
||||||
*Futurerestore + Libipatcher*
|
*Futurerestore + libipatcher*
|
||||||
|
|
||||||
<a href="http://www.youtube.com/watch?feature=player_embedded&v=8Ro4g6StPeI" target="_blank"><img src="http://img.youtube.com/vi/8Ro4g6StPeI/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
<a href="http://www.youtube.com/watch?feature=player_embedded&v=8Ro4g6StPeI" target="_blank"><img src="http://img.youtube.com/vi/8Ro4g6StPeI/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
||||||
*kDFU App*
|
*kDFU app*
|
||||||
|
|
||||||
<a href="http://www.youtube.com/watch?feature=player_embedded&v=Wo7mGdMcjxw" target="_blank"><img src="http://img.youtube.com/vi/Wo7mGdMcjxw/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
<a href="http://www.youtube.com/watch?feature=player_embedded&v=Wo7mGdMcjxw" target="_blank"><img src="http://img.youtube.com/vi/Wo7mGdMcjxw/0.jpg" alt="Odysseus" width="240" height="180"/></a>
|
||||||
*Enter kDFU Mode (watch up to the point where the screen goes black)*
|
*Enter kDFU mode (watch up to the point where the screen goes black)*
|
||||||
|
|
||||||
*You can use **any** odysseus bundle for this*
|
*You can use **any** odysseus bundle for this*
|
||||||
|
|
||||||
## 4) iOS 9 Re-restore bug (found by @alitek123) (32bit devices):
|
## 5) iOS 9.x Re-restore bug (found by @alitek123) (only for 32-bit devices):
|
||||||
### Requirements
|
### Requirements
|
||||||
- No Jailbreak required
|
- Jailbreak doesn't required;
|
||||||
- SHSH files without a nonce (noNonce APTickets)
|
- Signing ticket files (`.shsh`, `.shsh2`, `.plist`) from by iOS 9.x without ApNonce (noNonce APTickets)
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
If you have shsh files for iOS9 which do not contain a nonce, you can restore to that firmware.
|
If you have **signing tickets files for iOS 9.x**, which **do not contain a ApNonce**, you can restore to that firmware.
|
||||||
|
|
||||||
### How to use
|
### How to use
|
||||||
1. Connect your device in DFU mode
|
1. Connect your device in DFU mode
|
||||||
|
|
|
@ -433,6 +433,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
INSTALL_PATH = /usr/local/bin;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
@ -481,6 +482,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
INSTALL_PATH = /usr/local/bin;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
|
|
@ -6,7 +6,7 @@ AM_LDFLAGS += $(libipatcher_LIBS)
|
||||||
AM_CFLAGS += $(libipatcher_CFLAGS)
|
AM_CFLAGS += $(libipatcher_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
bin_PROGRAMS = futurerestore
|
bin_PROGRAMS = futurerestore
|
||||||
futurerestore_CXXFLAGS = $(AM_CFLAGS)
|
futurerestore_CXXFLAGS = $(AM_CFLAGS)
|
||||||
futurerestore_LDADD = $(top_srcdir)/external/idevicerestore/src/libidevicerestore.la $(top_srcdir)/external/tsschecker/tsschecker/libtsschecker.la $(top_srcdir)/external/tsschecker/tsschecker/libjssy.a $(AM_LDFLAGS)
|
futurerestore_LDADD = $(top_srcdir)/external/idevicerestore/src/libidevicerestore.la $(top_srcdir)/external/tsschecker/tsschecker/libtsschecker.la $(top_srcdir)/external/tsschecker/tsschecker/libjssy.a $(AM_LDFLAGS)
|
||||||
futurerestore_SOURCES = futurerestore.cpp main.cpp
|
futurerestore_SOURCES = futurerestore.cpp main.cpp
|
||||||
|
|
|
@ -75,14 +75,12 @@ extern "C"{
|
||||||
using namespace tihmstar;
|
using namespace tihmstar;
|
||||||
|
|
||||||
#pragma mark helpers
|
#pragma mark helpers
|
||||||
|
|
||||||
extern "C"{
|
extern "C"{
|
||||||
void irecv_event_cb(const irecv_device_event_t* event, void *userdata);
|
void irecv_event_cb(const irecv_device_event_t* event, void *userdata);
|
||||||
void idevice_event_cb(const idevice_event_t *event, void *userdata);
|
void idevice_event_cb(const idevice_event_t *event, void *userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma mark futurerestore
|
#pragma mark futurerestore
|
||||||
|
|
||||||
futurerestore::futurerestore(bool isUpdateInstall, bool isPwnDfu) : _isUpdateInstall(isUpdateInstall), _isPwnDfu(isPwnDfu){
|
futurerestore::futurerestore(bool isUpdateInstall, bool isPwnDfu) : _isUpdateInstall(isUpdateInstall), _isPwnDfu(isPwnDfu){
|
||||||
_client = idevicerestore_client_new();
|
_client = idevicerestore_client_new();
|
||||||
if (_client == NULL) throw std::string("could not create idevicerestore client\n");
|
if (_client == NULL) throw std::string("could not create idevicerestore client\n");
|
||||||
|
@ -90,8 +88,7 @@ futurerestore::futurerestore(bool isUpdateInstall, bool isPwnDfu) : _isUpdateIns
|
||||||
struct stat st{0};
|
struct stat st{0};
|
||||||
if (stat(FUTURERESTORE_TMP_PATH, &st) == -1) __mkdir(FUTURERESTORE_TMP_PATH, 0755);
|
if (stat(FUTURERESTORE_TMP_PATH, &st) == -1) __mkdir(FUTURERESTORE_TMP_PATH, 0755);
|
||||||
|
|
||||||
//tsschecker nocache
|
nocache = 1; //tsschecker nocache
|
||||||
nocache = 1;
|
|
||||||
_foundnonce = -1;
|
_foundnonce = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +96,9 @@ bool futurerestore::init(){
|
||||||
if (_didInit) return _didInit;
|
if (_didInit) return _didInit;
|
||||||
_didInit = (check_mode(_client) != MODE_UNKNOWN);
|
_didInit = (check_mode(_client) != MODE_UNKNOWN);
|
||||||
if (!(_client->image4supported = is_image4_supported(_client))){
|
if (!(_client->image4supported = is_image4_supported(_client))){
|
||||||
info("[INFO] 32bit device detected\n");
|
info("[INFO] 32-bit device detected\n");
|
||||||
}else{
|
}else{
|
||||||
info("[INFO] 64bit device detected\n");
|
info("[INFO] 64-bit device detected\n");
|
||||||
}
|
}
|
||||||
return _didInit;
|
return _didInit;
|
||||||
}
|
}
|
||||||
|
@ -109,9 +106,7 @@ bool futurerestore::init(){
|
||||||
uint64_t futurerestore::getDeviceEcid(){
|
uint64_t futurerestore::getDeviceEcid(){
|
||||||
retassure(_didInit, "did not init\n");
|
retassure(_didInit, "did not init\n");
|
||||||
uint64_t ecid;
|
uint64_t ecid;
|
||||||
|
|
||||||
get_ecid(_client, &ecid);
|
get_ecid(_client, &ecid);
|
||||||
|
|
||||||
return ecid;
|
return ecid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,13 +147,12 @@ void futurerestore::putDeviceIntoRecovery(){
|
||||||
){
|
){
|
||||||
info("requesting to get into pwnRecovery later\n");
|
info("requesting to get into pwnRecovery later\n");
|
||||||
}else if (!_client->image4supported){
|
}else if (!_client->image4supported){
|
||||||
info("32bit device in DFU mode found, assuming user wants to use iOS9 re-restore bug. Not failing here\n");
|
info("32-bit device in DFU mode found, assuming user wants to use iOS 9.x re-restore bug. Not failing here\n");
|
||||||
}else{
|
}else{
|
||||||
reterror("unsupported devicemode, please put device in recovery mode or normal mode\n");
|
reterror("unsupported device mode, please put device in recovery or normal mode\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
//only needs to be freed manually when function did't throw exception
|
safeFree(_client->udid); //only needs to be freed manually when function did't throw exception
|
||||||
safeFree(_client->udid);
|
|
||||||
|
|
||||||
//these get also freed by destructor
|
//these get also freed by destructor
|
||||||
dfu_client_free(_client);
|
dfu_client_free(_client);
|
||||||
|
@ -168,8 +162,7 @@ void futurerestore::putDeviceIntoRecovery(){
|
||||||
void futurerestore::setAutoboot(bool val){
|
void futurerestore::setAutoboot(bool val){
|
||||||
retassure(_didInit, "did not init\n");
|
retassure(_didInit, "did not init\n");
|
||||||
|
|
||||||
retassure(getDeviceMode(false) == MODE_RECOVERY, "can't set autoboot, when device isn't in recovery mode\n");
|
retassure(getDeviceMode(false) == MODE_RECOVERY, "can't set auto-boot, when device isn't in recovery mode\n");
|
||||||
|
|
||||||
retassure(!_client->recovery && recovery_client_new(_client),"Could not connect to device in recovery mode.\n");
|
retassure(!_client->recovery && recovery_client_new(_client),"Could not connect to device in recovery mode.\n");
|
||||||
retassure(!recovery_set_autoboot(_client, val),"Setting auto-boot failed?!\n");
|
retassure(!recovery_set_autoboot(_client, val),"Setting auto-boot failed?!\n");
|
||||||
}
|
}
|
||||||
|
@ -185,19 +178,19 @@ plist_t futurerestore::nonceMatchesApTickets(){
|
||||||
|
|
||||||
if (getDeviceMode(true) != MODE_RECOVERY){
|
if (getDeviceMode(true) != MODE_RECOVERY){
|
||||||
if (getDeviceMode(false) != MODE_DFU || *_client->version != '9')
|
if (getDeviceMode(false) != MODE_DFU || *_client->version != '9')
|
||||||
reterror("Device not in recovery mode, can't check apnonce\n");
|
reterror("Device is not in recovery mode, can't check apnonce\n");
|
||||||
else
|
else
|
||||||
_rerestoreiOS9 = (info("Detected iOS 9 re-restore, proceeding in DFU mode\n"),true);
|
_rerestoreiOS9 = (info("Detected iOS 9.x 32-bit re-restore, proceeding in DFU mode\n"),true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* realnonce;
|
unsigned char* realnonce;
|
||||||
int realNonceSize = 0;
|
int realNonceSize = 0;
|
||||||
if (_rerestoreiOS9) {
|
if (_rerestoreiOS9) {
|
||||||
info("Skipping APNonce check\n");
|
info("Skipping ApNonce check\n");
|
||||||
}else{
|
}else{
|
||||||
recovery_get_ap_nonce(_client, &realnonce, &realNonceSize);
|
recovery_get_ap_nonce(_client, &realnonce, &realNonceSize);
|
||||||
|
|
||||||
info("Got APNonce from device: ");
|
info("Got ApNonce from device: ");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (i = 0; i < realNonceSize; i++) {
|
for (i = 0; i < realNonceSize; i++) {
|
||||||
info("%02x ", ((unsigned char *)realnonce)[i]);
|
info("%02x ", ((unsigned char *)realnonce)[i]);
|
||||||
|
@ -217,13 +210,12 @@ plist_t futurerestore::nonceMatchesApTickets(){
|
||||||
size_t ticketNonceSize = 0;
|
size_t ticketNonceSize = 0;
|
||||||
const char *nonce = NULL;
|
const char *nonce = NULL;
|
||||||
try {
|
try {
|
||||||
//nonce might not exist, which we use in re-restoring iOS9
|
//nonce might not exist, which we use in re-restoring iOS 9.x for 32-bit
|
||||||
auto n = getNonceFromSCAB(_im4ms[i].first, _im4ms[i].second);
|
auto n = getNonceFromSCAB(_im4ms[i].first, _im4ms[i].second);
|
||||||
ticketNonceSize = n.second;
|
ticketNonceSize = n.second;
|
||||||
nonce = n.first;
|
nonce = n.first;
|
||||||
} catch (...) {
|
} catch (...)
|
||||||
//
|
{ }
|
||||||
}
|
|
||||||
if (memcmp(realnonce, nonce, ticketNonceSize) == 0 &&
|
if (memcmp(realnonce, nonce, ticketNonceSize) == 0 &&
|
||||||
( (ticketNonceSize == realNonceSize && realNonceSize+ticketNonceSize > 0) ||
|
( (ticketNonceSize == realNonceSize && realNonceSize+ticketNonceSize > 0) ||
|
||||||
(!ticketNonceSize && *_client->version == '9' &&
|
(!ticketNonceSize && *_client->version == '9' &&
|
||||||
|
@ -233,19 +225,18 @@ plist_t futurerestore::nonceMatchesApTickets(){
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
//either nonce needs to match or using re-restore bug in iOS 9
|
//either nonce needs to match or using re-restore bug in iOS 9.x
|
||||||
return _aptickets[i];
|
return _aptickets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<const char *,size_t> futurerestore::nonceMatchesIM4Ms(){
|
std::pair<const char *,size_t> futurerestore::nonceMatchesIM4Ms(){
|
||||||
retassure(_didInit, "did not init\n");
|
retassure(_didInit, "did not init\n");
|
||||||
|
|
||||||
retassure(getDeviceMode(true) == MODE_RECOVERY, "Device not in recovery mode, can't check apnonce\n");
|
retassure(getDeviceMode(true) == MODE_RECOVERY, "Device is not in recovery mode, can't check ApNonce\n");
|
||||||
|
|
||||||
unsigned char* realnonce;
|
unsigned char* realnonce;
|
||||||
int realNonceSize = 0;
|
int realNonceSize = 0;
|
||||||
|
@ -263,7 +254,7 @@ std::pair<const char *,size_t> futurerestore::nonceMatchesIM4Ms(){
|
||||||
size_t ticketNonceSize = 0;
|
size_t ticketNonceSize = 0;
|
||||||
const char *nonce = NULL;
|
const char *nonce = NULL;
|
||||||
try {
|
try {
|
||||||
//nonce might not exist, which we use in re-restoring iOS9
|
//nonce might not exist, which we use in re-restoring iOS 9.x for 32-bit
|
||||||
auto n = getNonceFromSCAB(_im4ms[i].first, _im4ms[i].second);
|
auto n = getNonceFromSCAB(_im4ms[i].first, _im4ms[i].second);
|
||||||
ticketNonceSize = n.second;
|
ticketNonceSize = n.second;
|
||||||
nonce = n.first;
|
nonce = n.first;
|
||||||
|
@ -277,8 +268,6 @@ std::pair<const char *,size_t> futurerestore::nonceMatchesIM4Ms(){
|
||||||
return {NULL,0};
|
return {NULL,0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void futurerestore::waitForNonce(vector<const char *>nonces, size_t nonceSize){
|
void futurerestore::waitForNonce(vector<const char *>nonces, size_t nonceSize){
|
||||||
retassure(_didInit, "did not init\n");
|
retassure(_didInit, "did not init\n");
|
||||||
setAutoboot(false);
|
setAutoboot(false);
|
||||||
|
@ -287,7 +276,7 @@ void futurerestore::waitForNonce(vector<const char *>nonces, size_t nonceSize){
|
||||||
int realNonceSize = 0;
|
int realNonceSize = 0;
|
||||||
|
|
||||||
for (auto nonce : nonces){
|
for (auto nonce : nonces){
|
||||||
info("waiting for nonce: ");
|
info("waiting for ApNonce: ");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (i = 0; i < nonceSize; i++) {
|
for (i = 0; i < nonceSize; i++) {
|
||||||
info("%02x ", ((unsigned char *)nonce)[i]);
|
info("%02x ", ((unsigned char *)nonce)[i]);
|
||||||
|
@ -319,13 +308,14 @@ void futurerestore::waitForNonce(vector<const char *>nonces, size_t nonceSize){
|
||||||
|
|
||||||
setAutoboot(true);
|
setAutoboot(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void futurerestore::waitForNonce(){
|
void futurerestore::waitForNonce(){
|
||||||
retassure(_im4ms.size(), "No IM4M loaded\n");
|
retassure(_im4ms.size(), "No IM4M loaded\n");
|
||||||
|
|
||||||
size_t nonceSize = 0;
|
size_t nonceSize = 0;
|
||||||
vector<const char*>nonces;
|
vector<const char*>nonces;
|
||||||
|
|
||||||
retassure(_client->image4supported, "Error: waitForNonce is not supported on 32bit devices\n");
|
retassure(_client->image4supported, "Error: ApNonce collision function is not supported on 32-bit devices\n");
|
||||||
|
|
||||||
for (auto im4m : _im4ms){
|
for (auto im4m : _im4ms){
|
||||||
auto nonce = img4tool::getValFromIM4M({im4m.first,im4m.second}, 'BNCH');
|
auto nonce = img4tool::getValFromIM4M({im4m.first,im4m.second}, 'BNCH');
|
||||||
|
@ -345,7 +335,7 @@ void futurerestore::loadAPTickets(const vector<const char *> &apticketPaths){
|
||||||
char *im4m = NULL;
|
char *im4m = NULL;
|
||||||
struct stat fst;
|
struct stat fst;
|
||||||
|
|
||||||
retassure(!stat(apticketPath, &fst), "failed to load apticket at %s\n",apticketPath);
|
retassure(!stat(apticketPath, &fst), "failed to load APTicket at %s\n",apticketPath);
|
||||||
|
|
||||||
gzFile zf = gzopen(apticketPath, "rb");
|
gzFile zf = gzopen(apticketPath, "rb");
|
||||||
if (zf) {
|
if (zf) {
|
||||||
|
@ -390,11 +380,11 @@ void futurerestore::loadAPTickets(const vector<const char *> &apticketPaths){
|
||||||
uint64_t im4msize=0;
|
uint64_t im4msize=0;
|
||||||
plist_get_data_val(ticket, &im4m, &im4msize);
|
plist_get_data_val(ticket, &im4m, &im4msize);
|
||||||
|
|
||||||
retassure(im4msize, "Error: failed to load shsh file %s\n",apticketPath);
|
retassure(im4msize, "Error: failed to load signing ticket file %s\n",apticketPath);
|
||||||
|
|
||||||
_im4ms.push_back({im4m,im4msize});
|
_im4ms.push_back({im4m,im4msize});
|
||||||
_aptickets.push_back(apticket);
|
_aptickets.push_back(apticket);
|
||||||
printf("reading ticket %s done\n",apticketPath);
|
printf("reading signing ticket %s is done\n",apticketPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,12 +412,11 @@ char *futurerestore::getiBootBuild(){
|
||||||
retassure(!recovery_client_new(_client), "Error: can't create new recovery client");
|
retassure(!recovery_client_new(_client), "Error: can't create new recovery client");
|
||||||
}
|
}
|
||||||
irecv_getenv(_client->recovery->client, "build-version", &_ibootBuild);
|
irecv_getenv(_client->recovery->client, "build-version", &_ibootBuild);
|
||||||
retassure(_ibootBuild, "Error: can't get build-version");
|
retassure(_ibootBuild, "Error: can't get a build-version");
|
||||||
}
|
}
|
||||||
return _ibootBuild;
|
return _ibootBuild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pair<ptr_smart<char*>, size_t> getIPSWComponent(struct idevicerestore_client_t* client, plist_t build_identity, string component){
|
pair<ptr_smart<char*>, size_t> getIPSWComponent(struct idevicerestore_client_t* client, plist_t build_identity, string component){
|
||||||
ptr_smart<char *> path;
|
ptr_smart<char *> path;
|
||||||
unsigned char* component_data = NULL;
|
unsigned char* component_data = NULL;
|
||||||
|
@ -442,15 +431,14 @@ pair<ptr_smart<char*>, size_t> getIPSWComponent(struct idevicerestore_client_t*
|
||||||
return {(char*)component_data,component_size};
|
return {(char*)component_data,component_size};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
#ifndef HAVE_LIBIPATCHER
|
#ifndef HAVE_LIBIPATCHER
|
||||||
reterror("compiled without libipatcher");
|
reterror("compiled without libipatcher");
|
||||||
#else
|
#else
|
||||||
if (_client->image4supported) {
|
if (_client->image4supported) {
|
||||||
retassure(libipatcher::has64bitSupport(), "libipatcher was compiled without 64bit support");
|
retassure(libipatcher::has64bitSupport(), "libipatcher was compiled without 64-bit support");
|
||||||
std::string generator = getGeneratorFromSHSH2(_client->tss);
|
std::string generator = getGeneratorFromSHSH2(_client->tss);
|
||||||
retassure(img4tool::isGeneratorValidForIM4M({_im4ms[0].first,_im4ms[0].second}, generator), "generator returned from device is not valid from apticket");
|
retassure(img4tool::isGeneratorValidForIM4M({_im4ms[0].first,_im4ms[0].second}, generator), "generator returned from device is not valid from APTicket");
|
||||||
}
|
}
|
||||||
|
|
||||||
int mode = 0;
|
int mode = 0;
|
||||||
|
@ -471,13 +459,12 @@ void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
auto iBSS = getIPSWComponent(_client, build_identity, "iBSS");
|
auto iBSS = getIPSWComponent(_client, build_identity, "iBSS");
|
||||||
iBSS = move(libipatcher::patchiBSS((char*)iBSS.first, iBSS.second, iBSSKeys));
|
iBSS = move(libipatcher::patchiBSS((char*)iBSS.first, iBSS.second, iBSSKeys));
|
||||||
|
|
||||||
|
|
||||||
auto iBEC = getIPSWComponent(_client, build_identity, "iBEC");
|
auto iBEC = getIPSWComponent(_client, build_identity, "iBEC");
|
||||||
iBEC = move(libipatcher::patchiBEC((char*)iBEC.first, iBEC.second, iBECKeys, bootargs));
|
iBEC = move(libipatcher::patchiBEC((char*)iBEC.first, iBEC.second, iBECKeys, bootargs));
|
||||||
|
|
||||||
if (_client->image4supported) {
|
if (_client->image4supported) {
|
||||||
//if this is 64bit, we need to back IM4P to IMG4
|
/* if this is 64-bit, we need to back IM4P to IMG4
|
||||||
//also due to the nature of iBoot64Patchers sigpatches we need to stich a valid signed im4m to it (but nonce is ignored)
|
also due to the nature of iBoot64Patchers sigpatches we need to stich a valid signed im4m to it (but nonce is ignored) */
|
||||||
iBSS = move(libipatcher::packIM4PToIMG4(iBSS.first, iBSS.second, _im4ms[0].first, _im4ms[0].second));
|
iBSS = move(libipatcher::packIM4PToIMG4(iBSS.first, iBSS.second, _im4ms[0].first, _im4ms[0].second));
|
||||||
iBEC = move(libipatcher::packIM4PToIMG4(iBEC.first, iBEC.second, _im4ms[0].first, _im4ms[0].second));
|
iBEC = move(libipatcher::packIM4PToIMG4(iBEC.first, iBEC.second, _im4ms[0].first, _im4ms[0].second));
|
||||||
}
|
}
|
||||||
|
@ -489,7 +476,7 @@ void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
if (mode == i)
|
if (mode == i)
|
||||||
modeIsRecovery = true;
|
modeIsRecovery = true;
|
||||||
}
|
}
|
||||||
retassure(modeIsRecovery, "device not in recovery mode\n");
|
retassure(modeIsRecovery, "device is not in recovery mode\n");
|
||||||
}else{
|
}else{
|
||||||
info("Sending %s (%lu bytes)...\n", "iBSS", iBSS.second);
|
info("Sending %s (%lu bytes)...\n", "iBSS", iBSS.second);
|
||||||
mutex_lock(&_client->device_event_mutex);
|
mutex_lock(&_client->device_event_mutex);
|
||||||
|
@ -554,20 +541,20 @@ void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
cleanup([&]{
|
cleanup([&]{
|
||||||
safeFree(deviceGen);
|
safeFree(deviceGen);
|
||||||
});
|
});
|
||||||
//IMG4 requires to have a generator set for the device to successfully boot after restore.
|
/* IMG4 requires to have a generator set for the device to successfully boot after restore
|
||||||
//set generator now and make sure the nonce is the one we are trying to restore
|
set generator now and make sure the nonce is the one we are trying to restore */
|
||||||
|
|
||||||
assure(!irecv_send_command(_client->recovery->client, "bgcolor 255 0 0"));
|
assure(!irecv_send_command(_client->recovery->client, "bgcolor 255 0 0"));
|
||||||
sleep(2); //yes i like displaying colored screens to the user and making him wait for no reason :P
|
sleep(2); //yes, I like displaying colored screens to the user and making him wait for no reason :P
|
||||||
|
|
||||||
auto nonceelem = img4tool::getValFromIM4M({_im4ms[0].first,_im4ms[0].second}, 'BNCH');
|
auto nonceelem = img4tool::getValFromIM4M({_im4ms[0].first,_im4ms[0].second}, 'BNCH');
|
||||||
|
|
||||||
printf("APNonce pre-hax:\n");
|
printf("ApNonce pre-hax:\n");
|
||||||
get_ap_nonce(_client, &_client->nonce, &_client->nonce_size);
|
get_ap_nonce(_client, &_client->nonce, &_client->nonce_size);
|
||||||
std::string generator = getGeneratorFromSHSH2(_client->tss);
|
std::string generator = getGeneratorFromSHSH2(_client->tss);
|
||||||
|
|
||||||
if (memcmp(_client->nonce, nonceelem.payload(), _client->nonce_size) != 0) {
|
if (memcmp(_client->nonce, nonceelem.payload(), _client->nonce_size) != 0) {
|
||||||
printf("APNonce from device doesn't match IM4M nonce, applying hax...\n");
|
printf("ApNonce from device doesn't match IM4M nonce, applying hax...\n");
|
||||||
|
|
||||||
assure(_client->tss);
|
assure(_client->tss);
|
||||||
printf("Writing generator=%s to nvram!\n",generator.c_str());
|
printf("Writing generator=%s to nvram!\n",generator.c_str());
|
||||||
|
@ -581,7 +568,7 @@ void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
irecv_error_t err = irecv_send_buffer(_client->recovery->client, (unsigned char*)(char*)iBEC.first, (unsigned long)iBEC.second, 1);
|
irecv_error_t err = irecv_send_buffer(_client->recovery->client, (unsigned char*)(char*)iBEC.first, (unsigned long)iBEC.second, 1);
|
||||||
retassure(err == IRECV_E_SUCCESS,"ERROR: Unable to send %s component: %s\n", "iBEC", irecv_strerror(err));
|
retassure(err == IRECV_E_SUCCESS,"ERROR: Unable to send %s component: %s\n", "iBEC", irecv_strerror(err));
|
||||||
printf("waiting for device to reconnect...\n");
|
printf("waiting for device to reconnect...\n");
|
||||||
retassure(!irecv_send_command(_client->recovery->client, "go"),"failed to re-launch iBEC after nonce hax");
|
retassure(!irecv_send_command(_client->recovery->client, "go"),"failed to re-launch iBEC after ApNonce hax");
|
||||||
recovery_client_free(_client);
|
recovery_client_free(_client);
|
||||||
|
|
||||||
debug("Waiting for device to disconnect...\n");
|
debug("Waiting for device to disconnect...\n");
|
||||||
|
@ -595,19 +582,19 @@ void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){
|
||||||
retassure((_client->mode == &idevicerestore_modes[MODE_RECOVERY] || (mutex_unlock(&_client->device_event_mutex),0)), "Device did not reconnect after sending hax-iBEC in pwn-iBEC mode");
|
retassure((_client->mode == &idevicerestore_modes[MODE_RECOVERY] || (mutex_unlock(&_client->device_event_mutex),0)), "Device did not reconnect after sending hax-iBEC in pwn-iBEC mode");
|
||||||
mutex_unlock(&_client->device_event_mutex);
|
mutex_unlock(&_client->device_event_mutex);
|
||||||
|
|
||||||
retassure(!recovery_client_new(_client), "failed to reconnect to recovery after nonce hax");
|
retassure(!recovery_client_new(_client), "failed to reconnect to recovery after ApNonce hax");
|
||||||
|
|
||||||
printf("APnonce post-hax:\n");
|
printf("APnonce post-hax:\n");
|
||||||
get_ap_nonce(_client, &_client->nonce, &_client->nonce_size);
|
get_ap_nonce(_client, &_client->nonce, &_client->nonce_size);
|
||||||
assure(!irecv_send_command(_client->recovery->client, "bgcolor 255 255 0"));
|
assure(!irecv_send_command(_client->recovery->client, "bgcolor 255 255 0"));
|
||||||
retassure(memcmp(_client->nonce, nonceelem.payload(), _client->nonce_size) == 0, "APNonce from device doesn't match IM4M nonce after applying noncehax. Aborting!");
|
retassure(memcmp(_client->nonce, nonceelem.payload(), _client->nonce_size) == 0, "ApNonce from device doesn't match IM4M nonce after applying ApNonce hax. Aborting!");
|
||||||
}else{
|
}else{
|
||||||
printf("APNonce from device already matches IM4M nonce, no need for extra hax...\n");
|
printf("APNonce from device already matches IM4M nonce, no need for extra hax...\n");
|
||||||
}
|
}
|
||||||
retassure(!irecv_setenv(_client->recovery->client, "com.apple.System.boot-nonce", generator.c_str()),"failed to write generator to nvram");
|
retassure(!irecv_setenv(_client->recovery->client, "com.apple.System.boot-nonce", generator.c_str()),"failed to write generator to nvram");
|
||||||
retassure(!irecv_saveenv(_client->recovery->client), "failed to save nvram");
|
retassure(!irecv_saveenv(_client->recovery->client), "failed to save nvram");
|
||||||
|
|
||||||
sleep(2); //yes i like displaying colored screens to the user and making him wait for no reason :P
|
sleep(2); //yes, I like displaying colored screens to the user and making him wait for no reason :P
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //HAVE_LIBIPATCHER
|
#endif //HAVE_LIBIPATCHER
|
||||||
|
@ -630,7 +617,6 @@ void get_custom_component(struct idevicerestore_client_t* client, plist_t build_
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void futurerestore::doRestore(const char *ipsw){
|
void futurerestore::doRestore(const char *ipsw){
|
||||||
plist_t buildmanifest = NULL;
|
plist_t buildmanifest = NULL;
|
||||||
int delete_fs = 0;
|
int delete_fs = 0;
|
||||||
|
@ -656,10 +642,10 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
retassure(client->mode != &idevicerestore_modes[MODE_UNKNOWN], "Unable to discover device mode. Please make sure a device is attached.\n");
|
retassure(client->mode != &idevicerestore_modes[MODE_UNKNOWN], "Unable to discover device mode. Please make sure a device is attached.\n");
|
||||||
if (client->mode != &idevicerestore_modes[MODE_RECOVERY]) {
|
if (client->mode != &idevicerestore_modes[MODE_RECOVERY]) {
|
||||||
retassure(client->mode == &idevicerestore_modes[MODE_DFU], "Device in unexpected mode detected!");
|
retassure(client->mode == &idevicerestore_modes[MODE_DFU], "Device is in unexpected mode detected!");
|
||||||
retassure(_enterPwnRecoveryRequested, "Device in DFU mode detected, but we were expecting recovery mode!");
|
retassure(_enterPwnRecoveryRequested, "Device is in DFU mode detected, but we were expecting recovery mode!");
|
||||||
}else{
|
}else{
|
||||||
retassure(!_enterPwnRecoveryRequested, "--pwn-dfu was specified, but device found in recovery mode!");
|
retassure(!_enterPwnRecoveryRequested, "--use-pwndfu was specified, but device found in recovery mode!");
|
||||||
}
|
}
|
||||||
|
|
||||||
info("Found device in %s mode\n", client->mode->string);
|
info("Found device in %s mode\n", client->mode->string);
|
||||||
|
@ -667,10 +653,9 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
info("Identified device as %s, %s\n", getDeviceBoardNoCopy(), getDeviceModelNoCopy());
|
info("Identified device as %s, %s\n", getDeviceBoardNoCopy(), getDeviceModelNoCopy());
|
||||||
|
|
||||||
// verify if ipsw file exists
|
retassure(!access(client->ipsw, F_OK),"ERROR: Firmware file %s does not exist.\n", client->ipsw); // verify if ipsw file exists
|
||||||
retassure(!access(client->ipsw, F_OK),"ERROR: Firmware file %s does not exist.\n", client->ipsw);
|
|
||||||
|
|
||||||
info("Extracting BuildManifest from IPSW\n");
|
info("Extracting BuildManifest from iPSW\n");
|
||||||
{
|
{
|
||||||
int unused;
|
int unused;
|
||||||
retassure(!ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &unused),"ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw);
|
retassure(!ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &unused),"ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw);
|
||||||
|
@ -678,46 +663,40 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
/* check if device type is supported by the given build manifest */
|
/* check if device type is supported by the given build manifest */
|
||||||
retassure(!build_manifest_check_compatibility(buildmanifest, client->device->product_type),"ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n");
|
retassure(!build_manifest_check_compatibility(buildmanifest, client->device->product_type),"ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n");
|
||||||
|
|
||||||
/* print iOS information from the manifest */
|
/* print iOS information from the manifest */
|
||||||
build_manifest_get_version_information(buildmanifest, client);
|
build_manifest_get_version_information(buildmanifest, client);
|
||||||
|
info("Product version: %s\n", client->version);
|
||||||
info("Product Version: %s\n", client->version);
|
info("Product build: %s Major: %d\n", client->build, client->build_major);
|
||||||
info("Product Build: %s Major: %d\n", client->build, client->build_major);
|
|
||||||
|
|
||||||
client->image4supported = is_image4_supported(client);
|
client->image4supported = is_image4_supported(client);
|
||||||
info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false");
|
info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false");
|
||||||
|
|
||||||
|
|
||||||
if (_enterPwnRecoveryRequested) //we are in pwnDFU, so we don't need to check nonces
|
if (_enterPwnRecoveryRequested) //we are in pwnDFU, so we don't need to check nonces
|
||||||
client->tss = _aptickets.at(0);
|
client->tss = _aptickets.at(0);
|
||||||
else if (!(client->tss = nonceMatchesApTickets()))
|
else if (!(client->tss = nonceMatchesApTickets()))
|
||||||
reterror("Devicenonce does not match APTicket nonce\n");
|
reterror("Device ApNonce does not match APTicket nonce\n");
|
||||||
|
|
||||||
plist_dict_remove_item(client->tss, "BBTicket");
|
plist_dict_remove_item(client->tss, "BBTicket");
|
||||||
plist_dict_remove_item(client->tss, "BasebandFirmware");
|
plist_dict_remove_item(client->tss, "BasebandFirmware");
|
||||||
|
|
||||||
if (_enterPwnRecoveryRequested && _client->image4supported) {
|
if (_enterPwnRecoveryRequested && _client->image4supported) {
|
||||||
retassure(plist_dict_get_item(_client->tss, "generator"), "shsh file does not contain generator. But a generator is required for 64bit pwndfu restore");
|
retassure(plist_dict_get_item(_client->tss, "generator"), "signing ticket file does not contain generator. But a generator is required for 64-bit pwnDFU restore");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retassure(build_identity = getBuildidentityWithBoardconfig(buildmanifest, client->device->hardware_model, _isUpdateInstall),"ERROR: Unable to find any build identities for iPSW\n");
|
||||||
retassure(build_identity = getBuildidentityWithBoardconfig(buildmanifest, client->device->hardware_model, _isUpdateInstall),"ERROR: Unable to find any build identities for IPSW\n");
|
|
||||||
|
|
||||||
if (_client->image4supported) {
|
if (_client->image4supported) {
|
||||||
if (!(sep_build_identity = getBuildidentityWithBoardconfig(_sepbuildmanifest, client->device->hardware_model, _isUpdateInstall))){
|
if (!(sep_build_identity = getBuildidentityWithBoardconfig(_sepbuildmanifest, client->device->hardware_model, _isUpdateInstall))){
|
||||||
retassure(_isPwnDfu, "ERROR: Unable to find any build identities for SEP\n");
|
retassure(_isPwnDfu, "ERROR: Unable to find any build identities for SEP\n");
|
||||||
warning("can't find buildidentity for SEP with InstallType=%s. However pwnDfu was requested, so trying fallback to %s",(_isUpdateInstall ? "UPDATE" : "ERASE"),(!_isUpdateInstall ? "UPDATE" : "ERASE"));
|
warning("can't find buildidentity for SEP with InstallType=%s. However pwnDFU was requested, so trying fallback to %s",(_isUpdateInstall ? "UPDATE" : "ERASE"),(!_isUpdateInstall ? "UPDATE" : "ERASE"));
|
||||||
retassure((sep_build_identity = getBuildidentityWithBoardconfig(_sepbuildmanifest, client->device->hardware_model, !_isUpdateInstall)),
|
retassure((sep_build_identity = getBuildidentityWithBoardconfig(_sepbuildmanifest, client->device->hardware_model, !_isUpdateInstall)),
|
||||||
"ERROR: Unable to find any build identities for SEP\n");
|
"ERROR: Unable to find any build identities for SEP\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plist_t manifest = plist_dict_get_item(build_identity, "Manifest"); //this is the buildidentity used for restore
|
||||||
|
|
||||||
//this is the buildidentity used for restore
|
printf("checking APTicket to be valid for this restore...\n"); //if we are in pwnDFU, just use first APTicket. We don't need to check nonces.
|
||||||
plist_t manifest = plist_dict_get_item(build_identity, "Manifest");
|
|
||||||
|
|
||||||
printf("checking APTicket to be valid for this restore...\n");
|
|
||||||
//if we are in pwnDFU, just use first apticket. no need to check nonces
|
|
||||||
auto im4m = (_enterPwnRecoveryRequested || _rerestoreiOS9) ? _im4ms.at(0) : nonceMatchesIM4Ms();
|
auto im4m = (_enterPwnRecoveryRequested || _rerestoreiOS9) ? _im4ms.at(0) : nonceMatchesIM4Ms();
|
||||||
|
|
||||||
uint64_t deviceEcid = getDeviceEcid();
|
uint64_t deviceEcid = getDeviceEcid();
|
||||||
|
@ -762,14 +741,13 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
ticketIdentity = img4tool::getBuildIdentityForIm4m({im4m.first,im4m.second}, buildmanifest, {"RestoreRamDisk","RestoreTrustCache"});
|
ticketIdentity = img4tool::getBuildIdentityForIm4m({im4m.first,im4m.second}, buildmanifest, {"RestoreRamDisk","RestoreTrustCache"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: make this nicer!
|
||||||
//TODO: make this nicer!
|
for now a simple pointercompare should be fine, because both plist_t should point into the same buildidentity inside the buildmanifest */
|
||||||
//for now a simple pointercompare should be fine, because both plist_t should point into the same buildidentity inside the buildmanifest
|
|
||||||
if (ticketIdentity != build_identity ){
|
if (ticketIdentity != build_identity ){
|
||||||
error("BuildIdentity selected for restore does not match APTicket\n\n");
|
error("BuildIdentity selected for restore does not match APTicket\n\n");
|
||||||
printf("BuildIdentity selected for restore:\n");
|
printf("BuildIdentity selected for restore:\n");
|
||||||
img4tool::printGeneralBuildIdentityInformation(build_identity);
|
img4tool::printGeneralBuildIdentityInformation(build_identity);
|
||||||
printf("\nBuildIdentiy valid for the APTicket:\n");
|
printf("\nBuildIdentity is valid for the APTicket:\n");
|
||||||
|
|
||||||
if (ticketIdentity) img4tool::printGeneralBuildIdentityInformation(ticketIdentity),putchar('\n');
|
if (ticketIdentity) img4tool::printGeneralBuildIdentityInformation(ticketIdentity),putchar('\n');
|
||||||
else{
|
else{
|
||||||
|
@ -794,7 +772,6 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
const char *tickethash = ticket.first;
|
const char *tickethash = ticket.first;
|
||||||
size_t tickethashSize = ticket.second;
|
size_t tickethashSize = ticket.second;
|
||||||
|
|
||||||
|
|
||||||
uint64_t manifestDigestSize = 0;
|
uint64_t manifestDigestSize = 0;
|
||||||
char *manifestDigest = NULL;
|
char *manifestDigest = NULL;
|
||||||
|
|
||||||
|
@ -803,7 +780,6 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
plist_get_data_val(digest, &manifestDigest, &manifestDigestSize);
|
plist_get_data_val(digest, &manifestDigest, &manifestDigestSize);
|
||||||
|
|
||||||
|
|
||||||
if (tickethashSize == manifestDigestSize && memcmp(tickethash, manifestDigest, tickethashSize) == 0){
|
if (tickethashSize == manifestDigestSize && memcmp(tickethash, manifestDigest, tickethashSize) == 0){
|
||||||
printf("Verified APTicket to be valid for this restore\n");
|
printf("Verified APTicket to be valid for this restore\n");
|
||||||
free(manifestDigest);
|
free(manifestDigest);
|
||||||
|
@ -814,7 +790,6 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_basebandbuildmanifest){
|
if (_basebandbuildmanifest){
|
||||||
if (!(client->basebandBuildIdentity = getBuildidentityWithBoardconfig(_basebandbuildmanifest, client->device->hardware_model, _isUpdateInstall))){
|
if (!(client->basebandBuildIdentity = getBuildidentityWithBoardconfig(_basebandbuildmanifest, client->device->hardware_model, _isUpdateInstall))){
|
||||||
retassure(client->basebandBuildIdentity = getBuildidentityWithBoardconfig(_basebandbuildmanifest, client->device->hardware_model, !_isUpdateInstall), "ERROR: Unable to find any build identities for Baseband\n");
|
retassure(client->basebandBuildIdentity = getBuildidentityWithBoardconfig(_basebandbuildmanifest, client->device->hardware_model, !_isUpdateInstall), "ERROR: Unable to find any build identities for Baseband\n");
|
||||||
|
@ -829,20 +804,20 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
retassure(_client->basebandBuildIdentity, "BasebandBuildIdentity not loaded, refusing to continue");
|
retassure(_client->basebandBuildIdentity, "BasebandBuildIdentity not loaded, refusing to continue");
|
||||||
}else{
|
}else{
|
||||||
warning("WARNING: we don't have a basebandbuildmanifest, not flashing baseband!\n");
|
warning("WARNING: we don't have a basebandbuildmanifest, does not flashing baseband!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_client->image4supported) {
|
if (_client->image4supported) {
|
||||||
|
//check SEP
|
||||||
plist_t sep_manifest = plist_dict_get_item(sep_build_identity, "Manifest");
|
plist_t sep_manifest = plist_dict_get_item(sep_build_identity, "Manifest");
|
||||||
plist_t sep_sep = plist_copy(plist_dict_get_item(sep_manifest, "SEP"));
|
plist_t sep_sep = plist_copy(plist_dict_get_item(sep_manifest, "SEP"));
|
||||||
plist_dict_set_item(manifest, "SEP", sep_sep);
|
plist_dict_set_item(manifest, "SEP", sep_sep);
|
||||||
//check SEP
|
|
||||||
unsigned char genHash[48]; //SHA384 digest length
|
unsigned char genHash[48]; //SHA384 digest length
|
||||||
ptr_smart<unsigned char *>sephash = NULL;
|
ptr_smart<unsigned char *>sephash = NULL;
|
||||||
uint64_t sephashlen = 0;
|
uint64_t sephashlen = 0;
|
||||||
plist_t digest = plist_dict_get_item(sep_sep, "Digest");
|
plist_t digest = plist_dict_get_item(sep_sep, "Digest");
|
||||||
|
|
||||||
retassure(digest && plist_get_node_type(digest) == PLIST_DATA, "ERROR: can't find sep digest\n");
|
retassure(digest && plist_get_node_type(digest) == PLIST_DATA, "ERROR: can't find SEP digest\n");
|
||||||
|
|
||||||
plist_get_data_val(digest, reinterpret_cast<char **>(&sephash), &sephashlen);
|
plist_get_data_val(digest, reinterpret_cast<char **>(&sephash), &sephashlen);
|
||||||
|
|
||||||
|
@ -853,8 +828,7 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
retassure(!memcmp(genHash, sephash, sephashlen), "ERROR: SEP does not match sepmanifest\n");
|
retassure(!memcmp(genHash, sephash, sephashlen), "ERROR: SEP does not match sepmanifest\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print information about current build identity */
|
build_identity_print_information(build_identity); // print information about current build identity
|
||||||
build_identity_print_information(build_identity);
|
|
||||||
|
|
||||||
//check for enterpwnrecovery, because we could be in DFU mode
|
//check for enterpwnrecovery, because we could be in DFU mode
|
||||||
if (_enterPwnRecoveryRequested){
|
if (_enterPwnRecoveryRequested){
|
||||||
|
@ -864,7 +838,7 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
|
|
||||||
// Get filesystem name from build identity
|
// Get filesystem name from build identity
|
||||||
char* fsname = NULL;
|
char* fsname = NULL;
|
||||||
retassure(!build_identity_get_component_path(build_identity, "OS", &fsname), "ERROR: Unable get path for filesystem component\n");
|
retassure(!build_identity_get_component_path(build_identity, "OS", &fsname), "ERROR: Unable to get path for filesystem component\n");
|
||||||
|
|
||||||
// check if we already have an extracted filesystem
|
// check if we already have an extracted filesystem
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -933,12 +907,11 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
}
|
}
|
||||||
remove(lockfn);
|
remove(lockfn);
|
||||||
|
|
||||||
// Extract filesystem from IPSW
|
info("Extracting filesystem from iPSW\n");
|
||||||
info("Extracting filesystem from IPSW\n");
|
retassure(!ipsw_extract_to_file_with_progress(client->ipsw, fsname, filesystem, 1),"ERROR: Unable to extract filesystem from iPSW\n");
|
||||||
retassure(!ipsw_extract_to_file_with_progress(client->ipsw, fsname, filesystem, 1),"ERROR: Unable to extract filesystem from IPSW\n");
|
|
||||||
|
|
||||||
|
// rename <fsname>.extract to <fsname>
|
||||||
if (strstr(filesystem, ".extract")) {
|
if (strstr(filesystem, ".extract")) {
|
||||||
// rename <fsname>.extract to <fsname>
|
|
||||||
remove(tmpf);
|
remove(tmpf);
|
||||||
rename(filesystem, tmpf);
|
rename(filesystem, tmpf);
|
||||||
free(filesystem);
|
free(filesystem);
|
||||||
|
@ -991,11 +964,10 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
retassure((client->mode == &idevicerestore_modes[MODE_RECOVERY] || (mutex_unlock(&client->device_event_mutex),0)), "Device did not reconnect. Possibly invalid iBEC. Reset device and try again");
|
retassure((client->mode == &idevicerestore_modes[MODE_RECOVERY] || (mutex_unlock(&client->device_event_mutex),0)), "Device did not reconnect. Possibly invalid iBEC. Reset device and try again");
|
||||||
mutex_unlock(&client->device_event_mutex);
|
mutex_unlock(&client->device_event_mutex);
|
||||||
|
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
if ((client->build_major > 8)) {
|
if ((client->build_major > 8)) {
|
||||||
if (!client->image4supported) {
|
if (!client->image4supported) {
|
||||||
/* send ApTicket */
|
/* send APTicket */
|
||||||
if (recovery_send_ticket(client) < 0) {
|
if (recovery_send_ticket(client) < 0) {
|
||||||
error("WARNING: Unable to send APTicket\n");
|
error("WARNING: Unable to send APTicket\n");
|
||||||
}
|
}
|
||||||
|
@ -1003,8 +975,6 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (_enterPwnRecoveryRequested){
|
if (_enterPwnRecoveryRequested){
|
||||||
if (!_client->image4supported) {
|
if (!_client->image4supported) {
|
||||||
if (strncmp(client->version, "10.", 3))//if pwnrecovery send all components decrypted, unless we're dealing with iOS 10
|
if (strncmp(client->version, "10.", 3))//if pwnrecovery send all components decrypted, unless we're dealing with iOS 10
|
||||||
|
@ -1035,14 +1005,12 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
//do magic
|
//do magic
|
||||||
if (_client->image4supported) get_sep_nonce(client, &client->sepnonce, &client->sepnonce_size);
|
if (_client->image4supported) get_sep_nonce(client, &client->sepnonce, &client->sepnonce_size);
|
||||||
get_ap_nonce(client, &client->nonce, &client->nonce_size);
|
get_ap_nonce(client, &client->nonce, &client->nonce_size);
|
||||||
|
|
||||||
get_ecid(client, &client->ecid);
|
get_ecid(client, &client->ecid);
|
||||||
|
|
||||||
if (client->mode->index == MODE_RECOVERY) {
|
if (client->mode->index == MODE_RECOVERY) {
|
||||||
retassure(client->srnm,"ERROR: could not retrieve device serial number. Can't continue.\n");
|
retassure(client->srnm,"ERROR: could not retrieve device serial number. Can't continue.\n");
|
||||||
|
|
||||||
retassure(!irecv_send_command(client->recovery->client, "bgcolor 0 255 0"), "ERROR: Unable to set bgcolor\n");
|
retassure(!irecv_send_command(client->recovery->client, "bgcolor 0 255 0"), "ERROR: Unable to set bgcolor\n");
|
||||||
|
|
||||||
info("[WARNING] Setting bgcolor to green! If you don't see a green screen, then your device didn't boot iBEC correctly\n");
|
info("[WARNING] Setting bgcolor to green! If you don't see a green screen, then your device didn't boot iBEC correctly\n");
|
||||||
sleep(2); //show the user a green screen!
|
sleep(2); //show the user a green screen!
|
||||||
|
|
||||||
|
@ -1052,16 +1020,15 @@ void futurerestore::doRestore(const char *ipsw){
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_client->image4supported) {
|
if (_client->image4supported) {
|
||||||
info("getting sep ticket\n");
|
info("getting SEP ticket\n");
|
||||||
retassure(!get_tss_response(client, sep_build_identity, &client->septss), "ERROR: Unable to get SHSH blobs for SEP\n");
|
retassure(!get_tss_response(client, sep_build_identity, &client->septss), "ERROR: Unable to get signing tickets for SEP\n");
|
||||||
retassure(_client->sepfwdatasize && _client->sepfwdata, "SEP not loaded, refusing to continue");
|
retassure(_client->sepfwdatasize && _client->sepfwdata, "SEP is not loaded, refusing to continue");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mutex_lock(&client->device_event_mutex);
|
mutex_lock(&client->device_event_mutex);
|
||||||
debug("Waiting for device to enter restore mode...\n");
|
debug("Waiting for device to enter restore mode...\n");
|
||||||
cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 180000);
|
cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 180000);
|
||||||
retassure((client->mode == &idevicerestore_modes[MODE_RESTORE] || (mutex_unlock(&client->device_event_mutex),0)), "Device failed to enter restore mode");
|
retassure((client->mode == &idevicerestore_modes[MODE_RESTORE] || (mutex_unlock(&client->device_event_mutex),0)), "Device can't enter to restore mode");
|
||||||
mutex_unlock(&client->device_event_mutex);
|
mutex_unlock(&client->device_event_mutex);
|
||||||
|
|
||||||
info("About to restore device... \n");
|
info("About to restore device... \n");
|
||||||
|
@ -1234,7 +1201,6 @@ const char *futurerestore::getDeviceBoardNoCopy(){
|
||||||
return _client->device->hardware_model;
|
return _client->device->hardware_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char *futurerestore::getLatestManifest(){
|
char *futurerestore::getLatestManifest(){
|
||||||
if (!__latestManifest){
|
if (!__latestManifest){
|
||||||
loadFirmwareTokens();
|
loadFirmwareTokens();
|
||||||
|
@ -1246,24 +1212,23 @@ char *futurerestore::getLatestManifest(){
|
||||||
int versionCnt = 0;
|
int versionCnt = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
char **versions = getListOfiOSForDevice(_firmwareTokens, device, 0, &versionCnt);
|
char **versions = getListOfiOSForDevice(_firmwareTokens, device, 0, &versionCnt);
|
||||||
retassure(versionCnt, "[TSSC] failed finding latest iOS\n");
|
retassure(versionCnt, "[TSSC] failed finding latest firmware version\n");
|
||||||
char *bpos = NULL;
|
char *bpos = NULL;
|
||||||
while((bpos = strstr((char*)(versVals.version = strdup(versions[i++])),"[B]")) != 0){
|
while((bpos = strstr((char*)(versVals.version = strdup(versions[i++])),"[B]")) != 0){
|
||||||
free((char*)versVals.version);
|
free((char*)versVals.version);
|
||||||
if (--versionCnt == 0) reterror("[TSSC] automatic iOS selection couldn't find non-beta iOS\n");
|
if (--versionCnt == 0) reterror("[TSSC] automatic selection of firmware couldn't find for non-beta versions\n");
|
||||||
}
|
}
|
||||||
info("[TSSC] selecting latest iOS: %s\n",versVals.version);
|
info("[TSSC] selecting latest firmware version: %s\n",versVals.version);
|
||||||
if (bpos) *bpos= '\0';
|
if (bpos) *bpos= '\0';
|
||||||
if (versions) free(versions[versionCnt-1]),free(versions);
|
if (versions) free(versions[versionCnt-1]),free(versions);
|
||||||
|
|
||||||
//make sure it get's freed after function finishes execution by either reaching end or throwing exception
|
ptr_smart<const char*>autofree(versVals.version); //make sure it get's freed after function finishes execution by either reaching end or throwing exception
|
||||||
ptr_smart<const char*>autofree(versVals.version);
|
|
||||||
|
|
||||||
__latestFirmwareUrl = getFirmwareUrl(device, &versVals, _firmwareTokens);
|
__latestFirmwareUrl = getFirmwareUrl(device, &versVals, _firmwareTokens);
|
||||||
retassure(__latestFirmwareUrl, "could not find url of latest firmware\n");
|
retassure(__latestFirmwareUrl, "could not find url of latest firmware version\n");
|
||||||
|
|
||||||
__latestManifest = getBuildManifest(__latestFirmwareUrl, device, versVals.version, versVals.buildID, 0);
|
__latestManifest = getBuildManifest(__latestFirmwareUrl, device, versVals.version, versVals.buildID, 0);
|
||||||
retassure(__latestManifest, "could not get buildmanifest of latest firmware\n");
|
retassure(__latestManifest, "could not get buildmanifest of latest firmware version\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return __latestManifest;
|
return __latestManifest;
|
||||||
|
@ -1273,7 +1238,6 @@ char *futurerestore::getLatestFirmwareUrl(){
|
||||||
return getLatestManifest(),__latestFirmwareUrl;
|
return getLatestManifest(),__latestFirmwareUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void futurerestore::loadLatestBaseband(){
|
void futurerestore::loadLatestBaseband(){
|
||||||
char * manifeststr = getLatestManifest();
|
char * manifeststr = getLatestManifest();
|
||||||
char *pathStr = getPathOfElementInManifest("BasebandFirmware", manifeststr, getDeviceModelNoCopy(), 0);
|
char *pathStr = getPathOfElementInManifest("BasebandFirmware", manifeststr, getDeviceModelNoCopy(), 0);
|
||||||
|
@ -1319,7 +1283,6 @@ void futurerestore::loadSep(const char *sepPath){
|
||||||
fclose(fsep);
|
fclose(fsep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void futurerestore::setBasebandPath(const char *basebandPath){
|
void futurerestore::setBasebandPath(const char *basebandPath){
|
||||||
FILE *fbb = NULL;
|
FILE *fbb = NULL;
|
||||||
|
|
||||||
|
@ -1328,9 +1291,7 @@ void futurerestore::setBasebandPath(const char *basebandPath){
|
||||||
fclose(fbb);
|
fclose(fbb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark static methods
|
#pragma mark static methods
|
||||||
|
|
||||||
inline void futurerestore::saveStringToFile(const char *str, const char *path){
|
inline void futurerestore::saveStringToFile(const char *str, const char *path){
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
retassure(f = fopen(path, "w"), "can't save file at %s\n",path);
|
retassure(f = fopen(path, "w"), "can't save file at %s\n",path);
|
||||||
|
@ -1472,7 +1433,7 @@ std::string futurerestore::getGeneratorFromSHSH2(const plist_t shsh2){
|
||||||
cleanup([&]{
|
cleanup([&]{
|
||||||
safeFree(genstr);
|
safeFree(genstr);
|
||||||
});
|
});
|
||||||
retassure(pGenerator = plist_dict_get_item(shsh2, "generator"), "shsh file does not contain generator");
|
retassure(pGenerator = plist_dict_get_item(shsh2, "generator"), "signing ticket file does not contain generator");
|
||||||
|
|
||||||
retassure(plist_get_node_type(pGenerator) == PLIST_STRING, "generator has unexpected type! We expect string of the format 0x%16llx");
|
retassure(plist_get_node_type(pGenerator) == PLIST_STRING, "generator has unexpected type! We expect string of the format 0x%16llx");
|
||||||
|
|
||||||
|
|
|
@ -59,27 +59,34 @@ static struct option longopts[] = {
|
||||||
#define FLAG_IS_PWN_DFU 1 << 5
|
#define FLAG_IS_PWN_DFU 1 << 5
|
||||||
|
|
||||||
void cmd_help(){
|
void cmd_help(){
|
||||||
printf("Usage: futurerestore [OPTIONS] IPSW\n");
|
printf("Usage: futurerestore [OPTIONS] iPSW\n");
|
||||||
printf("Allows restoring nonmatching iOS/Sep/Baseband\n\n");
|
printf("Allows restoring to non-matching firmware with custom SEP+baseband\n");
|
||||||
|
printf("\nGeneral options:\n");
|
||||||
|
printf(" -t, --apticket PATH\t\tSigning tickets used for restoring\n");
|
||||||
printf(" -t, --apticket PATH\t\tApticket used for restoring\n");
|
printf(" -u, --update\t\t\tUpdate instead of erase install (requires appropriate APTicket)\n");
|
||||||
printf(" -b, --baseband PATH\t\tBaseband to be flashed\n");
|
printf(" \t\t\tDO NOT use this parameter, if you update from jailbroken firmware!\n");
|
||||||
printf(" -p, --baseband-manifest PATH\tBuildmanifest for requesting baseband ticket\n");
|
printf(" -w, --wait\t\t\tKeep rebooting until ApNonce matches APTicket (ApNonce collision, unreliable)\n");
|
||||||
printf(" -s, --sep PATH\t\tSep to be flashed\n");
|
printf(" -d, --debug\t\t\tShow all code, use to save a log for debug testing\n");
|
||||||
printf(" -m, --sep-manifest PATH\tBuildmanifest for requesting sep ticket\n");
|
|
||||||
printf(" -w, --wait\t\t\tkeep rebooting until nonce matches APTicket\n");
|
|
||||||
printf(" -u, --update\t\t\tupdate instead of erase install\n");
|
|
||||||
printf(" --latest-sep\t\tuse latest signed sep instead of manually specifying one(may cause bad restore)\n");
|
|
||||||
printf(" --latest-baseband\t\tuse latest signed baseband instead of manually specifying one(may cause bad restore)\n");
|
|
||||||
#ifdef HAVE_LIBIPATCHER
|
|
||||||
printf(" --use-pwndfu\t\tuse this for restoring devices with odysseus method. Device needs to be in kDFU mode already\n");
|
|
||||||
printf(" --just-boot=\"-v\"\t\tuse this to tethered boot the device from kDFU mode. You can optionally set bootargs\n");
|
|
||||||
#endif
|
|
||||||
printf(" --exit-recovery\t\tExit recovery mode and quit\n");
|
printf(" --exit-recovery\t\tExit recovery mode and quit\n");
|
||||||
printf(" --no-baseband\t\tskip checks and don't flash baseband.\n");
|
|
||||||
printf(" \t\tWARNING: only use this for device without baseband (eg iPod or some wifi only iPads)\n\n");
|
#ifdef HAVE_LIBIPATCHER
|
||||||
}
|
printf("\nOptions for downgrading with Odysseus (32-bit/64-bit):\n");
|
||||||
|
printf(" --use-pwndfu\t\tRestoring devices with Odysseus method. Device needs to be in pwned DFU mode already\n");
|
||||||
|
printf(" --just-boot=\"-v\"\t\tTethered booting the device from pwned DFU mode. You can optionally set boot-args\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("\nOptions for SEP:\n");
|
||||||
|
printf(" --latest-sep\t\tUse latest signed SEP instead of manually specifying one (may cause bad restore)\n");
|
||||||
|
printf(" -s, --sep PATH\t\tSEP to be flashed\n");
|
||||||
|
printf(" -m, --sep-manifest PATH\tBuildManifest for requesting SEP ticket\n");
|
||||||
|
|
||||||
|
printf("\nOptions for baseband:\n");
|
||||||
|
printf(" --latest-baseband\t\tUse latest signed baseband instead of manually specifying one (may cause bad restore)\n");
|
||||||
|
printf(" -b, --baseband PATH\t\tBaseband to be flashed\n");
|
||||||
|
printf(" -p, --baseband-manifest PATH\tBuildManifest for requesting baseband ticket\n");
|
||||||
|
printf(" --no-baseband\t\tSkip checks and don't flash baseband\n");
|
||||||
|
printf(" \t\tOnly use this for device without a baseband (eg. iPod touch or some Wi-Fi only iPads)\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
DWORD termFlags;
|
DWORD termFlags;
|
||||||
|
@ -95,10 +102,10 @@ int main_r(int argc, const char * argv[]) {
|
||||||
printf("Version: " VERSION_COMMIT_SHA " - " VERSION_COMMIT_COUNT "\n");
|
printf("Version: " VERSION_COMMIT_SHA " - " VERSION_COMMIT_COUNT "\n");
|
||||||
#ifdef HAVE_LIBIPATCHER
|
#ifdef HAVE_LIBIPATCHER
|
||||||
printf("%s\n",libipatcher::version().c_str());
|
printf("%s\n",libipatcher::version().c_str());
|
||||||
printf("Odysseus Support: yes\n");
|
printf("Odysseus for 32-bit support: yes\n");
|
||||||
printf("Odysseus 64bit support: %s\n",(libipatcher::has64bitSupport() ? "yes" : "no"));
|
printf("Odysseus for 64-bit support: %s\n",(libipatcher::has64bitSupport() ? "yes" : "no"));
|
||||||
#else
|
#else
|
||||||
printf("Odysseus Support: no\n");
|
printf("Odysseus support: no\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int optindex = 0;
|
int optindex = 0;
|
||||||
|
@ -184,9 +191,9 @@ int main_r(int argc, const char * argv[]) {
|
||||||
|
|
||||||
ipsw = argv[0];
|
ipsw = argv[0];
|
||||||
}else if (argc == optind && flags & FLAG_WAIT) {
|
}else if (argc == optind && flags & FLAG_WAIT) {
|
||||||
info("User requested to only wait for APNonce to match, but not actually restoring\n");
|
info("User requested to only wait for ApNonce to match, but not for actually restoring\n");
|
||||||
}else if (exitRecovery){
|
}else if (exitRecovery){
|
||||||
info("Exiting recovery mode\n");
|
info("Exiting to recovery mode\n");
|
||||||
}else{
|
}else{
|
||||||
error("argument parsing failed! agrc=%d optind=%d\n",argc,optind);
|
error("argument parsing failed! agrc=%d optind=%d\n",argc,optind);
|
||||||
if (idevicerestore_debug){
|
if (idevicerestore_debug){
|
||||||
|
@ -246,12 +253,12 @@ int main_r(int argc, const char * argv[]) {
|
||||||
|
|
||||||
versVals.basebandMode = kBasebandModeWithoutBaseband;
|
versVals.basebandMode = kBasebandModeWithoutBaseband;
|
||||||
if (!client.is32bit() && !(isSepManifestSigned = isManifestSignedForDevice(client.sepManifestPath(), &devVals, &versVals))){
|
if (!client.is32bit() && !(isSepManifestSigned = isManifestSignedForDevice(client.sepManifestPath(), &devVals, &versVals))){
|
||||||
reterror("sep firmware isn't signed\n");
|
reterror("SEP firmware doesn't signed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAG_NO_BASEBAND){
|
if (flags & FLAG_NO_BASEBAND){
|
||||||
printf("\nWARNING: user specified not to flash a baseband. This can make the restore fail if the device needs a baseband!\n");
|
printf("\nWARNING: user specified is not to flash a baseband. This can make the restore fail if the device needs a baseband!\n");
|
||||||
printf("if you added this flag by mistake you can press CTRL-C now to cancel\n");
|
printf("if you added this flag by mistake, you can press CTRL-C now to cancel\n");
|
||||||
int c = 10;
|
int c = 10;
|
||||||
printf("continuing restore in ");
|
printf("continuing restore in ");
|
||||||
while (c) {
|
while (c) {
|
||||||
|
@ -267,7 +274,7 @@ int main_r(int argc, const char * argv[]) {
|
||||||
}else{
|
}else{
|
||||||
client.setBasebandPath(basebandPath);
|
client.setBasebandPath(basebandPath);
|
||||||
client.setBasebandManifestPath(basebandManifestPath);
|
client.setBasebandManifestPath(basebandManifestPath);
|
||||||
printf("Did set sep+baseband path and firmware\n");
|
printf("Did set SEP+baseband path and firmware\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
versVals.basebandMode = kBasebandModeOnlyBaseband;
|
versVals.basebandMode = kBasebandModeOnlyBaseband;
|
||||||
|
@ -275,7 +282,7 @@ int main_r(int argc, const char * argv[]) {
|
||||||
printf("[WARNING] using tsschecker's fallback to get BasebandGoldCertID. This might result in invalid baseband signing status information\n");
|
printf("[WARNING] using tsschecker's fallback to get BasebandGoldCertID. This might result in invalid baseband signing status information\n");
|
||||||
}
|
}
|
||||||
if (!(isBasebandSigned = isManifestSignedForDevice(client.basebandManifestPath(), &devVals, &versVals))) {
|
if (!(isBasebandSigned = isManifestSignedForDevice(client.basebandManifestPath(), &devVals, &versVals))) {
|
||||||
reterror("baseband firmware isn't signed\n");
|
reterror("baseband firmware doesn't signed\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,7 +309,7 @@ int main_r(int argc, const char * argv[]) {
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (err){
|
if (err){
|
||||||
printf("Failed with errorcode=%d\n",err);
|
printf("Failed with error code=%d\n",err);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
#undef reterror
|
#undef reterror
|
||||||
|
|
Loading…
Reference in a new issue