From d1c90d11851c053a7febfa7ba9839c5a0a02807a Mon Sep 17 00:00:00 2001 From: tihmstar Date: Fri, 2 Jun 2017 20:07:10 +0200 Subject: [PATCH] added optional libipatcher support --- configure.ac | 35 ++++++ futurerestore/Makefile.am | 5 + futurerestore/futurerestore.cpp | 184 ++++++++++++++++++++++++++++---- futurerestore/futurerestore.hpp | 15 ++- futurerestore/main.cpp | 22 +++- 5 files changed, 234 insertions(+), 27 deletions(-) diff --git a/configure.ac b/configure.ac index 18210a20..c9f1b235 100644 --- a/configure.ac +++ b/configure.ac @@ -37,9 +37,44 @@ PKG_CHECK_MODULES(libirecovery, libirecovery >= 0.2.0) AC_PROG_CC AC_PROG_CXX + +# Optional module libipatcher +AC_ARG_WITH([libipatcher], + [AS_HELP_STRING([--without-libipatcher], + [build with bundle-less odysseus support (default is yes)])], + [build_libipatcher=false], + [build_libipatcher=true]) + +PKG_PROG_PKG_CONFIG +LIBIPATCHER_FLAGS=$(pkg-config --cflags libipatcher) +if [test -z "$LIBIPATCHER_FLAGS"]; then + do_libipatcher=no +else + AC_MSG_CHECKING([for futurerestore libipatcher]) + if test "$build_libipatcher" = "true"; then + PKG_CHECK_MODULES(libipatcher, libipatcher >= 1.0) + do_libipatcher=yes + else + do_libipatcher=no + fi +fi +AM_CONDITIONAL([HAVE_LIBIPATCHER],[test "x$do_libipatcher" = "xyes"]) + + LT_INIT AC_OUTPUT([ Makefile futurerestore/Makefile ]) + + +echo " +Configuration for $PACKAGE $VERSION: +------------------------------------------- + + Install prefix ..........: $prefix + With libipatcher ........: $do_libipatcher + Now type 'make' to build $PACKAGE $VERSION, + and then 'make install' for installation. +" diff --git a/futurerestore/Makefile.am b/futurerestore/Makefile.am index 3e712bb1..947fdd02 100644 --- a/futurerestore/Makefile.am +++ b/futurerestore/Makefile.am @@ -1,6 +1,11 @@ AM_CFLAGS = $(libplist_CFLAGS) $(libzip_CFLAGS) $(libimobiledevice_CFLAGS) $(libfragmentzip_CFLAGS) $(libirecovery_CFLAGS) -I$(top_srcdir)/external/tsschecker/tsschecker -I$(top_srcdir)/external/img4tool/img4tool -I$(top_srcdir)/external/idevicerestore/src -std=c++11 AM_LDFLAGS = $(libplist_LIBS) $(libzip_LIBS) $(libimobiledevice_LIBS) $(libfragmentzip_LIBS) $(libirecovery_LIBS) +if HAVE_LIBIPATCHER +AM_LDFLAGS += $(libipatcher_LIBS) +AM_CFLAGS += $(libipatcher_CFLAGS) -DHAVE_LIBIPATCHER +endif + bin_PROGRAMS = futurerestore futurerestore_CXXFLAGS = $(AM_CFLAGS) futurerestore_LDADD = $(top_srcdir)/external/idevicerestore/src/libidevicerestore.la $(top_srcdir)/external/img4tool/img4tool/libimg4tool.la $(top_srcdir)/external/tsschecker/tsschecker/libtsschecker.la $(AM_LDFLAGS) diff --git a/futurerestore/futurerestore.cpp b/futurerestore/futurerestore.cpp index f12d94c0..fa19febf 100644 --- a/futurerestore/futurerestore.cpp +++ b/futurerestore/futurerestore.cpp @@ -20,17 +20,22 @@ #include #include #include "futurerestore.hpp" +#ifdef HAVE_LIBIPATCHER +#include +#endif extern "C"{ #include "common.h" #include "../external/img4tool/img4tool/img4.h" #include "img4tool.h" #include "normal.h" #include "recovery.h" +#include "dfu.h" #include "ipsw.h" #include "locking.h" #include "restore.h" #include "tsschecker.h" #include "all_tsschecker.h" +#include } @@ -64,16 +69,15 @@ extern "C"{ # include #endif // __APPLE__ +#ifndef HAVE_LIBIPATCHER +#define _enterPwnRecoveryRequested false +#endif #define reterror(code,msg ...) error(msg),throw int(code) #define safeFree(buf) if (buf) free(buf), buf = NULL #define safePlistFree(buf) if (buf) plist_free(buf), buf = NULL -futurerestore::futurerestore(){ - futurerestore(false); -} - -futurerestore::futurerestore(bool isUpdateInstall) : _isUpdateInstall(isUpdateInstall){ +futurerestore::futurerestore(bool isUpdateInstall, bool isPwnDfu) : _isUpdateInstall(isUpdateInstall), _isPwnDfu(isPwnDfu){ _client = idevicerestore_client_new(); if (_client == NULL) throw std::string("could not create idevicerestore client\n"); @@ -92,6 +96,7 @@ bool futurerestore::init(){ info("[INFO] 32bit device detected\n"); }else{ info("[INFO] 64bit device detected\n"); + if (_isPwnDfu) reterror(-90, "isPwnDfu is only allowed for 32bit devices\n"); } return _didInit; } @@ -111,6 +116,7 @@ int futurerestore::getDeviceMode(bool reRequest){ return _client->mode->index; }else{ normal_client_free(_client); + dfu_client_free(_client); recovery_client_free(_client); return check_mode(_client); } @@ -128,6 +134,14 @@ void futurerestore::putDeviceIntoRecovery(){ } }else if (_client->mode->index == MODE_RECOVERY){ info("Device already in Recovery mode\n"); + }else if (_client->mode->index == MODE_DFU && _isPwnDfu && +#ifdef HAVE_LIBIPATCHER + (_enterPwnRecoveryRequested = true) +#else + false +#endif + ){ + info("requesting to get into pwnRecovery later\n"); }else{ reterror(-3, "unsupported devicemode, please put device in recovery mode or normal mode\n"); } @@ -137,6 +151,7 @@ void futurerestore::putDeviceIntoRecovery(){ //these get also freed by destructor normal_client_free(_client); + dfu_client_free(_client); recovery_client_free(_client); } @@ -368,6 +383,114 @@ char *futurerestore::getiBootBuild(){ } +pair, size_t> getIPSWComponent(struct idevicerestore_client_t* client, plist_t build_identity, string component){ + ptr_smart path; + unsigned char* component_data = NULL; + unsigned int component_size = 0; + + if (!(char*)path) { + if (build_identity_get_component_path(build_identity, component.c_str(), &path) < 0) { + reterror(-95,"ERROR: Unable to get path for component '%s'\n", component.c_str()); + } + } + + if (extract_component(client->ipsw, (char*)path, &component_data, &component_size) < 0) { + reterror(-95,"ERROR: Unable to extract component: %s\n", component.c_str()); + } + + return {(char*)component_data,component_size}; +} + + +void futurerestore::enterPwnRecovery(plist_t build_identity){ +#ifndef HAVE_LIBIPATCHER + reterror(-404, "compiled without libipatcher"); +#else + int mode = 0; + + 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"); + } + + auto iBSS = getIPSWComponent(_client, build_identity, "iBSS"); + iBSS = move(libipatcher::patchiBSS((char*)iBSS.first, iBSS.second, libipatcher::getFirmwareKey(_client->device->product_type, _client->build, "iBSS"))); + + + auto iBEC = getIPSWComponent(_client, build_identity, "iBEC"); + iBEC = move(libipatcher::patchiBEC((char*)iBEC.first, iBEC.second, libipatcher::getFirmwareKey(_client->device->product_type, _client->build, "iBEC"))); + + + 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) { + /* reconnect */ + dfu_client_free(_client); + sleep(3); + dfu_client_new(_client); + + if (irecv_usb_set_configuration(_client->dfu->client, 1) < 0) { + reterror(-92,"ERROR: set configuration failed\n"); + } + + /* send iBEC */ + info("Sending %s (%lu bytes)...\n", "iBEC", iBEC.second); + // FIXME: Did I do this right???? + irecv_error_t err = irecv_send_buffer(_client->dfu->client, (unsigned char*)(char*)iBEC.first, (unsigned long)iBEC.second, 1); + if (err != IRECV_E_SUCCESS) { + reterror(-92,"ERROR: Unable to send %s component: %s\n", "iBSS", irecv_strerror(err)); + } + } + + dfu_client_free(_client); + + sleep(7); + + // Reconnect to device, but this time make sure we're not still in DFU mode + if (recovery_client_new(_client) < 0) { + if (_client->recovery->client) { + irecv_close(_client->recovery->client); + _client->recovery->client = NULL; + } + reterror(-93,"ERROR: Unable to connect to recovery device\n"); + } + + irecv_get_mode(_client->recovery->client, &mode); + + if (mode == IRECV_K_DFU_MODE) { + if (_client->recovery->client) { + irecv_close(_client->recovery->client); + _client->recovery->client = NULL; + } + reterror(-94,"ERROR: Unable to connect to recovery device\n"); + } +#endif +} + +void get_custom_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component, unsigned char** data, unsigned int *size){ +#ifndef HAVE_LIBIPATCHER + reterror(-404, "compiled without libipatcher"); +#else + auto comp = getIPSWComponent(client, build_identity, component); + comp = move(libipatcher::decryptFile3((char*)comp.first, comp.second, libipatcher::getFirmwareKey(client->device->product_type, client->build, component))); + *data = (unsigned char*)(char*)comp.first; + *size = comp.second; + comp.first = NULL; //don't free on destruction +#endif +} + + int futurerestore::doRestore(const char *ipsw){ 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. @@ -386,7 +509,9 @@ int futurerestore::doRestore(const char *ipsw){ getDeviceMode(true); info("Found device in %s mode\n", client->mode->string); - if (client->mode->index != MODE_RECOVERY) reterror(-6, "device not in recovery mode\n"); + + if (client->mode->index != MODE_RECOVERY && client->mode->index != MODE_DFU && !_enterPwnRecoveryRequested) + reterror(-6, "device not in 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"); @@ -415,8 +540,9 @@ int futurerestore::doRestore(const char *ipsw){ client->image4supported = is_image4_supported(client); info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false"); - - if (!(client->tss = nonceMatchesApTickets())) + if (_enterPwnRecoveryRequested) //we are in pwnDFU, so we don't need to check nonces + client->tss = _aptickets.at(0); + else if (!(client->tss = nonceMatchesApTickets())) reterror(-20, "Devicenonce does not match APTicket nonce\n"); plist_dict_remove_item(client->tss, "BBTicket"); @@ -432,7 +558,8 @@ int futurerestore::doRestore(const char *ipsw){ plist_t manifest = plist_dict_get_item(build_identity, "Manifest"); printf("checking APTicket to be valid for this restore...\n"); - const char * im4m = nonceMatchesIM4Ms(); + //if we are in pwnDFU, just use first apticket. no need to check nonces + const char * im4m = (!_enterPwnRecoveryRequested) ? nonceMatchesIM4Ms() : _im4ms.at(0); uint64_t deviceEcid = getDeviceEcid(); uint64_t im4mEcid = (_client->image4supported) ? getEcidFromIM4M(im4m) : getEcidFromSCAB(im4m); @@ -484,6 +611,9 @@ int futurerestore::doRestore(const char *ipsw){ } printf("Verified APTicket to be valid for this restore\n"); } + }else if (_enterPwnRecoveryRequested){ + info("[WARNING] skipping ramdisk hash check, since device is in pwnDFU according to user\n"); + }else{ info("[WARNING] full buildidentity check is not implemented, only comparing ramdisk hash.\n"); size_t tickethashSize = 0; @@ -553,6 +683,14 @@ int futurerestore::doRestore(const char *ipsw){ /* print information about current build identity */ build_identity_print_information(build_identity); + //check for enterpwnrecovery, because we could be in DFU mode + if (_enterPwnRecoveryRequested){ + if (getDeviceMode(true) != MODE_DFU) + reterror(-6, "unexpected device mode"); + enterPwnRecovery(build_identity); + } + + // Get filesystem name from build identity char* fsname = NULL; if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { @@ -651,16 +789,19 @@ int futurerestore::doRestore(const char *ipsw){ } } - /* now we load the iBEC */ - if (recovery_send_ibec(client, build_identity) < 0) { - reterror(-8,"ERROR: Unable to send iBEC\n"); - } - printf("waiting for device to reconnect... "); - recovery_client_free(client); - - /* this must be long enough to allow the device to run the iBEC */ - /* FIXME: Probably better to detect if the device is back then */ - sleep(7); + if (!_enterPwnRecoveryRequested) { + /* now we load the iBEC */ + if (recovery_send_ibec(client, build_identity) < 0) { + reterror(-8,"ERROR: Unable to send iBEC\n"); + } + printf("waiting for device to reconnect... "); + recovery_client_free(client); + /* this must be long enough to allow the device to run the iBEC */ + /* FIXME: Probably better to detect if the device is back then */ + sleep(7); + }else //if pwnrecovery send all components decrypted + 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'); @@ -670,7 +811,9 @@ int futurerestore::doRestore(const char *ipsw){ //do magic if (_client->image4supported) get_sep_nonce(client, &client->sepnonce, &client->sepnonce_size); get_ap_nonce(client, &client->nonce, &client->nonce_size); + get_ecid(client, &client->ecid); + if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { reterror(-9,"ERROR: could not retrieve device serial number. Can't continue.\n"); @@ -719,7 +862,6 @@ error: return result ? abs(result) : err; } - futurerestore::~futurerestore(){ normal_client_free(_client); recovery_client_free(_client); @@ -752,7 +894,7 @@ const char *futurerestore::getDeviceModelNoCopy(){ if (!_client->device || !_client->device->product_type){ int mode = getDeviceMode(true); - if (mode != MODE_NORMAL && mode != MODE_RECOVERY) + if (mode == MODE_NORMAL && mode != MODE_RECOVERY && mode != MODE_DFU) reterror(-20, "unexpected device mode=%d\n",mode); if (check_hardware_model(_client) == NULL || _client->device == NULL) diff --git a/futurerestore/futurerestore.hpp b/futurerestore/futurerestore.hpp index 7348c7c0..d5c419fc 100644 --- a/futurerestore/futurerestore.hpp +++ b/futurerestore/futurerestore.hpp @@ -27,9 +27,12 @@ public: ptr_smart(T p, function ptr_free){static_assert(is_pointer(), "error: this is for pointers only\n"); _p = p;_ptr_free = ptr_free;} ptr_smart(T p){_p = p;} ptr_smart(){_p = NULL;} - T operator=(T p){return _p = p;} + ptr_smart(ptr_smart &&p){ _p = p._p; _ptr_free = p._ptr_free; p._p = NULL; p._ptr_free = NULL;} + ptr_smart& operator =(ptr_smart &&p){_p = p._p; _ptr_free = p._ptr_free; p._p = NULL; p._ptr_free = NULL; return *this;} + T operator =(T p){ _p = p; return _p;} + T operator =(T &p){_p = p; p = NULL; return _p;} T *operator&(){return &_p;} - operator const T() const {return _p;} + explicit operator const T() const {return _p;} operator const void*() const {return _p;} ~ptr_smart(){if (_p) (_ptr_free) ? _ptr_free(_p) : free((void*)_p);} }; @@ -42,6 +45,7 @@ class futurerestore { vector_im4ms; int _foundnonce = -1; bool _isUpdateInstall = false; + bool _isPwnDfu = false; char *_firmwareJson = NULL; jsmntok_t *_firmwareTokens = NULL;; @@ -55,10 +59,13 @@ class futurerestore { const char *_sepbuildmanifestPath = NULL; const char *_basebandbuildmanifestPath = NULL; + bool _enterPwnRecoveryRequested = false; + //methods + void enterPwnRecovery(plist_t build_identity); + public: - futurerestore(); - futurerestore(bool isUpdateInstall); + futurerestore(bool isUpdateInstall = false, bool isPwnDfu = false); bool init(); int getDeviceMode(bool reRequest); uint64_t getDeviceEcid(); diff --git a/futurerestore/main.cpp b/futurerestore/main.cpp index 8440e21f..6c93d8fc 100644 --- a/futurerestore/main.cpp +++ b/futurerestore/main.cpp @@ -32,6 +32,9 @@ static struct option longopts[] = { { "latest-sep", no_argument, NULL, '0' }, { "latest-baseband", no_argument, NULL, '1' }, { "no-baseband", no_argument, NULL, '2' }, +#ifdef HAVE_LIBIPATCHER + { "use-pwndfu", no_argument, NULL, '3' }, +#endif { NULL, 0, NULL, 0 } }; @@ -40,10 +43,18 @@ static struct option longopts[] = { #define FLAG_LATEST_SEP 1 << 2 #define FLAG_LATEST_BASEBAND 1 << 3 #define FLAG_NO_BASEBAND 1 << 4 +#define FLAG_IS_PWN_DFU 1 << 5 void cmd_help(){ printf("Usage: futurerestore [OPTIONS] IPSW\n"); - printf("Allows restoring nonmatching iOS/Sep/Baseband\n\n"); + printf("Allows restoring nonmatching iOS/Sep/Baseband\n"); + printf("Odysseus Support: %s\n\n", +#ifdef HAVE_LIBIPATCHER + "yes" +#else + "no" +#endif + ); printf(" -t, --apticket PATH\t\tApticket used for restoring\n"); printf(" -b, --baseband PATH\t\tBaseband to be flashed\n"); @@ -55,6 +66,9 @@ void cmd_help(){ printf(" --latest-sep\t\tuse latest signed sep instead of manually specifying one(may cause bad restore)\n"); printf(" --latest-baseband\t\tse latest signed baseband instead of manually specifying one(may cause bad restore)\n"); printf(" --no-baseband\t\tskip checks and don't flash baseband.\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"); +#endif printf(" \t\tWARNING: only use this for device without baseband (eg iPod or some wifi only iPads)\n"); printf("\n"); } @@ -88,6 +102,7 @@ int main(int argc, const char * argv[]) { cmd_help(); return -1; } + while ((opt = getopt_long(argc, (char* const *)argv, "ht:b:p:s:m:wud01", longopts, &optindex)) > 0) { switch (opt) { @@ -121,6 +136,9 @@ int main(int argc, const char * argv[]) { case '2': // long option: "no-baseband"; flags |= FLAG_NO_BASEBAND; break; + case '3': // long option: "no-baseband"; + flags |= FLAG_IS_PWN_DFU; + break; case 'd': // long option: "debug"; can be called as short option idevicerestore_debug = 1; break; @@ -146,7 +164,7 @@ int main(int argc, const char * argv[]) { return -5; } - futurerestore client(flags & FLAG_UPDATE); + futurerestore client(flags & FLAG_UPDATE, flags & FLAG_IS_PWN_DFU); if (!client.init()) reterror(-3,"can't init, no device found\n"); printf("futurerestore init done\n");