From c90da707b1ba5f38528708d1b211e5ee0b854c1c Mon Sep 17 00:00:00 2001 From: tihmstar Date: Thu, 27 Jul 2017 14:47:23 +0200 Subject: [PATCH] added support, for booting with custom bootargs from pwndfu --- configure.ac | 2 +- futurerestore/futurerestore.cpp | 147 ++++++++++++++++++++++++++++---- futurerestore/futurerestore.hpp | 3 +- futurerestore/main.cpp | 110 ++++++++++++++---------- 4 files changed, 197 insertions(+), 65 deletions(-) diff --git a/configure.ac b/configure.ac index 788ce076..a7dfdf27 100644 --- a/configure.ac +++ b/configure.ac @@ -52,7 +52,7 @@ if [test -z "$LIBIPATCHER_FLAGS"]; then else AC_MSG_CHECKING([for futurerestore libipatcher]) if test "$build_libipatcher" = "true"; then - PKG_CHECK_MODULES(libipatcher, libipatcher >= 32) + PKG_CHECK_MODULES(libipatcher, libipatcher >= 38) do_libipatcher=yes else do_libipatcher=no diff --git a/futurerestore/futurerestore.cpp b/futurerestore/futurerestore.cpp index 7a988821..96dd8f16 100644 --- a/futurerestore/futurerestore.cpp +++ b/futurerestore/futurerestore.cpp @@ -125,9 +125,17 @@ int futurerestore::getDeviceMode(bool reRequest){ void futurerestore::putDeviceIntoRecovery(){ if (!_didInit) reterror(-1, "did not init\n"); +#ifdef HAVE_LIBIPATCHER + _enterPwnRecoveryRequested = _isPwnDfu; +#endif + getDeviceMode(false); info("Found device in %s mode\n", _client->mode->string); - if (_client->mode->index == MODE_NORMAL) { + if (_client->mode->index == MODE_NORMAL){ +#ifdef HAVE_LIBIPATCHER + if (_isPwnDfu) + reterror(-501, "isPwnDfu enabled, but device was found in normal mode\n"); +#endif info("Entering recovery mode...\n"); if (normal_enter_recovery(_client) < 0) { reterror(-2,"Unable to place device into recovery mode from %s mode\n", _client->mode->string); @@ -136,7 +144,7 @@ void futurerestore::putDeviceIntoRecovery(){ info("Device already in Recovery mode\n"); }else if (_client->mode->index == MODE_DFU && _isPwnDfu && #ifdef HAVE_LIBIPATCHER - (_enterPwnRecoveryRequested = true) + true #else false #endif @@ -420,7 +428,7 @@ pair, size_t> getIPSWComponent(struct idevicerestore_client_t* } -void futurerestore::enterPwnRecovery(plist_t build_identity){ +void futurerestore::enterPwnRecovery(plist_t build_identity, string bootargs){ #ifndef HAVE_LIBIPATCHER reterror(-404, "compiled without libipatcher"); #else @@ -430,15 +438,8 @@ void futurerestore::enterPwnRecovery(plist_t build_identity){ if (dfu_client_new(_client) < 0) reterror(-91,"Unable to connect to DFU device\n"); - irecv_get_mode(_client->dfu->client, &mode); - if (mode != IRECV_K_DFU_MODE) { - info("NOTE: device is not in DFU mode, assuming recovery mode.\n"); - _client->mode = &idevicerestore_modes[MODE_RECOVERY]; - reterror(-91, "Device is in wrong mode\n"); - } - try { iBSSKeys = libipatcher::getFirmwareKey(_client->device->product_type, _client->build, "iBSS"); iBECKeys = libipatcher::getFirmwareKey(_client->device->product_type, _client->build, "iBEC"); @@ -453,14 +454,24 @@ void futurerestore::enterPwnRecovery(plist_t build_identity){ auto iBEC = getIPSWComponent(_client, build_identity, "iBEC"); - iBEC = move(libipatcher::patchiBEC((char*)iBEC.first, iBEC.second, iBECKeys)); + iBEC = move(libipatcher::patchiBEC((char*)iBEC.first, iBEC.second, iBECKeys, bootargs)); - - info("Sending %s (%lu bytes)...\n", "iBSS", iBSS.second); - // FIXME: Did I do this right???? - irecv_error_t err = irecv_send_buffer(_client->dfu->client, (unsigned char*)(char*)iBSS.first, (unsigned long)iBSS.second, 1); - if (err != IRECV_E_SUCCESS) { - reterror(-92,"ERROR: Unable to send %s component: %s\n", "iBSS", irecv_strerror(err)); + bool modeIsRecovery = false; + if (mode != IRECV_K_DFU_MODE) { + info("NOTE: device is not in DFU mode, assuming pwn recovery mode.\n"); + for (int i=IRECV_K_RECOVERY_MODE_1; i<=IRECV_K_RECOVERY_MODE_4; i++) { + if (mode == i) + modeIsRecovery = true; + } + if (!modeIsRecovery) + reterror(-505, "device not in recovery mode\n"); + }else{ + info("Sending %s (%lu bytes)...\n", "iBSS", iBSS.second); + // FIXME: Did I do this right???? + irecv_error_t err = irecv_send_buffer(_client->dfu->client, (unsigned char*)(char*)iBSS.first, (unsigned long)iBSS.second, 1); + if (err != IRECV_E_SUCCESS) { + reterror(-92,"ERROR: Unable to send %s component: %s\n", "iBSS", irecv_strerror(err)); + } } if (_client->build_major > 8) { @@ -480,6 +491,8 @@ void futurerestore::enterPwnRecovery(plist_t build_identity){ if (err != IRECV_E_SUCCESS) { reterror(-92,"ERROR: Unable to send %s component: %s\n", "iBSS", irecv_strerror(err)); } + if (modeIsRecovery) + irecv_send_command(_client->dfu->client, "go"); } dfu_client_free(_client); @@ -922,6 +935,106 @@ error: return result ? abs(result) : err; } +int futurerestore::doJustBoot(const char *ipsw, string bootargs){ + int err = 0; + //some memory might not get freed if this function throws an exception, but you probably don't want to catch that anyway. + + struct idevicerestore_client_t* client = _client; + int unused; + int result = 0; + plist_t buildmanifest = NULL; + plist_t build_identity = NULL; + + client->ipsw = strdup(ipsw); + + getDeviceMode(true); + info("Found device in %s mode\n", client->mode->string); + + if (!(client->mode->index == MODE_DFU || client->mode->index == MODE_RECOVERY) || !_enterPwnRecoveryRequested) + reterror(-6, "device not in DFU/Recovery mode\n"); + // discover the device type + if (check_hardware_model(client) == NULL || client->device == NULL) { + reterror(-2,"ERROR: Unable to discover device model\n"); + } + info("Identified device as %s, %s\n", client->device->hardware_model, client->device->product_type); + + // verify if ipsw file exists + if (access(client->ipsw, F_OK) < 0) { + error("ERROR: Firmware file %s does not exist.\n", client->ipsw); + return -1; + } + info("Extracting BuildManifest from IPSW\n"); + if (ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &unused) < 0) { + reterror(-3,"ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); + } + /* check if device type is supported by the given build manifest */ + if (build_manifest_check_compatibility(buildmanifest, client->device->product_type) < 0) { + reterror(-4,"ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n"); + } + /* print iOS information from the manifest */ + build_manifest_get_version_information(buildmanifest, client); + + info("Product Version: %s\n", client->version); + info("Product Build: %s Major: %d\n", client->build, client->build_major); + + client->image4supported = is_image4_supported(client); + info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false"); + + if (!(build_identity = getBuildidentityWithBoardconfig(buildmanifest, client->device->hardware_model, 0))) + reterror(-5,"ERROR: Unable to find any build identities for IPSW\n"); + + + /* print information about current build identity */ + build_identity_print_information(build_identity); + + + //check for enterpwnrecovery, because we could be in DFU mode + if (!_enterPwnRecoveryRequested) + reterror(-6, "enterPwnRecoveryRequested is not set, but required"); + + if (getDeviceMode(true) != MODE_DFU && getDeviceMode(false) != MODE_RECOVERY) + reterror(-6, "unexpected device mode"); + enterPwnRecovery(build_identity, bootargs); + + client->recovery_custom_component_function = get_custom_component; + + for (int i=0;getDeviceMode(true) != MODE_RECOVERY && i<40; i++) putchar('.'),usleep(USEC_PER_SEC*0.5); + putchar('\n'); + + if (!check_mode(client)) + reterror(-15, "failed to reconnect to device in recovery (iBEC) mode\n"); + + get_ecid(client, &client->ecid); + + client->flags |= FLAG_BOOT; + + if (client->mode->index == MODE_RECOVERY) { + if (client->srnm == NULL) { + reterror(-9,"ERROR: could not retrieve device serial number. Can't continue.\n"); + } + if (irecv_send_command(client->recovery->client, "bgcolor 0 255 0") != IRECV_E_SUCCESS) { + error("ERROR: Unable to set bgcolor\n"); + return -1; + } + 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! + client->image4supported = true; //dirty hack to not require apticket + if (recovery_enter_restore(client, build_identity) < 0) { + reterror(-10,"ERROR: Unable to place device into restore mode\n"); + } + client->image4supported = false; + recovery_client_free(client); + } + + info("Cleaning up...\n"); + +error: + safeFree(client->sepfwdata); + safePlistFree(buildmanifest); + if (!result && !err) info("DONE\n"); + return result ? abs(result) : err; +} + futurerestore::~futurerestore(){ normal_client_free(_client); recovery_client_free(_client); diff --git a/futurerestore/futurerestore.hpp b/futurerestore/futurerestore.hpp index d94343f6..7c81f463 100644 --- a/futurerestore/futurerestore.hpp +++ b/futurerestore/futurerestore.hpp @@ -62,7 +62,7 @@ class futurerestore { bool _enterPwnRecoveryRequested = false; bool _rerestoreiOS9 = false; //methods - void enterPwnRecovery(plist_t build_identity); + void enterPwnRecovery(plist_t build_identity, std::string bootargs = ""); public: @@ -105,6 +105,7 @@ public: uint64_t getBasebandGoldCertIDFromDevice(); int doRestore(const char *ipsw); + int doJustBoot(const char *ipsw, std::string bootargs = ""); ~futurerestore(); diff --git a/futurerestore/main.cpp b/futurerestore/main.cpp index ea77d0c5..dd1ac8fb 100644 --- a/futurerestore/main.cpp +++ b/futurerestore/main.cpp @@ -35,7 +35,8 @@ static struct option longopts[] = { { "latest-baseband", no_argument, NULL, '1' }, { "no-baseband", no_argument, NULL, '2' }, #ifdef HAVE_LIBIPATCHER - { "use-pwndfu", no_argument, NULL, '3' }, + { "use-pwndfu", no_argument, NULL, '3' }, + { "just-boot", optional_argument, NULL, '4' }, #endif { NULL, 0, NULL, 0 } }; @@ -94,6 +95,7 @@ int main(int argc, const char * argv[]) { const char *basebandManifestPath = NULL; const char *sepPath = NULL; const char *sepManifestPath = NULL; + const char *bootargs = NULL; vector apticketPaths; @@ -142,6 +144,10 @@ int main(int argc, const char * argv[]) { case '3': // long option: "no-baseband"; flags |= FLAG_IS_PWN_DFU; break; + case '4': // long option: "just-boot"; + bootargs = (optarg) ? optarg : ""; + break; + break; #endif case 'd': // long option: "debug"; can be called as short option idevicerestore_debug = 1; @@ -172,13 +178,20 @@ int main(int argc, const char * argv[]) { if (!client.init()) reterror(-3,"can't init, no device found\n"); printf("futurerestore init done\n"); + if (bootargs && !(flags & FLAG_IS_PWN_DFU)) { + reterror(-2,"--just-boot required --use-pwndfu\n"); + } try { if (apticketPaths.size()) client.loadAPTickets(apticketPaths); - if (!((apticketPaths.size() && ipsw) - && ((basebandPath && basebandManifestPath) || ((flags & FLAG_LATEST_BASEBAND) || (flags & FLAG_NO_BASEBAND))) - && ((sepPath && sepManifestPath) || (flags & FLAG_LATEST_SEP) || client.is32bit()) )) { + if (!( + ((apticketPaths.size() && ipsw) + && ((basebandPath && basebandManifestPath) || ((flags & FLAG_LATEST_BASEBAND) || (flags & FLAG_NO_BASEBAND))) + && ((sepPath && sepManifestPath) || (flags & FLAG_LATEST_SEP) || client.is32bit()) + ) || (ipsw && bootargs && (flags & FLAG_IS_PWN_DFU)) + )) { + if (!(flags & FLAG_WAIT) || ipsw){ error("missing argument\n"); cmd_help(); @@ -190,53 +203,55 @@ int main(int argc, const char * argv[]) { } goto error; } - devVals.deviceModel = (char*)client.getDeviceModelNoCopy(); - devVals.deviceBoard = (char*)client.getDeviceBoardNoCopy(); - - if (flags & FLAG_LATEST_SEP){ - info("user specified to use latest signed sep\n"); - client.loadLatestSep(); - }else if (!client.is32bit()){ - client.loadSep(sepPath); - client.setSepManifestPath(sepManifestPath); - } - - versVals.basebandMode = kBasebandModeWithoutBaseband; - if (!client.is32bit() && !(isSepManifestSigned = isManifestSignedForDevice(client.sepManifestPath(), &devVals, &versVals))){ - reterror(-3,"sep firmware isn't signed\n"); - } - - 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("if you added this flag by mistake you can press CTRL-C now to cancel\n"); - int c = 5; - printf("continuing restore in "); - while (c) { - printf("%d ",c--); - fflush(stdout); - sleep(1); - } - printf("\n"); + if (bootargs){ + }else{ - if (flags & FLAG_LATEST_BASEBAND){ - info("user specified to use latest signed baseband (WARNING, THIS CAN CAUSE A NON-WORKING RESTORE)\n"); - client.loadLatestBaseband(); - }else{ - client.setBasebandPath(basebandPath); - client.setBasebandManifestPath(basebandManifestPath); - printf("Did set sep+baseband path and firmware\n"); + devVals.deviceModel = (char*)client.getDeviceModelNoCopy(); + devVals.deviceBoard = (char*)client.getDeviceBoardNoCopy(); + + if (flags & FLAG_LATEST_SEP){ + info("user specified to use latest signed sep\n"); + client.loadLatestSep(); + }else if (!client.is32bit()){ + client.loadSep(sepPath); + client.setSepManifestPath(sepManifestPath); } - 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"); + versVals.basebandMode = kBasebandModeWithoutBaseband; + if (!client.is32bit() && !(isSepManifestSigned = isManifestSignedForDevice(client.sepManifestPath(), &devVals, &versVals))){ + reterror(-3,"sep firmware isn't signed\n"); } - if (!(isBasebandSigned = isManifestSignedForDevice(client.basebandManifestPath(), &devVals, &versVals))) { - reterror(-3,"baseband firmware isn't signed\n"); + + 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("if you added this flag by mistake you can press CTRL-C now to cancel\n"); + int c = 5; + printf("continuing restore in "); + while (c) { + printf("%d ",c--); + fflush(stdout); + sleep(1); + } + printf("\n"); + }else{ + if (flags & FLAG_LATEST_BASEBAND){ + info("user specified to use latest signed baseband (WARNING, THIS CAN CAUSE A NON-WORKING RESTORE)\n"); + client.loadLatestBaseband(); + }else{ + client.setBasebandPath(basebandPath); + client.setBasebandManifestPath(basebandManifestPath); + printf("Did set sep+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"); + } + if (!(isBasebandSigned = isManifestSignedForDevice(client.basebandManifestPath(), &devVals, &versVals))) { + reterror(-3,"baseband firmware isn't signed\n"); + } } } - - client.putDeviceIntoRecovery(); if (flags & FLAG_WAIT){ client.waitForNonce(); @@ -248,7 +263,10 @@ int main(int argc, const char * argv[]) { } try { - res = client.doRestore(ipsw); + if (bootargs) + res = client.doJustBoot(ipsw,bootargs); + else + res = client.doRestore(ipsw); } catch (int error) { if (error == -20) error("maybe you forgot -w ?\n"); err = error;