diff --git a/CMakeLists.txt b/CMakeLists.txt index 3706a55f..0948b08b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,12 @@ execute_process(COMMAND git submodule update --init --recursive WORKING_DIRECTOR set(ignoreMe "${NO_PKGCFG} ${ASAN} ${NO_XCODE} ${ARCH}") set(SUBPROJECT_BUILD 1) set(ASAN_FLAG "") +set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG") +set(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) if(DEFINED ASAN OR "$ENV{ASAN}" MATCHES "1") set(ASAN 1) set(ASAN_FLAG "-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer") @@ -40,12 +46,6 @@ elseif("${CMAKE_HOST_SYSTEM_NAME}" MATCHES "Linux") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--allow-multiple-definition -pthread -mrelax-all -std=gnu++20 ${ASAN_FLAG}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,--allow-multiple-definition -pthread -mrelax-all -std=gnu17 ${ASAN_FLAG}") endif() -set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG") -set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") -set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG") -set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG") -set(CMAKE_C_STANDARD 17) -set(CMAKE_CXX_STANDARD 20) project(futurerestore) add_subdirectory(external/tsschecker) add_subdirectory(external/idevicerestore) diff --git a/README.md b/README.md index c32e2cd6..9f930fe8 100644 --- a/README.md +++ b/README.md @@ -33,35 +33,36 @@ See [COMPILING](COMPILING.md) # Futurerestore Usage Usage: `futurerestore [OPTIONS] iPSW` -| option (short) | option (long) | description | -|----------------|------------------------------------------|-----------------------------------------------------------------------------------| -| ` -t ` | ` --apticket PATH ` | Signing tickets used for restoring, commonly known as blobs | -| ` -u ` | ` --update ` | Update instead of erase install (requires appropriate APTicket) | -| | | This parameter is recommended to not be used for downgrading. If you are jailbroken, make sure to have your orig-fs snapshot restored (Restore RootFS). | -| ` -w ` | ` --wait ` | Keep rebooting until ApNonce matches APTicket (ApNonce collision, unreliable) | -| ` -d ` | ` --debug ` | Show all code, use to save a log for debug testing | -| ` -e ` | ` --exit-recovery ` | Exit recovery mode and quit | -| ` -c ` | ` --custom-latest VERSION ` | Specify custom latest version to use for SEP, Baseband and other FirmwareUpdater components | -| ` -g ` | ` --custom-latest-buildid BUILDID ` | Specify custom latest buildid to use for SEP, Baseband and other FirmwareUpdater components | -| ` -i ` | ` --custom-latest-beta ` | Get custom url from list of beta firmwares | -| | ` --use-pwndfu ` | Restoring devices with Odysseus method. Device needs to be in pwned DFU mode already | -| | ` --no-ibss ` | Restoring devices with Odysseus method. For checkm8/iPwnder32 specifically, bootrom needs to be patched already with unless iPwnder. | -| | ` --rdsk PATH ` | Set custom restore ramdisk for entering restoremode(requires use-pwndfu) | -| | ` --rkrn PATH ` | Set custom restore kernelcache for entering restoremode(requires use-pwndfu) | -| | ` --set-nonce ` | Set custom nonce from your blob then exit recovery(requires use-pwndfu) | -| | ` --set-nonce=0xNONCE ` | Set custom nonce then exit recovery(requires use-pwndfu) | -| | ` --serial ` | Enable serial during boot(requires serial cable and use-pwndfu) | -| | ` --boot-args "BOOTARGS" ` | Set custom restore boot-args(PROCEED WITH CAUTION)(requires use-pwndfu) | -| | ` --no-cache ` | Disable cached patched iBSS/iBEC(requires use-pwndfu) | -| | ` --skip-blob ` | Skip SHSH blob validation(PROCEED WITH CAUTION)(requires use-pwndfu) | -| | ` --latest-sep ` | Use latest signed SEP instead of manually specifying one | -| ` -s ` | ` --sep PATH ` | Manually specify SEP to be flashed | -| ` -m ` | ` --sep-manifest PATH ` | BuildManifest for requesting SEP ticket | -| | ` --latest-baseband ` | Use latest signed baseband instead of manually specifying one | -| ` -b ` | ` --baseband PATH ` | Manually specify 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 Wi-Fi only iPads) | +| option (short) | option (long) | description | +|----------------|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| ` -t ` | ` --apticket PATH ` | Signing tickets used for restoring, commonly known as blobs | +| ` -u ` | ` --update ` | Update instead of erase install (requires appropriate APTicket) | +| | | This parameter is recommended to not be used for downgrading. If you are jailbroken, make sure to have your orig-fs snapshot restored (Restore RootFS). | +| ` -w ` | ` --wait ` | Keep rebooting until ApNonce matches APTicket (ApNonce collision, unreliable) | +| ` -d ` | ` --debug ` | Show all code, use to save a log for debug testing | +| ` -e ` | ` --exit-recovery ` | Exit recovery mode and quit | +| ` -c ` | ` --custom-latest VERSION ` | Specify custom latest version to use for SEP, Baseband and other FirmwareUpdater components | +| ` -g ` | ` --custom-latest-buildid BUILDID ` | Specify custom latest buildid to use for SEP, Baseband and other FirmwareUpdater components | +| ` -i ` | ` --custom-latest-beta ` | Get custom url from list of beta firmwares | +| | ` --use-pwndfu ` | Restoring devices with Odysseus method. Device needs to be in pwned DFU mode already | +| | ` --no-ibss ` | Restoring devices with Odysseus method. For checkm8/iPwnder32 specifically, bootrom needs to be patched already with unless iPwnder. | +| | ` --rdsk PATH ` | Set custom restore ramdisk for entering restoremode(requires use-pwndfu) | +| | ` --rkrn PATH ` | Set custom restore kernelcache for entering restoremode(requires use-pwndfu) | +| | ` --set-nonce ` | Set custom nonce from your blob then exit recovery(requires use-pwndfu) | +| | ` --set-nonce=0xNONCE ` | Set custom nonce then exit recovery(requires use-pwndfu) | +| | ` --serial ` | Enable serial during boot(requires serial cable and use-pwndfu) | +| | ` --boot-args "BOOTARGS" ` | Set custom restore boot-args(PROCEED WITH CAUTION)(requires use-pwndfu) | +| | ` --no-cache ` | Disable cached patched iBSS/iBEC(requires use-pwndfu) | +| | ` --skip-blob ` | Skip SHSH blob validation(PROCEED WITH CAUTION)(requires use-pwndfu) | +| | ` --latest-sep ` | Use latest signed SEP instead of manually specifying one | +| ` -s ` | ` --sep PATH ` | Manually specify SEP to be flashed | +| ` -m ` | ` --sep-manifest PATH ` | BuildManifest for requesting SEP ticket | +| ` -j ` | ` --no-rsep ` | Choose not to send Restore Mode SEP | +| | ` --latest-baseband ` | Use latest signed baseband instead of manually specifying one | +| ` -b ` | ` --baseband PATH ` | Manually specify 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 Wi-Fi only iPads) | --- diff --git a/external/idevicerestore b/external/idevicerestore index b4663705..97ab482c 160000 --- a/external/idevicerestore +++ b/external/idevicerestore @@ -1 +1 @@ -Subproject commit b46637056fce7cb771f53916b1a8c527d256c5f2 +Subproject commit 97ab482cf251b83e71887f9cd7a8f1d9e066e671 diff --git a/external/tsschecker b/external/tsschecker index 9a046508..6e55c67e 160000 --- a/external/tsschecker +++ b/external/tsschecker @@ -1 +1 @@ -Subproject commit 9a046508ffb7cbd62d3ff50042944237389c51b4 +Subproject commit 6e55c67ec78df55b8185c17ea2f79f838d0c034b diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7689ed21..18e363b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,7 @@ endif() execute_process(COMMAND cat version.txt WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE VERSION_RELEASE ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) add_definitions( -DHAVE_LIBIPATCHER=1 + -DCUSTOM_LOGGING= -DVERSION_COMMIT_COUNT="${VERSION_COMMIT_COUNT}" -DVERSION_COMMIT_SHA="${VERSION_COMMIT_SHA}" -DVERSION_RELEASE="${VERSION_RELEASE}" diff --git a/src/futurerestore.cpp b/src/futurerestore.cpp index 5b6d8ebe..edf4b1eb 100644 --- a/src/futurerestore.cpp +++ b/src/futurerestore.cpp @@ -108,9 +108,15 @@ std::string sepManifestTempPath = futurerestoreTempPath + "/sepManifest.plist"; # include # define SHA1(d, n, md) CC_SHA1(d, n, md) +# define SHA256(d, n, md) CC_SHA256(d, n, md) # define SHA384(d, n, md) CC_SHA384(d, n, md) +# define SHA512(d, n, md) CC_SHA512(d, n, md) #else # include +extern "C" { + unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); + unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); +} #endif // __APPLE__ #ifndef HAVE_LIBIPATCHER @@ -128,10 +134,10 @@ void idevice_event_cb(const idevice_event_t *event, void *userdata); #pragma mark futurerestore futurerestore::futurerestore(bool isUpdateInstall, bool isPwnDfu, bool noIBSS, bool setNonce, bool serial, - bool noRestore) : _isUpdateInstall(isUpdateInstall), _isPwnDfu(isPwnDfu), _noIBSS(noIBSS), - _setNonce(setNonce), _serial(serial), _noRestore(noRestore) { + bool noRestore, bool noRSEP) : _isUpdateInstall(isUpdateInstall), _isPwnDfu(isPwnDfu), _noIBSS(noIBSS), + _setNonce(setNonce), _serial(serial), _noRestore(noRestore), _noRSEP(noRSEP) { _client = idevicerestore_client_new(); - retassure(_client != nullptr, "could not create idevicerestore client\n"); + retassure(_client != nullptr, "Could not create idevicerestore client\n"); struct stat st{0}; if (stat(futurerestoreTempPath.c_str(), &st) == -1) safe_mkdir(futurerestoreTempPath.c_str(), 0755); @@ -468,7 +474,7 @@ uint64_t futurerestore::getBasebandGoldCertIDFromDevice() { plist_t node; node = plist_dict_get_item(_client->preflight_info, "CertID"); if (!node || plist_get_node_type(node) != PLIST_UINT) { - error("Unable to find required BbGoldCertId in parameters\n"); + debug("Unable to find required BbGoldCertId in parameters\n"); return 0; } uint64_t val = 0; @@ -893,6 +899,7 @@ void futurerestore::doRestore(const char *ipsw) { client->ipsw = strdup(ipsw); if (_noRestore) client->flags |= FLAG_NO_RESTORE; + if (_noRSEP) client->flags |= FLAG_NO_RSEP; if (!_isUpdateInstall) client->flags |= FLAG_ERASE; irecv_device_event_subscribe(&client->irecv_e_ctx, irecv_event_cb, client); @@ -1119,13 +1126,13 @@ void futurerestore::doRestore(const char *ipsw) { if (sephashlen == 20) SHA1((unsigned char *) _client->sepfwdata, (unsigned int) _client->sepfwdatasize, genHash); else - SHA384((unsigned char *) _client->sepfwdata, (unsigned int) _client->sepfwdatasize, genHash); + SHA384((const unsigned char *) _client->sepfwdata, (unsigned long) _client->sepfwdatasize, genHash); retassure(!memcmp(genHash, sephash, sephashlen), "ERROR: SEP does not match sepmanifest\n"); } build_identity_print_information(build_identity); // print information about current build identity - //check for enterpwnrecovery, because we could be in DFU mode + //check for enterpwnrecovery, because we Could be in DFU mode if (_enterPwnRecoveryRequested) { retassure((getDeviceMode(true) == _MODE_DFU) || (getDeviceMode(false) == _MODE_RECOVERY && _noIBSS), "unexpected device mode\n"); @@ -1352,7 +1359,7 @@ void futurerestore::doRestore(const char *ipsw) { get_ap_nonce(client, &client->nonce, &client->nonce_size); if (client->mode == 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"); if (client->device->chip_id < 0x8015) { retassure(!irecv_send_command(client->recovery->client, "bgcolor 0 255 0"), @@ -1401,17 +1408,17 @@ int futurerestore::findProc(const char *procName, bool load) { do { ctlRet = sysctl(mib, mibLen, nullptr, &size, nullptr, 0); if (ctlRet < 0) { - info("daemonManager: findProc: failed sysctl(KERN_PROC)!\n"); + debug("daemonManager: findProc: failed sysctl(KERN_PROC)!\n"); return -1; } if (!size) { - info("daemonManager: findProc: failed sysctl(KERN_PROC) size!\n"); + debug("daemonManager: findProc: failed sysctl(KERN_PROC) size!\n"); return -1; } size += size / 10; procs2 = static_cast(realloc(procs, size)); if (!procs2) { - info("daemonManager: findProc: realloc failed!\n"); + debug("daemonManager: findProc: realloc failed!\n"); safeFree(procs); safeFree(procs2); return -1; @@ -1478,7 +1485,7 @@ int futurerestore::findProc(const char *procName, bool load) { } if (strcmp(cmd, procName) == 0) { if(!load) { - info("daemonManager: findProc: found %s!\n", procName); + debug("daemonManager: findProc: found %s!\n", procName); } return pid; } @@ -1488,7 +1495,7 @@ int futurerestore::findProc(const char *procName, bool load) { void futurerestore::daemonManager(bool load) { if(!load) { - info("daemonManager: suspending invasive macOS daemons...\n"); + debug("daemonManager: suspending invasive macOS daemons...\n"); } int pid = 0; const char *procList[] = { "MobileDeviceUpdater", "AMPDevicesAgent", "AMPDeviceDiscoveryAgent", 0}; @@ -1498,14 +1505,14 @@ void futurerestore::daemonManager(bool load) { if (load) { int ret = kill(pid, SIGCONT); } else { - info("daemonManager: killing %s.\n", procList[i]); + debug("daemonManager: killing %s.\n", procList[i]); int ret = kill(pid, SIGSTOP); } } } if(!load) { - info("daemonManager: done!\n"); + debug("daemonManager: done!\n"); } } #endif @@ -1536,19 +1543,22 @@ futurerestore::~futurerestore() { void futurerestore::loadFirmwareTokens() { if (!_firmwareTokens) { if (!_firmwareJson) _firmwareJson = getFirmwareJson(); - retassure(_firmwareJson, "[TSSC] could not get firmware.json\n"); + retassure(_firmwareJson, "[TSSC] Could not get firmware.json\n"); long cnt = parseTokens(_firmwareJson, &_firmwareTokens); retassure(cnt > 0, "[TSSC] parsing %s.json failed\n", (0) ? "ota" : "firmware"); } if(!_betaFirmwareTokens) { if (!_betaFirmwareJson) _betaFirmwareJson = getBetaFirmwareJson(getDeviceModelNoCopy()); - retassure(_betaFirmwareJson, "[TSSC] could not get betas json\n"); + retassure(_betaFirmwareJson, "[TSSC] Could not get betas json\n"); long cnt = parseTokens(_betaFirmwareJson, &_betaFirmwareTokens); retassure(cnt > 0, "[TSSC] parsing %s.json failed\n", (0) ? "beta ota" : "beta firmware"); } } const char *futurerestore::getDeviceModelNoCopy() { + if(_model) { + return _model; + } if (!_client->device || !_client->device->product_type) { int mode = getDeviceMode(true); @@ -1571,10 +1581,13 @@ const char *futurerestore::getDeviceModelNoCopy() { } } - return _client->device->product_type; + return _model = _client->device->product_type; } const char *futurerestore::getDeviceBoardNoCopy() { + if(_board) { + return _board; + } if (!_client->device || !_client->device->product_type) { int mode = getDeviceMode(true); @@ -1596,7 +1609,7 @@ const char *futurerestore::getDeviceBoardNoCopy() { break; } } - return _client->device->hardware_model; + return _board = _client->device->hardware_model; } char *futurerestore::getLatestManifest() { @@ -1620,7 +1633,7 @@ char *futurerestore::getLatestManifest() { while ((bpos = strstr((char *) (versVals.version = strdup(versions[i++])), "[B]")) != nullptr) { free((char *) versVals.version); if (--versionCnt == 0) - reterror("[TSSC] automatic selection of firmware couldn't find for non-beta versions\n"); + reterror("[TSSC] automatic selection of firmware Couldn't find for non-beta versions\n"); } if(_useCustomLatest) { i = 0; @@ -1655,9 +1668,9 @@ char *futurerestore::getLatestManifest() { } if(!_useCustomLatestBeta) { if(_useCustomLatestBuildID) { - info("[TSSC] selecting latest firmware version: %s\n", versVals.buildID); + debug("[TSSC] selecting latest firmware version: %s\n", versVals.buildID); } else { - info("[TSSC] selecting latest firmware version: %s\n", versVals.version); + debug("[TSSC] selecting latest firmware version: %s\n", versVals.version); } } if (bpos) *bpos = '\0'; @@ -1669,7 +1682,7 @@ char *futurerestore::getLatestManifest() { versVals.buildID); //make sure it gets freed after function finishes execution by either reaching end or throwing exception if(_useCustomLatestBeta) { - info("[TSSC] selecting latest firmware version: %s\n", _customLatestBuildID.c_str()); + debug("[TSSC] selecting latest firmware version: %s\n", _customLatestBuildID.c_str()); _latestFirmwareUrl = getBetaURLForDevice(_betaFirmwareTokens, _customLatestBuildID.c_str()); _latestManifest = getBuildManifest(_latestFirmwareUrl, device, nullptr, _customLatestBuildID.c_str(), 0); } else { @@ -1680,8 +1693,8 @@ char *futurerestore::getLatestManifest() { _latestManifest = getBuildManifest(_latestFirmwareUrl, device, versVals.version, versVals.buildID, 0); } } - retassure(_latestFirmwareUrl, "could not find url of latest firmware version\n"); - retassure(_latestManifest, "could not get buildmanifest of latest firmware version\n"); + retassure(_latestFirmwareUrl, "Could not find url of latest firmware version\n"); + retassure(_latestManifest, "Could not get buildmanifest of latest firmware version\n"); } return _latestManifest; @@ -1696,9 +1709,20 @@ void futurerestore::downloadLatestRose() { char *roseStr = (elemExists("Rap,RTKitOS", manifeststr, getDeviceBoardNoCopy(), 0) ? getPathOfElementInManifest( "Rap,RTKitOS", manifeststr, getDeviceBoardNoCopy(), 0) : nullptr); if (roseStr) { - info("downloading Rose firmware\n\n"); - retassure(!downloadPartialzip(getLatestFirmwareUrl(), roseStr, roseTempPath.c_str()), - "could not download Rose\n"); + auto *digestString = getDigestOfElementInManifest("Rap,RTKitOS", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(roseTempPath); + if(hash && digestString) { + if(!memcmp(digestString, hash, 48)) { + info("Using cached Rose\n"); + safeFree(digestString); + safeFree(hash); + return; + } + } else { + info("Downloading Rose firmware\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), roseStr, roseTempPath.c_str()), + "Could not download Rose\n"); + } loadRose(roseTempPath); } } @@ -1708,8 +1732,10 @@ void futurerestore::downloadLatestSE() { char *seStr = (elemExists("SE,UpdatePayload", manifeststr, getDeviceBoardNoCopy(), 0) ? getPathOfElementInManifest( "SE,UpdatePayload", manifeststr, getDeviceBoardNoCopy(), 0) : nullptr); if (seStr) { - info("downloading SE firmware\n\n"); - retassure(!downloadPartialzip(getLatestFirmwareUrl(), seStr, seTempPath.c_str()), "could not download SE\n"); + // TODO: SE caching how does ProductionUpdatePayloadHash work? + info("Downloading SE firmware\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), seStr, seTempPath.c_str()), + "Could not download SE\n"); loadSE(seTempPath); } } @@ -1736,41 +1762,102 @@ void futurerestore::downloadLatestSavage() { : nullptr); std::array savagePaths{}; + if (savageB0ProdStr) { - info("downloading Savage,B0-Prod-Patch\n\n"); savagePaths[0] = futurerestoreTempPath + "/savageB0PP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB0ProdStr, savagePaths[0].c_str()), - "could not download Savage,B0-Prod-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,B0-Prod-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[0], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,B0-Prod-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,B0-Prod-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB0ProdStr, savagePaths[0].c_str()), + "Could not download Savage,B0-Prod-Patch\n"); + } } if (savageB0DevStr) { - info("downloading Savage,B0-Dev-Patch\n\n"); savagePaths[1] = futurerestoreTempPath + "//savageB0DP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB0DevStr, savagePaths[1].c_str()), - "could not download Savage,B0-Dev-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,B0-Dev-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[1], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,B0-Dev-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,B0-Dev-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB0DevStr, savagePaths[1].c_str()), + "Could not download Savage,B0-Dev-Patch\n"); + } } if (savageB2ProdStr) { - info("downloading Savage,B2-Prod-Patch\n\n"); savagePaths[2] = futurerestoreTempPath + "//savageB2PP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB2ProdStr, savagePaths[2].c_str()), - "could not download Savage,B2-Prod-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,B2-Prod-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[2], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,B2-Prod-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,B2-Prod-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB2ProdStr, savagePaths[2].c_str()), + "Could not download Savage,B2-Prod-Patch\n"); + } } if (savageB2DevStr) { - info("downloading Savage,B2-Dev-Patch\n\n"); savagePaths[3] = futurerestoreTempPath + "//savageB2DP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB2DevStr, savagePaths[3].c_str()), - "could not download Savage,B2-Dev-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,B2-Dev-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[3], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,B2-Dev-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,B2-Dev-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageB2DevStr, savagePaths[3].c_str()), + "Could not download Savage,B2-Dev-Patch\n"); + } } if (savageBAProdStr) { - info("downloading Savage,BA-Prod-Patch\n\n"); savagePaths[4] = futurerestoreTempPath + "//savageBAPP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageBAProdStr, savagePaths[4].c_str()), - "could not download Savage,BA-Prod-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,BA-Prod-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[4], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,BA-Prod-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,BA-Prod-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageBAProdStr, savagePaths[4].c_str()), + "Could not download Savage,BA-Prod-Patch\n"); + } } if (savageBADevStr) { - info("downloading Savage,BA-Dev-Patch\n\n"); savagePaths[5] = futurerestoreTempPath + "//savageBADP.fw"; - retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageBADevStr, savagePaths[5].c_str()), - "could not download Savage,BA-Dev-Patch\n"); + auto *digestString = getDigestOfElementInManifest("Savage,BA-Dev-Patch", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(savagePaths[5], 1); + if(hash && digestString) { + if(!memcmp(digestString, hash, 32)) { + info("Using cached Savage,BA-Dev-Patch.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Savage,BA-Dev-Patch\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), savageBADevStr, savagePaths[5].c_str()), + "Could not download Savage,BA-Dev-Patch\n"); + } } if (savageB0ProdStr && savageB0DevStr && @@ -1790,15 +1877,36 @@ void futurerestore::downloadLatestVeridian() { char *veridianFWMStr = (elemExists("BMU,FirmwareMap", manifeststr, getDeviceBoardNoCopy(), 0) ? getPathOfElementInManifest("BMU,FirmwareMap", manifeststr, getDeviceBoardNoCopy(), 0) : nullptr); + if (veridianDGMStr) { - info("downloading Veridian DigestMap\n\n"); - retassure(!downloadPartialzip(getLatestFirmwareUrl(), veridianDGMStr, veridianDGMTempPath.c_str()), - "could not download Veridian DigestMap\n"); + auto *digestString = getDigestOfElementInManifest("BMU,DigestMap", manifeststr, getDeviceBoardNoCopy(), 0); + unsigned char *hash = getSHA(veridianDGMTempPath); + if(hash && digestString) { + if(!memcmp(digestString, hash, 48)) { + info("Using cached BMU,DigestMap(Veridian).\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Veridian DigestMap\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), veridianDGMStr, veridianDGMTempPath.c_str()), + "Could not download Veridian DigestMap\n"); + } } if (veridianFWMStr) { - info("downloading Veridian FirmwareMap\n\n"); - retassure(!downloadPartialzip(getLatestFirmwareUrl(), veridianFWMStr, veridianFWMTempPath.c_str()), - "could not download Veridian FirmwareMap\n"); + auto digestString = getDigestOfElementInManifest("BMU,FirmwareMap", manifeststr, getDeviceBoardNoCopy(), 0); + auto hash = getSHA(veridianFWMTempPath); + if(hash && digestString) { + if(!memcmp(digestString, hash, 48)) { + info("Using cached BMU,FirmwareMap(Veridian).\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading Veridian FirmwareMap\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), veridianFWMStr, veridianFWMTempPath.c_str()), + "Could not download Veridian FirmwareMap\n"); + } } if (veridianDGMStr && veridianFWMStr) loadVeridian(veridianDGMTempPath, veridianFWMTempPath); @@ -1826,11 +1934,12 @@ void futurerestore::downloadLatestFirmwareComponents() { } void futurerestore::downloadLatestBaseband() { - char *manifeststr = getLatestManifest(); - char *pathStr = getPathOfElementInManifest("BasebandFirmware", manifeststr, getDeviceBoardNoCopy(), 0); - info("downloading Baseband\n\n"); + auto manifeststr = getLatestManifest(); + auto pathStr = getPathOfElementInManifest("BasebandFirmware", manifeststr, getDeviceBoardNoCopy(), 0); + // TODO: Baseband caching. How on earth does basebandfirmware digest work? + info("Downloading Baseband\n\n"); retassure(!downloadPartialzip(getLatestFirmwareUrl(), pathStr, basebandTempPath.c_str()), - "could not download baseband\n"); + "Could not download baseband\n"); saveStringToFile(manifeststr, basebandManifestTempPath); setBasebandPath(basebandTempPath); setBasebandManifestPath(basebandManifestTempPath); @@ -1839,10 +1948,20 @@ void futurerestore::downloadLatestBaseband() { } void futurerestore::downloadLatestSep() { - std::string manifestString = getLatestManifest(); - std::string pathString = getPathOfElementInManifest("SEP", manifestString.c_str(), getDeviceBoardNoCopy(), 0); - info("downloading SEP\n\n"); - retassure(!downloadPartialzip(getLatestFirmwareUrl(), pathString.c_str(), sepTempPath.c_str()), "could not download SEP\n"); + auto manifestString = getLatestManifest(); + auto pathString = getPathOfElementInManifest("SEP", manifestString, getDeviceBoardNoCopy(), 0); + auto *digestString = getDigestOfElementInManifest("SEP",manifestString,getDeviceBoardNoCopy(),0); + auto *hash = getSHA(sepTempPath); + if(hash && digestString) { + if(!memcmp(digestString, hash, 48)) { + info("Using cached SEP.\n"); + safeFree(digestString); + safeFree(hash); + } + } else { + info("Downloading SEP\n\n"); + retassure(!downloadPartialzip(getLatestFirmwareUrl(), pathString, sepTempPath.c_str()), "Could not download SEP\n"); + } saveStringToFile(manifestString, sepManifestTempPath); setSepPath(sepTempPath); setSepManifestPath(sepManifestTempPath); @@ -1853,13 +1972,13 @@ void futurerestore::downloadLatestSep() { void futurerestore::loadSepManifest(std::string sepManifestPath) { this->_sepManifestPath = sepManifestPath; retassure(_sepbuildmanifest = loadPlistFromFile(sepManifestPath.c_str()), - "failed to load SEP Manifest"); + "Failed to load SEP Manifest"); } void futurerestore::loadBasebandManifest(std::string basebandManifestPath) { this->_basebandManifestPath = basebandManifestPath; retassure(_basebandbuildmanifest = loadPlistFromFile(basebandManifestPath.c_str()), - "failed to load Baseband Manifest"); + "Failed to load Baseband Manifest"); }; void futurerestore::loadRose(std::string rosePath) { @@ -2004,6 +2123,54 @@ void futurerestore::loadBaseband(std::string basebandPath) { __func__, basebandPath.c_str()); } +unsigned char *futurerestore::getSHA(const std::string& filePath, int type) { + std::ifstream fileStream(filePath); + if(!fileStream.good()) { + info("Cached %s not found, downloading a new one.\n", filePath.c_str()); + return nullptr; + } + fileStream.seekg(0, std::ios_base::end); + size_t dataSize = fileStream.tellg(); + fileStream.seekg(0, std::ios_base::beg); + std::allocator alloc; + char *data = nullptr; + if(!(data = (char *)alloc.allocate(dataSize))) { + error("%s: failed to allocate memory for %s\n", __func__, filePath.c_str()); + return nullptr; + } + fileStream.read((char *) data, + (std::streamsize) dataSize); + if(*(uint64_t *)(data) == 0) { + error("%s: failed to load File for %s with the size %zu!\n", + __func__, filePath.c_str(), dataSize); + return nullptr; + } + auto *fileHash = (unsigned char *)nullptr; + switch(type) { + case 0: + fileHash = (unsigned char *) alloc.allocate(48); + SHA384((const unsigned char*)data, (unsigned int)dataSize, fileHash); + break; + case 1: + fileHash = (unsigned char *) alloc.allocate(32); + SHA256((const unsigned char*)data, (unsigned int)dataSize, fileHash); + break; + case 2: + fileHash = (unsigned char *) alloc.allocate(64); + SHA512((const unsigned char*)data, (unsigned int)dataSize, fileHash); + break; + case 3: + fileHash = (unsigned char *) alloc.allocate(20); + SHA1((const unsigned char*)data, (unsigned int)dataSize, fileHash); + break; + default: + fileHash = (unsigned char *) alloc.allocate(48); + SHA384((const unsigned char*)data, (unsigned int)dataSize, fileHash); + break; + } + return fileHash; +} + #pragma mark static methods inline void futurerestore::saveStringToFile(std::string str, std::string path) { @@ -2094,7 +2261,7 @@ plist_t futurerestore::loadPlistFromFile(const char *path) { FILE *f = fopen(path, "rb"); if (!f) { - error("could not open file %s\n", path); + error("Could not open file %s\n", path); return nullptr; } fseek(f, 0, SEEK_END); @@ -2138,11 +2305,31 @@ char *futurerestore::getPathOfElementInManifest(const char *element, const char if (plist_get_string_val(path, &pathStr), pathStr) goto noerror; - reterror("could not get %s path\n", element); + reterror("Could not get %s path\n", element); noerror: return pathStr; } +unsigned char *futurerestore::getDigestOfElementInManifest(const char *element, const char *manifeststr, const char *boardConfig, + int isUpdateInstall) { + char *digestStr = nullptr; + ptr_smart buildmanifest(NULL, plist_free); + uint64_t size; + + plist_from_xml(manifeststr, (uint32_t) strlen(manifeststr), &buildmanifest); + + if (plist_t identity = getBuildidentityWithBoardconfig(buildmanifest._p, boardConfig, isUpdateInstall)) + if (plist_t manifest = plist_dict_get_item(identity, "Manifest")) + if (plist_t elem = plist_dict_get_item(manifest, element)) + if (plist_t path = plist_dict_get_item(elem, "Digest")) + if (plist_get_data_val(path, &digestStr, &size), digestStr) + goto noerror; + + return nullptr; + noerror: + return (unsigned char *)digestStr; +} + bool futurerestore::elemExists(const char *element, const char *manifeststr, const char *boardConfig, int isUpdateInstall) { char *pathStr = nullptr; diff --git a/src/futurerestore.hpp b/src/futurerestore.hpp index ebcfd235..39d2c16c 100644 --- a/src/futurerestore.hpp +++ b/src/futurerestore.hpp @@ -63,7 +63,8 @@ class futurerestore { bool _setNonce = false; bool _serial = false; bool _noRestore = false; - + bool _noRSEP = false; + char *_firmwareJson = nullptr; char *_betaFirmwareJson = nullptr; jssytok_t *_firmwareTokens = nullptr;; @@ -75,9 +76,12 @@ class futurerestore { bool _useCustomLatestBeta = false; std::string _customLatest; std::string _customLatestBuildID; + const char *_model = nullptr; + const char *_board = nullptr; plist_t _sepbuildmanifest = nullptr; plist_t _basebandbuildmanifest = nullptr; + plist_t _buildidentity = nullptr; std::string _ramdiskPath; std::string _kernelPath; @@ -98,7 +102,7 @@ class futurerestore { void enterPwnRecovery(plist_t build_identity, std::string bootargs); public: - futurerestore(bool isUpdateInstall = false, bool isPwnDfu = false, bool noIBSS = false, bool setNonce = false, bool serial = false, bool noRestore = false); + futurerestore(bool isUpdateInstall = false, bool isPwnDfu = false, bool noIBSS = false, bool setNonce = false, bool serial = false, bool noRestore = false, bool noRSEP = false); bool init(); int getDeviceMode(bool reRequest); uint64_t getDeviceEcid(); @@ -138,6 +142,7 @@ public: void loadKernel(std::string kernelPath); void loadSep(std::string sepPath); void loadBaseband(std::string basebandPath); + unsigned char *getSHA(const std::string& filePath, int type = 0); void setCustomLatest(std::string version){_customLatest = version; _useCustomLatest = true;} void setCustomLatestBuildID(std::string version, bool beta){_customLatestBuildID = version; _useCustomLatest = false; _useCustomLatestBuildID = true; _useCustomLatestBeta = beta;} @@ -171,8 +176,10 @@ public: static plist_t loadPlistFromFile(const char *path); static void saveStringToFile(std::string str, std::string path); static char *getPathOfElementInManifest(const char *element, const char *manifeststr, const char *boardConfig, int isUpdateInstall); + static unsigned char *getDigestOfElementInManifest(const char *element, const char *manifeststr, const char *boardConfig, int isUpdateInstall); static bool elemExists(const char *element, const char *manifeststr, const char *boardConfig, int isUpdateInstall); static std::string getGeneratorFromSHSH2(plist_t shsh2); + }; #endif /* futurerestore_hpp */ diff --git a/src/main.cpp b/src/main.cpp index 880e2aeb..09881090 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,7 @@ static struct option longopts[] = { { "no-restore", no_argument, nullptr, 'z' }, { "latest-baseband", no_argument, nullptr, '1' }, { "no-baseband", no_argument, nullptr, '2' }, + { "no-rsep", no_argument, nullptr, 'j' }, #ifdef HAVE_LIBIPATCHER { "use-pwndfu", no_argument, nullptr, '3' }, { "no-ibss", no_argument, nullptr, '4' }, @@ -76,6 +77,7 @@ static struct option longopts[] = { #define FLAG_CUSTOM_LATEST 1 << 15 #define FLAG_CUSTOM_LATEST_BUILDID 1 << 16 #define FLAG_CUSTOM_LATEST_BETA 1 << 17 +#define FLAG_NO_RSEP_FR 1 << 18 void cmd_help(){ printf("Usage: futurerestore [OPTIONS] iPSW\n"); @@ -111,6 +113,7 @@ void cmd_help(){ printf(" --latest-sep\t\t\tUse latest signed SEP instead of manually specifying one\n"); printf(" -s, --sep PATH\t\t\tSEP to be flashed\n"); printf(" -m, --sep-manifest PATH\t\tBuildManifest for requesting SEP ticket\n"); + printf(" -j, --no-rsep\t\tChoose not to send Restore Mode SEP\n"); printf("\nOptions for baseband:\n"); printf(" --latest-baseband\t\t\tUse latest signed baseband instead of manually specifying one\n"); @@ -167,7 +170,7 @@ int main_r(int argc, const char * argv[]) { return -1; } - while ((opt = getopt_long(argc, (char* const *)argv, "ht:b:p:s:m:c:g:hiwude0z123456789af", longopts, &optindex)) > 0) { + while ((opt = getopt_long(argc, (char* const *)argv, "ht:b:p:s:m:c:g:hiwude0z123456789afj", longopts, &optindex)) > 0) { switch (opt) { case 'h': // long option: "help"; can be called as short option cmd_help(); @@ -214,6 +217,9 @@ int main_r(int argc, const char * argv[]) { case '2': // long option: "no-baseband"; flags |= FLAG_NO_BASEBAND; break; + case 'j': // long option: "no-rsep"; + flags |= FLAG_NO_RSEP_FR; + break; #ifdef HAVE_LIBIPATCHER case '3': // long option: "use-pwndfu"; flags |= FLAG_IS_PWN_DFU; @@ -286,7 +292,7 @@ int main_r(int argc, const char * argv[]) { return -5; } - futurerestore client(flags & FLAG_UPDATE, flags & FLAG_IS_PWN_DFU, flags & FLAG_NO_IBSS, flags & FLAG_SET_NONCE, flags & FLAG_SERIAL, flags & FLAG_NO_RESTORE_FR); + futurerestore client(flags & FLAG_UPDATE, flags & FLAG_IS_PWN_DFU, flags & FLAG_NO_IBSS, flags & FLAG_SET_NONCE, flags & FLAG_SERIAL, flags & FLAG_NO_RESTORE_FR, flags & FLAG_NO_RSEP_FR); retassure(client.init(),"can't init, no device found\n"); printf("futurerestore init done\n"); @@ -384,7 +390,7 @@ int main_r(int argc, const char * argv[]) { } if (flags & FLAG_LATEST_SEP){ - info("user specified to use latest signed SEP\n"); + info("User specified to use latest signed SEP\n"); client.downloadLatestSep(); }else if (!client.is32bit()){ client.setSepPath(sepPath); @@ -394,38 +400,44 @@ int main_r(int argc, const char * argv[]) { } versVals.basebandMode = kBasebandModeWithoutBaseband; - if (!client.is32bit() && !(isManifestSignedForDevice(client.getSepManifestPath().c_str(), &devVals, &versVals, nullptr))){ + info("Checking if SEP is being signed...\n"); + if (!client.is32bit() && !(isManifestSignedForDevice(client.getSepManifestPath().c_str(), &devVals, &versVals, nullptr))) { reterror("SEP firmware is NOT being signed!\n"); + } else { + info("SEP is being signed!\n"); } if (flags & FLAG_NO_BASEBAND){ - 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"); + info("\nWARNING: user specified is not to flash a baseband. This can make the restore fail if the device needs a baseband!\n"); + info("\nIf you added this flag by mistake, you can press CTRL-C now to cancel\n"); int c = 10; - printf("continuing restore in "); + info("Continuing restore in "); while (c) { - printf("%d ",c--); + info("%d ",c--); fflush(stdout); sleep(1); } - printf("\n"); + info(""); }else{ if (flags & FLAG_LATEST_BASEBAND){ - info("user specified to use latest signed baseband\n"); + info("User specified to use latest signed baseband\n"); client.downloadLatestBaseband(); }else{ client.setBasebandPath(basebandPath); client.setBasebandManifestPath(basebandManifestPath); client.loadBaseband(basebandPath); client.loadBasebandManifest(basebandManifestPath); - printf("Did set SEP+baseband path and firmware\n"); + info("Did set SEP and baseband path and firmware\n"); } versVals.basebandMode = kBasebandModeOnlyBaseband; if (!(devVals.bbgcid = client.getBasebandGoldCertIDFromDevice())){ - printf("[WARNING] using tsschecker's fallback to get BasebandGoldCertID. This might result in invalid baseband signing status information\n"); + debug("[WARNING] using tsschecker's fallback to get BasebandGoldCertID. This might result in invalid baseband signing status information\n"); } + info("Checking if Baseband is being signed...\n"); if (!(isManifestSignedForDevice(client.getBasebandManifestPath().c_str(), &devVals, &versVals, nullptr))) { - reterror("baseband firmware is NOT being signed!\n"); + reterror("Baseband firmware is NOT being signed!\n"); + } else { + info("Baseband is being signed!\n"); } }