pax_global_header00006660000000000000000000000064136717361750014532gustar00rootroot0000000000000052 comment=a88351d58f9041ae104a794ec9b747d7d0790084 idevicerestore-1.0.0/000077500000000000000000000000001367173617500145445ustar00rootroot00000000000000idevicerestore-1.0.0/.gitignore000066400000000000000000000010161367173617500165320ustar00rootroot00000000000000# git-ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): *.[oa] *~ *.po *.lo *.la autom4te.cache/* *.in */.deps/* m4/* swig/* *.swp *.patch *.diff aclocal.m4 config.h config.log config.sub config.guess config.status configure depcomp install-sh compile main ltmain.sh missing mkinstalldirs libtool *Makefile py-compile stamp-h1 src/.libs src/idevicerestore idevicerestore-1.0.0/AUTHORS000066400000000000000000000001621367173617500156130ustar00rootroot00000000000000Joshua Hill Martin Szulecki Nikias Bassen idevicerestore-1.0.0/COPYING000066400000000000000000000167431367173617500156120ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. idevicerestore-1.0.0/Makefile.am000066400000000000000000000001511367173617500165750ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src docs EXTRA_DIST = \ docs \ README.md idevicerestore-1.0.0/NEWS000066400000000000000000000011241367173617500152410ustar00rootroot00000000000000Version 1.0.0 ~~~~~~~~~~~~~ * First official public release * Features: - Restore firmware files to iOS devices - Use official IPSW firmware archive file or directory - Updates the device by default or allows full restore erasing all data - Download latest available firmware for device on demand - Cache downloaded firmware files - Restore using custom firmware files (requires bootrom exploit) - Skip NOR/Baseband upgrade - Fetch TSS records as ".shsh" files - Put devices in pwned DFU mode (limera1n devices only) - Use custom AP ticket from file - Developed since 2010 idevicerestore-1.0.0/README.md000066400000000000000000000117461367173617500160340ustar00rootroot00000000000000# idevicerestore *A command-line application to restore firmware files to iOS devices.* ## Features The idevicerestore application is a full reimplementation of all granular steps which are performed during the restore of a firmware to a device. In general, upgrades and downgrades are possible, however subject to availability of SHSH blobs from Apple for signing the firmare files. Some key features are: - **Restore:** Update firmware on iOS devices - **Firmware:** Use official IPSW firmware archive file or a directory as source - **Update:** Allows updating the device by default or erasing all data - **Download:** On demand download of latest available firmware for a device - **Cache:** Downloaded firmware files are cached locally - **Custom Firmware:** Restore custom firmware files *(requires bootrom exploit)* - **Baseband:** Allows you to skip NOR/Baseband upgrade - **SHSH:** Fetch TSS records and save them as ".shsh" files - **DFU:** Put devices in pwned DFU mode *(limera1n devices only)* - **AP Ticket:** Use custom AP ticket from a file - **Cross-Platform:** Tested on Linux, macOS, Windows and Android platforms - **History:** Developed since 2010 **WARNING:** This tool can easily __destroy your user data__ irreversibly. Use with caution and make sure to backup your data before trying to restore. **In any case, usage is at your own risk.** ## Installation / Getting started ### Debian / Ubuntu Linux First install all required dependencies and build tools: ```shell sudo apt-get install \ build-essential \ checkinstall \ git \ autoconf \ automake \ libtool-bin \ libreadline-dev \ libusb-1.0-0-dev \ libplist-dev \ libimobiledevice-dev \ libcurl4-openssl-dev \ libssl-dev \ libzip-dev \ zlib1g-dev ``` Then clone, build and install [libirecovery](https://github.com/libimobiledevice/libirecovery.git) which is not yet packaged: ```shell git clone https://github.com/libimobiledevice/libirecovery.git cd libirecovery ./autogen.sh make sudo make install cd .. ``` If the configure processes indicates old or missing libraries, your distribution might not have yet packaged the latest versions. In that case you will have to clone [these libraries](https://github.com/libimobiledevice/) separately and repeat the process in order to proceed. Continue with cloning the actual project repository: ```shell git clone https://github.com/libimobiledevice/idevicerestore.git cd idevicerestore ``` Now you can build and install it: ```shell ./autogen.sh make sudo make install ``` ## Usage The primary scenario is to restore a new firmware to a device. First of all attach your device to your machine. Then simply run: ```shell idevicerestore --latest ``` This will print a selection of firmware versions that are currently being signed and can be restored to the attached device. It will then attempt to download and restore the selected firmware. By default, an update restore is performed which will preserve user data. Mind that if the firmware file does not contain a 'Customer Upgrade Install' variant, an erase restore will be performed. You can force restoring with erasing all data and basically resetting the device by using: ```shell idevicerestore --erase --latest ``` Please consult the usage information or manual page for a full documentation of available command line options: ```shell idevicerestore --help man idevicerestore ``` ## Contributing We welcome contributions from anyone and are grateful for every pull request! If you'd like to contribute, please fork the `master` branch, change, commit and send a pull request for review. Once approved it can be merged into the main code base. If you plan to contribute larger changes or a major refactoring, please create a ticket first to discuss the idea upfront to ensure less effort for everyone. Please make sure your contribution adheres to: * Try to follow the code style of the project * Commit messages should describe the change well without being to short * Try to split larger changes into individual commits of a common domain * Use your real name and a valid email address for your commits We are still working on the guidelines so bear with us! ## Links * Homepage: https://libimobiledevice.org/ * Repository: https://git.libimobiledevice.org/idevicerestore.git * Repository (Mirror): https://github.com/libimobiledevice/idevicerestore.git * Issue Tracker: https://github.com/libimobiledevice/idevicerestore/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel * Twitter: https://twitter.com/libimobiledev ## License This project is licensed under the [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html), also included in the repository in the `COPYING` file. ## Credits Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS, iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. This project is an independent software application and has not been authorized, sponsored, or otherwise approved by Apple Inc. README Updated on: 2020-06-12 idevicerestore-1.0.0/autogen.sh000077500000000000000000000005551367173617500165520ustar00rootroot00000000000000#!/bin/sh olddir=`pwd` srcdir=`dirname $0` test -z "$srcdir" && srcdir=. ( cd "$srcdir" gprefix=`which glibtoolize 2>&1 >/dev/null` if [ $? -eq 0 ]; then glibtoolize --force else libtoolize --force fi aclocal -I m4 autoheader automake --add-missing autoconf cd "$olddir" ) if [ -z "$NOCONFIGURE" ]; then $srcdir/configure "$@" fi idevicerestore-1.0.0/configure.ac000066400000000000000000000116151367173617500170360ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.64) AC_INIT([idevicerestore], [1.0.0], [https://github.com/libimobiledevice/idevicerestore/issues],, [https://libimobiledevice.org]) AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) # Minimum package versions LIBIRECOVERY_VERSION=1.0.0 LIBIMOBILEDEVICE_VERSION=1.3.0 LIBPLIST_VERSION=2.2.0 LIBZIP_VERSION=0.8 LIBCURL_VERSION=7.0 OPENSSL_VERSION=0.9.8 AC_SUBST(LIBIRECOVERY_VERSION) AC_SUBST(LIBIMOBILEDEVICE_VERSION) AC_SUBST(LIBPLIST_VERSION) AC_SUBST(LIBZIP_VERSION) AC_SUBST(LIBCURL_VERSION) AC_SUBST(OPENSSL_VERSION) # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_PROG_LIBTOOL # Checks for libraries. PKG_CHECK_MODULES(libirecovery, libirecovery-1.0 >= $LIBIRECOVERY_VERSION) PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= $LIBIMOBILEDEVICE_VERSION) PKG_CHECK_MODULES(libplist, libplist-2.0 >= $LIBPLIST_VERSION) PKG_CHECK_MODULES(libzip, libzip >= $LIBZIP_VERSION) PKG_CHECK_MODULES(libcurl, libcurl >= $LIBCURL_VERSION) PKG_CHECK_MODULES(zlib, zlib) # optional PKG_CHECK_MODULES(openssl, openssl >= $OPENSSL_VERSION, have_openssl=yes, have_openssl=no) GLOBAL_CFLAGS="-Wno-multichar -O2" AC_LDADD="" AC_LDFLAGS="" AC_MSG_CHECKING([whether we need platform-specific build settings]) case ${host_os} in *mingw32*|*cygwin*) AC_MSG_RESULT([yes]) win32=true GLOBAL_CFLAGS+="-DWIN32 -D__LITTLE_ENDIAN__=1" AC_LDFLAGS+="-static-libgcc" ;; darwin*) AC_MSG_RESULT([yes]) AC_DEFINE([_DARWIN_BETTER_REALPATH], [1], [Use better method for realpath]) AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])]) ;; *) AC_MSG_RESULT([yes]) AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])]) ;; esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) AC_CHECK_FUNCS([strsep strcspn mkstemp realpath]) if test x$ac_cv_func_strsep != xyes; then if test x$ac_cv_func_strcspn != xyes; then AC_MSG_ERROR([You need either strsep or strcspn to build $PACKAGE]) fi fi CACHED_CFLAGS="$CFLAGS" CFLAGS+=" $libimobiledevice_CFLAGS" # check if libimobiledevice has timeout errors AC_CACHE_CHECK(for IDEVICE_E_TIMEOUT in enum idevice_error_t, ac_cv_idevice_error_has_timeout, AC_TRY_COMPILE([ #include ], [ return IDEVICE_E_TIMEOUT; ], ac_cv_idevice_error_has_timeout=yes, ac_cv_idevice_error_has_timeout=no)) if test "$ac_cv_idevice_error_has_timeout" = "yes"; then AC_DEFINE(HAVE_IDEVICE_E_TIMEOUT, 1, [Define if enum idevice_error_t defines IDEVICE_E_TIMEOUT]) fi AC_CACHE_CHECK(for RESTORE_E_RECEIVE_TIMEOUT in enum restored_error_t, ac_cv_restored_error_has_timeout, AC_TRY_COMPILE([ #include ], [ return RESTORE_E_RECEIVE_TIMEOUT; ], ac_cv_restored_error_has_timeout=yes, ac_cv_restored_error_has_timeout=no)) if test "$ac_cv_restored_error_has_timeout" = "yes"; then AC_DEFINE(HAVE_RESTORE_E_RECEIVE_TIMEOUT, 1, [Define if enum restored_error_t defines RESTORE_E_RECEIVE_TIMEOUT]) fi # check if libimobiledevice has enum idevice_connection_type AC_CACHE_CHECK(for enum idevice_connection_type, ac_cv_enum_idevice_connection_type, AC_TRY_COMPILE([ #include ], [ enum idevice_connection_type conn_type = CONNECTION_USBMUXD; ], ac_cv_enum_idevice_connection_type=yes, ac_cv_enum_idevice_connection_type=no)) if (test "$ac_cv_enum_idevice_connection_type" = "yes"); then AC_DEFINE(HAVE_ENUM_IDEVICE_CONNECTION_TYPE, 1, [Define if enum idevice_connection_type is available]) fi CFLAGS="$CACHED_CFLAGS" AC_ARG_WITH([openssl], [AS_HELP_STRING([--without-openssl], [Do not use OpenSSL])], [use_openssl=$withval], [use_openssl=$have_openssl]) if test "x$use_openssl" == "xyes"; then if test "x$have_openssl" != "xyes"; then echo "*** NOTE: --with-openssl passed but OpenSSL is not available ***" use_openssl=no fi fi if test "x$use_openssl" != "xyes"; then echo "*** NOTE: Using internal SHA1 implementation ***" have_openssl=no openssl_CFLAGS= openssl_LIBS= fi if test "x$have_openssl" == "xyes"; then AC_DEFINE(HAVE_OPENSSL, [1], [Define if you have OpenSSL]) fi AC_SUBST(openssl_CFLAGS) AC_SUBST(openssl_LIBS) AM_CONDITIONAL(USE_INTERNAL_SHA1, test x$use_openssl != xyes) AC_SUBST(GLOBAL_CFLAGS) AC_SUBST(AC_LDFLAGS) AC_SUBST(AC_LDADD) # check for large file support AC_SYS_LARGEFILE m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_OUTPUT([ Makefile src/Makefile docs/Makefile ]) echo " Configuration for $PACKAGE $VERSION: ------------------------------------------- Install prefix: .........: $prefix Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. " idevicerestore-1.0.0/docs/000077500000000000000000000000001367173617500154745ustar00rootroot00000000000000idevicerestore-1.0.0/docs/Makefile.am000066400000000000000000000000641367173617500175300ustar00rootroot00000000000000man_MANS = idevicerestore.1 EXTRA_DIST = $(man_MANS)idevicerestore-1.0.0/docs/idevicerestore.1000066400000000000000000000033401367173617500205720ustar00rootroot00000000000000.TH "idevicerestore" 1 .SH NAME idevicerestore \- Restore IPSW firmware FILE to an iOS device .SH SYNOPSIS .B idevicerestore [OPTIONS] FILE .SH DESCRIPTION Restore firmware files to iOS devices while either erasing the device or updating to preserve content and settings. .SH OPTIONS .TP .B \-i, \-\-ecid ECID target specific device by its hexadecimal ECID e.g. 0xaabb123456 or 00000012AABBCCDD. .TP .B \-u, \-\-udid UDID target specific device by its 40-digit device UDID. NOTE: only works with devices in normal mode. .TP .B \-e, \-\-erase perform a full restore, erasing all data (defaults to update). .TP .B \-c, \-\-custom restore with a custom firmware. .TP .B \-l, \-\-latest use latest available firmware (with download on demand). \ DO NOT USE if you need to preserve the baseband (unlock)! \ USE WITH CARE if you want to keep a jailbreakable firmware! \ The FILE argument is ignored when using this option. .TP .B \-s, \-\-cydia use Cydia's signature service instead of Apple's. .TP .B \-x, \-\-exclude exclude nor/baseband upgrade. .TP .B \-t, \-\-shsh fetch TSS record and save to .shsh file, then exit. .TP .B \-p, \-\-pwn put device in pwned DFU mode and exit (limera1n devices only). .TP .B \-n, \-\-no\-action do not perform any restore action. If combined with -l option the on demand IPSW download is performed before exiting. .TP .B \-C, \-\-cache\-path DIR use specified directory for caching extracted or other reused files. .TP .B \-d, \-\-debug enable communication debugging. .TP .B \-h, \-\-help prints usage information. .TP .B \-v, \-\-version prints version information. .SH AUTHORS Martin Szulecki Nikias Bassen Joshua Hill .SH ON THE WEB https://libimobiledevice.org https://github.com/libimobiledevice/idevicerestore idevicerestore-1.0.0/m4/000077500000000000000000000000001367173617500150645ustar00rootroot00000000000000idevicerestore-1.0.0/m4/ax_pthread.m4000066400000000000000000000505221367173617500174510ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 24 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD idevicerestore-1.0.0/src/000077500000000000000000000000001367173617500153335ustar00rootroot00000000000000idevicerestore-1.0.0/src/Makefile.am000066400000000000000000000021451367173617500173710ustar00rootroot00000000000000AM_CFLAGS = \ $(GLOBAL_CFLAGS) \ $(LFS_CFLAGS) \ $(libirecovery_CFLAGS) \ $(libimobiledevice_CFLAGS) \ $(libplist_CFLAGS) \ $(libzip_CFLAGS) \ $(zlib_CFLAGS) \ $(openssl_CFLAGS) \ $(libcurl_CFLAGS) AM_LDFLAGS = \ $(AC_LDFLAGS) \ $(libirecovery_LIBS) \ $(libimobiledevice_LIBS) \ $(libplist_LIBS) \ $(libzip_LIBS) \ $(zlib_LIBS) \ $(openssl_LIBS) \ $(libcurl_LIBS) AM_LDADD = $(AC_LDADD) bin_PROGRAMS = idevicerestore idevicerestore_SOURCES = \ idevicerestore.c idevicerestore.h \ endianness.h \ common.c common.h \ tss.c tss.h \ fls.c fls.h \ mbn.c mbn.h \ img3.c img3.h \ img4.c img4.h \ ftab.c ftab.h \ ipsw.c ipsw.h \ normal.c normal.h \ dfu.c dfu.h \ recovery.c recovery.h \ restore.c restore.h \ asr.c asr.h \ fdr.c fdr.h \ limera1n_payload.h \ limera1n.c limera1n.h \ download.c download.h \ locking.c locking.h \ socket.c socket.h \ thread.c thread.h \ jsmn.c jsmn.h \ json_plist.c json_plist.h if USE_INTERNAL_SHA1 idevicerestore_SOURCES += sha1.c sha1.h endif idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) idevicerestore_LDADD = $(AM_LDADD) idevicerestore-1.0.0/src/asr.c000066400000000000000000000262111367173617500162660ustar00rootroot00000000000000/* * asr.c * Functions for handling asr connections * * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_OPENSSL #include #else #include "sha1.h" #define SHA_CTX SHA1_CTX #define SHA1_Init SHA1Init #define SHA1_Update SHA1Update #define SHA1_Final SHA1Final #endif #include "asr.h" #include "idevicerestore.h" #include "common.h" #define ASR_VERSION 1 #define ASR_STREAM_ID 1 #define ASR_PORT 12345 #define ASR_BUFFER_SIZE 65536 #define ASR_FEC_SLICE_STRIDE 40 #define ASR_PACKETS_PER_FEC 25 #define ASR_PAYLOAD_PACKET_SIZE 1450 #define ASR_PAYLOAD_CHUNK_SIZE 131072 #define ASR_CHECKSUM_CHUNK_SIZE 131072 int asr_open_with_timeout(idevice_t device, asr_client_t* asr) { int i = 0; int attempts = 10; idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; *asr = NULL; if (device == NULL) { return -1; } debug("Connecting to ASR\n"); for (i = 1; i <= attempts; i++) { device_error = idevice_connect(device, ASR_PORT, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } if (i >= attempts) { error("ERROR: Unable to connect to ASR client\n"); return -1; } sleep(2); debug("Retrying connection...\n"); } asr_client_t asr_loc = (asr_client_t)malloc(sizeof(struct asr_client)); memset(asr_loc, '\0', sizeof(struct asr_client)); asr_loc->connection = connection; /* receive Initiate command message */ plist_t data = NULL; asr_loc->checksum_chunks = 0; if (asr_receive(asr_loc, &data) < 0) { error("ERROR: Unable to receive data from ASR\n"); asr_free(asr_loc); plist_free(data); return -1; } plist_t node; node = plist_dict_get_item(data, "Command"); if (node && (plist_get_node_type(node) == PLIST_STRING)) { char* strval = NULL; plist_get_string_val(node, &strval); if (strval && (strcmp(strval, "Initiate") != 0)) { error("ERROR: unexpected ASR plist received:\n"); debug_plist(data); plist_free(data); asr_free(asr_loc); return -1; } } node = plist_dict_get_item(data, "Checksum Chunks"); if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { plist_get_bool_val(node, &(asr_loc->checksum_chunks)); } plist_free(data); *asr = asr_loc; return 0; } void asr_set_progress_callback(asr_client_t asr, asr_progress_cb_t cbfunc, void* userdata) { if (!asr) { return; } asr->progress_cb = cbfunc; asr->progress_cb_data = userdata; } int asr_receive(asr_client_t asr, plist_t* data) { uint32_t size = 0; char* buffer = NULL; plist_t request = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; *data = NULL; buffer = (char*)malloc(ASR_BUFFER_SIZE); if (buffer == NULL) { error("ERROR: Unable to allocate memory for ASR receive buffer\n"); return -1; } device_error = idevice_connection_receive(asr->connection, buffer, ASR_BUFFER_SIZE, &size); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to receive data from ASR\n"); free(buffer); return -1; } plist_from_xml(buffer, size, &request); *data = request; debug("Received %d bytes:\n", size); if (idevicerestore_debug) debug_plist(request); free(buffer); return 0; } int asr_send(asr_client_t asr, plist_t data) { uint32_t size = 0; char* buffer = NULL; plist_to_xml(data, &buffer, &size); if (asr_send_buffer(asr, buffer, size) < 0) { error("ERROR: Unable to send plist to ASR\n"); free(buffer); return -1; } if (buffer) free(buffer); return 0; } int asr_send_buffer(asr_client_t asr, const char* data, uint32_t size) { uint32_t bytes = 0; idevice_error_t device_error = IDEVICE_E_SUCCESS; device_error = idevice_connection_send(asr->connection, data, size, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != size) { error("ERROR: Unable to send data to ASR. Sent %u of %u bytes.\n", bytes, size); return -1; } return 0; } void asr_free(asr_client_t asr) { if (asr != NULL) { if (asr->connection != NULL) { idevice_disconnect(asr->connection); asr->connection = NULL; } free(asr); asr = NULL; } } int asr_perform_validation(asr_client_t asr, const char* filesystem) { FILE* file = NULL; uint64_t length = 0; char* command = NULL; plist_t node = NULL; plist_t packet = NULL; plist_t packet_info = NULL; plist_t payload_info = NULL; int attempts = 0; file = fopen(filesystem, "rb"); if (file == NULL) { return -1; } #ifdef WIN32 length = _lseeki64(fileno(file), 0, SEEK_END); _lseeki64(fileno(file), 0, SEEK_SET); rewind(file); #else fseeko(file, 0, SEEK_END); length = ftello(file); fseeko(file, 0, SEEK_SET); #endif payload_info = plist_new_dict(); plist_dict_set_item(payload_info, "Port", plist_new_uint(1)); plist_dict_set_item(payload_info, "Size", plist_new_uint(length)); packet_info = plist_new_dict(); if (asr->checksum_chunks) { plist_dict_set_item(packet_info, "Checksum Chunk Size", plist_new_uint(ASR_CHECKSUM_CHUNK_SIZE)); } plist_dict_set_item(packet_info, "FEC Slice Stride", plist_new_uint(ASR_FEC_SLICE_STRIDE)); plist_dict_set_item(packet_info, "Packet Payload Size", plist_new_uint(ASR_PAYLOAD_PACKET_SIZE)); plist_dict_set_item(packet_info, "Packets Per FEC", plist_new_uint(ASR_PACKETS_PER_FEC)); plist_dict_set_item(packet_info, "Payload", payload_info); plist_dict_set_item(packet_info, "Stream ID", plist_new_uint(ASR_STREAM_ID)); plist_dict_set_item(packet_info, "Version", plist_new_uint(ASR_VERSION)); if (asr_send(asr, packet_info)) { error("ERROR: Unable to sent packet information to ASR\n"); plist_free(packet_info); return -1; } plist_free(packet_info); while (1) { if (asr_receive(asr, &packet) < 0) { error("ERROR: Unable to receive validation packet\n"); return -1; } if (packet == NULL) { if (attempts < 5) { info("Retrying to receive validation packet... %d\n", attempts); attempts++; sleep(1); continue; } } attempts = 0; node = plist_dict_get_item(packet, "Command"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find command node in validation request\n"); return -1; } plist_get_string_val(node, &command); if (!strcmp(command, "OOBData")) { int ret = asr_handle_oob_data_request(asr, packet, file); plist_free(packet); if (ret < 0) return ret; } else if(!strcmp(command, "Payload")) { plist_free(packet); break; } else { error("ERROR: Unknown command received from ASR\n"); plist_free(packet); return -1; } } return 0; } int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) { char* oob_data = NULL; uint64_t oob_offset = 0; uint64_t oob_length = 0; plist_t oob_length_node = NULL; plist_t oob_offset_node = NULL; oob_length_node = plist_dict_get_item(packet, "OOB Length"); if (!oob_length_node || PLIST_UINT != plist_get_node_type(oob_length_node)) { error("ERROR: Unable to find OOB data length\n"); return -1; } plist_get_uint_val(oob_length_node, &oob_length); oob_offset_node = plist_dict_get_item(packet, "OOB Offset"); if (!oob_offset_node || PLIST_UINT != plist_get_node_type(oob_offset_node)) { error("ERROR: Unable to find OOB data offset\n"); return -1; } plist_get_uint_val(oob_offset_node, &oob_offset); oob_data = (char*) malloc(oob_length); if (oob_data == NULL) { error("ERROR: Out of memory\n"); return -1; } #ifdef WIN32 rewind(file); _lseeki64(fileno(file), oob_offset, SEEK_SET); #else fseeko(file, oob_offset, SEEK_SET); #endif if (fread(oob_data, 1, oob_length, file) != oob_length) { error("ERROR: Unable to read OOB data from filesystem offset: %s\n", strerror(errno)); free(oob_data); return -1; } if (asr_send_buffer(asr, oob_data, oob_length) < 0) { error("ERROR: Unable to send OOB data to ASR\n"); free(oob_data); return -1; } free(oob_data); return 0; } int asr_send_payload(asr_client_t asr, const char* filesystem) { char *data = NULL; FILE* file = NULL; uint64_t i, length, bytes = 0; double progress = 0; file = fopen(filesystem, "rb"); if (file == NULL) { error("ERROR: Unable to open filesystem image %s: %s\n", filesystem, strerror(errno)); return -1; } #ifdef WIN32 length = _lseeki64(fileno(file), 0, SEEK_END); _lseeki64(fileno(file), 0, SEEK_SET); rewind(file); #else fseeko(file, 0, SEEK_END); length = ftello(file); fseeko(file, 0, SEEK_SET); #endif int chunk = 0; int add_checksum = 0; data = (char*)malloc(ASR_PAYLOAD_CHUNK_SIZE); SHA_CTX sha1; if (asr->checksum_chunks) { SHA1_Init(&sha1); } int size = 0; for (i = length; i > 0; i -= size) { size = ASR_PAYLOAD_CHUNK_SIZE; if (i < ASR_PAYLOAD_CHUNK_SIZE) { size = i; } if (add_checksum) { add_checksum = 0; } if (asr->checksum_chunks && ((chunk + size) >= ASR_CHECKSUM_CHUNK_SIZE)) { // reduce packet size to match checksum chunk size size -= ((chunk + size) - ASR_CHECKSUM_CHUNK_SIZE); add_checksum = 1; } if (fread(data, 1, size, file) != (size_t)size) { error("Error reading filesystem\n"); free(data); fclose(file); return -1; } if (asr_send_buffer(asr, data, size) < 0) { error("ERROR: Unable to send filesystem payload\n"); free(data); fclose(file); return -1; } if (asr->checksum_chunks) { SHA1_Update(&sha1, (unsigned char*)data, size); chunk += size; if (add_checksum) { // get sha1 of last chunk SHA1_Final((unsigned char*)data, &sha1); // send checksum if (asr_send_buffer(asr, data, 20) < 0) { error("ERROR: Unable to send chunk checksum\n"); free(data); fclose(file); return -1; } // reset SHA1 context SHA1_Init(&sha1); // reset chunk byte counter chunk = 0; } } bytes += size; progress = ((double)bytes / (double)length); if (asr->progress_cb && ((int)(progress*100) > asr->lastprogress)) { asr->progress_cb(progress, asr->progress_cb_data); asr->lastprogress = (int)(progress*100); } } // if last chunk wasn't terminated with a checksum we do it here if (asr->checksum_chunks && !add_checksum) { // get sha1 of last chunk SHA1_Final((unsigned char*)data, &sha1); // send checksum if (asr_send_buffer(asr, data, 20) < 0) { error("ERROR: Unable to send chunk checksum\n"); free(data); fclose(file); return -1; } } free(data); fclose(file); return 0; } idevicerestore-1.0.0/src/asr.h000066400000000000000000000036741367173617500163030ustar00rootroot00000000000000/* * asr.h * Functions for handling asr connections * * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_ASR_H #define IDEVICERESTORE_ASR_H #ifdef __cplusplus extern "C" { #endif #include typedef void (*asr_progress_cb_t)(double, void*); struct asr_client { idevice_connection_t connection; uint8_t checksum_chunks; int lastprogress; asr_progress_cb_t progress_cb; void* progress_cb_data; }; typedef struct asr_client *asr_client_t; int asr_open_with_timeout(idevice_t device, asr_client_t* asr); void asr_set_progress_callback(asr_client_t asr, asr_progress_cb_t, void* userdata); int asr_send(asr_client_t asr, plist_t data); int asr_receive(asr_client_t asr, plist_t* data); int asr_send_buffer(asr_client_t asr, const char* data, uint32_t size); void asr_free(asr_client_t asr); int asr_perform_validation(asr_client_t asr, const char* filesystem); int asr_send_payload(asr_client_t asr, const char* filesystem); int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/common.c000066400000000000000000000343071367173617500167760ustar00rootroot00000000000000/* * common.c * Misc functions used in idevicerestore * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #include #ifndef _O_EXCL #define _O_EXCL 0x0400 #endif #ifndef O_EXCL #define O_EXCL _O_EXCL #endif #else #include #include #include #endif #include "common.h" #include "endianness.h" #define MAX_PRINT_LEN 64*1024 struct idevicerestore_mode_t idevicerestore_modes[] = { { 0, "WTF" }, { 1, "DFU" }, { 2, "Recovery" }, { 3, "Restore" }, { 4, "Normal" }, { -1, NULL } }; int idevicerestore_debug = 0; #define idevicerestore_err_buff_size 256 static char idevicerestore_err_buff[idevicerestore_err_buff_size] = {0, }; static FILE* info_stream = NULL; static FILE* error_stream = NULL; static FILE* debug_stream = NULL; static int info_disabled = 0; static int error_disabled = 0; static int debug_disabled = 0; void info(const char* format, ...) { if (info_disabled) return; va_list vargs; va_start(vargs, format); vfprintf((info_stream) ? info_stream : stdout, format, vargs); va_end(vargs); } void error(const char* format, ...) { va_list vargs, vargs2; va_start(vargs, format); va_copy(vargs2, vargs); vsnprintf(idevicerestore_err_buff, idevicerestore_err_buff_size, format, vargs); va_end(vargs); if (!error_disabled) { vfprintf((error_stream) ? error_stream : stderr, format, vargs2); } va_end(vargs2); } void debug(const char* format, ...) { if (debug_disabled) return; if (!idevicerestore_debug) { return; } va_list vargs; va_start(vargs, format); vfprintf((debug_stream) ? debug_stream : stderr, format, vargs); va_end(vargs); } void idevicerestore_set_info_stream(FILE* strm) { if (strm) { info_disabled = 0; info_stream = strm; } else { info_disabled = 1; } } void idevicerestore_set_error_stream(FILE* strm) { if (strm) { error_disabled = 0; error_stream = strm; } else { error_disabled = 1; } } void idevicerestore_set_debug_stream(FILE* strm) { if (strm) { debug_disabled = 0; debug_stream = strm; } else { debug_disabled = 1; } } const char* idevicerestore_get_error(void) { if (idevicerestore_err_buff[0] == 0) { return NULL; } else { char* p = NULL; while ((strlen(idevicerestore_err_buff) > 0) && (p = strrchr(idevicerestore_err_buff, '\n'))) { p[0] = '\0'; } return (const char*)idevicerestore_err_buff; } } int write_file(const char* filename, const void* data, size_t size) { size_t bytes = 0; FILE* file = NULL; debug("Writing data to %s\n", filename); file = fopen(filename, "wb"); if (file == NULL) { error("write_file: Unable to open file %s\n", filename); return -1; } bytes = fwrite(data, 1, size, file); fclose(file); if (bytes != size) { error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, (int)bytes, (int)size); return -1; } return size; } int read_file(const char* filename, void** data, size_t* size) { size_t bytes = 0; size_t length = 0; FILE* file = NULL; char* buffer = NULL; struct stat fst; debug("Reading data from %s\n", filename); *size = 0; *data = NULL; file = fopen(filename, "rb"); if (file == NULL) { error("read_file: cannot open %s: %s\n", filename, strerror(errno)); return -1; } if (fstat(fileno(file), &fst) < 0) { error("read_file: fstat: %s\n", strerror(errno)); return -1; } length = fst.st_size; buffer = (char*) malloc(length); if (buffer == NULL) { error("ERROR: Out of memory\n"); fclose(file); return -1; } bytes = fread(buffer, 1, length, file); fclose(file); if (bytes != length) { error("ERROR: Unable to read entire file\n"); free(buffer); return -1; } *size = length; *data = buffer; return 0; } void debug_plist(plist_t plist) { uint32_t size = 0; char* data = NULL; plist_to_xml(plist, &data, &size); if (size <= MAX_PRINT_LEN) info("%s:printing %i bytes plist:\n%s", __FILE__, size, data); else info("%s:supressed printing %i bytes plist...\n", __FILE__, size); free(data); } void print_progress_bar(double progress) { #ifndef WIN32 if (info_disabled) return; int i = 0; if(progress < 0) return; if(progress > 100) progress = 100; info("\r["); for(i = 0; i < 50; i++) { if(i < progress / 2) info("="); else info(" "); } info("] %5.1f%%", progress); if(progress >= 100) info("\n"); fflush((info_stream) ? info_stream : stdout); #endif } #define GET_RAND(min, max) ((rand() % (max - min)) + min) char *generate_guid(void) { char *guid = (char *) malloc(sizeof(char) * 37); const char *chars = "ABCDEF0123456789"; srand(time(NULL)); int i = 0; for (i = 0; i < 36; i++) { if (i == 8 || i == 13 || i == 18 || i == 23) { guid[i] = '-'; continue; } else { guid[i] = chars[GET_RAND(0, 16)]; } } guid[36] = '\0'; return guid; } int mkdir_with_parents(const char *dir, int mode) { if (!dir) return -1; if (__mkdir(dir, mode) == 0) { return 0; } else { if (errno == EEXIST) { return 0; } else if (errno == ENOENT) { // ignore } else { return -1; } } int res; char *parent = strdup(dir); char *parentdir = dirname(parent); if (parentdir && (strcmp(parentdir, ".") != 0) && (strcmp(parentdir, dir) != 0)) { res = mkdir_with_parents(parentdir, mode); } else { res = -1; } free(parent); if (res == 0) { mkdir_with_parents(dir, mode); } return res; } #ifndef HAVE_MKSTEMP /* Based on libc's __gen_tempname() from sysdeps/posix/tempname.c Copyright (C) 1991-2018 Free Software Foundation, Inc. With changes from https://stackoverflow.com/a/6036308 and some additional changes. */ int mkstemp(char *tmpl) { static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; int len; char *XXXXXX; static unsigned long long value; unsigned long long random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6. It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3) fail to give the system administrator the chance to remove the problems. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { errno = EINVAL; return -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ #ifdef WIN32 { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32) | (unsigned long long)ftNow.dwLowDateTime); } value += random_time_bits ^ ((unsigned long long)GetCurrentProcessId() << 32 | (unsigned long long)GetCurrentThreadId()); #else { struct timeval tvNow = {0, 0}; gettimeofday(&tvNow, NULL); random_time_bits = (((unsigned long long)tvNow.tv_sec << 32) | (unsigned long long)tvNow.tv_usec); } value += random_time_bits ^ ((unsigned long long)getpid() << 32 | (unsigned long long)(uintptr_t)pthread_self()); #endif for (count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; #ifdef WIN32 fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE); #else fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); #endif if (fd >= 0) { errno = save_errno; return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } #endif char *get_temp_filename(const char *prefix) { char *result = NULL; char *tmpdir; size_t lt; size_t lp; const char *TMPVARS[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL }; int i = 0; int fd; /* check the prefix parameter */ if (!prefix) { prefix = "tmp_"; } #ifdef WIN32 if (strchr(prefix, '/') || strchr(prefix, '\\')) return NULL; #else if (strchr(prefix, '/')) return NULL; #endif while (TMPVARS[i] && ((tmpdir = getenv(TMPVARS[i])) == NULL)) i++; if (!tmpdir || access(tmpdir, W_OK|X_OK) != 0) { #ifdef WIN32 tmpdir = "C:\\WINDOWS\\TEMP"; #else tmpdir = P_tmpdir; #endif } if (!tmpdir || access(tmpdir, W_OK|X_OK) != 0) { return NULL; } lt = strlen(tmpdir); if (lt < 1) { return NULL; } lp = strlen(prefix); result = malloc(lt + lp + 8); memcpy(result, tmpdir, lt); #ifdef WIN32 if (tmpdir[lt-1] != '/' && tmpdir[lt-1] != '\\') result[lt++] = '\\'; #else if (tmpdir[lt-1] != '/') result[lt++] = '/'; #endif strncpy(result + lt, prefix, lp); strcpy(result + lt + lp, "XXXXXX"); fd = mkstemp(result); if (fd < 0) { free(result); result = NULL; } close(fd); return result; } void idevicerestore_progress(struct idevicerestore_client_t* client, int step, double progress) { if(client && client->progress_cb) { client->progress_cb(step, progress, client->progress_cb_data); } else { // we don't want to be too verbose in regular idevicerestore. if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW)) { print_progress_bar(100.0 * progress); } } } #ifndef HAVE_STRSEP char* strsep(char** strp, const char* delim) { char *p, *s; if (strp == NULL || *strp == NULL || **strp == '\0') return NULL; s = *strp; p = s + strcspn(s, delim); if (*p != '\0') *p++ = '\0'; *strp = p; return s; } #endif #ifndef HAVE_REALPATH char* realpath(const char *filename, char *resolved_name) { #ifdef WIN32 if (access(filename, F_OK) != 0) { return NULL; } if (GetFullPathName(filename, MAX_PATH, resolved_name, NULL) == 0) { return NULL; } return resolved_name; #else #error please provide a realpath implementation for this platform return NULL; #endif } #endif #ifdef WIN32 #define BS_CC '\b' #define CTRL_C_CC 0x03 #define ESC_CC 0x1B #define my_getch _getch #else #define BS_CC 0x7f static int my_getch(void) { struct termios oldt, newt; int ch; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); return ch; } #endif void get_user_input(char *buf, int maxlen, int secure) { int len = 0; int c; while ((c = my_getch()) > 0) { if ((c == '\r') || (c == '\n')) { break; } else if (isprint(c)) { if (len < maxlen-1) buf[len++] = c; fputc((secure) ? '*' : c, stdout); } else if (c == BS_CC) { if (len > 0) { fputs("\b \b", stdout); len--; } } #ifdef WIN32 else if (c == CTRL_C_CC || c == ESC_CC) { c = -1; break; } #endif } if (c < 0) { len = 0; } fputs("\n", stdout); buf[len] = 0; } uint64_t _plist_dict_get_uint(plist_t dict, const char *key) { uint64_t uintval = 0; char *strval = NULL; uint64_t strsz = 0; plist_t node = plist_dict_get_item(dict, key); if (!node) { return (uint64_t)-1LL; } switch (plist_get_node_type(node)) { case PLIST_UINT: plist_get_uint_val(node, &uintval); break; case PLIST_STRING: plist_get_string_val(node, &strval); if (strval) { uintval = strtoull(strval, NULL, 0); free(strval); } break; case PLIST_DATA: plist_get_data_val(node, &strval, &strsz); if (strval) { if (strsz == 8) { uintval = le64toh(*(uint64_t*)strval); } else if (strsz == 4) { uintval = le32toh(*(uint32_t*)strval); } else if (strsz == 2) { uintval = le16toh(*(uint16_t*)strval); } else if (strsz == 1) { uintval = strval[0]; } else { error("%s: ERROR: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); } free(strval); } break; default: break; } return uintval; } uint8_t _plist_dict_get_bool(plist_t dict, const char *key) { uint8_t bval = 0; uint64_t uintval = 0; char *strval = NULL; uint64_t strsz = 0; plist_t node = plist_dict_get_item(dict, key); if (!node) { return 0; } switch (plist_get_node_type(node)) { case PLIST_BOOLEAN: plist_get_bool_val(node, &bval); break; case PLIST_UINT: plist_get_uint_val(node, &uintval); bval = (uint8_t)uintval; break; case PLIST_STRING: plist_get_string_val(node, &strval); if (strval) { if (strcmp(strval, "true")) { bval = 1; } else if (strcmp(strval, "false")) { bval = 0; } free(strval); } break; case PLIST_DATA: plist_get_data_val(node, &strval, &strsz); if (strval) { if (strsz == 1) { bval = strval[0]; } else { error("%s: ERROR: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); } free(strval); } break; default: break; } return bval; } idevicerestore-1.0.0/src/common.h000066400000000000000000000104371367173617500170010ustar00rootroot00000000000000/* * common.h * Misc functions used in idevicerestore * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_COMMON_H #define IDEVICERESTORE_COMMON_H #ifdef __cplusplus extern "C" { #endif #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "idevicerestore.h" #include "thread.h" #define MODE_UNKNOWN -1 #define MODE_WTF 0 #define MODE_DFU 1 #define MODE_RECOVERY 2 #define MODE_RESTORE 3 #define MODE_NORMAL 4 #define FLAG_QUIT 1 #define CPFM_FLAG_SECURITY_MODE 1 << 0 #define CPFM_FLAG_PRODUCTION_MODE 1 << 1 #define IBOOT_FLAG_IMAGE4_AWARE 1 << 2 #define IBOOT_FLAG_EFFECTIVE_SECURITY_MODE 1 << 3 #define IBOOT_FLAG_EFFECTIVE_PRODUCTION_MODE 1 << 4 #define USER_AGENT_STRING "InetURL/1.0" struct dfu_client_t; struct normal_client_t; struct restore_client_t; struct recovery_client_t; struct idevicerestore_mode_t { int index; const char* string; }; struct idevicerestore_entry_t { char* name; char* path; char* filename; char* blob_data; uint32_t blob_size; struct idevicerestore_entry* next; struct idevicerestore_entry* prev; }; struct idevicerestore_client_t { int flags; plist_t tss; char* tss_url; plist_t version_data; uint64_t ecid; unsigned char* nonce; int nonce_size; int image4supported; plist_t preflight_info; char* udid; char* srnm; char* ipsw; const char* filesystem; struct dfu_client_t* dfu; struct restore_client_t* restore; struct recovery_client_t* recovery; irecv_device_t device; struct idevicerestore_entry_t** entries; struct idevicerestore_mode_t* mode; char* version; char* build; int build_major; char* restore_boot_args; char* cache_dir; unsigned char* root_ticket; int root_ticket_len; idevicerestore_progress_cb_t progress_cb; void* progress_cb_data; irecv_device_event_context_t irecv_e_ctx; void* idevice_e_ctx; mutex_t device_event_mutex; cond_t device_event_cond; int ignore_device_add_events; }; extern struct idevicerestore_mode_t idevicerestore_modes[]; extern int idevicerestore_debug; __attribute__((format(printf, 1, 2))) void info(const char* format, ...); __attribute__((format(printf, 1, 2))) void error(const char* format, ...); __attribute__((format(printf, 1, 2))) void debug(const char* format, ...); void debug_plist(plist_t plist); void print_progress_bar(double progress); int read_file(const char* filename, void** data, size_t* size); int write_file(const char* filename, const void* data, size_t size); char *generate_guid(void); #ifdef WIN32 #include #include #define __mkdir(path, mode) mkdir(path) #ifndef sleep #define sleep(x) Sleep(x*1000) #endif #define __usleep(x) Sleep(x/1000) #else #include #define __mkdir(path, mode) mkdir(path, mode) #define __usleep(x) usleep(x) #endif int mkdir_with_parents(const char *dir, int mode); char *get_temp_filename(const char *prefix); void idevicerestore_progress(struct idevicerestore_client_t* client, int step, double progress); #ifndef HAVE_STRSEP char* strsep(char** strp, const char* delim); #endif #ifndef HAVE_REALPATH char* realpath(const char *filename, char *resolved_name); #endif void get_user_input(char *buf, int maxlen, int secure); uint8_t _plist_dict_get_bool(plist_t dict, const char *key); uint64_t _plist_dict_get_uint(plist_t dict, const char *key); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/dfu.c000066400000000000000000000315441367173617500162640ustar00rootroot00000000000000/* * dfu.c * Functions for handling idevices in DFU mode * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2013 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "dfu.h" #include "tss.h" #include "recovery.h" #include "idevicerestore.h" #include "common.h" static int dfu_progress_callback(irecv_client_t client, const irecv_event_t* event) { if (event->type == IRECV_PROGRESS) { print_progress_bar(event->progress); } return 0; } int dfu_client_new(struct idevicerestore_client_t* client) { int i = 0; int attempts = 10; irecv_client_t dfu = NULL; if (client->dfu == NULL) { client->dfu = (struct dfu_client_t*)malloc(sizeof(struct dfu_client_t)); memset(client->dfu, 0, sizeof(struct dfu_client_t)); if (client->dfu == NULL) { error("ERROR: Out of memory\n"); return -1; } } for (i = 1; i <= attempts; i++) { if (irecv_open_with_ecid(&dfu, client->ecid) == IRECV_E_SUCCESS) { break; } if (i >= attempts) { error("ERROR: Unable to connect to device in DFU mode\n"); return -1; } sleep(1); debug("Retrying connection...\n"); } irecv_event_subscribe(dfu, IRECV_PROGRESS, &dfu_progress_callback, NULL); client->dfu->client = dfu; return 0; } void dfu_client_free(struct idevicerestore_client_t* client) { if(client != NULL) { if (client->dfu != NULL) { if(client->dfu->client != NULL) { irecv_close(client->dfu->client); client->dfu->client = NULL; } free(client->dfu); } client->dfu = NULL; } } int dfu_check_mode(struct idevicerestore_client_t* client, int* mode) { irecv_client_t dfu = NULL; int probe_mode = -1; if (client->udid && client->ecid == 0) { /* if we have a UDID but no ECID we can't make sure this is the correct device */ return -1; } irecv_init(); if (irecv_open_with_ecid(&dfu, client->ecid) != IRECV_E_SUCCESS) { return -1; } irecv_get_mode(dfu, &probe_mode); if ((probe_mode != IRECV_K_DFU_MODE) && (probe_mode != IRECV_K_WTF_MODE)) { irecv_close(dfu); return -1; } *mode = (probe_mode == IRECV_K_WTF_MODE) ? MODE_WTF : MODE_DFU; irecv_close(dfu); return 0; } irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) { irecv_client_t dfu = NULL; irecv_error_t dfu_error = IRECV_E_SUCCESS; irecv_device_t device = NULL; irecv_init(); if (irecv_open_with_ecid(&dfu, client->ecid) != IRECV_E_SUCCESS) { return NULL; } dfu_error = irecv_devices_get_device_by_client(dfu, &device); irecv_close(dfu); if (dfu_error != IRECV_E_SUCCESS) { return NULL; } return device; } int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) { irecv_error_t err = 0; info("Sending data (%d bytes)...\n", size); err = irecv_send_buffer(client->dfu->client, buffer, size, 1); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send data: %s\n", irecv_strerror(err)); return -1; } return 0; } int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { char* path = NULL; if (client->tss) { if (tss_response_get_path_by_entry(client->tss, component, &path) < 0) { debug("NOTE: No path for component %s in TSS, will fetch from build_identity\n", component); } } if (!path) { if (build_identity_get_component_path(build_identity, component, &path) < 0) { error("ERROR: Unable to get path for component '%s'\n", component); free(path); return -1; } } unsigned char* component_data = NULL; unsigned int component_size = 0; if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { error("ERROR: Unable to extract component: %s\n", component); free(path); return -1; } free(path); path = NULL; unsigned char* data = NULL; uint32_t size = 0; if (personalize_component(component, component_data, component_size, client->tss, &data, &size) < 0) { error("ERROR: Unable to get personalized component: %s\n", component); free(component_data); return -1; } free(component_data); component_data = NULL; if (!client->image4supported && client->build_major > 8 && !(client->flags & FLAG_CUSTOM) && !strcmp(component, "iBEC")) { unsigned char* ticket = NULL; unsigned int tsize = 0; if (tss_response_get_ap_ticket(client->tss, &ticket, &tsize) < 0) { error("ERROR: Unable to get ApTicket from TSS request\n"); return -1; } uint32_t fillsize = 0; if (tsize % 64 != 0) { fillsize = ((tsize / 64) + 1) * 64; } debug("ticket size = %d\nfillsize = %d\n", tsize, fillsize); unsigned char* newdata = (unsigned char*)malloc(size + fillsize); memcpy(newdata, ticket, tsize); memset(newdata + tsize, '\xFF', fillsize - tsize); memcpy(newdata + fillsize, data, size); free(data); data = newdata; size += fillsize; } info("Sending %s (%d bytes)...\n", component, size); // FIXME: Did I do this right???? irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, 1); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(err)); free(data); return -1; } free(data); return 0; } int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); if (!device_info) { return -1; } *cpid = device_info->cpid; return 0; } int dfu_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); if (!device_info) { return -1; } *ecid = device_info->ecid; return 0; } int dfu_is_image4_supported(struct idevicerestore_client_t* client) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { return 0; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); if (!device_info) { return 0; } return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); if (!device_info) { return -1; } if (device_info->ap_nonce && device_info->ap_nonce_size > 0) { *nonce = (unsigned char*)malloc(device_info->ap_nonce_size); if (!*nonce) { return -1; } *nonce_size = device_info->ap_nonce_size; memcpy(*nonce, device_info->ap_nonce, *nonce_size); } return 0; } int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); if (!device_info) { return -1; } if (device_info->sep_nonce && device_info->sep_nonce_size > 0) { *nonce = (unsigned char*)malloc(device_info->sep_nonce_size); if (!*nonce) { return -1; } *nonce_size = device_info->sep_nonce_size; memcpy(*nonce, device_info->sep_nonce, *nonce_size); } return 0; } int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity) { int mode = 0; if (dfu_client_new(client) < 0) { error("ERROR: Unable to connect to DFU device\n"); return -1; } 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]; return 0; } mutex_lock(&client->device_event_mutex); if (dfu_send_component(client, build_identity, "iBSS") < 0) { error("ERROR: Unable to send iBSS to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } dfu_client_free(client); if (client->build_major > 8) { /* reconnect */ debug("Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_UNKNOWN] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Possibly invalid iBSS. Reset device and try again.\n"); } return -1; } debug("Waiting for device to reconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if ((client->mode != &idevicerestore_modes[MODE_DFU] && client->mode != &idevicerestore_modes[MODE_RECOVERY]) || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in DFU or recovery mode. Possibly invalid iBSS. Reset device and try again.\n"); } return -1; } mutex_unlock(&client->device_event_mutex); dfu_client_new(client); /* get nonce */ unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (dfu_get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get ApNonce from device!\n"); return -1; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } info("Nonce: "); int i; for (i = 0; i < client->nonce_size; i++) { info("%02x ", client->nonce[i]); } info("\n"); if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); return -1; } fixup_tss(client->tss); } if (irecv_usb_set_configuration(client->dfu->client, 1) < 0) { error("ERROR: set configuration failed\n"); } mutex_lock(&client->device_event_mutex); /* send iBEC */ if (dfu_send_component(client, build_identity, "iBEC") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBEC to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (client->mode == &idevicerestore_modes[MODE_RECOVERY]) { if (irecv_send_command(client->dfu->client, "go") != IRECV_E_SUCCESS) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to execute iBEC\n"); return -1; } irecv_usb_control_transfer(client->dfu->client, 0x21, 1, 0, 0, 0, 0, 5000); } dfu_client_free(client); } debug("Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_UNKNOWN] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Possibly invalid %s. Reset device and try again.\n", (client->build_major > 8) ? "iBEC" : "iBSS"); } return -1; } debug("Waiting for device to reconnect in recovery mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_RECOVERY] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in recovery mode. Possibly invalid %s. Reset device and try again.\n", (client->build_major > 8) ? "iBEC" : "iBSS"); } return -1; } mutex_unlock(&client->device_event_mutex); if (recovery_client_new(client) < 0) { error("ERROR: Unable to connect to recovery device\n"); if (client->recovery->client) { irecv_close(client->recovery->client); client->recovery->client = NULL; } return -1; } return 0; } idevicerestore-1.0.0/src/dfu.h000066400000000000000000000043241367173617500162650ustar00rootroot00000000000000/* * dfu.h * Functions for handling idevices in DFU mode * * Copyright (c) 2010-2013 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012-2015 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_DFU_H #define IDEVICERESTORE_DFU_H #ifdef __cplusplus extern "C" { #endif #include #include "common.h" struct dfu_client_t { irecv_client_t client; const char* ipsw; plist_t tss; }; int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); int dfu_check_mode(struct idevicerestore_client_t* client, int* mode); irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client); int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); int dfu_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int dfu_is_image4_supported(struct idevicerestore_client_t* client); int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/download.c000066400000000000000000000104171367173617500173110ustar00rootroot00000000000000/* * download.c * file download helper functions * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012-2013 Martin Szulecki. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "download.h" #include "common.h" typedef struct { int length; char* content; } curl_response; static size_t download_write_buffer_callback(char* data, size_t size, size_t nmemb, curl_response* response) { size_t total = size * nmemb; if (total != 0) { response->content = realloc(response->content, response->length + total + 1); memcpy(response->content + response->length, data, total); response->content[response->length + total] = '\0'; response->length += total; } return total; } int download_to_buffer(const char* url, char** buf, uint32_t* length) { int res = 0; CURL* handle = curl_easy_init(); if (handle == NULL) { error("ERROR: could not initialize CURL\n"); return -1; } curl_response response; response.length = 0; response.content = malloc(1); response.content[0] = '\0'; if (idevicerestore_debug) curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&download_write_buffer_callback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &response); if (strncmp(url, "https://api.ipsw.me/", 20) == 0) { curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING " idevicerestore/" PACKAGE_VERSION); } else { curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING); } curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(handle, CURLOPT_URL, url); curl_easy_perform(handle); curl_easy_cleanup(handle); if (response.length > 0) { *length = response.length; *buf = response.content; } else { res = -1; } return res; } static int lastprogress = 0; static int download_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { double p = (dlnow / dltotal) * 100; if (p < 100.0) { if ((int)p > lastprogress) { info("downloading: %d%%\n", (int)p); lastprogress = (int)p; } } return 0; } int download_to_file(const char* url, const char* filename, int enable_progress) { int res = 0; CURL* handle = curl_easy_init(); if (handle == NULL) { error("ERROR: could not initialize CURL\n"); return -1; } FILE* f = fopen(filename, "wb"); if (!f) { error("ERROR: cannot open '%s' for writing\n", filename); return -1; } lastprogress = 0; if (idevicerestore_debug) curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(handle, CURLOPT_WRITEDATA, f); if (enable_progress > 0) curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, (curl_progress_callback)&download_progress); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, enable_progress > 0 ? 0: 1); curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING); curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(handle, CURLOPT_URL, url); curl_easy_perform(handle); curl_easy_cleanup(handle); #ifdef WIN32 fflush(f); uint64_t sz = _lseeki64(fileno(f), 0, SEEK_CUR); #else off_t sz = ftello(f); #endif fclose(f); if ((sz == 0) || ((int64_t)sz == (int64_t)-1)) { res = -1; remove(filename); } return res; } idevicerestore-1.0.0/src/download.h000066400000000000000000000023461367173617500173200ustar00rootroot00000000000000/* * download.h * file download helper functions (header file) * * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_DOWNLOAD_H #define IDEVICERESTORE_DOWNLOAD_H #ifdef __cplusplus extern "C" { #endif #include int download_to_buffer(const char* url, char** buf, uint32_t* length); int download_to_file(const char* url, const char* filename, int enable_progress); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/endianness.h000066400000000000000000000041121367173617500176310ustar00rootroot00000000000000#ifndef ENDIANNESS_H #define ENDIANNESS_H #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN 1234 #endif #ifndef __BIG_ENDIAN #define __BIG_ENDIAN 4321 #endif #ifndef __BYTE_ORDER #ifdef __LITTLE_ENDIAN__ #define __BYTE_ORDER __LITTLE_ENDIAN #else #ifdef __BIG_ENDIAN__ #define __BYTE_ORDER __BIG_ENDIAN #endif #endif #endif #ifndef be16toh #if __BYTE_ORDER == __BIG_ENDIAN #define be16toh(x) (x) #else #define be16toh(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) #endif #endif #ifndef le16toh #if __BYTE_ORDER == __BIG_ENDIAN #define le16toh(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) #else #define le16toh(x) (x) #endif #endif #ifndef __bswap_32 #define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \ | (((x) & 0x00FF0000) >> 8) \ | (((x) & 0x0000FF00) << 8) \ | (((x) & 0x000000FF) << 24)) #endif #ifndef be32toh #if __BYTE_ORDER == __BIG_ENDIAN #define be32toh(x) (x) #else #define be32toh(x) __bswap_32(x) #endif #endif #ifndef htobe32 #define htobe32 be32toh #endif #ifndef le32toh #if __BYTE_ORDER == __BIG_ENDIAN #define le32toh(x) __bswap_32(x) #else #define le32toh(x) (x) #endif #endif #ifndef htole32 #define htole32 le32toh #endif #ifndef __bswap_64 #define __bswap_64(x) ((((x) & 0xFF00000000000000ull) >> 56) \ | (((x) & 0x00FF000000000000ull) >> 40) \ | (((x) & 0x0000FF0000000000ull) >> 24) \ | (((x) & 0x000000FF00000000ull) >> 8) \ | (((x) & 0x00000000FF000000ull) << 8) \ | (((x) & 0x0000000000FF0000ull) << 24) \ | (((x) & 0x000000000000FF00ull) << 40) \ | (((x) & 0x00000000000000FFull) << 56)) #endif #ifndef htobe64 #if __BYTE_ORDER == __BIG_ENDIAN #define htobe64(x) (x) #else #define htobe64(x) __bswap_64(x) #endif #endif #ifndef be64toh #define be64toh htobe64 #endif #ifndef le64toh #if __BYTE_ORDER == __LITTLE_ENDIAN #define le64toh(x) (x) #else #define le64toh(x) __bswap_64(x) #endif #endif #ifndef htole64 #define htole64 le64toh #endif #endif /* ENDIANNESS_H */ idevicerestore-1.0.0/src/fdr.c000066400000000000000000000407221367173617500162570ustar00rootroot00000000000000/* * fdr.c * Connection proxy service used by FDR * * Copyright (c) 2014 BALATON Zoltan. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "socket.h" /* from libimobiledevice/common */ #include "common.h" #include "idevicerestore.h" #include "fdr.h" #include /* from libimobiledevice */ #define CTRL_PORT 0x43a /*1082*/ #define CTRLCMD "BeginCtrl" #define HELLOCTRLCMD "HelloCtrl" #define HELLOCMD "HelloConn" #define FDR_SYNC_MSG 0x1 #define FDR_PROXY_MSG 0x105 #define FDR_PLIST_MSG 0xbbaa static uint64_t conn_port; static int ctrlprotoversion = 2; static int serial; static int fdr_receive_plist(fdr_client_t fdr, plist_t* data); static int fdr_send_plist(fdr_client_t fdr, plist_t data); static int fdr_ctrl_handshake(fdr_client_t fdr); static int fdr_sync_handshake(fdr_client_t fdr); static int fdr_handle_sync_cmd(fdr_client_t fdr); static int fdr_handle_plist_cmd(fdr_client_t fdr); static int fdr_handle_proxy_cmd(fdr_client_t fdr); int fdr_connect(idevice_t device, fdr_type_t type, fdr_client_t* fdr) { int res = -1, i = 0; int attempts = 10; idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; uint16_t port = (type == FDR_CONN ? conn_port : CTRL_PORT); *fdr = NULL; debug("Connecting to FDR client at port %u\n", port); for (i = 1; i <= attempts; i++) { device_error = idevice_connect(device, port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } if (i >= attempts) { error("ERROR: Unable to connect to FDR client (%d)\n", device_error); return -1; } sleep(2); debug("Retrying connection...\n"); } fdr_client_t fdr_loc = calloc(1, sizeof(struct fdr_client)); if (!fdr_loc) { error("ERROR: Unable to allocate memory\n"); return -1; } fdr_loc->connection = connection; fdr_loc->device = device; fdr_loc->type = type; /* Do handshake */ if (type == FDR_CTRL) res = fdr_ctrl_handshake(fdr_loc); else if (type == FDR_CONN) res = fdr_sync_handshake(fdr_loc); if (res) { fdr_free(fdr_loc); return -1; } *fdr = fdr_loc; return 0; } void fdr_disconnect(fdr_client_t fdr) { if (!fdr) return; if (fdr->connection) { idevice_connection_t conn = fdr->connection; fdr->connection = NULL; idevice_disconnect(conn); } } void fdr_free(fdr_client_t fdr) { if (!fdr) return; fdr_disconnect(fdr); free(fdr); fdr = NULL; } int fdr_poll_and_handle_message(fdr_client_t fdr) { idevice_error_t device_error = IDEVICE_E_SUCCESS; uint32_t bytes = 0; uint16_t cmd; if (!fdr) { error("ERROR: Invalid FDR client\n"); return -1; } device_error = idevice_connection_receive_timeout(fdr->connection, (char *)&cmd, sizeof(cmd), &bytes, 20000); #ifdef HAVE_IDEVICE_E_TIMEOUT if (device_error == IDEVICE_E_TIMEOUT || (device_error == IDEVICE_E_SUCCESS && bytes != sizeof(cmd))) #else if (device_error == IDEVICE_E_SUCCESS && bytes != sizeof(cmd)) #endif { debug("FDR %p timeout waiting for command\n", fdr); return 0; } else if (device_error != IDEVICE_E_SUCCESS) { if (fdr->connection) { error("ERROR: Unable to receive message from FDR %p (%d). %u/%u bytes\n", fdr, device_error, bytes, (uint32_t)sizeof(cmd)); } return -1; } if (cmd == FDR_SYNC_MSG) { debug("FDR %p got sync message\n", fdr); return fdr_handle_sync_cmd(fdr); } if (cmd == FDR_PROXY_MSG) { debug("FDR %p got proxy message\n", fdr); return fdr_handle_proxy_cmd(fdr); } if (cmd == FDR_PLIST_MSG) { debug("FDR %p got plist message\n", fdr); return fdr_handle_plist_cmd(fdr); } error("WARNING: FDR %p received unknown packet %#x of size %u\n", fdr, cmd, bytes); return 0; } void *fdr_listener_thread(void *cdata) { fdr_client_t fdr = cdata; int res; while (fdr && fdr->connection) { debug("FDR %p waiting for message...\n", fdr); res = fdr_poll_and_handle_message(fdr); if (fdr->type == FDR_CTRL && res >= 0) continue; // main thread should always retry if (res != 0) break; } debug("FDR %p terminating...\n", fdr); fdr_free(fdr); return (void *)(intptr_t)res; } static int fdr_receive_plist(fdr_client_t fdr, plist_t* data) { idevice_error_t device_error = IDEVICE_E_SUCCESS; uint32_t len, bytes = 0; char* buf = NULL; device_error = idevice_connection_receive(fdr->connection, (char*)&len, sizeof(len), &bytes); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to receive packet length from FDR (%d)\n", device_error); return -1; } buf = calloc(1, len); if (!buf) { error("ERROR: Unable to allocate memory for FDR receive buffer\n"); return -1; } device_error = idevice_connection_receive(fdr->connection, buf, len, &bytes); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to receive data from FDR\n"); free(buf); return -1; } plist_from_bin(buf, bytes, data); free(buf); debug("FDR Received %d bytes\n", bytes); return 0; } static int fdr_send_plist(fdr_client_t fdr, plist_t data) { idevice_error_t device_error = IDEVICE_E_SUCCESS; char *buf = NULL; uint32_t len = 0, bytes = 0; if (!data) return -1; plist_to_bin(data, &buf, &len); if (!buf) return -1; debug("FDR sending %d bytes:\n", len); if (idevicerestore_debug) debug_plist(data); device_error = idevice_connection_send(fdr->connection, (char *)&len, sizeof(len), &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != sizeof(len)) { error("ERROR: FDR unable to send data length. (%d) Sent %u of %u bytes.\n", device_error, bytes, (uint32_t)sizeof(len)); free(buf); return -1; } device_error = idevice_connection_send(fdr->connection, buf, len, &bytes); free(buf); if (device_error != IDEVICE_E_SUCCESS || bytes != len) { error("ERROR: FDR unable to send data (%d). Sent %u of %u bytes.\n", device_error, bytes, len); return -1; } debug("FDR Sent %d bytes\n", bytes); return 0; } static int fdr_ctrl_handshake(fdr_client_t fdr) { idevice_error_t device_error = IDEVICE_E_SUCCESS; uint32_t bytes = 0, len = sizeof(CTRLCMD); plist_t dict, node; int res; debug("About to do ctrl handshake\n"); ctrlprotoversion = 2; device_error = idevice_connection_send(fdr->connection, CTRLCMD, len, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != len) { debug("Hmm... lookes like the device doesn't like the newer protocol, using the old one\n"); ctrlprotoversion = 1; len = sizeof(HELLOCTRLCMD); device_error = idevice_connection_send(fdr->connection, HELLOCTRLCMD, len, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != len) { error("ERROR: FDR unable to send BeginCtrl. Sent %u of %u bytes.\n", bytes, len); return -1; } } if (ctrlprotoversion == 2) { dict = plist_new_dict(); plist_dict_set_item(dict, "Command", plist_new_string(CTRLCMD)); plist_dict_set_item(dict, "CtrlProtoVersion", plist_new_uint(ctrlprotoversion)); res = fdr_send_plist(fdr, dict); plist_free(dict); if (res) { error("ERROR: FDR could not send Begin command.\n"); return -1; } if (fdr_receive_plist(fdr, &dict)) { error("ERROR: FDR did not get Begin command reply.\n"); return -1; } if (idevicerestore_debug) debug_plist(dict); node = plist_dict_get_item(dict, "ConnPort"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &conn_port); } else { error("ERROR: Could not get FDR ConnPort value\n"); return -1; } plist_free(dict); } else { char buf[16]; uint16_t cport = 0; memset(buf, '\0', sizeof(buf)); bytes = 0; device_error = idevice_connection_receive(fdr->connection, buf, 10, &bytes); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Could not receive reply to HelloCtrl command\n"); return -1; } if (memcmp(buf, "HelloCtrl", 10) != 0) { buf[9] = '\0'; error("ERROR: Did not receive HelloCtrl as reply, but %s\n", buf); return -1; } bytes = 0; device_error = idevice_connection_receive(fdr->connection, (char*)&cport, 2, &bytes); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Failed to receive conn port\n"); return -1; } conn_port = le16toh(cport); } debug("Ctrl handshake done (ConnPort = %" PRIu64 ")\n", (uint64_t)conn_port); return 0; } static int fdr_sync_handshake(fdr_client_t fdr) { idevice_error_t device_error = IDEVICE_E_SUCCESS; uint32_t bytes = 0, len = sizeof(HELLOCMD); plist_t reply; device_error = idevice_connection_send(fdr->connection, HELLOCMD, len, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != len) { error("ERROR: FDR unable to send Hello. Sent %u of %u bytes.\n", bytes, len); return -1; } if (ctrlprotoversion == 2) { if (fdr_receive_plist(fdr, &reply)) { error("ERROR: FDR did not get HelloConn reply.\n"); return -1; } char* identifier = NULL; char* cmd = NULL; plist_t node = NULL; node = plist_dict_get_item(reply, "Command"); if (node) { plist_get_string_val(node, &cmd); } node = plist_dict_get_item(reply, "Identifier"); if (node) { plist_get_string_val(node, &identifier); } plist_free(reply); if (!cmd || (strcmp(cmd, "HelloConn") != 0)) { if (cmd) { free(cmd); } if (identifier) { free(identifier); } error("ERROR: Did not receive HelloConn reply...\n"); return -1; } free(cmd); if (identifier) { debug("Got device identifier %s\n", identifier); free(identifier); } } else { char buf[16]; memset(buf, '\0', sizeof(buf)); bytes = 0; device_error = idevice_connection_receive(fdr->connection, buf, 10, &bytes); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Could not receive reply to HelloConn command\n"); return -1; } if (memcmp(buf, "HelloConn", 10) != 0) { buf[9] = '\0'; error("ERROR: Did not receive HelloConn as reply, but %s\n", buf); return -1; } } return 0; } static int fdr_handle_sync_cmd(fdr_client_t fdr_ctrl) { idevice_error_t device_error = IDEVICE_E_SUCCESS; fdr_client_t fdr; thread_t fdr_thread = (thread_t)NULL; int res = 0; uint32_t bytes = 0; char buf[4096]; device_error = idevice_connection_receive(fdr_ctrl->connection, buf, sizeof(buf), &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != 2) { error("ERROR: Unexpected data from FDR\n"); return -1; } /* Open a new connection and wait for messages on it */ if (fdr_connect(fdr_ctrl->device, FDR_CONN, &fdr)) { error("ERROR: Failed to connect to FDR port\n"); return -1; } debug("FDR connected in reply to sync message, starting command thread\n"); res = thread_new(&fdr_thread, fdr_listener_thread, fdr); if(res) { error("ERROR: Failed to start FDR command thread\n"); fdr_free(fdr); } return res; } static int fdr_handle_plist_cmd(fdr_client_t fdr) { int res = 0; plist_t dict; if (fdr_receive_plist(fdr, &dict)) { error("ERROR: FDR %p could not receive plist command.\n", fdr); return -1; } plist_t node = plist_dict_get_item(dict, "Command"); if (!node || (plist_get_node_type(node) != PLIST_STRING)) { error("ERROR: FDR %p Could not find Command in plist command\n", fdr); plist_free(dict); return -1; } char *command = NULL; plist_get_string_val(node, &command); plist_free(dict); if (!command) { info("FDR %p received empty plist command\n", fdr); return -1; } if (!strcmp(command, "Ping")) { dict = plist_new_dict(); plist_dict_set_item(dict, "Pong", plist_new_bool(1)); res = fdr_send_plist(fdr, dict); plist_free(dict); if (res) { error("ERROR: FDR %p could not send Ping command reply.\n", fdr); free(command); return -1; } } else { error("WARNING: FDR %p received unknown plist command: %s\n", fdr, command); free(command); return -1; } free(command); /* FDR connection will be terminated remotely. Next receive will get nothing, error and terminate this worker thread. */ return 0; } static int fdr_handle_proxy_cmd(fdr_client_t fdr) { idevice_error_t device_error = IDEVICE_E_SUCCESS; char *buf = NULL; size_t bufsize = 1048576; uint32_t sent = 0, bytes = 0; char *host = NULL; uint16_t port = 0; buf = malloc(bufsize); if (!buf) { error("ERROR: %s: malloc failed\n", __func__); return -1; } device_error = idevice_connection_receive(fdr->connection, buf, bufsize, &bytes); if (device_error != IDEVICE_E_SUCCESS) { free(buf); error("ERROR: FDR %p failed to read data for proxy command\n", fdr); return -1; } debug("Got proxy command with %u bytes\n", bytes); /* Just return success here unconditionally because we don't know * anything else and we will eventually abort on failure anyway */ uint16_t ack = 5; device_error = idevice_connection_send(fdr->connection, (char *)&ack, sizeof(ack), &sent); if (device_error != IDEVICE_E_SUCCESS || sent != sizeof(ack)) { free(buf); error("ERROR: FDR %p unable to send ack. Sent %u of %u bytes.\n", fdr, sent, (uint32_t)sizeof(ack)); return -1; } if (bytes < 3) { debug("FDR %p proxy command data too short, retrying\n", fdr); return fdr_poll_and_handle_message(fdr); } /* ack command data too */ device_error = idevice_connection_send(fdr->connection, buf, bytes, &sent); if (device_error != IDEVICE_E_SUCCESS || sent != bytes) { free(buf); error("ERROR: FDR %p unable to send data. Sent %u of %u bytes.\n", fdr, sent, bytes); return -1; } /* Now try to handle actual messages */ /* Connect: 0 3 hostlen */ if (buf[0] == 0 && buf[1] == 3) { uint16_t *p = (uint16_t *)&buf[bytes - 2]; port = be16toh(*p); buf[bytes - 2] = '\0'; host = strdup(&buf[3]); debug("FDR %p Proxy connect request to %s:%u\n", fdr, host, port); } if (!host || !buf[2]) { /* missing or zero length host name */ free(buf); return 0; } /* else wait for messages and forward them */ int sockfd = socket_connect(host, port); free(host); if (sockfd < 0) { free(buf); error("ERROR: Failed to connect socket: %s\n", strerror(errno)); return -1; } int res = 0, bytes_ret; while (1) { bytes = 0; device_error = idevice_connection_receive_timeout(fdr->connection, buf, bufsize, &bytes, 100); #ifdef HAVE_IDEVICE_E_TIMEOUT if (device_error == IDEVICE_E_TIMEOUT || (device_error == IDEVICE_E_SUCCESS && !bytes)) #else if (device_error == IDEVICE_E_SUCCESS && !bytes) #endif { //debug("WARNING: Timeout waiting for proxy payload. %p\n", fdr); } else if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: FDR %p Unable to receive proxy payload (%d)\n", fdr, device_error); res = -1; break; } if (bytes) { debug("FDR %p got payload of %u bytes, now try to proxy it\n", fdr, bytes); debug("Sending %u bytes of data\n", bytes); sent = 0; while (sent < bytes) { int s = socket_send(sockfd, buf + sent, bytes - sent); if (s < 0) { break; } sent += s; } if (sent != bytes) { error("ERROR: Sending proxy payload failed: %s. Sent %u of %u bytes. \n", strerror(errno), sent, bytes); socket_close(sockfd); res = -1; break; } } bytes_ret = socket_receive_timeout(sockfd, buf, bufsize, 0, 100); if (bytes_ret < 0) { if (errno) error("ERROR: FDR %p receiving proxy payload failed: %s\n", fdr, strerror(errno)); else res = 1; /* close connection if no data with no error */ break; } bytes = bytes_ret; if (bytes) { debug("FDR %p Received %u bytes reply data,%s sending to device\n", fdr, bytes, (bytes ? "" : " not")); sent = 0; while (sent < bytes) { uint32_t s; device_error = idevice_connection_send(fdr->connection, buf + sent, bytes - sent, &s); if (device_error != IDEVICE_E_SUCCESS) { break; } sent += s; } if (device_error != IDEVICE_E_SUCCESS || bytes != sent) { error("ERROR: FDR %p unable to send data (%d). Sent %u of %u bytes.\n", fdr, device_error, sent, bytes); res = -1; break; } } else serial++; } socket_close(sockfd); free(buf); return res; } idevicerestore-1.0.0/src/fdr.h000066400000000000000000000027651367173617500162710ustar00rootroot00000000000000/* * fdr.h * Functions for handling FDR connections * * Copyright (c) 2014 BALATON Zoltan. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_FDR_H #define IDEVICERESTORE_FDR_H #ifdef __cplusplus extern "C" { #endif #include #include "thread.h" /* from libimobiledevice/common */ typedef enum { FDR_CTRL, FDR_CONN } fdr_type_t; struct fdr_client { idevice_connection_t connection; idevice_t device; fdr_type_t type; }; typedef struct fdr_client *fdr_client_t; int fdr_connect(idevice_t device, fdr_type_t type, fdr_client_t *fdr); void fdr_disconnect(fdr_client_t fdr); void fdr_free(fdr_client_t fdr); int fdr_poll_and_handle_message(fdr_client_t fdr); void *fdr_listener_thread(void *cdata); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/fls.c000066400000000000000000000243371367173617500162740ustar00rootroot00000000000000/* * fls.c * support for .fls file format (found in .bbfw files) * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "fls.h" #include "common.h" #ifndef offsetof #define offsetof(type, member) __builtin_offsetof (type, member) #endif static void fls_parse_elements(fls_file* fls) { /* FIXME: the following code is not big endian safe */ if (!fls || !fls->data) { return; } uint32_t offset = 0; fls->max_elements = 32; fls->elements = (fls_element**)malloc(sizeof(fls_element*) * fls->max_elements); fls_element* cur = NULL; do { void* p = fls->data + offset; uint32_t hdrsize = 0; cur = (fls_element*)p; if ((offset + cur->size) > fls->size) { break; } fls_element* ne; switch (cur->type) { case 0x0c: { hdrsize = offsetof(fls_0c_element, data); fls_0c_element* xe = (fls_0c_element*)malloc(sizeof(fls_0c_element)); memset(xe, '\0', sizeof(fls_0c_element)); memcpy((void*)xe, p, hdrsize); xe->data = (xe->size > hdrsize) ? p + hdrsize : NULL; ne = (fls_element*)xe; fls->c_element = xe; } break; case 0x10: { hdrsize = offsetof(fls_10_element, data); fls_10_element* xe = (fls_10_element*)malloc(sizeof(fls_10_element)); memset(xe, '\0', sizeof(fls_10_element)); memcpy((void*)xe, p, hdrsize); xe->data = (xe->size > hdrsize) ? p + hdrsize : NULL; ne = (fls_element*)xe; } break; case 0x14: { hdrsize = offsetof(fls_14_element, data); fls_14_element* xe = (fls_14_element*)malloc(sizeof(fls_14_element)); memset(xe, '\0', sizeof(fls_14_element)); memcpy((void*)xe, p, hdrsize); xe->data = (xe->size > hdrsize) ? p + hdrsize : NULL; ne = (fls_element*)xe; } break; default: hdrsize = offsetof(fls_element, data); ne = (fls_element*)malloc(sizeof(fls_element)); memset(ne, '\0', sizeof(fls_element)); ne->type = cur->type; ne->size = cur->size; ne->data = (ne->size > hdrsize) ? p + hdrsize : NULL; break; } if ((fls->num_elements + 1) > fls->max_elements) { fls->max_elements += 10; fls->elements = (fls_element**)realloc(fls->elements, sizeof(fls_element*) * fls->max_elements); } fls->elements[fls->num_elements++] = ne; offset += cur->size; } while (offset < fls->size); if (offset != fls->size) { error("ERROR: %s: error parsing elements\n", __func__); return; } } fls_file* fls_parse(unsigned char* data, unsigned int size) { fls_file* fls = (fls_file*)malloc(sizeof(fls_file)); if (!fls) { return NULL; } memset(fls, '\0', sizeof(fls_file)); fls->data = malloc(size); fls->size = size; memcpy(fls->data, data, size); fls_parse_elements(fls); return fls; } void fls_free(fls_file* fls) { if (fls) { if (fls->num_elements > 0) { int i; for (i = fls->num_elements-1; i >=0; i--) { free(fls->elements[i]); } free(fls->elements); } if (fls->data) { free(fls->data); } free(fls); } } int fls_update_sig_blob(fls_file* fls, const unsigned char* sigdata, unsigned int siglen) { /* FIXME: the code in this function is not big endian safe */ if (!fls || !fls->num_elements) { error("ERROR: %s: no data\n", __func__); return -1; } if (!fls->c_element) { error("ERROR: %s: no fls_0c_element in fls data\n", __func__); return -1; } uint32_t datasize = *(uint32_t*)(fls->c_element->data + 0x10); if (datasize != fls->c_element->data_size) { error("ERROR: %s: data size mismatch (0x%x != 0x%x)\n", __func__, datasize, fls->c_element->data_size); return -1; } uint32_t sigoffset = *(uint32_t*)(fls->c_element->data + 0x14); if (sigoffset > datasize) { error("ERROR: %s: signature offset greater than data size (0x%x > 0x%x)\n", __func__, sigoffset, datasize); return -1; } uint32_t oldsiglen = datasize - sigoffset; uint32_t newsize = fls->size - oldsiglen + siglen; unsigned int i; uint32_t offset = 0; void* newdata = malloc(newsize); if (!newdata) { error("ERROR: %s: out of memory\n", __func__); return -1; } uint32_t hdrsize = 0; uint32_t firstpartlen = 0; for (i = 0; i < fls->num_elements; i++) { switch (fls->elements[i]->type) { case 0x0c: hdrsize = offsetof(fls_0c_element, data); // update offset ((fls_0c_element*)fls->elements[i])->offset = offset+hdrsize; // copy first part of data firstpartlen = fls->elements[i]->size - hdrsize - oldsiglen; memcpy(newdata+offset+hdrsize, ((fls_0c_element*)fls->elements[i])->data, firstpartlen); // copy new signature data memcpy(newdata+offset+hdrsize+firstpartlen, sigdata, siglen); ((fls_0c_element*)fls->elements[i])->data = newdata+offset+hdrsize; fls->elements[i]->size -= oldsiglen; fls->elements[i]->size += siglen; ((fls_0c_element*)fls->elements[i])->data_size -= oldsiglen; ((fls_0c_element*)fls->elements[i])->data_size += siglen; memcpy(newdata+offset+hdrsize+0x10, &(((fls_0c_element*)fls->elements[i])->data_size), 4); // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); break; case 0x10: hdrsize = offsetof(fls_10_element, data); // update offset ((fls_10_element*)fls->elements[i])->offset = offset+hdrsize; // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, ((fls_10_element*)fls->elements[i])->data, fls->elements[i]->size - hdrsize); ((fls_10_element*)fls->elements[i])->data = newdata+offset+hdrsize; } else { ((fls_10_element*)fls->elements[i])->data = NULL; } break; case 0x14: hdrsize = offsetof(fls_14_element, data); // update offset ((fls_14_element*)fls->elements[i])->offset = offset+hdrsize; // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, ((fls_14_element*)fls->elements[i])->data, fls->elements[i]->size - hdrsize); ((fls_14_element*)fls->elements[i])->data = newdata+offset+hdrsize; } else { ((fls_14_element*)fls->elements[i])->data = NULL; } break; default: hdrsize = offsetof(fls_element, data); // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, fls->elements[i]->data, fls->elements[i]->size - hdrsize); fls->elements[i]->data = newdata+offset+hdrsize; } else { fls->elements[i]->data = NULL; } break; } offset += fls->elements[i]->size; } if (fls->data) { free(fls->data); } fls->data = newdata; fls->size = newsize; return 0; } int fls_insert_ticket(fls_file* fls, const unsigned char* data, unsigned int size) { /* FIXME: the code in this function is not big endian safe */ if (!fls || !fls->num_elements) { error("ERROR: %s: no data\n", __func__); return -1; } if (!fls->c_element) { error("ERROR: %s: no fls_0c_element in fls data\n", __func__); return -1; } uint32_t padding = 0; if (size%4 != 0) { padding = 4-(size%4); } uint32_t newsize = fls->size + size + padding; unsigned int i; uint32_t offset = 0; void* newdata = malloc(newsize); if (!newdata) { error("ERROR: %s: out of memory\n", __func__); return -1; } uint32_t hdrsize = 0; for (i = 0; i < fls->num_elements; i++) { switch (fls->elements[i]->type) { case 0x0c: hdrsize = offsetof(fls_0c_element, data); // update offset ((fls_0c_element*)fls->elements[i])->offset = offset+hdrsize; // copy ticket data memcpy(newdata+offset+hdrsize, data, size); if (padding > 0) { // padding memset(newdata+offset+hdrsize+size, '\xFF', padding); } // copy remaining data memcpy(newdata+offset+hdrsize+size+padding, ((fls_0c_element*)fls->elements[i])->data, fls->elements[i]->size); ((fls_0c_element*)fls->elements[i])->data = newdata+offset+hdrsize; fls->elements[i]->size += (size + padding); ((fls_0c_element*)fls->elements[i])->data_size += (size + padding); // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); break; case 0x10: hdrsize = offsetof(fls_10_element, data); // update offset ((fls_10_element*)fls->elements[i])->offset = offset+hdrsize; // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, ((fls_10_element*)fls->elements[i])->data, fls->elements[i]->size - hdrsize); ((fls_10_element*)fls->elements[i])->data = newdata+offset+hdrsize; } else { ((fls_10_element*)fls->elements[i])->data = NULL; } break; case 0x14: hdrsize = offsetof(fls_14_element, data); // update offset ((fls_14_element*)fls->elements[i])->offset = offset+hdrsize; // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, ((fls_14_element*)fls->elements[i])->data, fls->elements[i]->size - hdrsize); ((fls_14_element*)fls->elements[i])->data = newdata+offset+hdrsize; } else { ((fls_14_element*)fls->elements[i])->data = NULL; } break; default: hdrsize = offsetof(fls_element, data); // copy header memcpy(newdata+offset, fls->elements[i], hdrsize); // copy data if (fls->elements[i]->size > hdrsize) { memcpy(newdata+offset+hdrsize, fls->elements[i]->data, fls->elements[i]->size - hdrsize); fls->elements[i]->data = newdata+offset+hdrsize; } else { fls->elements[i]->data = NULL; } break; } offset += fls->elements[i]->size; } if (fls->data) { free(fls->data); } fls->data = newdata; fls->size = newsize; return 0; } idevicerestore-1.0.0/src/fls.h000066400000000000000000000046461367173617500163020ustar00rootroot00000000000000/* * fls.h * support for .fls file format (found in .bbfw files) * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FLS_H #define FLS_H #include struct _fls_element { uint32_t type; uint32_t size; uint32_t empty; const unsigned char* data; } __attribute__((packed)); typedef struct _fls_element fls_element; struct _fls_0c_element { uint32_t type; uint32_t size; uint32_t empty; uint32_t off_0x0c; uint32_t off_0x10; uint32_t off_0x14; uint32_t off_0x18; uint32_t data_size; // size without header uint32_t off_0x20; uint32_t offset; // absolute offset of data in file const unsigned char* data; // data+0x14 contains offset to sig blob } __attribute__((packed)); typedef struct _fls_0c_element fls_0c_element; struct _fls_10_element { uint32_t type; uint32_t size; uint32_t empty; uint32_t data_size; // size without header uint32_t off_0x10; uint32_t offset; const unsigned char* data; } __attribute__((packed)); typedef struct _fls_10_element fls_10_element; struct _fls_14_element { uint32_t type; uint32_t size; uint32_t empty; uint32_t data_size; // size without header uint32_t off_0x10; uint32_t offset; const unsigned char* data; } __attribute__((packed)); typedef struct _fls_14_element fls_14_element; typedef struct { unsigned int num_elements; unsigned int max_elements; fls_element** elements; const fls_0c_element* c_element; void* data; uint32_t size; } fls_file; fls_file* fls_parse(unsigned char* data, unsigned int size); void fls_free(fls_file* fls); int fls_update_sig_blob(fls_file* fls, const unsigned char* data, unsigned int size); int fls_insert_ticket(fls_file* fls, const unsigned char* data, unsigned int size); #endif idevicerestore-1.0.0/src/ftab.c000066400000000000000000000140511367173617500164140ustar00rootroot00000000000000/* * ftab.c * Functions for handling the ftab format * * Copyright (c) 2019 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "ftab.h" #include "common.h" #include "endianness.h" int ftab_parse(unsigned char *data, unsigned int data_size, ftab_t *ftab, uint32_t *tag) { if (!data || !data_size || !ftab) { return -1; } if (data_size < sizeof(struct ftab_header)) { error("ERROR: %s: Buffer too small for ftab data\n", __func__); return -1; } struct ftab_header *hdr_ptr = (struct ftab_header*)data; if (be32toh(hdr_ptr->magic) != 'ftab') { error("ERROR: %s: Unexpected magic value 0x%08x\n", __func__, le32toh(hdr_ptr->magic)); return -1; } /* copy header */ ftab_t ftab_new = (ftab_t)calloc(1, sizeof(struct ftab_fmt)); memcpy(&ftab_new->header, data, sizeof(struct ftab_header)); ftab_new->header.always_01 = le32toh(ftab_new->header.always_01); ftab_new->header.always_ff = le32toh(ftab_new->header.always_ff); ftab_new->header.tag = be32toh(ftab_new->header.tag); if (tag) { *tag = ftab_new->header.tag; } ftab_new->header.magic = be32toh(ftab_new->header.magic); ftab_new->header.num_entries = le32toh(ftab_new->header.num_entries); /* copy entries */ ftab_new->entries = (struct ftab_entry*)malloc(sizeof(struct ftab_entry) * ftab_new->header.num_entries); memcpy(ftab_new->entries, data + sizeof(struct ftab_header), sizeof(struct ftab_entry) * ftab_new->header.num_entries); /* create data storage */ ftab_new->storage = (unsigned char**)calloc(ftab_new->header.num_entries, sizeof(unsigned char*)); /* fill data storage */ uint32_t i = 0; for (i = 0; i < ftab_new->header.num_entries; i++) { ftab_new->entries[i].tag = be32toh(ftab_new->entries[i].tag); ftab_new->entries[i].offset = le32toh(ftab_new->entries[i].offset); ftab_new->entries[i].size = le32toh(ftab_new->entries[i].size); ftab_new->storage[i] = malloc(ftab_new->entries[i].size); memcpy(ftab_new->storage[i], data + ftab_new->entries[i].offset, ftab_new->entries[i].size); } *ftab = ftab_new; return 0; } int ftab_get_entry_ptr(ftab_t ftab, uint32_t tag, unsigned char **data, unsigned int *data_size) { if (!ftab || !tag || !data || !data_size) { return -1; } uint32_t i; int res = -1; for (i = 0; i < ftab->header.num_entries; i++) { if (ftab->entries[i].tag == tag) { *data = ftab->storage[i]; *data_size = ftab->entries[i].size; res = 0; } } return res; } int ftab_add_entry(ftab_t ftab, uint32_t tag, unsigned char *data, unsigned int data_size) { if (!ftab || !tag || !data || !data_size) { return -1; } uint32_t new_index = ftab->header.num_entries; struct ftab_entry *new_entries = realloc(ftab->entries, sizeof(struct ftab_entry) * (ftab->header.num_entries + 1)); if (!new_entries) { error("ERROR: %s: realloc failed!\n", __func__); return -1; } ftab->entries = new_entries; unsigned char **new_storage = realloc(ftab->storage, sizeof(unsigned char*) * (ftab->header.num_entries + 1)); if (!new_storage) { error("ERROR: %s: realloc failed!\n", __func__); return -1; } ftab->storage = new_storage; unsigned char *data_copy = (unsigned char*)malloc(data_size); if (!data_copy) { return -1; } memcpy(data_copy, data, data_size); ftab->storage[new_index] = data_copy; ftab->entries[new_index].tag = tag; ftab->entries[new_index].size = data_size; ftab->header.num_entries++; uint32_t off = sizeof(struct ftab_header) + sizeof(struct ftab_entry) * ftab->header.num_entries; uint32_t i; for (i = 0; i < ftab->header.num_entries; i++) { ftab->entries[i].offset = off; off += ftab->entries[i].size; } return 0; } int ftab_write(ftab_t ftab, unsigned char **data, unsigned int *data_size) { uint32_t i; unsigned int total_size = sizeof(struct ftab_header); total_size += ftab->header.num_entries * sizeof(struct ftab_entry); for (i = 0; i < ftab->header.num_entries; i++) { total_size += ftab->entries[i].size; } unsigned char *data_out = (unsigned char*)malloc(total_size); if (!data_out) { error("ERROR: %s: Out of memory?!\n", __func__); return -1; } struct ftab_header *ftab_header = (struct ftab_header*)data_out; memset(ftab_header, '\0', sizeof(struct ftab_header)); ftab_header->always_01 = htole32(ftab->header.always_01); ftab_header->always_ff = htole32(ftab->header.always_ff); ftab_header->tag = htobe32(ftab->header.tag); ftab_header->magic = htobe32(ftab->header.magic); ftab_header->num_entries = htole32(ftab->header.num_entries); for (i = 0; i < ftab->header.num_entries; i++) { struct ftab_entry* entry = (struct ftab_entry*)(data_out + sizeof(struct ftab_header) + (sizeof(struct ftab_entry) * i)); entry->tag = htobe32(ftab->entries[i].tag); entry->offset = htole32(ftab->entries[i].offset); entry->size = htole32(ftab->entries[i].size); entry->pad_0x0C = 0; } unsigned char *p = data_out + sizeof(struct ftab_header) + (sizeof(struct ftab_entry) * ftab->header.num_entries); for (i = 0; i < ftab->header.num_entries; i++) { memcpy(p, ftab->storage[i], ftab->entries[i].size); p += ftab->entries[i].size; } *data = data_out; *data_size = total_size; return 0; } int ftab_free(ftab_t ftab) { if (!ftab) return -1; uint32_t i = 0; for (i = 0; i < ftab->header.num_entries; i++) { free(ftab->storage[i]); } free(ftab->storage); free(ftab->entries); free(ftab); return 0; } idevicerestore-1.0.0/src/ftab.h000066400000000000000000000037161367173617500164270ustar00rootroot00000000000000/* * ftab.h * Functions for handling the ftab format * * Copyright (c) 2019 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_FTAB_H #define IDEVICERESTORE_FTAB_H #ifdef __cplusplus extern "C" { #endif #include struct ftab_header { uint32_t always_01; // 1 uint32_t always_ff; // 0xFFFFFFFF uint32_t unk_0x08; // 0 uint32_t unk_0x0C; // 0 uint32_t unk_0x10; // 0 uint32_t unk_0x14; // 0 uint32_t unk_0x18; // 0 uint32_t unk_0x1C; // 0 uint32_t tag; // e.g. 'rkos' uint32_t magic; // 'ftab' magic uint32_t num_entries; uint32_t pad_0x2C; }; struct ftab_entry { uint32_t tag; uint32_t offset; uint32_t size; uint32_t pad_0x0C; }; struct ftab_fmt { struct ftab_header header; struct ftab_entry *entries; unsigned char **storage; }; typedef struct ftab_fmt *ftab_t; int ftab_parse(unsigned char *data, unsigned int data_size, ftab_t *ftab, uint32_t *tag); int ftab_get_entry_ptr(ftab_t ftab, uint32_t tag, unsigned char **data, unsigned int *data_size); int ftab_add_entry(ftab_t ftab, uint32_t tag, unsigned char *data, unsigned int data_size); int ftab_write(ftab_t ftab, unsigned char **data, unsigned int *data_size); int ftab_free(ftab_t ftab); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/idevicerestore.c000066400000000000000000002256251367173617500205270ustar00rootroot00000000000000/* * idevicerestore.c * Restore device firmware and filesystem * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2015 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "dfu.h" #include "tss.h" #include "img3.h" #include "img4.h" #include "ipsw.h" #include "common.h" #include "normal.h" #include "restore.h" #include "download.h" #include "recovery.h" #include "idevicerestore.h" #include "limera1n.h" #include "locking.h" #define VERSION_XML "version.xml" #ifndef IDEVICERESTORE_NOMAIN static struct option longopts[] = { { "ecid", required_argument, NULL, 'i' }, { "udid", required_argument, NULL, 'u' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "erase", no_argument, NULL, 'e' }, { "custom", no_argument, NULL, 'c' }, { "latest", no_argument, NULL, 'l' }, { "cydia", no_argument, NULL, 's' }, { "exclude", no_argument, NULL, 'x' }, { "shsh", no_argument, NULL, 't' }, { "keep-pers", no_argument, NULL, 'k' }, { "pwn", no_argument, NULL, 'p' }, { "no-action", no_argument, NULL, 'n' }, { "cache-path", required_argument, NULL, 'C' }, { "no-input", no_argument, NULL, 'y' }, { "plain-progress", no_argument, NULL, 'P' }, { "restore-mode", no_argument, NULL, 'R' }, { "ticket", required_argument, NULL, 'T' }, { "no-restore", no_argument, NULL, 'z' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static void usage(int argc, char* argv[], int err) { char* name = strrchr(argv[0], '/'); fprintf((err) ? stderr : stdout, "Usage: %s [OPTIONS] PATH\n" \ "\n" \ "Restore IPSW firmware at PATH to an iOS device.\n" \ "\n" \ "PATH can be a compressed .ipsw file or a directory containing all files\n" \ "extracted from an IPSW.\n" \ "\n" \ "OPTIONS:\n" \ " -i, --ecid ECID Target specific device by its ECID\n" \ " e.g. 0xaabb123456 (hex) or 1234567890 (decimal)\n" \ " -u, --udid UDID Target specific device by its device UDID\n" \ " NOTE: only works with devices in normal mode.\n" \ " -l, --latest Use latest available firmware (with download on demand).\n" \ " Before performing any action it will interactively ask\n" \ " to select one of the currently signed firmware versions,\n" \ " unless -y has been given too.\n" \ " The PATH argument is ignored when using this option.\n" \ " DO NOT USE if you need to preserve the baseband/unlock!\n" \ " USE WITH CARE if you want to keep a jailbreakable\n" \ " firmware!\n" \ " -e, --erase Perform full restore instead of update, erasing all data\n" \ " DO NOT USE if you want to preserve user data on the device!\n" \ " -y, --no-input Non-interactive mode, do not ask for any input.\n" \ " WARNING: This will disable certain checks/prompts that\n" \ " are supposed to prevent DATA LOSS. Use with caution.\n" \ " -n, --no-action Do not perform any restore action. If combined with -l\n" \ " option the on-demand ipsw download is performed before\n" \ " exiting.\n" \ " -h, --help Prints this usage information\n" \ " -C, --cache-path DIR Use specified directory for caching extracted or other\n" \ " reused files.\n" \ " -d, --debug Enable communication debugging\n" \ " -v, --version Print version information\n" \ "\n" \ "Advanced/experimental options:\n" " -c, --custom Restore with a custom firmware (requires bootrom exploit)\n" \ " -s, --cydia Use Cydia's signature service instead of Apple's\n" \ " -x, --exclude Exclude nor/baseband upgrade\n" \ " -t, --shsh Fetch TSS record and save to .shsh file, then exit\n" \ " -z, --no-restore Do not restore and end after booting to the ramdisk\n" \ " -k, --keep-pers Write personalized components to files for debugging\n" \ " -p, --pwn Put device in pwned DFU mode and exit (limera1n devices)\n" \ " -P, --plain-progress Print progress as plain step and progress\n" \ " -R, --restore-mode Allow restoring from Restore mode\n" \ " -T, --ticket PATH Use file at PATH to send as AP ticket\n" \ "\n" \ "Homepage: <" PACKAGE_URL ">\n" \ "Bug Reports: <" PACKAGE_BUGREPORT ">\n", (name ? name + 1 : argv[0])); } #endif static int idevicerestore_keep_pers = 0; static int load_version_data(struct idevicerestore_client_t* client) { if (!client) { return -1; } struct stat fst; int cached = 0; char version_xml[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &fst) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(version_xml, client->cache_dir); strcat(version_xml, "/"); strcat(version_xml, VERSION_XML); } else { strcpy(version_xml, VERSION_XML); } if ((stat(version_xml, &fst) < 0) || ((time(NULL)-86400) > fst.st_mtime)) { char version_xml_tmp[1024]; strcpy(version_xml_tmp, version_xml); strcat(version_xml_tmp, ".tmp"); if (download_to_file("http://itunes.apple.com/check/version", version_xml_tmp, 0) == 0) { remove(version_xml); if (rename(version_xml_tmp, version_xml) < 0) { error("ERROR: Could not update '%s'\n", version_xml); } else { info("NOTE: Updated version data.\n"); } } } else { cached = 1; } char *verbuf = NULL; size_t verlen = 0; read_file(version_xml, (void**)&verbuf, &verlen); if (!verbuf) { error("ERROR: Could not load '%s'\n", version_xml); return -1; } client->version_data = NULL; plist_from_xml(verbuf, verlen, &client->version_data); free(verbuf); if (!client->version_data) { remove(version_xml); error("ERROR: Cannot parse plist data from '%s'.\n", version_xml); return -1; } if (cached) { info("NOTE: using cached version data\n"); } return 0; } static int32_t get_version_num(const char *s_ver) { int vers[3] = {0, 0, 0}; if (sscanf(s_ver, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { return ((vers[0] & 0xFF) << 16) | ((vers[1] & 0xFF) << 8) | (vers[2] & 0xFF); } return 0x00FFFFFF; } static int compare_versions(const char *s_ver1, const char *s_ver2) { return (get_version_num(s_ver1) & 0xFFFF00) - (get_version_num(s_ver2) & 0xFFFF00); } static void idevice_event_cb(const idevice_event_t *event, void *userdata) { struct idevicerestore_client_t *client = (struct idevicerestore_client_t*)userdata; #ifdef HAVE_ENUM_IDEVICE_CONNECTION_TYPE if (event->conn_type != CONNECTION_USBMUXD) { // ignore everything but devices connected through USB return; } #endif if (event->event == IDEVICE_DEVICE_ADD) { if (client->ignore_device_add_events) { return; } if (normal_check_mode(client) == 0) { mutex_lock(&client->device_event_mutex); client->mode = &idevicerestore_modes[MODE_NORMAL]; debug("%s: device %016" PRIx64 " (udid: %s) connected in normal mode\n", __func__, client->ecid, client->udid); cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } else if (client->ecid && restore_check_mode(client) == 0) { mutex_lock(&client->device_event_mutex); client->mode = &idevicerestore_modes[MODE_RESTORE]; debug("%s: device %016" PRIx64 " (udid: %s) connected in restore mode\n", __func__, client->ecid, client->udid); cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } else if (event->event == IDEVICE_DEVICE_REMOVE) { if (client->udid && !strcmp(event->udid, client->udid)) { mutex_lock(&client->device_event_mutex); client->mode = &idevicerestore_modes[MODE_UNKNOWN]; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, client->udid); client->ignore_device_add_events = 0; cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } } static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) { struct idevicerestore_client_t *client = (struct idevicerestore_client_t*)userdata; if (event->type == IRECV_DEVICE_ADD) { if (!client->udid && !client->ecid) { client->ecid = event->device_info->ecid; } if (client->ecid && event->device_info->ecid == client->ecid) { mutex_lock(&client->device_event_mutex); switch (event->mode) { case IRECV_K_WTF_MODE: client->mode = &idevicerestore_modes[MODE_WTF]; break; case IRECV_K_DFU_MODE: client->mode = &idevicerestore_modes[MODE_DFU]; break; case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: case IRECV_K_RECOVERY_MODE_4: client->mode = &idevicerestore_modes[MODE_RECOVERY]; break; default: client->mode = &idevicerestore_modes[MODE_UNKNOWN]; } debug("%s: device %016" PRIx64 " (udid: %s) connected in %s mode\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A", client->mode->string); cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } else if (event->type == IRECV_DEVICE_REMOVE) { if (client->ecid && event->device_info->ecid == client->ecid) { mutex_lock(&client->device_event_mutex); client->mode = &idevicerestore_modes[MODE_UNKNOWN]; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A"); cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } } int idevicerestore_start(struct idevicerestore_client_t* client) { int tss_enabled = 0; int result = 0; if (!client) { return -1; } if ((client->flags & FLAG_LATEST) && (client->flags & FLAG_CUSTOM)) { error("ERROR: FLAG_LATEST cannot be used with FLAG_CUSTOM.\n"); return -1; } if (!client->ipsw && !(client->flags & FLAG_PWN) && !(client->flags & FLAG_LATEST)) { error("ERROR: no ipsw file given\n"); return -1; } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); irecv_set_debug_level(1); idevicerestore_debug = 1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0); irecv_device_event_subscribe(&client->irecv_e_ctx, irecv_event_cb, client); idevice_event_subscribe(idevice_event_cb, client); client->idevice_e_ctx = idevice_event_cb; // check which mode the device is currently in so we know where to start mutex_lock(&client->device_event_mutex); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode == &idevicerestore_modes[MODE_UNKNOWN] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.1); info("Found device in %s mode\n", client->mode->string); mutex_unlock(&client->device_event_mutex); if (client->mode->index == MODE_WTF) { unsigned int cpid = 0; if (dfu_client_new(client) != 0) { error("ERROR: Could not open device in WTF mode\n"); return -1; } if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { error("ERROR: Could not get CPID for WTF mode device\n"); dfu_client_free(client); return -1; } char wtfname[256]; sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); unsigned char* wtftmp = NULL; unsigned int wtfsize = 0; // Prefer to get WTF file from the restore IPSW ipsw_extract_to_memory(client->ipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { // update version data (from cache, or apple if too old) load_version_data(client); // Download WTF IPSW char* s_wtfurl = NULL; plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { plist_get_string_val(wtfurl, &s_wtfurl); } if (!s_wtfurl) { info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); } // make a local file name char* fnpart = strrchr(s_wtfurl, '/'); if (!fnpart) { fnpart = (char*)"x12220000_5_Recovery.ipsw"; } else { fnpart++; } struct stat fst; char wtfipsw[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &fst) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(wtfipsw, client->cache_dir); strcat(wtfipsw, "/"); strcat(wtfipsw, fnpart); } else { strcpy(wtfipsw, fnpart); } if (stat(wtfipsw, &fst) != 0) { download_to_file(s_wtfurl, wtfipsw, 0); } ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } } mutex_lock(&client->device_event_mutex); if (wtftmp) { if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) { error("ERROR: Could not send WTF...\n"); } } dfu_client_free(client); free(wtftmp); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_DFU] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); /* TODO: verify if it actually goes from 0x1222 -> 0x1227 */ error("ERROR: Failed to put device into DFU from WTF mode\n"); return -1; } mutex_unlock(&client->device_event_mutex); } // discover the device type client->device = get_irecv_device(client); if (client->device == NULL) { error("ERROR: Unable to discover device type\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.2); info("Identified device as %s, %s\n", client->device->hardware_model, client->device->product_type); if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->flags & FLAG_PWN) { recovery_client_free(client); if (client->mode->index != MODE_DFU) { error("ERROR: Device needs to be in DFU mode for this option.\n"); return -1; } info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n...\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("Device should be in pwned DFU state now.\n"); return 0; } if (client->flags & FLAG_LATEST) { char *fwurl = NULL; unsigned char fwsha1[20]; unsigned char *p_fwsha1 = NULL; plist_t signed_fws = NULL; int res = ipsw_get_signed_firmwares(client->device->product_type, &signed_fws); if (res < 0) { error("ERROR: Could not fetch list of signed firmwares.\n"); return res; } uint32_t count = plist_array_get_size(signed_fws); if (count == 0) { plist_free(signed_fws); error("ERROR: No firmwares are currently being signed for %s (REALLY?!)\n", client->device->product_type); return -1; } plist_t selected_fw = NULL; if (client->flags & FLAG_INTERACTIVE) { uint32_t i = 0; info("The following firmwares are currently being signed for %s:\n", client->device->product_type); for (i = 0; i < count; i++) { plist_t fw = plist_array_get_item(signed_fws, i); plist_t p_version = plist_dict_get_item(fw, "version"); plist_t p_build = plist_dict_get_item(fw, "buildid"); char *s_version = NULL; char *s_build = NULL; plist_get_string_val(p_version, &s_version); plist_get_string_val(p_build, &s_build); info(" [%d] %s (build %s)\n", i+1, s_version, s_build); free(s_version); free(s_build); } while (1) { char input[64]; printf("Select the firmware you want to restore: "); fflush(stdout); fflush(stdin); get_user_input(input, 63, 0); if (*input == '\0') { plist_free(signed_fws); return -1; } if (client->flags & FLAG_QUIT) { return -1; } unsigned long selected = strtoul(input, NULL, 10); if (selected == 0 || selected > count) { printf("Invalid input value. Must be in range: 1..%u\n", count); continue; } selected_fw = plist_array_get_item(signed_fws, (uint32_t)selected-1); break; } } else { info("NOTE: Running non-interactively, automatically selecting latest available version\n"); selected_fw = plist_array_get_item(signed_fws, 0); } if (!selected_fw) { error("ERROR: failed to select latest firmware?!\n"); plist_free(signed_fws); return -1; } else { plist_t p_version = plist_dict_get_item(selected_fw, "version"); plist_t p_build = plist_dict_get_item(selected_fw, "buildid"); char *s_version = NULL; char *s_build = NULL; plist_get_string_val(p_version, &s_version); plist_get_string_val(p_build, &s_build); info("Selected firmware %s (build %s)\n", s_version, s_build); free(s_version); free(s_build); plist_t p_url = plist_dict_get_item(selected_fw, "url"); plist_t p_sha1 = plist_dict_get_item(selected_fw, "sha1sum"); char *s_sha1 = NULL; plist_get_string_val(p_url, &fwurl); plist_get_string_val(p_sha1, &s_sha1); if (strlen(s_sha1) == 40) { int i; int v; for (i = 0; i < 40; i+=2) { v = 0; sscanf(s_sha1+i, "%02x", &v); fwsha1[i/2] = (unsigned char)v; } p_fwsha1 = &fwsha1[0]; } else { error("ERROR: unexpected size of sha1sum\n"); } } plist_free(signed_fws); if (!fwurl || !p_fwsha1) { error("ERROR: Missing firmware URL or SHA1\n"); return -1; } char* ipsw = NULL; res = ipsw_download_fw(fwurl, p_fwsha1, client->cache_dir, &ipsw); if (res != 0) { if (ipsw) { free(ipsw); } return res; } else { client->ipsw = ipsw; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); if (client->flags & FLAG_NOACTION) { return 0; } if (client->mode->index == MODE_RESTORE) { if (client->flags & FLAG_ALLOW_RESTORE_MODE) { tss_enabled = 0; if (!client->root_ticket) { client->root_ticket = (void*)strdup(""); client->root_ticket_len = 0; } } else { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -2; } // we need to refresh the current mode again mutex_lock(&client->device_event_mutex); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode == &idevicerestore_modes[MODE_UNKNOWN] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } info("Found device in %s mode\n", client->mode->string); mutex_unlock(&client->device_event_mutex); } } // 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; } // extract buildmanifest plist_t buildmanifest = NULL; if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(client->ipsw, &buildmanifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(buildmanifest, client->device->product_type) < 0) { error("ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n"); return -1; } /* 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 (client->flags & FLAG_CUSTOM) { /* prevent signing custom firmware */ tss_enabled = 0; info("Custom firmware requested. Disabled TSS request.\n"); } // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); { plist_t node; plist_t comp; plist_t inf; plist_t manifest; char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; strcpy(lcmodel, client->device->hardware_model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); x++; } sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); // get all_flash file manifest char *files[16]; char *fmanifest = NULL; uint32_t msize = 0; if (ipsw_extract_to_memory(client->ipsw, tmpstr, (unsigned char**)&fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); return -1; } char *tok = strtok(fmanifest, "\r\n"); int fc = 0; while (tok) { files[fc++] = strdup(tok); if (fc >= 16) { break; } tok = strtok(NULL, "\r\n"); } free(fmanifest); manifest = plist_new_dict(); for (x = 0; x < fc; x++) { inf = plist_new_dict(); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/"); strcat(tmpstr, files[x]); plist_dict_set_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); const char* compname = get_component_name(files[x]); if (compname) { plist_dict_set_item(manifest, compname, comp); if (!strncmp(files[x], "DeviceTree", 10)) { plist_dict_set_item(manifest, "RestoreDeviceTree", plist_copy(comp)); } } else { error("WARNING: unhandled component %s\n", files[x]); plist_free(comp); } free(files[x]); files[x] = NULL; } // add iBSS sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); plist_dict_set_item(manifest, "iBSS", comp); // add iBEC sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); plist_dict_set_item(manifest, "iBEC", comp); // add kernel cache plist_t kdict = NULL; node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { char tt[4]; strncpy(tt, lcmodel, 3); tt[3] = 0; kdict = plist_dict_get_item(node, tt); } else { // Populated in older iOS IPSWs kdict = plist_dict_get_item(buildmanifest, "RestoreKernelCaches"); } if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) { plist_t kc = plist_dict_get_item(kdict, "Release"); if (kc && (plist_get_node_type(kc) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_copy(kc)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); plist_dict_set_item(manifest, "KernelCache", comp); plist_dict_set_item(manifest, "RestoreKernelCache", plist_copy(comp)); } } // add ramdisk node = plist_dict_get_item(buildmanifest, "RestoreRamDisks"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update"); // if no "Update" ram disk entry is found try "User" ram disk instead if (!rd && !(client->flags & FLAG_ERASE)) { rd = plist_dict_get_item(node, "User"); // also, set the ERASE flag since we actually change the restore variant client->flags |= FLAG_ERASE; } if (rd && (plist_get_node_type(rd) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_copy(rd)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); plist_dict_set_item(manifest, "RestoreRamDisk", comp); } } // add OS filesystem node = plist_dict_get_item(buildmanifest, "SystemRestoreImages"); if (!node) { error("ERROR: missing SystemRestoreImages in Restore.plist\n"); } plist_t os = plist_dict_get_item(node, "User"); if (!os) { error("ERROR: missing filesystem in Restore.plist\n"); } else { inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_copy(os)); comp = plist_new_dict(); plist_dict_set_item(comp, "Info", inf); plist_dict_set_item(manifest, "OS", comp); } // add info inf = plist_new_dict(); plist_dict_set_item(inf, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); plist_dict_set_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); plist_dict_set_item(build_identity, "Info", inf); // finally add manifest plist_dict_set_item(build_identity, "Manifest", manifest); } } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity_for_model_with_restore_behavior(buildmanifest, client->device->hardware_model, "Erase"); if (build_identity == NULL) { error("ERROR: Unable to find any build identities\n"); plist_free(buildmanifest); return -1; } } else { build_identity = build_manifest_get_build_identity_for_model_with_restore_behavior(buildmanifest, client->device->hardware_model, "Update"); if (!build_identity) { build_identity = build_manifest_get_build_identity_for_model(buildmanifest, client->device->hardware_model); } } /* print information about current build identity */ build_identity_print_information(build_identity); if (client->mode->index == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); char *device_version = NULL; if (pver) { plist_get_string_val(pver, &device_version); plist_free(pver); } if (device_version && (compare_versions(device_version, client->version) > 0)) { if (client->flags & FLAG_INTERACTIVE) { char input[64]; char spaces[16]; int num_spaces = 13 - strlen(client->version) - strlen(device_version); memset(spaces, ' ', num_spaces); spaces[num_spaces] = '\0'; printf("################################ [ WARNING ] #################################\n" "# You are trying to DOWNGRADE a %s device with an IPSW for %s while%s #\n" "# trying to preserve the user data (Upgrade restore). This *might* work, but #\n" "# there is a VERY HIGH chance it might FAIL BADLY with COMPLETE DATA LOSS. #\n" "# Hit CTRL+C now if you want to abort the restore. #\n" "# If you want to take the risk (and have a backup of your important data!) #\n" "# type YES and press ENTER to continue. You have been warned. #\n" "##############################################################################\n", device_version, client->version, spaces); while (1) { printf("> "); fflush(stdout); fflush(stdin); input[0] = '\0'; get_user_input(input, 63, 0); if (client->flags & FLAG_QUIT) { return -1; } if (*input != '\0' && !strcmp(input, "YES")) { break; } else { printf("Invalid input. Please type YES or hit CTRL+C to abort.\n"); continue; } } } } free(device_version); } if (client->flags & FLAG_ERASE && client->flags & FLAG_INTERACTIVE) { char input[64]; printf("################################ [ WARNING ] #################################\n" "# You are about to perform an *ERASE* restore. ALL DATA on the target device #\n" "# will be IRREVERSIBLY DESTROYED. If you want to update your device without #\n" "# erasing the user data, hit CTRL+C now and restart without -e or --erase #\n" "# command line switch. #\n" "# If you want to continue with the ERASE, please type YES and press ENTER. #\n" "##############################################################################\n"); while (1) { printf("> "); fflush(stdout); fflush(stdin); input[0] = '\0'; get_user_input(input, 63, 0); if (client->flags & FLAG_QUIT) { return -1; } if (*input != '\0' && !strcmp(input, "YES")) { break; } else { printf("Invalid input. Please type YES or hit CTRL+C to abort.\n"); continue; } } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.0); /* check if all components we need are actually there */ info("Checking IPSW for required components...\n"); if (build_identity_check_components_in_ipsw(build_identity, client->ipsw) < 0) { error("ERROR: Could not find all required components in IPSW %s\n", client->ipsw); return -1; } info("All required components found in IPSW\n"); // Get filesystem name from build identity char* fsname = NULL; if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { error("ERROR: Unable get path for filesystem component\n"); return -1; } // check if we already have an extracted filesystem int delete_fs = 0; char* filesystem = NULL; struct stat st; memset(&st, '\0', sizeof(struct stat)); char tmpf[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &st) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(tmpf, client->cache_dir); strcat(tmpf, "/"); char *ipswtmp = strdup(client->ipsw); strcat(tmpf, basename(ipswtmp)); free(ipswtmp); } else { strcpy(tmpf, client->ipsw); } if (!ipsw_is_directory(client->ipsw)) { // strip off file extension if given ipsw is not a directory char* s = tmpf + strlen(tmpf) - 1; char* p = s; while (*p != '\0' && *p != '.' && *p != '/' && *p != '\\') p--; if (s - p < 6) { if (*p == '.') { *p = '\0'; } } } if (stat(tmpf, &st) < 0) { __mkdir(tmpf, 0755); } strcat(tmpf, "/"); strcat(tmpf, fsname); memset(&st, '\0', sizeof(struct stat)); if (stat(tmpf, &st) == 0) { uint64_t fssize = 0; ipsw_get_file_size(client->ipsw, fsname, &fssize); if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { info("Using cached filesystem from '%s'\n", tmpf); filesystem = strdup(tmpf); } } if (!filesystem && !(client->flags & FLAG_SHSHONLY)) { char extfn[1024]; strcpy(extfn, tmpf); strcat(extfn, ".extract"); char lockfn[1024]; strcpy(lockfn, tmpf); strcat(lockfn, ".lock"); lock_info_t li; lock_file(lockfn, &li); FILE* extf = NULL; if (access(extfn, F_OK) != 0) { extf = fopen(extfn, "wb"); } unlock_file(&li); if (!extf) { // use temp filename filesystem = get_temp_filename("ipsw_"); if (!filesystem) { error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname); filesystem = strdup(fsname); } delete_fs = 1; } else { // use .extract as filename filesystem = strdup(extfn); fclose(extf); } remove(lockfn); // Extract filesystem from IPSW info("Extracting filesystem from IPSW: %s\n", fsname); if (ipsw_extract_to_file_with_progress(client->ipsw, fsname, filesystem, 1) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); info("Removing %s\n", filesystem); unlink(filesystem); return -1; } if (strstr(filesystem, ".extract")) { // rename .extract to remove(tmpf); rename(filesystem, tmpf); free(filesystem); filesystem = strdup(tmpf); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.2); /* retrieve shsh blobs if required */ if (tss_enabled) { int stashbag_commit_required = 0; debug("Getting device's ECID for TSS request\n"); /* fetch the device's ECID for the TSS request */ if (get_ecid(client, &client->ecid) < 0) { error("ERROR: Unable to find device ECID\n"); return -1; } info("Found ECID %" PRIu64 "\n", client->ecid); if (client->mode->index == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { plist_t node = normal_get_lockdown_value(client, NULL, "HasSiDP"); uint8_t needs_preboard = 0; if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &needs_preboard); } if (needs_preboard) { info("Checking if device requires stashbag...\n"); plist_t manifest; if (get_preboard_manifest(client, build_identity, &manifest) < 0) { error("ERROR: Unable to create preboard manifest.\n"); return -1; } debug("DEBUG: creating stashbag...\n"); int err = normal_handle_create_stashbag(client, manifest); if (err < 0) { if (err == -2) { error("ERROR: Could not create stashbag (timeout).\n"); } else { error("ERROR: An error occurred while creating the stashbag.\n"); } return -1; } else if (err == 1) { stashbag_commit_required = 1; } plist_free(manifest); } } if (client->build_major > 8) { unsigned char* nonce = NULL; int nonce_size = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { /* the first nonce request with older firmware releases can fail and it's OK */ info("NOTE: Unable to get nonce from device\n"); } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } } if (client->flags & FLAG_QUIT) { return -1; } if (get_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } if (stashbag_commit_required) { plist_t ticket = plist_dict_get_item(client->tss, "ApImg4Ticket"); if (!ticket || plist_get_node_type(ticket) != PLIST_DATA) { error("ERROR: Missing ApImg4Ticket in TSS response for stashbag commit\n"); return -1; } info("Committing stashbag...\n"); int err = normal_handle_commit_stashbag(client, ticket); if (err < 0) { error("ERROR: Could not commit stashbag (%d). Aborting.\n", err); return -1; } } } if (client->flags & FLAG_QUIT) { if (delete_fs && filesystem) unlink(filesystem); return -1; } if (client->flags & FLAG_SHSHONLY) { if (!tss_enabled) { info("This device does not require a TSS record\n"); return 0; } if (!client->tss) { error("ERROR: could not fetch TSS record\n"); plist_free(buildmanifest); return -1; } else { char *bin = NULL; uint32_t blen = 0; plist_to_bin(client->tss, &bin, &blen); if (bin) { char zfn[1024]; if (client->cache_dir) { strcpy(zfn, client->cache_dir); strcat(zfn, "/shsh"); } else { strcpy(zfn, "shsh"); } mkdir_with_parents(zfn, 0755); sprintf(zfn+strlen(zfn), "/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); gzwrite(zf, bin, blen); gzclose(zf); info("SHSH saved to '%s'\n", zfn); } else { info("SHSH '%s' already present.\n", zfn); } free(bin); } else { error("ERROR: could not get TSS record data\n"); } plist_free(client->tss); plist_free(buildmanifest); return 0; } } /* verify if we have tss records if required */ if ((tss_enabled) && (client->tss == NULL)) { error("ERROR: Unable to proceed without a TSS record.\n"); plist_free(buildmanifest); return -1; } if ((tss_enabled) && client->tss) { /* fix empty dicts */ fixup_tss(client->tss); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.25); if (client->flags & FLAG_QUIT) { if (delete_fs && filesystem) unlink(filesystem); return -1; } // if the device is in normal mode, place device into recovery mode if (client->mode->index == MODE_NORMAL) { info("Entering recovery mode...\n"); if (normal_enter_recovery(client) < 0) { error("ERROR: Unable to place device into recovery mode from normal mode\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -5; } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); if (client->flags & FLAG_QUIT) { if (delete_fs && filesystem) unlink(filesystem); return -1; } if (client->mode->index == MODE_DFU) { // if the device is in DFU mode, place it into recovery mode dfu_client_free(client); recovery_client_free(client); if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { if (delete_fs && filesystem) unlink(filesystem); return -1; } info("exploiting with limera1n\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); if (delete_fs && filesystem) unlink(filesystem); return -1; } dfu_client_free(client); info("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode from DFU mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } } else if (client->mode->index == MODE_RECOVERY) { // device is in recovery mode if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) { if (!client->image4supported) { /* send ApTicket */ if (recovery_send_ticket(client) < 0) { error("ERROR: Unable to send APTicket\n"); if (delete_fs && filesystem) unlink(filesystem); return -2; } } } mutex_lock(&client->device_event_mutex); /* now we load the iBEC */ if (recovery_send_ibec(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBEC\n"); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); debug("Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_UNKNOWN] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Possibly invalid iBEC. Reset device and try again.\n"); } if (delete_fs && filesystem) unlink(filesystem); return -2; } debug("Waiting for device to reconnect in recovery mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode != &idevicerestore_modes[MODE_RECOVERY] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in recovery mode. Possibly invalid iBEC. Reset device and try again.\n"); } if (delete_fs && filesystem) unlink(filesystem); return -2; } mutex_unlock(&client->device_event_mutex); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->flags & FLAG_QUIT) { if (delete_fs && filesystem) unlink(filesystem); return -1; } if (!client->image4supported && (client->build_major > 8)) { // we need another tss request with nonce. unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); if (delete_fs && filesystem) unlink(filesystem); return -2; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } fixup_tss(client->tss); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); if (client->flags & FLAG_QUIT) { if (delete_fs && filesystem) unlink(filesystem); return -1; } // now finally do the magic to put the device into restore mode if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { error("ERROR: could not retrieve device serial number. Can't continue.\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.9); if (client->mode->index != MODE_RESTORE) { mutex_lock(&client->device_event_mutex); info("Waiting for device to enter restore mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 180000); if (client->mode != &idevicerestore_modes[MODE_RESTORE] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Device failed to enter restore mode.\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } mutex_unlock(&client->device_event_mutex); } // device is finally in restore mode, let's do this if (client->mode->index == MODE_RESTORE) { if ((client->flags & FLAG_NO_RESTORE) != 0) { info("Device is now in restore mode. Exiting as requested."); return 0; } client->ignore_device_add_events = 1; info("About to restore device... \n"); result = restore_device(client, build_identity, filesystem); if (result < 0) { error("ERROR: Unable to restore device\n"); if (delete_fs && filesystem) unlink(filesystem); return result; } } info("Cleaning up...\n"); if (delete_fs && filesystem) unlink(filesystem); /* special handling of older AppleTVs as they enter Recovery mode on boot when plugged in to USB */ if ((strncmp(client->device->product_type, "AppleTV", 7) == 0) && (client->device->product_type[7] < '5')) { if (recovery_client_new(client) == 0) { if (recovery_set_autoboot(client, 1) == 0) { recovery_send_reset(client); } else { error("Setting auto-boot failed?!\n"); } } else { error("Could not connect to device in recovery mode.\n"); } } info("DONE\n"); if (result == 0) { idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); } if (buildmanifest) plist_free(buildmanifest); if (build_identity) plist_free(build_identity); return result; } struct idevicerestore_client_t* idevicerestore_client_new(void) { struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t)); if (client == NULL) { error("ERROR: Out of memory\n"); return NULL; } memset(client, '\0', sizeof(struct idevicerestore_client_t)); client->mode = &idevicerestore_modes[MODE_UNKNOWN]; mutex_init(&client->device_event_mutex); cond_init(&client->device_event_cond); return client; } void idevicerestore_client_free(struct idevicerestore_client_t* client) { if (!client) { return; } if (client->irecv_e_ctx) { irecv_device_event_unsubscribe(client->irecv_e_ctx); } if (client->idevice_e_ctx) { idevice_event_unsubscribe(); } cond_destroy(&client->device_event_cond); mutex_destroy(&client->device_event_mutex); if (client->tss_url) { free(client->tss_url); } if (client->version_data) { plist_free(client->version_data); } if (client->nonce) { free(client->nonce); } if (client->udid) { free(client->udid); } if (client->srnm) { free(client->srnm); } if (client->ipsw) { free(client->ipsw); } if (client->version) { free(client->version); } if (client->build) { free(client->build); } if (client->restore_boot_args) { free(client->restore_boot_args); } if (client->cache_dir) { free(client->cache_dir); } if (client->root_ticket) { free(client->root_ticket); } free(client); } void idevicerestore_set_ecid(struct idevicerestore_client_t* client, uint64_t ecid) { if (!client) return; client->ecid = ecid; } void idevicerestore_set_udid(struct idevicerestore_client_t* client, const char* udid) { if (!client) return; if (client->udid) { free(client->udid); client->udid = NULL; } if (udid) { client->udid = strdup(udid); } } void idevicerestore_set_flags(struct idevicerestore_client_t* client, int flags) { if (!client) return; client->flags = flags; } void idevicerestore_set_ipsw(struct idevicerestore_client_t* client, const char* path) { if (!client) return; if (client->ipsw) { free(client->ipsw); client->ipsw = NULL; } if (path) { client->ipsw = strdup(path); } } void idevicerestore_set_cache_path(struct idevicerestore_client_t* client, const char* path) { if (!client) return; if (client->cache_dir) { free(client->cache_dir); client->cache_dir = NULL; } if (path) { client->cache_dir = strdup(path); } } void idevicerestore_set_progress_callback(struct idevicerestore_client_t* client, idevicerestore_progress_cb_t cbfunc, void* userdata) { if (!client) return; client->progress_cb = cbfunc; client->progress_cb_data = userdata; } #ifndef IDEVICERESTORE_NOMAIN static struct idevicerestore_client_t* idevicerestore_client = NULL; static void handle_signal(int sig) { if (idevicerestore_client) { idevicerestore_client->flags |= FLAG_QUIT; ipsw_cancel(); } } void plain_progress_cb(int step, double step_progress, void* userdata) { printf("progress: %u %f\n", step, step_progress); fflush(stdout); } int main(int argc, char* argv[]) { int opt = 0; int optindex = 0; char* ipsw = NULL; int result = 0; struct idevicerestore_client_t* client = idevicerestore_client_new(); if (client == NULL) { error("ERROR: could not create idevicerestore client\n"); return -1; } idevicerestore_client = client; #ifdef WIN32 signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); signal(SIGABRT, handle_signal); #else struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = handle_signal; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); #endif if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) { client->flags &= ~FLAG_INTERACTIVE; } else { client->flags |= FLAG_INTERACTIVE; } while ((opt = getopt_long(argc, argv, "dhcesxtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv, 0); return 0; case 'd': client->flags |= FLAG_DEBUG; break; case 'e': client->flags |= FLAG_ERASE; break; case 'c': client->flags |= FLAG_CUSTOM; break; case 's': client->tss_url = strdup("http://cydia.saurik.com/TSS/controller?action=2"); break; case 'x': client->flags |= FLAG_EXCLUDE; break; case 'l': client->flags |= FLAG_LATEST; break; case 'i': if (optarg) { char* tail = NULL; client->ecid = strtoull(optarg, &tail, 0); if (tail && (tail[0] != '\0')) { client->ecid = 0; } if (client->ecid == 0) { error("ERROR: Could not parse ECID from '%s'\n", optarg); return -1; } } break; case 'u': if (!*optarg) { error("ERROR: UDID must not be empty!\n"); usage(argc, argv, 1); return -1; } client->udid = strdup(optarg); break; case 't': client->flags |= FLAG_SHSHONLY; break; case 'k': idevicerestore_keep_pers = 1; break; case 'p': client->flags |= FLAG_PWN; break; case 'n': client->flags |= FLAG_NOACTION; break; case 'C': client->cache_dir = strdup(optarg); break; case 'y': client->flags &= ~FLAG_INTERACTIVE; break; case 'P': idevicerestore_set_progress_callback(client, plain_progress_cb, NULL); break; case 'R': client->flags |= FLAG_ALLOW_RESTORE_MODE; break; case 'z': client->flags |= FLAG_NO_RESTORE; break; case 'v': info("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); return 0; case 'T': { size_t root_ticket_len = 0; unsigned char* root_ticket = NULL; if (read_file(optarg, (void**)&root_ticket, &root_ticket_len) != 0) { return -1; } client->root_ticket = root_ticket; client->root_ticket_len = (int)root_ticket_len; info("Using ApTicket found at %s length %u\n", optarg, client->root_ticket_len); break; } default: usage(argc, argv, 1); return -1; } } if (((argc-optind) == 1) || (client->flags & FLAG_PWN) || (client->flags & FLAG_LATEST)) { argc -= optind; argv += optind; ipsw = argv[0]; } else { usage(argc, argv, 1); return -1; } if ((client->flags & FLAG_LATEST) && (client->flags & FLAG_CUSTOM)) { error("ERROR: You can't use --custom and --latest options at the same time.\n"); return -1; } if (ipsw) { client->ipsw = strdup(ipsw); } curl_global_init(CURL_GLOBAL_ALL); result = idevicerestore_start(client); idevicerestore_client_free(client); curl_global_cleanup(); return result; } #endif int check_mode(struct idevicerestore_client_t* client) { int mode = MODE_UNKNOWN; int dfumode = MODE_UNKNOWN; if (recovery_check_mode(client) == 0) { mode = MODE_RECOVERY; } else if (dfu_check_mode(client, &dfumode) == 0) { mode = dfumode; } else if (normal_check_mode(client) == 0) { mode = MODE_NORMAL; } else if (restore_check_mode(client) == 0) { mode = MODE_RESTORE; } if (mode == MODE_UNKNOWN) { client->mode = NULL; } else { client->mode = &idevicerestore_modes[mode]; } return mode; } irecv_device_t get_irecv_device(struct idevicerestore_client_t *client) { int mode = MODE_UNKNOWN; if (client->mode) { mode = client->mode->index; } switch (mode) { case MODE_RESTORE: return restore_get_irecv_device(client); case MODE_NORMAL: return normal_get_irecv_device(client); case MODE_DFU: case MODE_RECOVERY: return dfu_get_irecv_device(client); default: return NULL; } } int is_image4_supported(struct idevicerestore_client_t* client) { int res = 0; int mode = MODE_UNKNOWN; if (client->mode) { mode = client->mode->index; } switch (mode) { case MODE_NORMAL: res = normal_is_image4_supported(client); break; case MODE_RESTORE: res = restore_is_image4_supported(client); break; case MODE_DFU: res = dfu_is_image4_supported(client); break; case MODE_RECOVERY: res = recovery_is_image4_supported(client); break; default: error("ERROR: Device is in an invalid state\n"); return 0; } return res; } int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) { int mode = MODE_UNKNOWN; if (client->mode) { mode = client->mode->index; } switch (mode) { case MODE_NORMAL: if (normal_get_ecid(client, ecid) < 0) { *ecid = 0; return -1; } break; case MODE_DFU: if (dfu_get_ecid(client, ecid) < 0) { *ecid = 0; return -1; } break; case MODE_RECOVERY: if (recovery_get_ecid(client, ecid) < 0) { *ecid = 0; return -1; } break; default: error("ERROR: Device is in an invalid state\n"); *ecid = 0; return -1; } return 0; } int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { int mode = MODE_UNKNOWN; *nonce = NULL; *nonce_size = 0; info("Getting ApNonce "); if (client->mode) { mode = client->mode->index; } switch (mode) { case MODE_NORMAL: info("in normal mode... "); if (normal_get_ap_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; case MODE_DFU: info("in dfu mode... "); if (dfu_get_ap_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; case MODE_RECOVERY: info("in recovery mode... "); if (recovery_get_ap_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; default: info("failed\n"); error("ERROR: Device is in an invalid state\n"); return -1; } int i = 0; for (i = 0; i < *nonce_size; i++) { info("%02x ", (*nonce)[i]); } info("\n"); return 0; } int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { int mode = MODE_UNKNOWN; *nonce = NULL; *nonce_size = 0; info("Getting SepNonce "); if (client->mode) { mode = client->mode->index; } switch (mode) { case MODE_NORMAL: info("in normal mode... "); if (normal_get_sep_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; case MODE_DFU: info("in dfu mode... "); if (dfu_get_sep_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; case MODE_RECOVERY: info("in recovery mode... "); if (recovery_get_sep_nonce(client, nonce, nonce_size) < 0) { info("failed\n"); return -1; } break; default: info("failed\n"); error("ERROR: Device is in an invalid state\n"); return -1; } int i = 0; for (i = 0; i < *nonce_size; i++) { info("%02x ", (*nonce)[i]); } info("\n"); return 0; } plist_t build_manifest_get_build_identity_for_model_with_restore_behavior(plist_t build_manifest, const char *hardware_model, const char *behavior) { plist_t build_identities_array = plist_dict_get_item(build_manifest, "BuildIdentities"); if (!build_identities_array || plist_get_node_type(build_identities_array) != PLIST_ARRAY) { error("ERROR: Unable to find build identities node\n"); return NULL; } uint32_t i; for (i = 0; i < plist_array_get_size(build_identities_array); i++) { plist_t ident = plist_array_get_item(build_identities_array, i); if (!ident || plist_get_node_type(ident) != PLIST_DICT) { continue; } plist_t info_dict = plist_dict_get_item(ident, "Info"); if (!info_dict || plist_get_node_type(ident) != PLIST_DICT) { continue; } plist_t devclass = plist_dict_get_item(info_dict, "DeviceClass"); if (!devclass || plist_get_node_type(devclass) != PLIST_STRING) { continue; } char *str = NULL; plist_get_string_val(devclass, &str); if (strcasecmp(str, hardware_model) != 0) { free(str); continue; } free(str); str = NULL; if (behavior) { plist_t rbehavior = plist_dict_get_item(info_dict, "RestoreBehavior"); if (!rbehavior || plist_get_node_type(rbehavior) != PLIST_STRING) { continue; } plist_get_string_val(rbehavior, &str); if (strcasecmp(str, behavior) != 0) { free(str); continue; } else { free(str); return plist_copy(ident); } free(str); } else { return plist_copy(ident); } } return NULL; } plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, const char *hardware_model) { return build_manifest_get_build_identity_for_model_with_restore_behavior(build_manifest, hardware_model, NULL); } int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest) { plist_t request = NULL; *manifest = NULL; if (!client->image4supported) { return -1; } /* populate parameters */ plist_t parameters = plist_new_dict(); plist_t overrides = plist_new_dict(); plist_dict_set_item(overrides, "@APTicket", plist_new_bool(1)); plist_dict_set_item(overrides, "ApProductionMode", plist_new_uint(0)); plist_dict_set_item(overrides, "ApSecurityDomain", plist_new_uint(0)); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(0)); plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(0)); plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1)); tss_parameters_add_from_manifest(parameters, build_identity); /* create basic request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); plist_free(parameters); return -1; } /* add common tags from manifest */ if (tss_request_add_common_tags(request, parameters, overrides) < 0) { error("ERROR: Unable to add common tags\n"); plist_free(request); plist_free(parameters); return -1; } plist_dict_set_item(parameters, "_OnlyFWComponents", plist_new_bool(1)); /* add tags from manifest */ if (tss_request_add_ap_tags(request, parameters, NULL) < 0) { error("ERROR: Unable to add ap tags\n"); plist_free(request); plist_free(parameters); return -1; } plist_t local_manifest = NULL; int res = img4_create_local_manifest(request, &local_manifest); *manifest = local_manifest; plist_free(request); plist_free(parameters); plist_free(overrides); return res; } int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; if ((client->build_major <= 8) || (client->flags & FLAG_CUSTOM)) { error("checking for local shsh\n"); /* first check for local copy */ char zfn[1024]; if (client->version) { if (client->cache_dir) { sprintf(zfn, "%s/shsh/%" PRIu64 "-%s-%s.shsh", client->cache_dir, client->ecid, client->device->product_type, client->version); } else { sprintf(zfn, "shsh/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); } struct stat fst; if (stat(zfn, &fst) == 0) { gzFile zf = gzopen(zfn, "rb"); if (zf) { int blen = 0; int readsize = 16384; int bufsize = readsize; char* bin = (char*)malloc(bufsize); char* p = bin; do { int bytes_read = gzread(zf, p, readsize); if (bytes_read < 0) { fprintf(stderr, "Error reading gz compressed data\n"); exit(EXIT_FAILURE); } blen += bytes_read; if (bytes_read < readsize) { if (gzeof(zf)) { bufsize += bytes_read; break; } } bufsize += readsize; bin = realloc(bin, bufsize); p = bin + blen; } while (!gzeof(zf)); gzclose(zf); if (blen > 0) { if (memcmp(bin, "bplist00", 8) == 0) { plist_from_bin(bin, blen, tss); } else { plist_from_xml(bin, blen, tss); } } free(bin); } } else { error("no local file %s\n", zfn); } } else { error("No version found?!\n"); } } if (*tss) { info("Using cached SHSH\n"); return 0; } else { info("Trying to fetch new SHSH blob\n"); } /* populate parameters */ plist_t parameters = plist_new_dict(); plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); if (client->nonce) { plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); if (sep_nonce) { plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size)); free(sep_nonce); } plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); if (client->image4supported) { plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1)); } else { plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } tss_parameters_add_from_manifest(parameters, build_identity); /* create basic request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); plist_free(parameters); return -1; } /* add common tags from manifest */ if (tss_request_add_common_tags(request, parameters, NULL) < 0) { error("ERROR: Unable to add common tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } /* add tags from manifest */ if (tss_request_add_ap_tags(request, parameters, NULL) < 0) { error("ERROR: Unable to add common tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } if (client->image4supported) { /* add personalized parameters */ if (tss_request_add_ap_img4_tags(request, parameters) < 0) { error("ERROR: Unable to add img4 tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } } else { /* add personalized parameters */ if (tss_request_add_ap_img3_tags(request, parameters) < 0) { error("ERROR: Unable to add img3 tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } } if (client->mode->index == MODE_NORMAL) { /* normal mode; request baseband ticket aswell */ plist_t pinfo = NULL; normal_get_preflight_info(client, &pinfo); if (pinfo) { plist_t node; node = plist_dict_get_item(pinfo, "Nonce"); if (node) { plist_dict_set_item(parameters, "BbNonce", plist_copy(node)); } node = plist_dict_get_item(pinfo, "ChipID"); if (node) { plist_dict_set_item(parameters, "BbChipID", plist_copy(node)); } node = plist_dict_get_item(pinfo, "CertID"); if (node) { plist_dict_set_item(parameters, "BbGoldCertId", plist_copy(node)); } node = plist_dict_get_item(pinfo, "ChipSerialNo"); if (node) { plist_dict_set_item(parameters, "BbSNUM", plist_copy(node)); } /* add baseband parameters */ tss_request_add_baseband_tags(request, parameters, NULL); node = plist_dict_get_item(pinfo, "EUICCChipID"); uint64_t euiccchipid = 0; if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &euiccchipid); plist_dict_set_item(parameters, "eUICC,ChipID", plist_copy(node)); } if (euiccchipid >= 5) { node = plist_dict_get_item(pinfo, "EUICCCSN"); if (node) { plist_dict_set_item(parameters, "eUICC,EID", plist_copy(node)); } node = plist_dict_get_item(pinfo, "EUICCCertIdentifier"); if (node) { plist_dict_set_item(parameters, "eUICC,RootKeyIdentifier", plist_copy(node)); } node = plist_dict_get_item(pinfo, "EUICCGoldNonce"); if (node) { plist_dict_set_item(parameters, "EUICCGoldNonce", plist_copy(node)); } node = plist_dict_get_item(pinfo, "EUICCMainNonce"); if (node) { plist_dict_set_item(parameters, "EUICCMainNonce", plist_copy(node)); } /* add vinyl parameters */ tss_request_add_vinyl_tags(request, parameters, NULL); } } client->preflight_info = pinfo; } /* send request and grab response */ response = tss_request_send(request, client->tss_url); if (response == NULL) { info("ERROR: Unable to send TSS request\n"); plist_free(request); plist_free(parameters); return -1; } info("Received SHSH blobs\n"); plist_free(request); plist_free(parameters); *tss = response; return 0; } void fixup_tss(plist_t tss) { plist_t node; plist_t node2; node = plist_dict_get_item(tss, "RestoreLogo"); if (node && (plist_get_node_type(node) == PLIST_DICT) && (plist_dict_get_size(node) == 0)) { node2 = plist_dict_get_item(tss, "AppleLogo"); if (node2 && (plist_get_node_type(node2) == PLIST_DICT)) { plist_dict_remove_item(tss, "RestoreLogo"); plist_dict_set_item(tss, "RestoreLogo", plist_copy(node2)); } } node = plist_dict_get_item(tss, "RestoreDeviceTree"); if (node && (plist_get_node_type(node) == PLIST_DICT) && (plist_dict_get_size(node) == 0)) { node2 = plist_dict_get_item(tss, "DeviceTree"); if (node2 && (plist_get_node_type(node2) == PLIST_DICT)) { plist_dict_remove_item(tss, "RestoreDeviceTree"); plist_dict_set_item(tss, "RestoreDeviceTree", plist_copy(node2)); } } node = plist_dict_get_item(tss, "RestoreKernelCache"); if (node && (plist_get_node_type(node) == PLIST_DICT) && (plist_dict_get_size(node) == 0)) { node2 = plist_dict_get_item(tss, "KernelCache"); if (node2 && (plist_get_node_type(node2) == PLIST_DICT)) { plist_dict_remove_item(tss, "RestoreKernelCache"); plist_dict_set_item(tss, "RestoreKernelCache", plist_copy(node2)); } } } int build_manifest_get_identity_count(plist_t build_manifest) { // fetch build identities array from BuildManifest plist_t build_identities_array = plist_dict_get_item(build_manifest, "BuildIdentities"); if (!build_identities_array || plist_get_node_type(build_identities_array) != PLIST_ARRAY) { error("ERROR: Unable to find build identities node\n"); return -1; } // check and make sure this identity exists in buildmanifest return plist_array_get_size(build_identities_array); } int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) { char* component_name = NULL; if (!ipsw || !path || !component_data || !component_size) { return -1; } component_name = strrchr(path, '/'); if (component_name != NULL) component_name++; else component_name = (char*) path; info("Extracting %s...\n", component_name); if (ipsw_extract_to_memory(ipsw, path, component_data, component_size) < 0) { error("ERROR: Unable to extract %s from %s\n", component_name, ipsw); return -1; } return 0; } int personalize_component(const char *component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size) { unsigned char* component_blob = NULL; unsigned int component_blob_size = 0; unsigned char* stitched_component = NULL; unsigned int stitched_component_size = 0; if (tss_response && tss_response_get_ap_img4_ticket(tss_response, &component_blob, &component_blob_size) == 0) { /* stitch ApImg4Ticket into IMG4 file */ img4_stitch_component(component_name, component_data, component_size, component_blob, component_blob_size, &stitched_component, &stitched_component_size); } else { /* try to get blob for current component from tss response */ if (tss_response && tss_response_get_blob_by_entry(tss_response, component_name, &component_blob) < 0) { debug("NOTE: No SHSH blob found for component %s\n", component_name); } if (component_blob != NULL) { if (img3_stitch_component(component_name, component_data, component_size, component_blob, 64, &stitched_component, &stitched_component_size) < 0) { error("ERROR: Unable to replace %s IMG3 signature\n", component_name); free(component_blob); return -1; } } else { info("Not personalizing component %s...\n", component_name); stitched_component = (unsigned char*)malloc(component_size); if (stitched_component) { stitched_component_size = component_size; memcpy(stitched_component, component_data, component_size); } } } free(component_blob); if (idevicerestore_keep_pers) { write_file(component_name, stitched_component, stitched_component_size); } *personalized_component = stitched_component; *personalized_component_size = stitched_component_size; return 0; } int build_manifest_check_compatibility(plist_t build_manifest, const char* product) { int res = -1; plist_t node = plist_dict_get_item(build_manifest, "SupportedProductTypes"); if (!node || (plist_get_node_type(node) != PLIST_ARRAY)) { debug("%s: ERROR: SupportedProductTypes key missing\n", __func__); debug("%s: WARNING: If attempting to install iPhoneOS 2.x, be advised that Restore.plist does not contain the", __func__); debug("%s: WARNING: key 'SupportedProductTypes'. Recommendation is to manually add it to the Restore.plist.", __func__); return -1; } uint32_t pc = plist_array_get_size(node); uint32_t i; for (i = 0; i < pc; i++) { plist_t prod = plist_array_get_item(node, i); if (plist_get_node_type(prod) == PLIST_STRING) { char *val = NULL; plist_get_string_val(prod, &val); if (val && (strcmp(val, product) == 0)) { res = 0; free(val); break; } } } return res; } void build_manifest_get_version_information(plist_t build_manifest, struct idevicerestore_client_t* client) { plist_t node = NULL; client->version = NULL; client->build = NULL; node = plist_dict_get_item(build_manifest, "ProductVersion"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ProductVersion node\n"); return; } plist_get_string_val(node, &client->version); node = plist_dict_get_item(build_manifest, "ProductBuildVersion"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ProductBuildVersion node\n"); return; } plist_get_string_val(node, &client->build); client->build_major = strtoul(client->build, NULL, 10); } void build_identity_print_information(plist_t build_identity) { char* value = NULL; plist_t info_node = NULL; plist_t node = NULL; info_node = plist_dict_get_item(build_identity, "Info"); if (!info_node || plist_get_node_type(info_node) != PLIST_DICT) { error("ERROR: Unable to find Info node\n"); return; } node = plist_dict_get_item(info_node, "Variant"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find Variant node\n"); return; } plist_get_string_val(node, &value); info("Variant: %s\n", value); free(value); node = plist_dict_get_item(info_node, "RestoreBehavior"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find RestoreBehavior node\n"); return; } plist_get_string_val(node, &value); if (!strcmp(value, "Erase")) info("This restore will erase your device data.\n"); if (!strcmp(value, "Update")) info("This restore will update your device without erasing user data.\n"); free(value); info_node = NULL; node = NULL; } int build_identity_check_components_in_ipsw(plist_t build_identity, const char *ipsw) { plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { return -1; } int res = 0; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); plist_t node = NULL; char *key = NULL; do { node = NULL; key = NULL; plist_dict_next_item(manifest_node, iter, &key, &node); if (key && node) { plist_t path = plist_access_path(node, 2, "Info", "Path"); if (path) { char *comp_path = NULL; plist_get_string_val(path, &comp_path); if (comp_path) { if (!ipsw_file_exists(ipsw, comp_path)) { error("ERROR: %s file %s not found in IPSW\n", key, comp_path); res = -1; } free(comp_path); } } } free(key); } while (node); return res; } int build_identity_has_component(plist_t build_identity, const char* component) { plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { return 0; } plist_t component_node = plist_dict_get_item(manifest_node, component); if (!component_node || plist_get_node_type(component_node) != PLIST_DICT) { return 0; } return 1; } int build_identity_get_component_path(plist_t build_identity, const char* component, char** path) { char* filename = NULL; plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find manifest node\n"); if (filename) free(filename); return -1; } plist_t component_node = plist_dict_get_item(manifest_node, component); if (!component_node || plist_get_node_type(component_node) != PLIST_DICT) { error("ERROR: Unable to find component node for %s\n", component); if (filename) free(filename); return -1; } plist_t component_info_node = plist_dict_get_item(component_node, "Info"); if (!component_info_node || plist_get_node_type(component_info_node) != PLIST_DICT) { error("ERROR: Unable to find component info node for %s\n", component); if (filename) free(filename); return -1; } plist_t component_info_path_node = plist_dict_get_item(component_info_node, "Path"); if (!component_info_path_node || plist_get_node_type(component_info_path_node) != PLIST_STRING) { error("ERROR: Unable to find component info path node for %s\n", component); if (filename) free(filename); return -1; } plist_get_string_val(component_info_path_node, &filename); *path = filename; return 0; } const char* get_component_name(const char* filename) { if (!strncmp(filename, "LLB", 3)) { return "LLB"; } else if (!strncmp(filename, "iBoot", 5)) { return "iBoot"; } else if (!strncmp(filename, "DeviceTree", 10)) { return "DeviceTree"; } else if (!strncmp(filename, "applelogo", 9)) { return "AppleLogo"; } else if (!strncmp(filename, "liquiddetect", 12)) { return "Liquid"; } else if (!strncmp(filename, "lowpowermode", 12)) { return "LowPowerWallet0"; } else if (!strncmp(filename, "recoverymode", 12)) { return "RecoveryMode"; } else if (!strncmp(filename, "batterylow0", 11)) { return "BatteryLow0"; } else if (!strncmp(filename, "batterylow1", 11)) { return "BatteryLow1"; } else if (!strncmp(filename, "glyphcharging", 13)) { return "BatteryCharging"; } else if (!strncmp(filename, "glyphplugin", 11)) { return "BatteryPlugin"; } else if (!strncmp(filename, "batterycharging0", 16)) { return "BatteryCharging0"; } else if (!strncmp(filename, "batterycharging1", 16)) { return "BatteryCharging1"; } else if (!strncmp(filename, "batteryfull", 11)) { return "BatteryFull"; } else if (!strncmp(filename, "needservice", 11)) { return "NeedService"; } else if (!strncmp(filename, "SCAB", 4)) { return "SCAB"; } else if (!strncmp(filename, "sep-firmware", 12)) { return "RestoreSEP"; } else { error("WARNING: Unhandled component '%s'", filename); return NULL; } } idevicerestore-1.0.0/src/idevicerestore.h000066400000000000000000000117571367173617500205330ustar00rootroot00000000000000/* * idevicerestore.h * Restore device firmware and filesystem * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_H #define IDEVICERESTORE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include // the flag with value 1 is reserved for internal use only. don't use it. #define FLAG_DEBUG (1 << 1) #define FLAG_ERASE (1 << 2) #define FLAG_CUSTOM (1 << 3) #define FLAG_EXCLUDE (1 << 4) #define FLAG_PWN (1 << 5) #define FLAG_NOACTION (1 << 6) #define FLAG_SHSHONLY (1 << 7) #define FLAG_LATEST (1 << 8) #define FLAG_INTERACTIVE (1 << 9) #define FLAG_ALLOW_RESTORE_MODE (1 << 10) #define FLAG_NO_RESTORE (1 << 11) struct idevicerestore_client_t; enum { RESTORE_STEP_DETECT = 0, RESTORE_STEP_PREPARE, RESTORE_STEP_UPLOAD_FS, RESTORE_STEP_VERIFY_FS, RESTORE_STEP_FLASH_FW, RESTORE_STEP_FLASH_BB, RESTORE_STEP_FUD, RESTORE_NUM_STEPS }; typedef void (*idevicerestore_progress_cb_t)(int step, double step_progress, void* userdata); struct idevicerestore_client_t* idevicerestore_client_new(void); void idevicerestore_client_free(struct idevicerestore_client_t* client); void idevicerestore_set_ecid(struct idevicerestore_client_t* client, uint64_t ecid); void idevicerestore_set_udid(struct idevicerestore_client_t* client, const char* udid); void idevicerestore_set_flags(struct idevicerestore_client_t* client, int flags); void idevicerestore_set_ipsw(struct idevicerestore_client_t* client, const char* path); void idevicerestore_set_cache_path(struct idevicerestore_client_t* client, const char* path); void idevicerestore_set_progress_callback(struct idevicerestore_client_t* client, idevicerestore_progress_cb_t cbfunc, void* userdata); void idevicerestore_set_info_stream(FILE* strm); void idevicerestore_set_error_stream(FILE* strm); void idevicerestore_set_debug_stream(FILE* strm); int idevicerestore_start(struct idevicerestore_client_t* client); const char* idevicerestore_get_error(void); int check_mode(struct idevicerestore_client_t* client); irecv_device_t get_irecv_device(struct idevicerestore_client_t* client); int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int is_image4_supported(struct idevicerestore_client_t* client); int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); void fixup_tss(plist_t tss); int build_manifest_get_identity_count(plist_t build_manifest); int build_manifest_check_compatibility(plist_t build_manifest, const char* product); void build_manifest_get_version_information(plist_t build_manifest, struct idevicerestore_client_t* client); plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, const char *hardware_model); plist_t build_manifest_get_build_identity_for_model_with_restore_behavior(plist_t build_manifest, const char *hardware_model, const char *behavior); int build_manifest_get_build_count(plist_t build_manifest); void build_identity_print_information(plist_t build_identity); int build_identity_check_components_in_ipsw(plist_t build_identity, const char* ipsw); int build_identity_has_component(plist_t build_identity, const char* component); int build_identity_get_component_path(plist_t build_identity, const char* component, char** path); int ipsw_extract_filesystem(const char* ipsw, plist_t build_identity, char** filesystem); int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size); int personalize_component(const char *component, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size); int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest); const char* get_component_name(const char* filename); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/img3.c000066400000000000000000000312051367173617500163370ustar00rootroot00000000000000/* * img3.c * Functions for handling with Apple's IMG3 format * * Copyright (c) 2012-2013 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "img3.h" #include "common.h" #include "idevicerestore.h" static void img3_free(img3_file* image); static img3_element* img3_parse_element(const unsigned char* data); static void img3_free_element(img3_element* element); static img3_file* img3_parse_file(const unsigned char* data, unsigned int size) { unsigned int data_offset = 0; img3_element* element; img3_header* header = (img3_header*) data; if (header->signature != kImg3Container) { error("ERROR: Invalid IMG3 file\n"); return NULL; } img3_file* image = (img3_file*) malloc(sizeof(img3_file)); if (image == NULL) { error("ERROR: Unable to allocate memory for IMG3 file\n"); return NULL; } memset(image, '\0', sizeof(img3_file)); image->idx_ecid_element = -1; image->idx_shsh_element = -1; image->idx_cert_element = -1; image->header = (img3_header*) malloc(sizeof(img3_header)); if (image->header == NULL) { error("ERROR: Unable to allocate memory for IMG3 header\n"); img3_free(image); return NULL; } memcpy(image->header, data, sizeof(img3_header)); data_offset += sizeof(img3_header); img3_element_header* current = NULL; while (data_offset < size) { current = (img3_element_header*) &data[data_offset]; switch (current->signature) { case kTypeElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse TYPE element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed TYPE element\n"); break; case kDataElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse DATA element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed DATA element\n"); break; case kVersElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse VERS element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed VERS element\n"); break; case kSepoElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse SEPO element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed SEPO element\n"); break; case kBordElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse BORD element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed BORD element\n"); break; case kChipElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse CHIP element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed CHIP element\n"); break; case kKbagElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse first KBAG element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed KBAG element\n"); break; case kEcidElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse ECID element\n"); img3_free(image); return NULL; } image->idx_ecid_element = image->num_elements; image->elements[image->num_elements++] = element; debug("Parsed ECID element\n"); break; case kShshElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse SHSH element\n"); img3_free(image); return NULL; } image->idx_shsh_element = image->num_elements; image->elements[image->num_elements++] = element; debug("Parsed SHSH element\n"); break; case kCertElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse CERT element\n"); img3_free(image); return NULL; } image->idx_cert_element = image->num_elements; image->elements[image->num_elements++] = element; debug("Parsed CERT element\n"); break; case kUnknElement: element = img3_parse_element(&data[data_offset]); if (element == NULL) { error("ERROR: Unable to parse UNKN element\n"); img3_free(image); return NULL; } image->elements[image->num_elements++] = element; debug("Parsed UNKN element\n"); break; default: error("ERROR: Unknown IMG3 element type %08x\n", current->signature); img3_free(image); return NULL; } data_offset += current->full_size; } return image; } static img3_element* img3_parse_element(const unsigned char* data) { img3_element_header* element_header = (img3_element_header*) data; img3_element* element = (img3_element*) malloc(sizeof(img3_element)); if (element == NULL) { error("ERROR: Unable to allocate memory for IMG3 element\n"); return NULL; } memset(element, '\0', sizeof(img3_element)); element->data = (unsigned char*) malloc(element_header->full_size); if (element->data == NULL) { error("ERROR: Unable to allocate memory for IMG3 element data\n"); free(element); return NULL; } memcpy(element->data, data, element_header->full_size); element->header = (img3_element_header*) element->data; element->type = (img3_element_type) element->header->signature; return element; } static void img3_free(img3_file* image) { if (image != NULL) { if (image->header != NULL) { free(image->header); } int i; for (i = 0; i < image->num_elements; i++) { img3_free_element(image->elements[i]); image->elements[i] = NULL; } free(image); image = NULL; } } static void img3_free_element(img3_element* element) { if (element != NULL) { if (element->data != NULL) { free(element->data); element->data = NULL; } free(element); element = NULL; } } static int img3_replace_signature(img3_file* image, const unsigned char* signature) { int i, oldidx; int offset = 0; img3_element* ecid = img3_parse_element(&signature[offset]); if (ecid == NULL || ecid->type != kEcidElement) { error("ERROR: Unable to find ECID element in signature\n"); return -1; } offset += ecid->header->full_size; img3_element* shsh = img3_parse_element(&signature[offset]); if (shsh == NULL || shsh->type != kShshElement) { error("ERROR: Unable to find SHSH element in signature\n"); return -1; } offset += shsh->header->full_size; img3_element* cert = img3_parse_element(&signature[offset]); if (cert == NULL || cert->type != kCertElement) { error("ERROR: Unable to find CERT element in signature\n"); return -1; } offset += cert->header->full_size; if (image->idx_ecid_element >= 0) { img3_free_element(image->elements[image->idx_ecid_element]); image->elements[image->idx_ecid_element] = ecid; } else { if (image->idx_shsh_element >= 0) { // move elements by 1 oldidx = image->idx_shsh_element; for (i = image->num_elements-1; i >= oldidx; i--) { image->elements[i+1] = image->elements[i]; switch (image->elements[i+1]->type) { case kShshElement: image->idx_shsh_element = i+1; break; case kCertElement: image->idx_cert_element = i+1; break; case kEcidElement: image->idx_ecid_element = i+1; break; default: break; } } image->elements[oldidx] = ecid; image->idx_ecid_element = oldidx; image->num_elements++; } else { // append if not found image->elements[image->num_elements] = ecid; image->idx_ecid_element = image->num_elements; image->num_elements++; } } if (image->idx_shsh_element >= 0) { img3_free_element(image->elements[image->idx_shsh_element]); image->elements[image->idx_shsh_element] = shsh; } else { if (image->idx_cert_element >= 0) { // move elements by 1 oldidx = image->idx_cert_element; for (i = image->num_elements-1; i >= oldidx; i--) { image->elements[i+1] = image->elements[i]; switch (image->elements[i+1]->type) { case kShshElement: image->idx_shsh_element = i+1; break; case kCertElement: image->idx_cert_element = i+1; break; case kEcidElement: image->idx_ecid_element = i+1; break; default: break; } } image->elements[oldidx] = shsh; image->idx_shsh_element = oldidx; image->num_elements++; } else { // append if not found image->elements[image->num_elements] = shsh; image->idx_shsh_element = image->num_elements; image->num_elements++; } } if (image->idx_cert_element >= 0) { img3_free_element(image->elements[image->idx_cert_element]); image->elements[image->idx_cert_element] = cert; } else { // append if not found image->elements[image->num_elements] = cert; image->idx_cert_element = image->num_elements; image->num_elements++; } return 0; } static int img3_get_data(img3_file* image, unsigned char** pdata, unsigned int* psize) { int i; int offset = 0; int size = sizeof(img3_header); // Add up the size of the image first so we can allocate our memory for (i = 0; i < image->num_elements; i++) { size += image->elements[i]->header->full_size; } info("reconstructed size: %d\n", size); unsigned char* data = (unsigned char*) malloc(size); if (data == NULL) { error("ERROR: Unable to allocate memory for IMG3 data\n"); return -1; } // Add data to our new header (except shsh_offset) img3_header* header = (img3_header*) data; header->full_size = size; header->signature = image->header->signature; header->data_size = size - sizeof(img3_header); header->image_type = image->header->image_type; offset += sizeof(img3_header); // Copy each section over to the new buffer for (i = 0; i < image->num_elements; i++) { memcpy(&data[offset], image->elements[i]->data, image->elements[i]->header->full_size); if (image->elements[i]->type == kShshElement) { header->shsh_offset = offset - sizeof(img3_header); } offset += image->elements[i]->header->full_size; } if (offset != size) { error("ERROR: Incorrectly sized image data\n"); free(data); *pdata = 0; *psize = 0; return -1; } *pdata = data; *psize = size; return 0; } int img3_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size) { img3_file *img3 = NULL; unsigned char* outbuf = NULL; unsigned int outsize = 0; if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img3_data || !img3_size) { return -1; } info("Personalizing IMG3 component %s...\n", component_name); /* parse current component as img3 */ img3 = img3_parse_file(component_data, component_size); if (img3 == NULL) { error("ERROR: Unable to parse %s IMG3 file\n", component_name); return -1; } if (((img3_element_header*)blob)->full_size != blob_size) { error("ERROR: Invalid blob passed for %s IMG3: The size %d embedded in the blob does not match the passed size of %d\n", component_name, ((img3_element_header*)blob)->full_size, blob_size); img3_free(img3); return -1; } /* personalize the component using the blob */ if (img3_replace_signature(img3, blob) < 0) { error("ERROR: Unable to replace %s IMG3 signature\n", component_name); img3_free(img3); return -1; } /* get the img3 file as data */ if (img3_get_data(img3, &outbuf, &outsize) < 0) { error("ERROR: Unable to reconstruct %s IMG3\n", component_name); img3_free(img3); return -1; } /* cleanup */ img3_free(img3); *img3_data = outbuf; *img3_size = outsize; return 0; } idevicerestore-1.0.0/src/img3.h000066400000000000000000000057361367173617500163560ustar00rootroot00000000000000/* * img3.h * Functions for handling with Apple's IMG3 format * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_IMG3_H #define IDEVICERESTORE_IMG3_H #ifdef __cplusplus extern "C" { #endif typedef enum { kNorContainer = 0x696D6733, // img3 kImg3Container = 0x496D6733, // Img3 k8900Container = 0x30303938, // 8900 kImg2Container = 0x494D4732 // IMG2 } img3_container; typedef enum { kDataElement = 0x44415441, // DATA kTypeElement = 0x54595045, // TYPE kKbagElement = 0x4B424147, // KBAG kShshElement = 0x53485348, // SHSH kCertElement = 0x43455254, // CERT kChipElement = 0x43484950, // CHIP kProdElement = 0x50524F44, // PROD kSdomElement = 0x53444F4D, // SDOM kVersElement = 0x56455253, // VERS kBordElement = 0x424F5244, // BORD kSepoElement = 0x5345504F, // SEPO kEcidElement = 0x45434944, // ECID kUnknElement = 0x53414c54 // FIXME } img3_element_type; typedef struct { unsigned int signature; unsigned int full_size; unsigned int data_size; unsigned int shsh_offset; unsigned int image_type; } img3_header; typedef struct { unsigned int signature; unsigned int full_size; unsigned int data_size; } img3_element_header; typedef struct { img3_element_header* header; img3_element_type type; unsigned char* data; } img3_element; typedef struct { unsigned char* data; img3_header* header; int num_elements; img3_element* elements[16]; int idx_ecid_element; int idx_shsh_element; int idx_cert_element; /* img3_element* type_element; img3_element* data_element; img3_element* vers_element; img3_element* sepo_element; img3_element* bord_element; img3_element* sepo2_element; img3_element* chip_element; img3_element* bord2_element; img3_element* kbag1_element; img3_element* kbag2_element; img3_element* ecid_element; img3_element* shsh_element; img3_element* cert_element; img3_element* unkn_element;*/ } img3_file; int img3_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/img4.c000066400000000000000000000460721367173617500163500ustar00rootroot00000000000000/* * img4.c * Functions for handling the IMG4 format * * Copyright (c) 2013-2019 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "common.h" #include "img4.h" #define ASN1_PRIVATE 0xc0 #define ASN1_PRIMITIVE_TAG 0x1f #define ASN1_CONSTRUCTED 0x20 #define ASN1_SEQUENCE 0x10 #define ASN1_SET 0x11 #define ASN1_CONTEXT_SPECIFIC 0x80 #define ASN1_IA5_STRING 0x16 #define ASN1_OCTET_STRING 0x04 #define ASN1_INTEGER 0x02 #define ASN1_BOOLEAN 0x01 #define IMG4_MAGIC "IMG4" #define IMG4_MAGIC_SIZE 4 static int asn1_calc_int_size(uint64_t value) { int i = 1; while ((value >>= 7) != 0) i++; return i; } static void asn1_write_int_value(unsigned char **p, uint64_t value, int size) { int value_size = (size > 0) ? size : asn1_calc_int_size(value); int i = 0; for (i = 1; i <= value_size; i++) { (*p)[value_size-i] = value & 0xFF; value >>= 8; } *p += value_size; } static void asn1_write_size(unsigned int size, unsigned char** data, unsigned int *data_size) { unsigned int off = 0; // first, calculate the size if (size >= 0x1000000) { // 1+4 bytes length (*data)[off++] = 0x84; (*data)[off++] = (size >> 24) & 0xFF; (*data)[off++] = (size >> 16) & 0xFF; (*data)[off++] = (size >> 8) & 0xFF; (*data)[off++] = size & 0xFF; } else if (size >= 0x10000) { // 1+3 bytes length (*data)[off++] = 0x83; (*data)[off++] = (size >> 16) & 0xFF; (*data)[off++] = (size >> 8) & 0xFF; (*data)[off++] = size & 0xFF; } else if (size >= 0x100) { // 1+2 bytes length (*data)[off++] = 0x82; (*data)[off++] = (size >> 8) & 0xFF; (*data)[off++] = (size & 0xFF); } else if (size >= 0x80) { // 1+1 byte length (*data)[off++] = 0x81; (*data)[off++] = (size & 0xFF); } else { // 1 byte length (*data)[off++] = size & 0xFF; } *data += off; *data_size += off; } static void asn1_write_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size) { unsigned int off = 0; if (!type || size == 0 || !data || !data_size) { return; } (*data)[off++] = type; *data += off; asn1_write_size(size, data, &off); *data_size += off; } static unsigned char* asn1_create_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size) { unsigned char buf[6]; unsigned int off = 0; if (!type || size == 0 || !data || !data_size) { return NULL; } buf[off++] = type; unsigned char* p = &buf[off]; asn1_write_size(size, &p, &off); *data = malloc(off); memcpy(*data, buf, off); *data_size = off; return *data; } static void asn1_write_priv_element(unsigned char **p, unsigned int *length, unsigned int value) { int i = 0; int ttag = 0; int tag = value; i = ASN1_CONSTRUCTED; i |= (0xFF & ASN1_PRIVATE); (*p)[0] = i | ASN1_PRIMITIVE_TAG; *p += 1; *length += 1; for (i = 0, ttag = tag; ttag > 0; i++) ttag >>= 7; ttag = i; while (i-- > 0) { (*p)[i] = tag & 0x7f; if (i != (ttag - 1)) (*p)[i] |= 0x80; tag >>= 7; } *p += ttag; *length += ttag; } static void asn1_write_element(unsigned char **p, unsigned int *length, unsigned char type, void *data, int data_len) { unsigned int this_len = 0; switch (type) { case ASN1_IA5_STRING: { char *str = (char*)data; size_t len = (data_len < 0) ? strlen(str) : data_len; asn1_write_element_header(type, len, p, &this_len); *length += this_len; memcpy(*p, str, len); *p += len; *length += len; } break; case ASN1_OCTET_STRING: { asn1_write_element_header(type, data_len, p, &this_len); *length += this_len; memcpy(*p, data, data_len); *p += data_len; *length += data_len; } break; case ASN1_INTEGER: { uint64_t value = *(uint64_t*)data; int value_size = asn1_calc_int_size(value); asn1_write_element_header(type, value_size, p, &this_len); *length += this_len; asn1_write_int_value(p, value, value_size); *length += value_size; } break; case ASN1_BOOLEAN: { unsigned int value = *(unsigned int*)data; asn1_write_element_header(type, 1, p, &this_len); *length += this_len; asn1_write_int_value(p, value ? 0xFF : 0x00, 1); *length += 1; } break; case (ASN1_SET | ASN1_CONSTRUCTED): { asn1_write_element_header(type, data_len, p, &this_len); *length += this_len; if (data && data_len > 0) { memcpy(*p, data, data_len); *p += data_len; *length += data_len; } } break; default: fprintf(stderr, "ERROR: %s: type %02x is not implemented", __func__, type); return; } } static unsigned int asn1_get_element(const unsigned char* data, unsigned char* type, unsigned char* size) { unsigned int off = 0; if (!data) return 0; if (type) *type = data[off++]; if (size) *size = data[off++]; return off; } static const unsigned char *asn1_find_element(unsigned int index, unsigned char type, const unsigned char* data) { unsigned char el_type = 0; unsigned char el_size = 0; unsigned int off = 0; int i; // verify data integrity if (data[off++] != (ASN1_CONSTRUCTED | ASN1_SEQUENCE)) return NULL; // check data size switch (data[off++]) { case 0x84: off += 4; break; case 0x83: off += 3; break; case 0x82: off += 2; break; case 0x81: off += 1; break; default: break; } // find the element we are searching for (i = 0; i <= index; i++) { off += asn1_get_element(&data[off], &el_type, &el_size); if (i == index) break; off += el_size; } // check element type if (el_type != type) return NULL; return &data[off]; } static const char *_img4_get_component_tag(const char *compname) { struct comp_tags { const char *comp; const char *tag; }; const struct comp_tags component_tags[] = { { "ACIBT", "acib" }, { "ACIWIFI", "aciw" }, { "Alamo", "almo" }, { "ANE", "anef" }, { "AOP", "aopf" }, { "Ap,HapticAssets", "hpas" }, { "AppleLogo", "logo" }, { "AudioCodecFirmware", "acfw" }, { "AVE", "avef" }, { "BatteryCharging", "glyC" }, { "BatteryCharging0", "chg0" }, { "BatteryCharging1", "chg1" }, { "BatteryFull", "batF" }, { "BatteryLow0", "bat0" }, { "BatteryLow1", "bat1" }, { "BatteryPlugin", "glyP" }, { "CFELoader", "cfel" }, { "Dali", "dali" }, { "DCP", "dcpf" }, { "DeviceTree", "dtre" }, { "Diags", "diag" }, { "EngineeringTrustCache", "dtrs" }, { "ftap", "ftap" }, { "ftsp", "ftsp" }, { "GFX", "gfxf" }, { "Hamm", "hamf" }, { "Homer", "homr" }, { "iBEC", "ibec" }, { "iBoot", "ibot" }, { "iBootData", "ibdt" }, { "iBootTest", "itst" }, { "iBSS", "ibss" }, { "InputDevice", "ipdf" }, { "ISP", "ispf" }, { "KernelCache", "krnl" }, { "LeapHaptics", "lphp" }, { "Liquid", "liqd" }, { "LLB", "illb" }, { "LoadableTrustCache", "ltrs" }, { "LowPowerWallet0", "lpw0" }, { "LowPowerWallet1", "lpw1" }, { "LowPowerWallet2", "lpw2" }, { "MacEFI", "mefi" }, { "Multitouch", "mtfw" }, { "NeedService", "nsrv" }, { "OS", "OS\0\0" }, { "OSRamdisk", "osrd" }, { "PersonalizedDMG", "pdmg" }, { "PEHammer", "hmmr" }, { "PERTOS", "pert" }, { "PHLEET", "phlt" }, { "PMP", "pmpf" }, { "RBM", "rmbt" }, { "Rap,SoftwareBinaryDsp1", "sbd1" }, { "Rap,RTKitOS", "rkos" }, { "Rap,RestoreRTKitOS", "rrko" }, { "RecoveryMode", "recm" }, { "RestoreDCP", "rdcp" }, { "RestoreDeviceTree", "rdtr" }, { "RestoreKernelCache", "rkrn" }, { "RestoreLogo", "rlgo" }, { "RestoreRamDisk", "rdsk" }, { "RestoreSEP", "rsep" }, { "RestoreTrustCache", "rtsc" }, { "rfta", "rfta" }, { "rfts", "rfts" }, { "RTP", "rtpf" }, { "SCE", "scef" }, { "SEP", "sepi" }, { "SIO", "siof" }, { "StaticTrustCache", "trst" }, { "SystemLocker", "lckr" }, { "WCHFirmwareUpdater", "wchf" }, { NULL, NULL } }; int i = 0; while (component_tags[i].comp) { if (!strcmp(component_tags[i].comp, compname)) { return component_tags[i].tag; } i++; } return NULL; } int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size) { unsigned char* magic_header = NULL; unsigned int magic_header_size = 0; unsigned char* blob_header = NULL; unsigned int blob_header_size = 0; unsigned char* img4header = NULL; unsigned int img4header_size = 0; unsigned int content_size; unsigned char* outbuf; unsigned char* p; if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img4_data || !img4_size) { return -1; } info("Personalizing IMG4 component %s...\n", component_name); /* first we need check if we have to change the tag for the given component */ const void *tag = asn1_find_element(1, ASN1_IA5_STRING, component_data); if (tag) { debug("Tag found\n"); if (strcmp(component_name, "RestoreKernelCache") == 0) { memcpy((void*)tag, "rkrn", 4); } else if (strcmp(component_name, "RestoreDeviceTree") == 0) { memcpy((void*)tag, "rdtr", 4); } else if (strcmp(component_name, "RestoreSEP") == 0) { memcpy((void*)tag, "rsep", 4); } else if (strcmp(component_name, "RestoreLogo") == 0) { memcpy((void*)tag, "rlgo", 4); } else if (strcmp(component_name, "RestoreTrustCache") == 0) { memcpy((void*)tag, "rtsc", 4); } } // create element header for the "IMG4" magic asn1_create_element_header(ASN1_IA5_STRING, IMG4_MAGIC_SIZE, &magic_header, &magic_header_size); // create element header for the blob (ApImg4Ticket) asn1_create_element_header(ASN1_CONTEXT_SPECIFIC|ASN1_CONSTRUCTED, blob_size, &blob_header, &blob_header_size); // calculate the size for the final IMG4 file (asn1 sequence) content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size; // create element header for the final IMG4 asn1 blob asn1_create_element_header(ASN1_SEQUENCE|ASN1_CONSTRUCTED, content_size, &img4header, &img4header_size); outbuf = (unsigned char*)malloc(img4header_size + content_size); if (!outbuf) { if (magic_header) { free(magic_header); } if (blob_header) { free(blob_header); } if (img4header) { free(img4header); } error("ERROR: out of memory when personalizing IMG4 component %s\n", component_name); return -1; } p = outbuf; // now put everything together memcpy(p, img4header, img4header_size); p += img4header_size; memcpy(p, magic_header, magic_header_size); p += magic_header_size; memcpy(p, IMG4_MAGIC, IMG4_MAGIC_SIZE); p += IMG4_MAGIC_SIZE; memcpy(p, component_data, component_size); p += component_size; memcpy(p, blob_header, blob_header_size); p += blob_header_size; memcpy(p, blob, blob_size); p += blob_size; *img4_data = outbuf; *img4_size = (p - outbuf); if (magic_header) { free(magic_header); } if (blob_header) { free(blob_header); } if (img4header) { free(img4header); } return 0; } #ifndef __bswap_32 #define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \ | (((x) & 0x00FF0000) >> 8) \ | (((x) & 0x0000FF00) << 8) \ | (((x) & 0x000000FF) << 24)) #endif static void _manifest_write_key_value(unsigned char **p, unsigned int *length, const char *tag, int type, void *value, int size) { uint32_t utag = __bswap_32(*(uint32_t*)tag); asn1_write_priv_element(p, length, utag); unsigned char *start = *p; unsigned char *outer_start = *p + 5; unsigned char *inner_start = *p + 5 + 6; unsigned int inner_length = 0; asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1); asn1_write_element(&inner_start, &inner_length, type, value, size); unsigned int outer_length = 0; unsigned int this_length = 0; if (!value && size > 0) { asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length + size, &outer_start, &outer_length); asn1_write_size(outer_length + inner_length + size, &start, &this_length); } else { asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length); asn1_write_size(outer_length + inner_length, &start, &this_length); } memmove(start, outer_start - outer_length, outer_length); outer_start = start + outer_length; *length += this_length; *length += outer_length; memmove(outer_start, inner_start - inner_length, inner_length); *length += inner_length; *p += this_length + outer_length + inner_length; } static void _manifest_write_component(unsigned char **p, unsigned int *length, const char *tag, plist_t comp) { uint32_t utag = __bswap_32(*(uint32_t*)tag); asn1_write_priv_element(p, length, utag); unsigned char *start = *p; unsigned char *outer_start = *p + 5; unsigned char *inner_start = *p + 5 + 6; unsigned int inner_length = 0; asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1); unsigned char tmp_[512] = { 0, }; unsigned int tmp_len = 0; unsigned char *tmp = &tmp_[0]; plist_t node = NULL; uint8_t boolval = 0; node = plist_dict_get_item(comp, "Digest"); if (node) { char *digest = NULL; uint64_t digest_len = 0; plist_get_data_val(node, &digest, &digest_len); if (digest_len > 0) { _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, digest, digest_len); } free(digest); } node = plist_dict_get_item(comp, "Trusted"); if (node) { boolval = 0; plist_get_bool_val(node, &boolval); unsigned int int_bool_val = boolval; _manifest_write_key_value(&tmp, &tmp_len, "EKEY", ASN1_BOOLEAN, &int_bool_val, -1); } node = plist_dict_get_item(comp, "EPRO"); if (node) { boolval = 0; plist_get_bool_val(node, &boolval); unsigned int int_bool_val = boolval; _manifest_write_key_value(&tmp, &tmp_len, "EPRO", ASN1_BOOLEAN, &int_bool_val, -1); } node = plist_dict_get_item(comp, "ESEC"); if (node) { boolval = 0; plist_get_bool_val(node, &boolval); unsigned int int_bool_val = boolval; _manifest_write_key_value(&tmp, &tmp_len, "ESEC", ASN1_BOOLEAN, &int_bool_val, -1); } node = plist_dict_get_item(comp, "TBMDigests"); if (node) { char *data = NULL; uint64_t datalen = 0; plist_get_data_val(node, &data, &datalen); const char *tbmtag = NULL; if (!strcmp(tag, "sepi")) { tbmtag = "tbms"; } else if (!strcmp(tag, "rsep")) { tbmtag = "tbmr"; } if (!tbmtag) { error("ERROR: Unexpected TMBDigests for comp '%s'\n", tag); } else { _manifest_write_key_value(&tmp, &tmp_len, tbmtag, ASN1_OCTET_STRING, data, datalen); } free(data); } asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, tmp_len, &inner_start, &inner_length); memcpy(inner_start, tmp_, tmp_len); inner_start += tmp_len; inner_length += tmp_len; unsigned int outer_length = 0; asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length); unsigned int this_length = 0; asn1_write_size(outer_length + inner_length, &start, &this_length); memmove(start, outer_start - outer_length, outer_length); outer_start = start + outer_length; *length += this_length; *length += outer_length; memmove(outer_start, inner_start - inner_length, inner_length); *length += inner_length; *p += this_length + outer_length + inner_length; } int img4_create_local_manifest(plist_t request, plist_t* manifest) { if (!request || !manifest) { return -1; } unsigned char *buf = calloc(1, 65536); unsigned char *p = buf; unsigned int length = 0; uint64_t uintval = 0; unsigned int boolval = 0; char *strval = NULL; plist_t node = NULL; unsigned char tmp_[1024]; unsigned char *tmp = &tmp_[0]; unsigned int tmp_len = 0; /* write manifest properties */ uintval = _plist_dict_get_uint(request, "ApBoardID"); _manifest_write_key_value(&tmp, &tmp_len, "BORD", ASN1_INTEGER, &uintval, -1); uintval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CEPO", ASN1_INTEGER, &uintval, -1); uintval = _plist_dict_get_uint(request, "ApChipID"); _manifest_write_key_value(&tmp, &tmp_len, "CHIP", ASN1_INTEGER, &uintval, -1); boolval = _plist_dict_get_bool(request, "ApProductionMode"); _manifest_write_key_value(&tmp, &tmp_len, "CPRO", ASN1_BOOLEAN, &boolval, -1); boolval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CSEC", ASN1_BOOLEAN, &boolval, -1); uintval = _plist_dict_get_uint(request, "ApSecurityDomain"); _manifest_write_key_value(&tmp, &tmp_len, "SDOM", ASN1_INTEGER, &uintval, -1); /* create manifest properties set */ _manifest_write_key_value(&p, &length, "MANP", ASN1_SET | ASN1_CONSTRUCTED, tmp_, tmp_len); /* now write the components */ plist_dict_iter iter = NULL; plist_dict_new_iter(request, &iter); char *key = NULL; plist_t val = NULL; do { plist_dict_next_item(request, iter, &key, &val); if (val && plist_get_node_type(val) == PLIST_DICT) { const char *comp = _img4_get_component_tag(key); if (!comp) { error("ERROR: %s: Unhandled component '%s' - can't create manifest\n", __func__, key); free(iter); free(buf); return -1; } debug("DEBUG: found component %s (%s)\n", comp, key); _manifest_write_component(&p, &length, comp, val); } free(key); } while (val); free(iter); /* write manifest body header */ unsigned char manb_[32]; unsigned char *manb = &manb_[0]; unsigned int manb_len = 0; _manifest_write_key_value(&manb, &manb_len, "MANB", ASN1_SET | ASN1_CONSTRUCTED, NULL, length); /* write inner set */ unsigned char inner_set_[8]; unsigned char *inner_set = &inner_set_[0]; unsigned int inner_set_len = 0; asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, length + manb_len, &inner_set, &inner_set_len); /* write header values */ unsigned char hdrdata_[16]; unsigned char *hdrdata = &hdrdata_[0]; unsigned int hdrdata_len = 0; asn1_write_element(&hdrdata, &hdrdata_len, ASN1_IA5_STRING, (void*)"IM4M", -1); uint64_t intval = 0; asn1_write_element(&hdrdata, &hdrdata_len, ASN1_INTEGER, &intval, -1); /* write outer sequence now that we know the entire size */ unsigned char seq_[8]; unsigned char *seq = &seq_[0]; unsigned int seq_len = 0; asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_set_len + length + manb_len + hdrdata_len, &seq, &seq_len); unsigned int header_len = seq_len + hdrdata_len + inner_set_len + manb_len; /* now put everything together */ memmove(buf + header_len, buf, length); unsigned char *hdr = buf; unsigned int hdr_len = 0; memcpy(hdr, seq_, seq_len); hdr += seq_len; hdr_len += seq_len; memcpy(hdr, hdrdata_, hdrdata_len); hdr += hdrdata_len; hdr_len += hdrdata_len; memcpy(hdr, inner_set_, inner_set_len); hdr += inner_set_len; hdr_len += inner_set_len; memcpy(hdr, manb_, manb_len); hdr += manb_len; hdr_len += manb_len; length += hdr_len; *manifest = plist_new_data((char*)buf, length); free(buf); return 0; } idevicerestore-1.0.0/src/img4.h000066400000000000000000000024241367173617500163460ustar00rootroot00000000000000/* * img4.h * Functions for handling the IMG4 format * * Copyright (c) 2013-2019 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_IMG4_H #define IDEVICERESTORE_IMG4_H #ifdef __cplusplus extern "C" { #endif int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size); int img4_create_local_manifest(plist_t request, plist_t* manifest); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/ipsw.c000066400000000000000000000460641367173617500164730ustar00rootroot00000000000000/* * ipsw.c * Utilities for extracting and manipulating IPSWs * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_OPENSSL #include #else #include "sha1.h" #define SHA_CTX SHA1_CTX #define SHA1_Init SHA1Init #define SHA1_Update SHA1Update #define SHA1_Final SHA1Final #endif #include "ipsw.h" #include "locking.h" #include "download.h" #include "common.h" #include "idevicerestore.h" #include "json_plist.h" #define BUFSIZE 0x100000 typedef struct { struct zip* zip; char *path; } ipsw_archive; static int cancel_flag = 0; ipsw_archive* ipsw_open(const char* ipsw); void ipsw_close(ipsw_archive* archive); static char* build_path(const char* path, const char* file) { size_t plen = strlen(path); size_t flen = strlen(file); char *fullpath = malloc(plen + flen + 2); if (!fullpath) { return NULL; } memcpy(fullpath, path, plen); fullpath[plen] = '/'; memcpy(fullpath+plen+1, file, flen); fullpath[plen+1+flen] = '\0'; return fullpath; } ipsw_archive* ipsw_open(const char* ipsw) { int err = 0; ipsw_archive* archive = (ipsw_archive*) malloc(sizeof(ipsw_archive)); if (archive == NULL) { error("ERROR: Out of memory\n"); return NULL; } struct stat fst; if (stat(ipsw, &fst) != 0) { error("ERROR: ipsw_open %s: %s\n", ipsw, strerror(errno)); return NULL; } archive->path = strdup(ipsw); if (S_ISDIR(fst.st_mode)) { archive->zip = NULL; } else { archive->zip = zip_open(ipsw, 0, &err); if (archive->zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw, err); free(archive); return NULL; } } return archive; } int ipsw_is_directory(const char* ipsw) { struct stat fst; memset(&fst, '\0', sizeof(fst)); if (stat(ipsw, &fst) != 0) { return 0; } return S_ISDIR(fst.st_mode); } int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) { ipsw_archive* archive = ipsw_open(ipsw); if (archive == NULL) { error("ERROR: Invalid archive\n"); return -1; } if (archive->zip) { int zindex = zip_name_locate(archive->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); ipsw_close(archive); return -1; } *size = zstat.size; } else { char *filepath = build_path(archive->path, infile); struct stat fst; if (stat(filepath, &fst) != 0) { free(filepath); ipsw_close(archive); return -1; } free(filepath); *size = fst.st_size; } ipsw_close(archive); return 0; } int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress) { int ret = 0; ipsw_archive* archive = ipsw_open(ipsw); if (archive == NULL) { error("ERROR: Invalid archive\n"); return -1; } cancel_flag = 0; if (archive->zip) { int zindex = zip_name_locate(archive->zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); return -1; } char* buffer = (char*) malloc(BUFSIZE); if (buffer == NULL) { error("ERROR: Unable to allocate memory\n"); return -1; } struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); return -1; } FILE* fd = fopen(outfile, "wb"); if (fd == NULL) { error("ERROR: Unable to open output file: %s\n", outfile); zip_fclose(zfile); return -1; } uint64_t i, bytes = 0; int count, size = BUFSIZE; double progress; for(i = zstat.size; i > 0; i -= count) { if (cancel_flag) { break; } if (i < BUFSIZE) size = i; count = zip_fread(zfile, buffer, size); if (count < 0) { error("ERROR: zip_fread: %s\n", infile); ret = -1; break; } if (fwrite(buffer, 1, count, fd) != count) { error("ERROR: frite: %s\n", outfile); ret = -1; break; } bytes += size; if (print_progress) { progress = ((double)bytes / (double)zstat.size) * 100.0; print_progress_bar(progress); } } free(buffer); fclose(fd); zip_fclose(zfile); } else { char *filepath = build_path(archive->path, infile); char actual_filepath[PATH_MAX+1]; char actual_outfile[PATH_MAX+1]; if (!realpath(filepath, actual_filepath)) { error("ERROR: realpath failed on %s: %s\n", filepath, strerror(errno)); ret = -1; goto leave; } else { actual_outfile[0] = '\0'; if (realpath(outfile, actual_outfile) && (strcmp(actual_filepath, actual_outfile) == 0)) { /* files are identical */ ret = 0; } else { if (actual_outfile[0] == '\0') { strcpy(actual_outfile, outfile); } FILE *fi = fopen(actual_filepath, "rb"); if (!fi) { error("ERROR: fopen: %s: %s\n", actual_filepath, strerror(errno)); ret = -1; goto leave; } struct stat fst; if (fstat(fileno(fi), &fst) != 0) { fclose(fi); error("ERROR: fstat: %s: %s\n", actual_filepath, strerror(errno)); ret = -1; goto leave; } FILE *fo = fopen(actual_outfile, "wb"); if (!fo) { fclose(fi); error("ERROR: fopen: %s: %s\n", actual_outfile, strerror(errno)); ret = -1; goto leave; } char* buffer = (char*) malloc(BUFSIZE); if (buffer == NULL) { fclose(fi); fclose(fo); error("ERROR: Unable to allocate memory\n"); ret = -1; goto leave;; } uint64_t bytes = 0; double progress; while (!feof(fi)) { if (cancel_flag) { break; } ssize_t r = fread(buffer, 1, BUFSIZE, fi); if (r < 0) { error("ERROR: fread failed: %s\n", strerror(errno)); ret = -1; break; } if (fwrite(buffer, 1, r, fo) != r) { error("ERROR: fwrite failed\n"); ret = -1; break; } bytes += r; if (print_progress) { progress = ((double)bytes / (double)fst.st_size) * 100.0; print_progress_bar(progress); } } free(buffer); fclose(fi); fclose(fo); } } leave: free(filepath); } ipsw_close(archive); if (cancel_flag) { ret = -2; } return ret; } int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile) { return ipsw_extract_to_file_with_progress(ipsw, infile, outfile, 0); } int ipsw_file_exists(const char* ipsw, const char* infile) { ipsw_archive* archive = ipsw_open(ipsw); if (archive == NULL) { return 0; } if (archive->zip) { int zindex = zip_name_locate(archive->zip, infile, 0); if (zindex < 0) { ipsw_close(archive); return 0; } } else { char *filepath = build_path(archive->path, infile); if (access(filepath, R_OK) != 0) { free(filepath); ipsw_close(archive); return 0; } free(filepath); } ipsw_close(archive); return 1; } int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) { size_t size = 0; unsigned char* buffer = NULL; ipsw_archive* archive = ipsw_open(ipsw); if (archive == NULL) { error("ERROR: Invalid archive\n"); return -1; } if (archive->zip) { int zindex = zip_name_locate(archive->zip, infile, 0); if (zindex < 0) { debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); ipsw_close(archive); return -1; } struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index: %s\n", infile); ipsw_close(archive); return -1; } size = zstat.size; buffer = (unsigned char*) malloc(size+1); if (buffer == NULL) { error("ERROR: Out of memory\n"); zip_fclose(zfile); ipsw_close(archive); return -1; } if (zip_fread(zfile, buffer, size) != size) { error("ERROR: zip_fread: %s\n", infile); zip_fclose(zfile); free(buffer); ipsw_close(archive); return -1; } buffer[size] = '\0'; zip_fclose(zfile); } else { char *filepath = build_path(archive->path, infile); FILE *f = fopen(filepath, "rb"); if (!f) { error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); ipsw_close(archive); return -2; } struct stat fst; if (fstat(fileno(f), &fst) != 0) { fclose(f); error("ERROR: %s: fstat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); ipsw_close(archive); return -1; } size = fst.st_size; buffer = (unsigned char*)malloc(size+1); if (buffer == NULL) { error("ERROR: Out of memory\n"); fclose(f); free(filepath); ipsw_close(archive); return -1; } if (fread(buffer, 1, size, f) != size) { fclose(f); error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); ipsw_close(archive); return -1; } buffer[size] = '\0'; fclose(f); free(filepath); } ipsw_close(archive); *pbuffer = buffer; *psize = size; return 0; } int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled) { unsigned int size = 0; unsigned char* data = NULL; *tss_enabled = 0; /* older devices don't require personalized firmwares and use a BuildManifesto.plist */ if (ipsw_file_exists(ipsw, "BuildManifesto.plist")) { if (ipsw_extract_to_memory(ipsw, "BuildManifesto.plist", &data, &size) == 0) { plist_from_xml((char*)data, size, buildmanifest); free(data); return 0; } } data = NULL; size = 0; /* whereas newer devices do not require personalized firmwares and use a BuildManifest.plist */ if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", &data, &size) == 0) { *tss_enabled = 1; plist_from_xml((char*)data, size, buildmanifest); free(data); return 0; } return -1; } int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) { unsigned int size = 0; unsigned char* data = NULL; if (ipsw_extract_to_memory(ipsw, "Restore.plist", &data, &size) == 0) { plist_from_xml((char*)data, size, restore_plist); free(data); return 0; } return -1; } void ipsw_close(ipsw_archive* archive) { if (archive != NULL) { free(archive->path); if (archive->zip) { zip_unchange_all(archive->zip); zip_close(archive->zip); } free(archive); } } int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) { char url[256]; char *jdata = NULL; uint32_t jsize = 0; plist_t dict = NULL; plist_t node = NULL; plist_t fws = NULL; uint32_t count = 0; uint32_t i = 0; if (!product || !firmwares) { return -1; } *firmwares = NULL; snprintf(url, sizeof(url), "https://api.ipsw.me/v3/device/%s", product); if (download_to_buffer(url, &jdata, &jsize) < 0) { error("ERROR: Download from %s failed.\n", url); return -1; } dict = json_to_plist(jdata); free(jdata); if (!dict || plist_get_node_type(dict) != PLIST_DICT) { error("ERROR: Failed to parse json data.\n"); plist_free(dict); return -1; } node = plist_dict_get_item(dict, product); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unexpected json data returned?!\n"); plist_free(dict); return -1; } fws = plist_dict_get_item(node, "firmwares"); if (!fws || plist_get_node_type(fws) != PLIST_ARRAY) { error("ERROR: Unexpected json data returned?!\n"); plist_free(dict); return -1; } *firmwares = plist_new_array(); count = plist_array_get_size(fws); for (i = 0; i < count; i++) { plist_t fw = plist_array_get_item(fws, i); node = plist_dict_get_item(fw, "signed"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t bv = 0; plist_get_bool_val(node, &bv); if (bv) { plist_array_append_item(*firmwares, plist_copy(fw)); } } } plist_free(dict); return 0; } int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, unsigned char* sha1buf) { *fwurl = NULL; if (sha1buf != NULL) { memset(sha1buf, '\0', 20); } plist_t n1 = plist_dict_get_item(version_data, "MobileDeviceSoftwareVersionsByVersion"); if (!n1) { error("%s: ERROR: Can't find MobileDeviceSoftwareVersionsByVersion dict in version data\n", __func__); return -1; } plist_dict_iter iter = NULL; plist_dict_new_iter(n1, &iter); if (!iter) { error("%s: ERROR: Can't get dict iter\n", __func__); return -1; } char* key = NULL; uint64_t major = 0; plist_t val = NULL; do { plist_dict_next_item(n1, iter, &key, &val); if (key) { plist_t pr = plist_access_path(n1, 3, key, "MobileDeviceSoftwareVersions", product); if (pr) { long long unsigned int v = strtoull(key, NULL, 10); if (v > major) major = v; } free(key); } } while (val); free(iter); if (major == 0) { error("%s: ERROR: Can't find major version?!\n", __func__); return -1; } char majstr[32]; // should be enough for a uint64_t value sprintf(majstr, "%"PRIu64, (uint64_t)major); n1 = plist_access_path(version_data, 7, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, "Unknown", "Universal", "Restore"); if (!n1) { error("%s: ERROR: Can't get Unknown/Universal/Restore node?!\n", __func__); return -1; } plist_t n2 = plist_dict_get_item(n1, "BuildVersion"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { error("%s: ERROR: Can't get build version node?!\n", __func__); return -1; } char* strval = NULL; plist_get_string_val(n2, &strval); n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); if (!n1) { error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s node?!\n", __func__, strval); free(strval); return -1; } free(strval); strval = NULL; n2 = plist_dict_get_item(n1, "SameAs"); if (n2) { plist_get_string_val(n2, &strval); } if (strval) { n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); free(strval); strval = NULL; if (!n1 || (plist_dict_get_size(n1) == 0)) { error("%s: ERROR: Can't get MobileDeviceSoftwareVersions/%s dict\n", __func__, product); return -1; } } n2 = plist_access_path(n1, 2, "Update", "BuildVersion"); if (n2) { strval = NULL; plist_get_string_val(n2, &strval); if (strval) { n1 = plist_access_path(version_data, 5, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, strval); free(strval); strval = NULL; } } n2 = plist_access_path(n1, 2, "Restore", "FirmwareURL"); if (!n2 || (plist_get_node_type(n2) != PLIST_STRING)) { error("%s: ERROR: Can't get FirmwareURL node\n", __func__); return -1; } plist_get_string_val(n2, fwurl); if (sha1buf != NULL) { n2 = plist_access_path(n1, 2, "Restore", "FirmwareSHA1"); if (n2 && plist_get_node_type(n2) == PLIST_STRING) { strval = NULL; plist_get_string_val(n2, &strval); if (strval) { if (strlen(strval) == 40) { int i; int v; for (i = 0; i < 40; i+=2) { v = 0; sscanf(strval+i, "%02x", &v); sha1buf[i/2] = (unsigned char)v; } } free(strval); } } } return 0; } static int sha1_verify_fp(FILE* f, unsigned char* expected_sha1) { unsigned char tsha1[20]; char buf[8192]; if (!f) return 0; SHA_CTX sha1ctx; SHA1_Init(&sha1ctx); rewind(f); while (!feof(f)) { size_t sz = fread(buf, 1, 8192, f); SHA1_Update(&sha1ctx, (const void*)buf, sz); } SHA1_Final(tsha1, &sha1ctx); return (memcmp(expected_sha1, tsha1, 20) == 0) ? 1 : 0; } int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, char** ipswfile) { char* fwfn = strrchr(fwurl, '/'); if (!fwfn) { error("ERROR: can't get local filename for firmware ipsw\n"); return -2; } fwfn++; char fwlfn[PATH_MAX - 5]; if (todir) { sprintf(fwlfn, "%s/%s", todir, fwfn); } else { sprintf(fwlfn, "%s", fwfn); } char fwlock[PATH_MAX]; sprintf(fwlock, "%s.lock", fwlfn); lock_info_t lockinfo; if (lock_file(fwlock, &lockinfo) != 0) { error("WARNING: Could not lock file '%s'\n", fwlock); } int need_dl = 0; unsigned char zsha1[20] = {0, }; FILE* f = fopen(fwlfn, "rb"); if (f) { if (memcmp(zsha1, isha1, 20) != 0) { info("Verifying '%s'...\n", fwlfn); if (sha1_verify_fp(f, isha1)) { info("Checksum matches.\n"); } else { info("Checksum does not match.\n"); need_dl = 1; } } fclose(f); } else { need_dl = 1; } int res = 0; if (need_dl) { if (strncmp(fwurl, "protected:", 10) == 0) { error("ERROR: Can't download '%s' because it needs a purchase.\n", fwfn); res = -3; } else { remove(fwlfn); info("Downloading firmware (%s)\n", fwurl); download_to_file(fwurl, fwlfn, 1); if (memcmp(isha1, zsha1, 20) != 0) { info("\nVerifying '%s'...\n", fwlfn); FILE* f = fopen(fwlfn, "rb"); if (f) { if (sha1_verify_fp(f, isha1)) { info("Checksum matches.\n"); } else { error("ERROR: File download failed (checksum mismatch).\n"); res = -4; } fclose(f); // make sure to remove invalid files if (res < 0) remove(fwlfn); } else { error("ERROR: Can't open '%s' for checksum verification\n", fwlfn); res = -5; } } } } if (res == 0) { *ipswfile = strdup(fwlfn); } if (unlock_file(&lockinfo) != 0) { error("WARNING: Could not unlock file '%s'\n", fwlock); } return res; } int ipsw_download_latest_fw(plist_t version_data, const char* product, const char* todir, char** ipswfile) { char* fwurl = NULL; unsigned char isha1[20]; *ipswfile = NULL; if ((ipsw_get_latest_fw(version_data, product, &fwurl, isha1) < 0) || !fwurl) { error("ERROR: can't get URL for latest firmware\n"); return -1; } char* fwfn = strrchr(fwurl, '/'); if (!fwfn) { error("ERROR: can't get local filename for firmware ipsw\n"); return -2; } fwfn++; info("Latest firmware is %s\n", fwfn); int res = ipsw_download_fw(fwurl, isha1, todir, ipswfile); free(fwurl); return res; } void ipsw_cancel(void) { cancel_flag++; } idevicerestore-1.0.0/src/ipsw.h000066400000000000000000000042731367173617500164740ustar00rootroot00000000000000/* * ipsw.h * Definitions for IPSW utilities * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_IPSW_H #define IDEVICERESTORE_IPSW_H #ifdef __cplusplus extern "C" { #endif #include #include int ipsw_is_directory(const char* ipsw); int ipsw_file_exists(const char* ipsw, const char* infile); int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size); int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile); int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress); int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled); int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist); int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares); int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, char** ipswfile); int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, unsigned char* sha1buf); int ipsw_download_latest_fw(plist_t version_data, const char* product, const char* todir, char** ipswfile); void ipsw_cancel(void); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/jsmn.c000066400000000000000000000160261367173617500164530ustar00rootroot00000000000000/* * jsmn.c * Simple JSON parser * * Copyright (c) 2010 Serge A. Zaitsev * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "jsmn.h" /** * Allocates a fresh unused token from the token pull. */ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *tok; if (parser->toknext >= num_tokens) { return NULL; } tok = &tokens[parser->toknext++]; tok->start = tok->end = -1; tok->size = 0; #ifdef JSMN_PARENT_LINKS tok->parent = -1; #endif return tok; } /** * Fills token type and boundaries. */ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) { token->type = type; token->start = start; token->end = end; token->size = 0; } /** * Fills next available token with JSON primitive. */ static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; int start; start = parser->pos; for (; js[parser->pos] != '\0'; parser->pos++) { switch (js[parser->pos]) { #ifndef JSMN_STRICT /* In strict mode primitive must be followed by "," or "}" or "]" */ case ':': #endif case '\t' : case '\r' : case '\n' : case ' ' : case ',' : case ']' : case '}' : goto found; } if (js[parser->pos] < 32 || js[parser->pos] >= 127) { parser->pos = start; return JSMN_ERROR_INVAL; } } #ifdef JSMN_STRICT /* In strict mode primitive must be followed by a comma/object/array */ parser->pos = start; return JSMN_ERROR_PART; #endif found: token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = start; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif parser->pos--; return JSMN_SUCCESS; } /** * Filsl next token with JSON string. */ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; int start = parser->pos; parser->pos++; /* Skip starting quote */ for (; js[parser->pos] != '\0'; parser->pos++) { char c = js[parser->pos]; /* Quote: end of string */ if (c == '\"') { token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = start; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif return JSMN_SUCCESS; } /* Backslash: Quoted symbol expected */ if (c == '\\') { parser->pos++; switch (js[parser->pos]) { /* Allowed escaped symbols */ case '\"': case '/' : case '\\' : case 'b' : case 'f' : case 'r' : case 'n' : case 't' : break; /* Allows escaped symbol \uXXXX */ case 'u': /* TODO */ break; /* Unexpected symbol */ default: parser->pos = start; return JSMN_ERROR_INVAL; } } } parser->pos = start; return JSMN_ERROR_PART; } /** * Parse JSON string and fill tokens. */ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, unsigned int num_tokens) { jsmnerr_t r; int i; jsmntok_t *token; for (; js[parser->pos] != '\0'; parser->pos++) { char c; jsmntype_t type; c = js[parser->pos]; switch (c) { case '{': case '[': token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) return JSMN_ERROR_NOMEM; if (parser->toksuper != -1) { tokens[parser->toksuper].size++; #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif } token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); token->start = parser->pos; parser->toksuper = parser->toknext - 1; break; case '}': case ']': type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); #ifdef JSMN_PARENT_LINKS if (parser->toknext < 1) { return JSMN_ERROR_INVAL; } token = &tokens[parser->toknext - 1]; for (;;) { if (token->start != -1 && token->end == -1) { if (token->type != type) { return JSMN_ERROR_INVAL; } token->end = parser->pos + 1; parser->toksuper = token->parent; break; } if (token->parent == -1) { break; } token = &tokens[token->parent]; } #else for (i = parser->toknext - 1; i >= 0; i--) { token = &tokens[i]; if (token->start != -1 && token->end == -1) { if (token->type != type) { return JSMN_ERROR_INVAL; } parser->toksuper = -1; token->end = parser->pos + 1; break; } } /* Error if unmatched closing bracket */ if (i == -1) return JSMN_ERROR_INVAL; for (; i >= 0; i--) { token = &tokens[i]; if (token->start != -1 && token->end == -1) { parser->toksuper = i; break; } } #endif break; case '\"': r = jsmn_parse_string(parser, js, tokens, num_tokens); if (r < 0) return r; if (parser->toksuper != -1) tokens[parser->toksuper].size++; break; case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': break; #ifdef JSMN_STRICT /* In strict mode primitives are: numbers and booleans */ case '-': case '0': case '1' : case '2': case '3' : case '4': case '5': case '6': case '7' : case '8': case '9': case 't': case 'f': case 'n' : #else /* In non-strict mode every unquoted value is a primitive */ default: #endif r = jsmn_parse_primitive(parser, js, tokens, num_tokens); if (r < 0) return r; if (parser->toksuper != -1) tokens[parser->toksuper].size++; break; #ifdef JSMN_STRICT /* Unexpected char in strict mode */ default: return JSMN_ERROR_INVAL; #endif } } for (i = parser->toknext - 1; i >= 0; i--) { /* Unmatched opened object or array */ if (tokens[i].start != -1 && tokens[i].end == -1) { return JSMN_ERROR_PART; } } return JSMN_SUCCESS; } /** * Creates a new parser based over a given buffer with an array of tokens * available. */ void jsmn_init(jsmn_parser *parser) { parser->pos = 0; parser->toknext = 0; parser->toksuper = -1; } idevicerestore-1.0.0/src/jsmn.h000066400000000000000000000053011367173617500164520ustar00rootroot00000000000000/* * jsmn.h * Simple JSON parser (header file) * * Copyright (c) 2010 Serge A. Zaitsev * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef __JSMN_H_ #define __JSMN_H_ /** * JSON type identifier. Basic types are: * o Object * o Array * o String * o Other primitive: number, boolean (true/false) or null */ typedef enum { JSMN_PRIMITIVE = 0, JSMN_OBJECT = 1, JSMN_ARRAY = 2, JSMN_STRING = 3 } jsmntype_t; typedef enum { /* Not enough tokens were provided */ JSMN_ERROR_NOMEM = -1, /* Invalid character inside JSON string */ JSMN_ERROR_INVAL = -2, /* The string is not a full JSON packet, more bytes expected */ JSMN_ERROR_PART = -3, /* Everything was fine */ JSMN_SUCCESS = 0 } jsmnerr_t; /** * JSON token description. * @param type type (object, array, string etc.) * @param start start position in JSON data string * @param end end position in JSON data string */ typedef struct { jsmntype_t type; int start; int end; int size; #ifdef JSMN_PARENT_LINKS int parent; #endif } jsmntok_t; /** * JSON parser. Contains an array of token blocks available. Also stores * the string being parsed now and current position in that string */ typedef struct { unsigned int pos; /* offset in the JSON string */ int toknext; /* next token to allocate */ int toksuper; /* superior token node, e.g parent object or array */ } jsmn_parser; /** * Create JSON parser over an array of tokens */ void jsmn_init(jsmn_parser *parser); /** * Run JSON parser. It parses a JSON data string into and array of tokens, each describing * a single JSON object. */ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, unsigned int num_tokens); #endif /* __JSMN_H_ */ idevicerestore-1.0.0/src/json_plist.c000066400000000000000000000135711367173617500176720ustar00rootroot00000000000000/* * json_plist.c * JSON/property list functions * * Copyright (c) 2013 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "json_plist.h" static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index); static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index); static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index); static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index); static char* get_string_value(const char* js, jsmntok_t token) { int len = (token.end - token.start); char* str = malloc(len+1); memcpy(str, js + token.start, len); str[len] = 0; return str; } static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index) { if (tokens[*index].type != JSMN_PRIMITIVE) { fprintf(stderr, "%s: ERROR: token type != JSMN_PRIMITIVE?!\n", __func__); return NULL; } plist_t val = NULL; char* strval = get_string_value(js, tokens[*index]); if (strval[0] == 'f') { val = plist_new_bool(0); } else if (strval[0] == 't') { val = plist_new_bool(1); } else if ((strval[0] == '-') || ((strval[0] >= '0') && (strval[0] <= '9'))) { val = plist_new_uint(strtoll(strval, NULL, 10)); } else { fprintf(stderr, "%s: WARNING: invalid primitive value '%s' encountered, will return as string\n", __func__, strval); val = plist_new_string(strval); } free(strval); (*index)++; return val; } static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index) { if (tokens[*index].type != JSMN_STRING) { fprintf(stderr, "%s: ERROR: token type != JSMN_STRING?!\n", __func__); return NULL; } char* str = get_string_value(js, tokens[*index]); plist_t val = plist_new_string(str); free(str); (*index)++; return val; } static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index) { if (tokens[*index].type != JSMN_ARRAY) { fprintf(stderr, "%s: ERROR: token type != JSMN_ARRAY?!\n", __func__); return NULL; } plist_t arr = plist_new_array(); int num_tokens = tokens[*index].size; int num; int j = (*index)+1; for (num = 0; num < num_tokens; num++) { plist_t val = NULL; switch (tokens[j].type) { case JSMN_OBJECT: val = parse_object(js, tokens, &j); break; case JSMN_ARRAY: val = parse_array(js, tokens, &j); break; case JSMN_STRING: val = parse_string(js, tokens, &j); break; case JSMN_PRIMITIVE: val = parse_primitive(js, tokens, &j); break; default: break; } if (val) { plist_array_append_item(arr, val); } } *(index) = j; return arr; } static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index) { if (tokens[*index].type != JSMN_OBJECT) { fprintf(stderr, "%s: ERROR: token type != JSMN_OBJECT?!\n", __func__); return NULL; } plist_t obj = plist_new_dict(); int num_tokens = tokens[*index].size; int num; int j = (*index)+1; for (num = 0; num < num_tokens; num++) { if (tokens[j].type == JSMN_STRING) { char* key = get_string_value(js, tokens[j]); plist_t val = NULL; j++; num++; switch (tokens[j].type) { case JSMN_OBJECT: val = parse_object(js, tokens, &j); break; case JSMN_ARRAY: val = parse_array(js, tokens, &j); break; case JSMN_STRING: val = parse_string(js, tokens, &j); break; case JSMN_PRIMITIVE: val = parse_primitive(js, tokens, &j); break; default: break; } if (val) { plist_dict_set_item(obj, key, val); } free(key); } else { fprintf(stderr, "%s: keys must be of type STRING\n", __func__); return NULL; } } (*index) = j; return obj; } plist_t json_to_plist(const char* json_string) { jsmn_parser parser; jsmn_init(&parser); int maxtoks = 256; jsmntok_t *tokens; if (!json_string) { fprintf(stderr, "%s: ERROR: no JSON string given.\n", __func__); return NULL; } tokens = malloc(sizeof(jsmntok_t)*maxtoks); if (!tokens) { fprintf(stderr, "%s: Out of memory\n", __func__); return NULL; } int r = 0; reparse: r = jsmn_parse(&parser, json_string, tokens, maxtoks); if (r == JSMN_ERROR_NOMEM) { //printf("not enough tokens (%d), retrying...\n", maxtoks); maxtoks+=256; jsmntok_t* newtokens = realloc(tokens, sizeof(jsmntok_t)*maxtoks); if (newtokens) { tokens = newtokens; goto reparse; } } switch(r) { case JSMN_ERROR_NOMEM: fprintf(stderr, "%s: ERROR: Out of memory...\n", __func__); return NULL; case JSMN_ERROR_INVAL: fprintf(stderr, "%s: ERROR: Invalid character inside JSON string\n", __func__); return NULL; case JSMN_ERROR_PART: fprintf(stderr, "%s: ERROR: The string is not a full JSON packet, more bytes expected\n", __func__); return NULL; default: break; } int startindex = 0; plist_t plist = NULL; switch (tokens[startindex].type) { case JSMN_PRIMITIVE: plist = parse_primitive(json_string, tokens, &startindex); break; case JSMN_STRING: plist = parse_string(json_string, tokens, &startindex); break; case JSMN_ARRAY: plist = parse_array(json_string, tokens, &startindex); break; case JSMN_OBJECT: plist = parse_object(json_string, tokens, &startindex); break; default: break; } free(tokens); return plist; } idevicerestore-1.0.0/src/json_plist.h000066400000000000000000000020261367173617500176700ustar00rootroot00000000000000/* * json_plist.h * JSON/property list functions (header file) * * Copyright (c) 2013 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __JSON_PLIST_H #define __JSON_PLIST_H #ifdef __cplusplus extern "C" { #endif plist_t json_to_plist(const char* json_string); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/limera1n.c000066400000000000000000000104411367173617500172070ustar00rootroot00000000000000/* * limera1n.c * Helper code for limera1n exploit based on discovery by geohot * * Copyright (c) 2012-2013 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (C) 2010 Chronic-Dev Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "common.h" #include "limera1n.h" #include "limera1n_payload.h" int limera1n_is_supported(struct irecv_device *device) { irecv_device_t iphone4 = NULL; irecv_device_t iphone3gs = NULL; irecv_device_t ipod3g = NULL; irecv_devices_get_device_by_product_type("iPhone3,1", &iphone4); irecv_devices_get_device_by_product_type("iPhone2,1", &iphone3gs); irecv_devices_get_device_by_product_type("iPod3,1", &ipod3g); return ((device->chip_id == iphone4->chip_id) || (device->chip_id == iphone3gs->chip_id) || (device->chip_id == ipod3g->chip_id)); } int limera1n_exploit(struct irecv_device *device, irecv_client_t *pclient) { irecv_error_t err = IRECV_E_SUCCESS; unsigned int i = 0; unsigned char buf[0x800]; unsigned char shellcode[0x800]; unsigned int max_size = 0x24000; //unsigned int load_address = 0x84000000; unsigned int stack_address = 0; unsigned int shellcode_address = 0; irecv_device_t iphone4 = NULL; irecv_device_t iphone3gs = NULL; irecv_device_t ipod3g = NULL; int mode = 0; irecv_devices_get_device_by_product_type("iPhone3,1", &iphone4); irecv_devices_get_device_by_product_type("iPhone2,1", &iphone3gs); irecv_devices_get_device_by_product_type("iPod3,1", &ipod3g); if (device->chip_id == iphone4->chip_id) { max_size = 0x2C000; stack_address = 0x8403BF9C; shellcode_address = 0x8402B001; } else if (device->chip_id == iphone3gs->chip_id) { max_size = 0x24000; stack_address = 0x84033FA4; shellcode_address = 0x84023001; } else if (device->chip_id == ipod3g->chip_id) { max_size = 0x24000; stack_address = 0x84033F98; shellcode_address = 0x84023001; } else { error("Unsupported ChipID 0x%04x. Can't exploit with limera1n.\n", device->chip_id); return -1; } memset(shellcode, 0x0, 0x800); memcpy(shellcode, limera1n_payload, sizeof(limera1n_payload)); irecv_client_t client = *pclient; debug("Resetting device counters\n"); err = irecv_reset_counters(client); if (err != IRECV_E_SUCCESS) { error("%s\n", irecv_strerror(err)); return -1; } memset(buf, 0xCC, 0x800); for(i = 0; i < 0x800; i += 0x40) { unsigned int* heap = (unsigned int*)(buf+i); heap[0] = 0x405; heap[1] = 0x101; heap[2] = shellcode_address; heap[3] = stack_address; } debug("Sending chunk headers\n"); irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); memset(buf, 0xCC, 0x800); for(i = 0; i < (max_size - (0x800 * 3)); i += 0x800) { irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); } debug("Sending exploit payload\n"); irecv_usb_control_transfer(client, 0x21, 1, 0, 0, shellcode, 0x800, 1000); debug("Sending fake data\n"); memset(buf, 0xBB, 0x800); irecv_usb_control_transfer(client, 0xA1, 1, 0, 0, buf, 0x800, 1000); irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 10); //debug("Executing exploit\n"); irecv_usb_control_transfer(client, 0x21, 2, 0, 0, buf, 0, 1000); irecv_reset(client); irecv_finish_transfer(client); debug("Exploit sent\n"); debug("Reconnecting to device\n"); *pclient = irecv_reconnect(client, 7); if (*pclient == NULL) { error("Unable to reconnect\n"); return -1; } irecv_get_mode((*pclient), &mode); if (mode != IRECV_K_DFU_MODE) { error("Device reconnected in non-DFU mode\n"); return -1; } return 0; } idevicerestore-1.0.0/src/limera1n.h000066400000000000000000000021441367173617500172150ustar00rootroot00000000000000/* * limera1n.h * Helper code for limera1n exploit based on discovery by geohot * * Copyright (c) 2012-2013 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LIMERA1N_H #define __LIMERA1N_H #include int limera1n_is_supported(struct irecv_device *device); int limera1n_exploit(struct irecv_device *device, irecv_client_t *client); #endif /* __LIMERA1N_H */ idevicerestore-1.0.0/src/limera1n_payload.h000066400000000000000000000106501367173617500207270ustar00rootroot00000000000000/* * limera1n_payload.h * Payload for limera1n exploit * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ extern unsigned char limera1n_payload[]; unsigned char limera1n_payload[] = { 0x7f, 0x46, 0x07, 0xe0, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0x61, 0x48, 0x02, 0x68, 0x61, 0x48, 0x90, 0x42, 0x06, 0xd1, 0x61, 0x49, 0x39, 0x60, 0x61, 0x49, 0x79, 0x60, 0x61, 0x49, 0xb9, 0x60, 0x1c, 0xe0, 0x60, 0x48, 0x90, 0x42, 0x06, 0xd1, 0x60, 0x49, 0x39, 0x60, 0x60, 0x49, 0x79, 0x60, 0x60, 0x49, 0xb9, 0x60, 0x12, 0xe0, 0x5f, 0x48, 0x90, 0x42, 0x06, 0xd1, 0x5f, 0x49, 0x39, 0x60, 0x5f, 0x49, 0x79, 0x60, 0x5f, 0x49, 0xb9, 0x60, 0x08, 0xe0, 0x5e, 0x48, 0x90, 0x42, 0x05, 0xd1, 0x5e, 0x49, 0x39, 0x60, 0x5a, 0x49, 0x79, 0x60, 0x5d, 0x49, 0xb9, 0x60, 0x5d, 0x48, 0x5d, 0x49, 0x3b, 0x68, 0x98, 0x47, 0x5d, 0x48, 0x5a, 0x49, 0x4a, 0x68, 0x00, 0xf0, 0x80, 0xf8, 0x00, 0x28, 0xcb, 0xd0, 0x06, 0x1c, 0x5a, 0x48, 0x56, 0x49, 0x4a, 0x68, 0x00, 0xf0, 0x78, 0xf8, 0x00, 0x28, 0xc3, 0xd0, 0x05, 0x1c, 0x11, 0x20, 0x14, 0x24, 0x29, 0x19, 0x2a, 0x19, 0x30, 0x23, 0x54, 0x4c, 0x00, 0x94, 0x00, 0x24, 0x01, 0x94, 0x00, 0x24, 0x02, 0x94, 0x7c, 0x68, 0xa0, 0x47, 0x11, 0x20, 0x0c, 0x24, 0x31, 0x19, 0x32, 0x19, 0xb3, 0x68, 0x4f, 0x4c, 0x00, 0x94, 0x24, 0x24, 0x64, 0x19, 0x01, 0x94, 0x14, 0x24, 0x64, 0x19, 0x02, 0x94, 0x7c, 0x68, 0xa0, 0x47, 0x45, 0x48, 0x0c, 0x21, 0x89, 0x19, 0xb2, 0x68, 0x15, 0x1c, 0x00, 0xf0, 0x4a, 0xf8, 0x47, 0x48, 0x41, 0x49, 0x2a, 0x1c, 0x00, 0xf0, 0x4d, 0xf8, 0x00, 0x28, 0x08, 0xd0, 0x01, 0x1c, 0x44, 0x48, 0x45, 0x4a, 0x00, 0xf0, 0x46, 0xf8, 0x00, 0x28, 0x01, 0xd0, 0x43, 0x49, 0x01, 0x60, 0x43, 0x48, 0x39, 0x49, 0x2a, 0x1c, 0x00, 0xf0, 0x3d, 0xf8, 0x00, 0x28, 0x08, 0xd0, 0x01, 0x1c, 0x40, 0x48, 0x3d, 0x4a, 0x00, 0xf0, 0x36, 0xf8, 0x00, 0x28, 0x01, 0xd0, 0x3e, 0x49, 0x01, 0x60, 0x3e, 0x48, 0x31, 0x49, 0x2a, 0x1c, 0x00, 0xf0, 0x2d, 0xf8, 0x00, 0x28, 0x08, 0xd0, 0x01, 0x1c, 0x3b, 0x48, 0x35, 0x4a, 0x00, 0xf0, 0x26, 0xf8, 0x00, 0x28, 0x01, 0xd0, 0x33, 0x49, 0x01, 0x60, 0x38, 0x48, 0x29, 0x49, 0x2a, 0x1c, 0x38, 0x4b, 0x00, 0xf0, 0x1e, 0xf8, 0x00, 0x28, 0x07, 0xd1, 0x36, 0x48, 0x25, 0x49, 0x2a, 0x1c, 0x36, 0x4b, 0x00, 0xf0, 0x16, 0xf8, 0x00, 0x28, 0x03, 0xd0, 0x34, 0x49, 0x01, 0x60, 0x34, 0x49, 0x41, 0x60, 0x00, 0x20, 0x1f, 0x49, 0x00, 0x22, 0xbb, 0x68, 0x98, 0x47, 0x55, 0xe7, 0x0b, 0x68, 0x03, 0x60, 0x01, 0x30, 0x01, 0x31, 0x01, 0x3a, 0x00, 0x2a, 0xf8, 0xd1, 0x70, 0x47, 0x00, 0x23, 0xff, 0xe7, 0x10, 0xb5, 0x0c, 0x68, 0x84, 0x42, 0x04, 0xd1, 0x00, 0x2b, 0x07, 0xd0, 0x4c, 0x68, 0x9c, 0x42, 0x04, 0xd0, 0x02, 0x31, 0x02, 0x3a, 0x00, 0x2a, 0xf3, 0xd1, 0x00, 0x21, 0x08, 0x1c, 0x10, 0xbd, 0x88, 0x02, 0x00, 0x00, 0x39, 0x2e, 0x35, 0x00, 0xe5, 0x36, 0x00, 0x00, 0x19, 0x09, 0x00, 0x00, 0xdd, 0x39, 0x00, 0x00, 0x34, 0x2e, 0x34, 0x00, 0x85, 0x4c, 0x00, 0x00, 0x6d, 0x68, 0x00, 0x00, 0x5d, 0x5a, 0x00, 0x00, 0x39, 0x2e, 0x33, 0x00, 0x9d, 0x34, 0x00, 0x00, 0x25, 0x09, 0x00, 0x00, 0x69, 0x39, 0x00, 0x00, 0x39, 0x2e, 0x33, 0x2e, 0xa5, 0x34, 0x00, 0x00, 0x71, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0xc0, 0x02, 0x00, 0x41, 0x54, 0x41, 0x44, 0x47, 0x41, 0x42, 0x4b, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x1a, 0x78, 0xff, 0x2a, 0x4f, 0xf0, 0xff, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0xf3, 0xdf, 0x90, 0xb5, 0x07, 0x4b, 0x1b, 0x68, 0x4f, 0xf0, 0xff, 0x33, 0x11, 0x9a, 0xd3, 0xf1, 0x18, 0xbf, 0x01, 0x20, 0x80, 0xb5, 0x00, 0xaf, 0x82, 0xb0, 0x4f, 0xf0, 0xb0, 0xb5, 0x02, 0xaf, 0x82, 0xb0, 0x01, 0x28, 0x00, 0x4b, 0x18, 0x47, 0x00, 0x00, 0x00, 0x41 }; __attribute__((unused)) static unsigned int limera1n_payload_len = 560; idevicerestore-1.0.0/src/locking.c000066400000000000000000000057551367173617500171410ustar00rootroot00000000000000/* * locking.c * locking extras * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef WIN32 #include #else #include #endif #include "locking.h" #include "common.h" int lock_file(const char* filename, lock_info_t* lockinfo) { if (!lockinfo) { return -1; } #ifdef WIN32 lockinfo->fp = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (lockinfo->fp == INVALID_HANDLE_VALUE) { debug("ERROR: could not open or create lockfile '%s'\n", filename); return -1; } lockinfo->ldata.Offset = 0; lockinfo->ldata.OffsetHigh = 0; if (!LockFileEx(lockinfo->fp, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &lockinfo->ldata)) { debug("ERROR: can't lock file, error %d\n", GetLastError()); CloseHandle(lockinfo->fp); lockinfo->fp = INVALID_HANDLE_VALUE; return -1; } #else lockinfo->fp = fopen(filename, "a+"); if (!lockinfo->fp) { debug("ERROR: could not open or create lockfile '%s'\n", filename); return -1; } lockinfo->ldata.l_type = F_WRLCK; lockinfo->ldata.l_whence = SEEK_SET; lockinfo->ldata.l_start = 0; lockinfo->ldata.l_len = 0; if (fcntl(fileno(lockinfo->fp), F_SETLKW, &lockinfo->ldata) < 0) { debug("ERROR: can't lock file, error %d\n", errno); fclose(lockinfo->fp); lockinfo->fp = NULL; return -1; } #endif return 0; } int unlock_file(lock_info_t* lockinfo) { if (!lockinfo) { return -1; } #ifdef WIN32 if (lockinfo->fp == INVALID_HANDLE_VALUE) { return -1; } lockinfo->ldata.Offset = 0; lockinfo->ldata.OffsetHigh = 0; if (!UnlockFileEx(lockinfo->fp, 0, 1, 0, &lockinfo->ldata)) { debug("ERROR: can't unlock file, error %d\n", GetLastError()); CloseHandle(lockinfo->fp); lockinfo->fp = INVALID_HANDLE_VALUE; return -1; } CloseHandle(lockinfo->fp); lockinfo->fp = INVALID_HANDLE_VALUE; #else if (!lockinfo->fp) { return -1; } lockinfo->ldata.l_type = F_UNLCK; lockinfo->ldata.l_whence = SEEK_SET; lockinfo->ldata.l_start = 0; lockinfo->ldata.l_len = 0; if (fcntl(fileno(lockinfo->fp), F_SETLK, &lockinfo->ldata) < 0) { debug("ERROR: can't unlock file, error %d\n", errno); fclose(lockinfo->fp); lockinfo->fp = NULL; return -1; } fclose(lockinfo->fp); lockinfo->fp = NULL; #endif return 0; } idevicerestore-1.0.0/src/locking.h000066400000000000000000000022611367173617500171330ustar00rootroot00000000000000/* * locking.h * locking extras header file * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LOCKING_H #define LOCKING_H #include #ifdef WIN32 #include #else #include #endif typedef struct { #ifdef WIN32 HANDLE fp; OVERLAPPED ldata; #else FILE* fp; struct flock ldata; #endif } lock_info_t; int lock_file(const char* filename, lock_info_t* lockp); int unlock_file(lock_info_t* lockp); #endif idevicerestore-1.0.0/src/mbn.c000066400000000000000000000055551367173617500162650ustar00rootroot00000000000000/* * mbn.c * support for .mbn file format (found in .bbfw files) * * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "mbn.h" #include "common.h" mbn_file* mbn_parse(unsigned char* data, unsigned int size) { mbn_file* mbn = (mbn_file*)malloc(sizeof(mbn_file)); if (!mbn) { return NULL; } memset(mbn, '\0', sizeof(mbn_file)); mbn->data = malloc(size); mbn->size = size; memcpy(mbn->data, data, size); /* FIXME: header parsing is not big endian safe */ if (memcmp(data, MBN_V2_MAGIC, MBN_V2_MAGIC_SIZE) == 0) { mbn->version = 2; memcpy(&mbn->header.v2, data, sizeof(mbn_header_v2)); mbn->parsed_size = mbn->header.v2.data_size + sizeof(mbn_header_v2); } else if (memcmp(data, MBN_V1_MAGIC, MBN_V1_MAGIC_SIZE) == 0) { mbn->version = 1; memcpy(&mbn->header.v1, data, sizeof(mbn_header_v1)); mbn->parsed_size = mbn->header.v1.data_size + sizeof(mbn_header_v1); } else if (memcmp(data, BIN_MAGIC, BIN_MAGIC_SIZE) == 0) { mbn->version = 3; memcpy(&mbn->header.bin, data, sizeof(bin_header)); mbn->parsed_size = mbn->header.bin.total_size; } else if (memcmp(data, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) { mbn->version = 4; memcpy(&mbn->header.elf, data, sizeof(elf_header)); // we cheat here since we don't parse the actual ELF file mbn->parsed_size = mbn->size; } else { debug("DEBUG: Unknown file format passed to %s\n", __func__); } if (mbn->parsed_size != mbn->size) { info("WARNING: size mismatch when parsing MBN file. Continuing anyway.\n"); } return mbn; } void mbn_free(mbn_file* mbn) { if (mbn) { if (mbn->data) { free(mbn->data); } free(mbn); } } int mbn_update_sig_blob(mbn_file* mbn, const unsigned char* sigdata, unsigned int siglen) { if (!mbn) { error("ERROR: %s: no data\n", __func__); return -1; } mbn->parsed_sig_offset = mbn->size - siglen; if ((mbn->parsed_sig_offset + siglen) > mbn->size) { error("ERROR: %s: signature is larger than mbn file size\n", __func__); return -1; } memcpy(mbn->data + mbn->parsed_sig_offset, sigdata, siglen); return 0; } idevicerestore-1.0.0/src/mbn.h000066400000000000000000000061011367173617500162560ustar00rootroot00000000000000/* * mbn.h * support for .mbn file format (found in .bbfw files) * * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MBN_H #define MBN_H #include #define MBN_V1_MAGIC "\x0A\x00\x00\x00" #define MBN_V1_MAGIC_SIZE 4 struct _mbn_header_v1 { uint32_t type; // the signed .mbn files have 0xA as value. uint32_t unk_0x04; uint32_t unk_0x08; uint32_t unk_0x0c; uint32_t data_size; // data_size = total_size - sizeof(mbn_header) uint32_t sig_offset; // real offset = enc_sig_offset & 0xFFFFFF00 uint32_t unk_0x18; uint32_t unk_0x1c; uint32_t unk_0x20; uint32_t unk_0x24; } __attribute__((packed)); typedef struct _mbn_header_v1 mbn_header_v1; #define MBN_V2_MAGIC "\xD1\xDC\x4B\x84\x34\x10\xD7\x73" #define MBN_V2_MAGIC_SIZE 8 struct _mbn_header_v2 { unsigned char magic1[8]; uint32_t unk_0x08; uint32_t unk_0x0c; // 0xFFFFFFFF uint32_t unk_0x10; // 0xFFFFFFFF uint32_t header_size; uint32_t unk_0x18; uint32_t data_size; // data_size = total_size - sizeof(mbn_header_v2) uint32_t sig_offset; uint32_t unk_0x24; uint32_t unk_0x28; uint32_t unk_0x2c; uint32_t unk_0x30; uint32_t unk_0x34; // 0x1 uint32_t unk_0x38; // 0x1 uint32_t unk_0x3c; // 0xFFFFFFFF uint32_t unk_0x40; // 0xFFFFFFFF uint32_t unk_0x44; // 0xFFFFFFFF uint32_t unk_0x48; // 0xFFFFFFFF uint32_t unk_0x4c; // 0xFFFFFFFF } __attribute__((packed)); typedef struct _mbn_header_v2 mbn_header_v2; #define BIN_MAGIC "\x7D\x04\x00\xEA\x6C\x69\x48\x55" #define BIN_MAGIC_SIZE 8 struct _bin_header { unsigned char magic[8]; uint32_t unk_0x08; uint32_t version; uint32_t total_size; // size including header uint32_t unk_0x14; // some offset } __attribute__((packed)); typedef struct _bin_header bin_header; #define ELF_MAGIC "\x7F\x45\x4C\x46\x01\x01\x01\x00" // ELF magic, 32bit, little endian, SYSV #define ELF_MAGIC_SIZE 8 struct _elf_header { unsigned char magic[8]; } __attribute__((packed)); typedef struct _elf_header elf_header; typedef struct { uint32_t version; union { mbn_header_v1 v1; mbn_header_v2 v2; bin_header bin; elf_header elf; } header; uint32_t parsed_size; uint32_t parsed_sig_offset; void* data; uint32_t size; } mbn_file; mbn_file* mbn_parse(unsigned char* data, unsigned int size); void mbn_free(mbn_file* mbn); int mbn_update_sig_blob(mbn_file* mbn, const unsigned char* data, unsigned int size); #endif idevicerestore-1.0.0/src/normal.c000066400000000000000000000412001367173617500167640ustar00rootroot00000000000000/* * normal.h * Functions for handling idevices in normal mode * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "common.h" #include "normal.h" #include "recovery.h" #include "thread.h" static int normal_idevice_new(struct idevicerestore_client_t* client, idevice_t* device) { int num_devices = 0; char **devices = NULL; idevice_t dev = NULL; idevice_error_t device_error; lockdownd_client_t lockdown = NULL; *device = NULL; if (client->udid) { device_error = idevice_new(&dev, client->udid); if (device_error != IDEVICE_E_SUCCESS) { debug("%s: can't open device with UDID %s\n", __func__, client->udid); return -1; } if (lockdownd_client_new(dev, &lockdown, "idevicerestore") != LOCKDOWN_E_SUCCESS) { error("ERROR: %s: can't connect to lockdownd on device with UDID %s\n", __func__, client->udid); return -1; } char* type = NULL; if (lockdownd_query_type(lockdown, &type) != LOCKDOWN_E_SUCCESS) { return -1; } if (strcmp(type, "com.apple.mobile.lockdown") != 0) { free(type); return -1; } free(type); lockdownd_client_free(lockdown); lockdown = NULL; *device = dev; return 0; } idevice_get_device_list(&devices, &num_devices); if (num_devices == 0) { return -1; } int j; for (j = 0; j < num_devices; j++) { if (lockdown != NULL) { lockdownd_client_free(lockdown); lockdown = NULL; } if (dev != NULL) { idevice_free(dev); dev = NULL; } device_error = idevice_new(&dev, devices[j]); if (device_error != IDEVICE_E_SUCCESS) { debug("%s: can't open device with UDID %s\n", __func__, devices[j]); continue; } if (lockdownd_client_new(dev, &lockdown, "idevicerestore") != LOCKDOWN_E_SUCCESS) { error("ERROR: %s: can't connect to lockdownd on device with UDID %s\n", __func__, devices[j]); continue; } char* type = NULL; if (lockdownd_query_type(lockdown, &type) != LOCKDOWN_E_SUCCESS) { continue; } if (strcmp(type, "com.apple.mobile.lockdown") != 0) { free(type); continue; } free(type); plist_t node = NULL; if ((lockdownd_get_value(lockdown, NULL, "UniqueChipID", &node) != LOCKDOWN_E_SUCCESS) || !node || (plist_get_node_type(node) != PLIST_UINT)){ if (node) { plist_free(node); } continue; } lockdownd_client_free(lockdown); lockdown = NULL; uint64_t this_ecid = 0; plist_get_uint_val(node, &this_ecid); plist_free(node); if (client->ecid != 0) { if (this_ecid != client->ecid) { continue; } } else { client->ecid = this_ecid; } client->udid = strdup(devices[j]); *device = dev; break; } idevice_device_list_free(devices); return 0; } int normal_check_mode(struct idevicerestore_client_t* client) { idevice_t device = NULL; normal_idevice_new(client, &device); if (!device) { return -1; } idevice_free(device); return 0; } irecv_device_t normal_get_irecv_device(struct idevicerestore_client_t* client) { idevice_t device = NULL; lockdownd_client_t lockdown = NULL; lockdownd_error_t lockdown_error = LOCKDOWN_E_SUCCESS; irecv_device_t irecv_device = NULL; normal_idevice_new(client, &device); if (!device) { return NULL; } lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"); if (!(client->flags & FLAG_ERASE) && lockdown_error == LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) { info("*** Device is not paired with this computer. Please trust this computer on the device to continue. ***\n"); if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(0); } while (!(client->flags & FLAG_QUIT)) { lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"); if (lockdown_error != LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) { break; } sleep(1); } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); } if (client->flags & FLAG_QUIT) { return NULL; } } if (lockdown_error != LOCKDOWN_E_SUCCESS) { lockdown_error = lockdownd_client_new(device, &lockdown, "idevicerestore"); } if (lockdown_error != LOCKDOWN_E_SUCCESS) { idevice_free(device); return NULL; } plist_t pval = NULL; lockdownd_get_value(lockdown, NULL, "HardwareModel", &pval); if (pval && (plist_get_node_type(pval) == PLIST_STRING)) { char *strval = NULL; plist_get_string_val(pval, &strval); if (strval) { irecv_devices_get_device_by_hardware_model(strval, &irecv_device); free(strval); } } plist_free(pval); lockdownd_client_free(lockdown); idevice_free(device); return irecv_device; } int normal_enter_recovery(struct idevicerestore_client_t* client) { idevice_t device = NULL; lockdownd_client_t lockdown = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; lockdownd_error_t lockdown_error = LOCKDOWN_E_SUCCESS; device_error = idevice_new(&device, client->udid); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to find device\n"); return -1; } lockdown_error = lockdownd_client_new(device, &lockdown, "idevicerestore"); if (lockdown_error != LOCKDOWN_E_SUCCESS) { error("ERROR: Unable to connect to lockdownd service\n"); idevice_free(device); return -1; } /* unpair the device */ lockdown_error = lockdownd_unpair(lockdown, NULL); if (lockdown_error != LOCKDOWN_E_SUCCESS) { error("WARNING: Could not unpair device\n"); } lockdown_error = lockdownd_enter_recovery(lockdown); if (lockdown_error != LOCKDOWN_E_SUCCESS) { error("ERROR: Unable to place device in recovery mode\n"); lockdownd_client_free(lockdown); idevice_free(device); return -1; } lockdownd_client_free(lockdown); idevice_free(device); lockdown = NULL; device = NULL; mutex_lock(&client->device_event_mutex); debug("DEBUG: Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode == &idevicerestore_modes[MODE_NORMAL] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Failed to place device in recovery mode\n"); return -1; } debug("DEBUG: Waiting for device to connect in recovery mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != &idevicerestore_modes[MODE_RECOVERY] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Failed to enter recovery mode\n"); return -1; } mutex_unlock(&client->device_event_mutex); if (recovery_client_new(client) < 0) { error("ERROR: Unable to enter recovery mode\n"); return -1; } return 0; } plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const char* domain, const char* key) { idevice_t device = NULL; plist_t node = NULL; lockdownd_client_t lockdown = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; lockdownd_error_t lockdown_error = LOCKDOWN_E_SUCCESS; device_error = idevice_new(&device, client->udid); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to connect to device?!\n"); return NULL; } lockdown_error = lockdownd_client_new(device, &lockdown, "idevicerestore"); if (lockdown_error != LOCKDOWN_E_SUCCESS) { error("ERROR: Unable to connect to lockdownd\n"); idevice_free(device); return NULL; } lockdown_error = lockdownd_get_value(lockdown, domain, key, &node); if (lockdown_error != LOCKDOWN_E_SUCCESS) { debug("ERROR: Unable to get %s-%s from lockdownd\n", domain, key); lockdownd_client_free(lockdown); idevice_free(device); return NULL; } lockdownd_client_free(lockdown); idevice_free(device); lockdown = NULL; device = NULL; return node; } static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const char* key, unsigned char** nonce, int* nonce_size) { plist_t nonce_node = normal_get_lockdown_value(client, NULL, key); if (!nonce_node || plist_get_node_type(nonce_node) != PLIST_DATA) { error("Unable to get %s\n", key); return -1; } uint64_t n_size = 0; plist_get_data_val(nonce_node, (char**)nonce, &n_size); *nonce_size = (int)n_size; plist_free(nonce_node); return 0; } int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { return normal_get_nonce_by_key(client, "SEPNonce", nonce, nonce_size); } int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { return normal_get_nonce_by_key(client, "ApNonce", nonce, nonce_size); } int normal_is_image4_supported(struct idevicerestore_client_t* client) { plist_t node = normal_get_lockdown_value(client, NULL, "Image4Supported"); if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { return 0; } uint8_t bval = 0; plist_get_bool_val(node, &bval); plist_free(node); return bval; } int normal_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) { plist_t unique_chip_node = normal_get_lockdown_value(client, NULL, "UniqueChipID"); if (!unique_chip_node || plist_get_node_type(unique_chip_node) != PLIST_UINT) { error("ERROR: Unable to get ECID\n"); return -1; } plist_get_uint_val(unique_chip_node, ecid); plist_free(unique_chip_node); return 0; } int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) { plist_t node = normal_get_lockdown_value(client, NULL, "FirmwarePreflightInfo"); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unable to get FirmwarePreflightInfo\n"); return -1; } *preflight_info = node; return 0; } int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest) { int result = -1; idevice_t device = NULL; idevice_error_t device_err; lockdownd_client_t lockdown; lockdownd_service_descriptor_t service = NULL; lockdownd_error_t lerr; preboard_client_t preboard = NULL; preboard_error_t perr; device_err = idevice_new(&device, client->udid); if (device_err != IDEVICE_E_SUCCESS) { error("ERROR: Could not connect to device (%d)\n", device_err); return -1; } lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"); if (lerr != LOCKDOWN_E_SUCCESS) { error("ERROR: Could not connect to lockdownd (%d)\n", lerr); idevice_free(device); return -1; } lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service); if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { info("*** Device is locked. Please unlock the device to continue. ***\n"); while (1) { lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service); if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) { break; } sleep(1); } } if (lerr != LOCKDOWN_E_SUCCESS) { error("ERROR: Could not start preboard service (%d)\n", lerr); lockdownd_client_free(lockdown); idevice_free(device); return -1; } perr = preboard_client_new(device, service, &preboard); lockdownd_service_descriptor_free(service); lockdownd_client_free(lockdown); if (perr != PREBOARD_E_SUCCESS) { error("ERROR: Could not connect to preboard service (%d)\n", perr); idevice_free(device); return -1; } perr = preboard_create_stashbag(preboard, manifest, NULL, NULL); if (perr != PREBOARD_E_SUCCESS) { error("ERROR: Failed to trigger stashbag creation (%d)\n", perr); preboard_client_free(preboard); idevice_free(device); return -1; } int ticks = 0; while (ticks++ < 130 && !(client->flags & FLAG_QUIT)) { plist_t pl = NULL; perr = preboard_receive_with_timeout(preboard, &pl, 1000); if (perr == PREBOARD_E_TIMEOUT) { continue; } else if (perr != PREBOARD_E_SUCCESS) { error("ERROR: could not receive from preboard service\n"); break; } else { plist_t node; if (_plist_dict_get_bool(pl, "Skip")) { result = 0; info("Device does not require stashbag.\n"); break; } if (_plist_dict_get_bool(pl, "ShowDialog")) { info("Device requires stashbag.\n"); printf("******************************************************************************\n" "* Please enter your passcode on the device. The device will store a token *\n" "* that will be used after restore to access the user data partition. This *\n" "* prevents an 'Attempting data recovery' process occurring after reboot that *\n" "* may take a long time to complete and will _also_ require the passcode. *\n" "******************************************************************************\n"); plist_free(pl); continue; } node = plist_dict_get_item(pl, "Error"); if (node) { char *strval = NULL; node = plist_dict_get_item(pl, "ErrorString"); if (node) { plist_get_string_val(node, &strval); } error("ERROR: Could not create stashbag: %s\n", (strval) ? strval : "(Unknown error)"); free(strval); plist_free(pl); break; } if (_plist_dict_get_bool(pl, "Timeout")) { error("ERROR: Timeout while waiting for user to enter passcode.\n"); result = -2; plist_free(pl); break; } if (_plist_dict_get_bool(pl, "HideDialog")) { plist_free(pl); /* hide dialog */ result = 1; info("Stashbag created.\n"); break; } } plist_free(pl); } preboard_client_free(preboard); idevice_free(device); return result; } int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_t manifest) { int result = -1; idevice_t device = NULL; idevice_error_t device_err; lockdownd_client_t lockdown; lockdownd_service_descriptor_t service = NULL; lockdownd_error_t lerr; preboard_client_t preboard = NULL; preboard_error_t perr; plist_t pl = NULL; device_err = idevice_new(&device, client->udid); if (device_err != IDEVICE_E_SUCCESS) { error("ERROR: Could not connect to device (%d)\n", device_err); return -1; } lerr = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"); if (lerr != LOCKDOWN_E_SUCCESS) { error("ERROR: Could not connect to lockdownd (%d)\n", lerr); idevice_free(device); return -1; } lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service); if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { info("*** Device is locked. Please unlock the device to continue. ***\n"); while (1) { lerr = lockdownd_start_service(lockdown, PREBOARD_SERVICE_NAME, &service); if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) { break; } sleep(1); } } if (lerr != LOCKDOWN_E_SUCCESS) { error("ERROR: Could not start preboard service (%d)\n", lerr); lockdownd_client_free(lockdown); idevice_free(device); return -1; } perr = preboard_client_new(device, service, &preboard); lockdownd_service_descriptor_free(service); lockdownd_client_free(lockdown); if (perr != PREBOARD_E_SUCCESS) { error("ERROR: Could not connect to preboard service (%d)\n", perr); idevice_free(device); return -1; } perr = preboard_commit_stashbag(preboard, manifest, NULL, NULL); if (perr != PREBOARD_E_SUCCESS) { error("ERROR: Failed to trigger stashbag creation (%d)\n", perr); preboard_client_free(preboard); idevice_free(device); return -1; } perr = preboard_receive_with_timeout(preboard, &pl, 30000); if (perr != PREBOARD_E_SUCCESS) { error("ERROR: could not receive from preboard service (%d)\n", perr); } else { int commit_complete = 0; plist_t node = plist_dict_get_item(pl, "Error"); if (node) { char *strval = NULL; node = plist_dict_get_item(pl, "ErrorString"); if (node) { plist_get_string_val(node, &strval); } error("ERROR: Could not commit stashbag: %s\n", (strval) ? strval : "(Unknown error)"); free(strval); } else if (_plist_dict_get_bool(pl, "StashbagCommitComplete")) { info("Stashbag committed!\n"); result = 0; } else { error("ERROR: Unexpected reply from preboard service\n"); debug_plist(pl); } plist_free(pl); } preboard_client_free(preboard); idevice_free(device); return result; } idevicerestore-1.0.0/src/normal.h000066400000000000000000000042411367173617500167750ustar00rootroot00000000000000/* * normal.h * Functions for handling idevices in normal mode * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_NORMAL_H #define IDEVICERESTORE_NORMAL_H #ifdef __cplusplus extern "C" { #endif #include #include #include int normal_check_mode(struct idevicerestore_client_t* client); irecv_device_t normal_get_irecv_device(struct idevicerestore_client_t* client); int normal_enter_recovery(struct idevicerestore_client_t* client); int normal_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int normal_is_image4_supported(struct idevicerestore_client_t* client); int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info); plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const char* domain, const char* key); int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest); int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_t manifest); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/recovery.c000066400000000000000000000404661367173617500173470ustar00rootroot00000000000000/* * recovery.c * Functions for handling idevices in recovery mode * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "idevicerestore.h" #include "tss.h" #include "img3.h" #include "restore.h" #include "recovery.h" static int recovery_progress_callback(irecv_client_t client, const irecv_event_t* event) { if (event->type == IRECV_PROGRESS) { //print_progress_bar(event->progress); } return 0; } void recovery_client_free(struct idevicerestore_client_t* client) { if(client) { if (client->recovery) { if(client->recovery->client) { irecv_close(client->recovery->client); client->recovery->client = NULL; } free(client->recovery); client->recovery = NULL; } } } int recovery_client_new(struct idevicerestore_client_t* client) { int i = 0; int attempts = 20; irecv_client_t recovery = NULL; irecv_error_t recovery_error = IRECV_E_UNKNOWN_ERROR; if(client->recovery == NULL) { client->recovery = (struct recovery_client_t*)malloc(sizeof(struct recovery_client_t)); if (client->recovery == NULL) { error("ERROR: Out of memory\n"); return -1; } memset(client->recovery, 0, sizeof(struct recovery_client_t)); } for (i = 1; i <= attempts; i++) { recovery_error = irecv_open_with_ecid(&recovery, client->ecid); if (recovery_error == IRECV_E_SUCCESS) { break; } if (i >= attempts) { error("ERROR: Unable to connect to device in recovery mode\n"); return -1; } sleep(4); debug("Retrying connection...\n"); } if (client->srnm == NULL) { const struct irecv_device_info *device_info = irecv_get_device_info(recovery); if (device_info && device_info->srnm) { client->srnm = strdup(device_info->srnm); info("INFO: device serial number is %s\n", client->srnm); } } irecv_event_subscribe(recovery, IRECV_PROGRESS, &recovery_progress_callback, NULL); client->recovery->client = recovery; return 0; } int recovery_check_mode(struct idevicerestore_client_t* client) { irecv_client_t recovery = NULL; irecv_error_t recovery_error = IRECV_E_SUCCESS; int mode = 0; if (client->udid && client->ecid == 0) { /* if we have a UDID but no ECID we can't make sure this is the correct device */ return -1; } irecv_init(); recovery_error=irecv_open_with_ecid(&recovery, client->ecid); if (recovery_error != IRECV_E_SUCCESS) { return -1; } irecv_get_mode(recovery, &mode); if ((mode == IRECV_K_DFU_MODE) || (mode == IRECV_K_WTF_MODE)) { irecv_close(recovery); return -1; } irecv_close(recovery); recovery = NULL; return 0; } int recovery_set_autoboot(struct idevicerestore_client_t* client, int enable) { irecv_error_t recovery_error = IRECV_E_SUCCESS; recovery_error = irecv_send_command(client->recovery->client, (enable) ? "setenv auto-boot true" : "setenv auto-boot false"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to set auto-boot environmental variable\n"); return -1; } recovery_error = irecv_send_command(client->recovery->client, "saveenv"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to save environmental variable\n"); return -1; } return 0; } int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build_identity) { if (client->build_major >= 8) { client->restore_boot_args = strdup("rd=md0 nand-enable-reformat=1 -progress"); } /* upload data to make device boot restore mode */ if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) { if (!client->image4supported) { /* send ApTicket */ if (recovery_send_ticket(client) < 0) { error("ERROR: Unable to send APTicket\n"); return -1; } } } info("Recovery Mode Environment:\n"); char* value = NULL; irecv_getenv(client->recovery->client, "build-version", &value); info("iBoot build-version=%s\n", (value) ? value : "(unknown)"); free(value); value = NULL; irecv_getenv(client->recovery->client, "build-style", &value); info("iBoot build-style=%s\n", (value) ? value : "(unknown)"); free(value); value = NULL; unsigned long radio_error = 0; irecv_getenv(client->recovery->client, "radio-error", &value); if (value) { radio_error = strtoul(value, NULL, 0); } if (radio_error > 0) { info("radio-error=%s\n", value); free(value); value = NULL; irecv_getenv(client->recovery->client, "radio-error-string", &value); if (value) { info("radio-error-string=%s\n", value); free(value); value = NULL; } } if (recovery_set_autoboot(client, 0) < 0) { return -1; } /* send logo and show it */ if (recovery_send_applelogo(client, build_identity) < 0) { error("ERROR: Unable to send AppleLogo\n"); return -1; } /* send components loaded by iBoot */ if (recovery_send_loaded_by_iboot(client, build_identity) < 0) { error("ERROR: Unable to send components supposed to be loaded by iBoot\n"); return -1; } /* send ramdisk and run it */ if (recovery_send_ramdisk(client, build_identity) < 0) { error("ERROR: Unable to send Ramdisk\n"); return -1; } /* send devicetree and load it */ if (recovery_send_devicetree(client, build_identity) < 0) { error("ERROR: Unable to send DeviceTree\n"); return -1; } mutex_lock(&client->device_event_mutex); if (recovery_send_kernelcache(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send KernelCache\n"); return -1; } debug("DEBUG: Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 30000); if (client->mode == &idevicerestore_modes[MODE_RECOVERY] || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Failed to place device in restore mode\n"); return -1; } mutex_unlock(&client->device_event_mutex); return 0; } int recovery_send_ticket(struct idevicerestore_client_t* client) { if (!client->tss) { error("ERROR: ApTicket requested but no TSS present\n"); return -1; } unsigned char* data = NULL; uint32_t size = 0; if (tss_response_get_ap_ticket(client->tss, &data, &size) < 0) { error("ERROR: Unable to get ApTicket from TSS request\n"); return -1; } info("Sending APTicket (%d bytes)\n", size); irecv_error_t err = irecv_send_buffer(client->recovery->client, data, size, 0); free(data); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send APTicket: %s\n", irecv_strerror(err)); return -1; } err = irecv_send_command(client->recovery->client, "ticket"); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send ticket command\n"); return -1; } return 0; } int recovery_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { unsigned int size = 0; unsigned char* data = NULL; char* path = NULL; irecv_error_t err = 0; if (client->tss) { if (tss_response_get_path_by_entry(client->tss, component, &path) < 0) { debug("NOTE: No path for component %s in TSS, will fetch from build_identity\n", component); } } if (!path) { if (build_identity_get_component_path(build_identity, component, &path) < 0) { error("ERROR: Unable to get path for component '%s'\n", component); free(path); return -1; } } unsigned char* component_data = NULL; unsigned int component_size = 0; int ret = extract_component(client->ipsw, path, &component_data, &component_size); free(path); if (ret < 0) { error("ERROR: Unable to extract component: %s\n", component); return -1; } ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); free(component_data); if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); return -1; } info("Sending %s (%d bytes)...\n", component, size); // FIXME: Did I do this right???? err = irecv_send_buffer(client->recovery->client, data, size, 0); free(data); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(err)); return -1; } return 0; } int recovery_send_ibec(struct idevicerestore_client_t* client, plist_t build_identity) { const char* component = "iBEC"; irecv_error_t recovery_error = IRECV_E_SUCCESS; if (client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } recovery_error = irecv_send_command(client->recovery->client, "go"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; } irecv_usb_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); return 0; } int recovery_send_applelogo(struct idevicerestore_client_t* client, plist_t build_identity) { const char* component = "RestoreLogo"; irecv_error_t recovery_error = IRECV_E_SUCCESS; if (!build_identity_has_component(build_identity, component)) { return 0; } info("Sending %s...\n", component); if (client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } recovery_error = irecv_send_command(client->recovery->client, "setpicture 4"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to set %s\n", component); return -1; } recovery_error = irecv_send_command(client->recovery->client, "bgcolor 0 0 0"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to display %s\n", component); return -1; } return 0; } int recovery_send_devicetree(struct idevicerestore_client_t* client, plist_t build_identity) { const char* component = "RestoreDeviceTree"; irecv_error_t recovery_error = IRECV_E_SUCCESS; if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } recovery_error = irecv_send_command(client->recovery->client, "devicetree"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; } return 0; } int recovery_send_loaded_by_iboot(struct idevicerestore_client_t* client, plist_t build_identity) { if (client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find manifest node\n"); return -1; } plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); int err = 0; while (iter) { char *key = NULL; plist_t node = NULL; plist_dict_next_item(manifest_node, iter, &key, &node); if (key == NULL) break; plist_t iboot_node = plist_access_path(node, 2, "Info", "IsLoadedByiBoot"); if (iboot_node && plist_get_node_type(iboot_node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(iboot_node, &b); if (b) { debug("DEBUG: %s is loaded by iBoot.\n", key); if (recovery_send_component(client, build_identity, key) < 0) { error("ERROR: Unable to send component '%s' to device.\n", key); err++; } else { if (irecv_send_command(client->recovery->client, "firmware") != IRECV_E_SUCCESS) { error("ERROR: iBoot command 'firmware' failed for component '%s'\n", key); err++; } } } } free(key); } free(iter); return (err) ? -1 : 0; } int recovery_send_ramdisk(struct idevicerestore_client_t* client, plist_t build_identity) { const char *component = "RestoreRamDisk"; irecv_error_t recovery_error = IRECV_E_SUCCESS; if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } char* value = NULL; irecv_getenv(client->recovery->client, "ramdisk-size", &value); info("ramdisk-size=%s\n", (value ? value : "(unknown)")); free(value); value = NULL; if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } irecv_send_command(client->recovery->client, "getenv ramdisk-delay"); recovery_error = irecv_send_command(client->recovery->client, "ramdisk"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; } sleep(2); return 0; } int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t build_identity) { const char* component = "RestoreKernelCache"; irecv_error_t recovery_error = IRECV_E_SUCCESS; if (client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } irecv_usb_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); if (client->restore_boot_args) { char setba[256]; strcpy(setba, "setenv boot-args "); strcat(setba, client->restore_boot_args); recovery_error = irecv_send_command(client->recovery->client, setba); } recovery_error = irecv_send_command(client->recovery->client, "bootx"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; } return 0; } int recovery_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->recovery->client); if (!device_info) { return -1; } *ecid = device_info->ecid; return 0; } int recovery_is_image4_supported(struct idevicerestore_client_t* client) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return 0; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->recovery->client); if (!device_info) { return 0; } return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->recovery->client); if (!device_info) { return -1; } if (device_info->ap_nonce && device_info->ap_nonce_size > 0) { *nonce = (unsigned char*)malloc(device_info->ap_nonce_size); if (!*nonce) { return -1; } *nonce_size = device_info->ap_nonce_size; memcpy(*nonce, device_info->ap_nonce, *nonce_size); } return 0; } int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { return -1; } } const struct irecv_device_info *device_info = irecv_get_device_info(client->recovery->client); if (!device_info) { return -1; } if (device_info->sep_nonce && device_info->sep_nonce_size > 0) { *nonce = (unsigned char*)malloc(device_info->sep_nonce_size); if (!*nonce) { return -1; } *nonce_size = device_info->sep_nonce_size; memcpy(*nonce, device_info->sep_nonce, *nonce_size); } return 0; } int recovery_send_reset(struct idevicerestore_client_t* client) { irecv_send_command(client->recovery->client, "reset"); return 0; } idevicerestore-1.0.0/src/recovery.h000066400000000000000000000054671367173617500173560ustar00rootroot00000000000000/* * recovery.h * Functions for handling idevices in recovery mode * * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_RECOVERY_H #define IDEVICERESTORE_RECOVERY_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "common.h" struct recovery_client_t { irecv_client_t client; const char* ipsw; plist_t tss; }; int recovery_check_mode(struct idevicerestore_client_t* client); int recovery_client_new(struct idevicerestore_client_t* client); void recovery_client_free(struct idevicerestore_client_t* client); int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); int recovery_send_ibec(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_applelogo(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_devicetree(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_ramdisk(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_loaded_by_iboot(struct idevicerestore_client_t* client, plist_t build_identity); int recovery_send_reset(struct idevicerestore_client_t* client); int recovery_send_ticket(struct idevicerestore_client_t* client); int recovery_set_autoboot(struct idevicerestore_client_t* client, int enable); int recovery_get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int recovery_is_image4_supported(struct idevicerestore_client_t* client); int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/restore.c000066400000000000000000002476351367173617500172030ustar00rootroot00000000000000/* * restore.c * Functions for handling idevices in restore mode * * Copyright (c) 2012-2019 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010-2013 Martin Szulecki. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "idevicerestore.h" #include "asr.h" #include "fdr.h" #include "fls.h" #include "mbn.h" #include "ftab.h" #include "tss.h" #include "ipsw.h" #include "restore.h" #include "common.h" #include "endianness.h" #define CREATE_PARTITION_MAP 11 #define CREATE_FILESYSTEM 12 #define RESTORE_IMAGE 13 #define VERIFY_RESTORE 14 #define CHECK_FILESYSTEMS 15 #define MOUNT_FILESYSTEMS 16 #define FIXUP_VAR 17 #define FLASH_FIRMWARE 18 #define UPDATE_BASEBAND 19 #define SET_BOOT_STAGE 20 #define REBOOT_DEVICE 21 #define SHUTDOWN_DEVICE 22 #define TURN_ON_ACCESSORY_POWER 23 #define CLEAR_BOOTARGS 24 #define MODIFY_BOOTARGS 25 #define INSTALL_ROOT 26 #define INSTALL_KERNELCACHE 27 #define WAIT_FOR_NAND 28 #define UNMOUNT_FILESYSTEMS 29 #define SET_DATETIME 30 #define EXEC_IBOOT 31 #define FINALIZE_NAND_EPOCH_UPDATE 32 #define CHECK_INAPPR_BOOT_PARTITIONS 33 #define CREATE_FACTORY_RESTORE_MARKER 34 #define LOAD_FIRMWARE 35 #define REQUESTING_FUD_DATA 36 #define REMOVING_ACTIVATION_RECORD 37 #define CHECK_BATTERY_VOLTAGE 38 #define WAIT_BATTERY_CHARGE 39 #define CLOSE_MODEM_TICKETS 40 #define MIGRATE_DATA 41 #define WIPE_STORAGE_DEVICE 42 #define SEND_APPLE_LOGO 43 #define CHECK_LOGS 44 #define CLEAR_NVRAM 46 #define UPDATE_GAS_GAUGE 47 #define PREPARE_BASEBAND_UPDATE 48 #define BOOT_BASEBAND 49 #define CREATE_SYSTEM_KEYBAG 50 #define UPDATE_IR_MCU_FIRMWARE 51 #define RESIZE_SYSTEM_PARTITION 52 #define COLLECTING_UPDATER_OUTPUT 53 #define PAIR_STOCKHOLM 54 #define UPDATE_STOCKHOLM 55 #define UPDATE_SWDHID 56 #define CERTIFY_SEP 57 #define UPDATE_NAND_FIRMWARE 58 #define UPDATE_SE_FIRMWARE 59 #define UPDATE_SAVAGE 60 #define INSTALLING_DEVICETREE 61 #define CERTIFY_SAVAGE 62 #define SUBMITTING_PROVINFO 63 #define CERTIFY_YONKERS 64 #define UPDATE_ROSE 65 #define UPDATE_VERIDIAN 66 #define CREATING_PROTECTED_VOLUME 67 #define RESIZING_MAIN_FS_PARTITION 68 static int restore_finished = 0; static int restore_device_connected = 0; int restore_client_new(struct idevicerestore_client_t* client) { struct restore_client_t* restore = (struct restore_client_t*) malloc(sizeof(struct restore_client_t)); if (restore == NULL) { error("ERROR: Out of memory\n"); return -1; } if (restore_open_with_timeout(client) < 0) { restore_client_free(client); return -1; } client->restore = restore; return 0; } void restore_client_free(struct idevicerestore_client_t* client) { if (client && client->restore) { if(client->restore->client) { restored_client_free(client->restore->client); client->restore->client = NULL; } if(client->restore->device) { idevice_free(client->restore->device); client->restore->device = NULL; } if(client->restore->bbtss) { plist_free(client->restore->bbtss); client->restore->bbtss = NULL; } free(client->restore); client->restore = NULL; } } static int restore_idevice_new(struct idevicerestore_client_t* client, idevice_t* device) { int num_devices = 0; char **devices = NULL; idevice_get_device_list(&devices, &num_devices); if (num_devices == 0) { return -1; } *device = NULL; idevice_t dev = NULL; idevice_error_t device_error; restored_client_t restore = NULL; int j; for (j = 0; j < num_devices; j++) { if (restore != NULL) { restored_client_free(restore); restore = NULL; } if (dev != NULL) { idevice_free(dev); dev = NULL; } device_error = idevice_new(&dev, devices[j]); if (device_error != IDEVICE_E_SUCCESS) { debug("%s: can't open device with UDID %s\n", __func__, devices[j]); continue; } if (restored_client_new(dev, &restore, "idevicerestore") != RESTORE_E_SUCCESS) { debug("%s: can't connect to restored on device with UDID %s\n", __func__, devices[j]); continue; } char* type = NULL; uint64_t version = 0; if (restored_query_type(restore, &type, &version) != RESTORE_E_SUCCESS) { continue; } if (strcmp(type, "com.apple.mobile.restored") != 0) { free(type); continue; } free(type); if (client->ecid != 0) { plist_t node = NULL; plist_t hwinfo = NULL; if (restored_query_value(restore, "HardwareInfo", &hwinfo) != RESTORE_E_SUCCESS) { continue; } node = plist_dict_get_item(hwinfo, "UniqueChipID"); if (!node || plist_get_node_type(node) != PLIST_UINT) { if (hwinfo) { plist_free(hwinfo); } continue; } restored_client_free(restore); restore = NULL; uint64_t this_ecid = 0; plist_get_uint_val(node, &this_ecid); plist_free(hwinfo); if (this_ecid != client->ecid) { continue; } } if (restore) { restored_client_free(restore); restore = NULL; } client->udid = strdup(devices[j]); *device = dev; break; } idevice_device_list_free(devices); return 0; } int restore_check_mode(struct idevicerestore_client_t* client) { idevice_t device = NULL; restore_idevice_new(client, &device); if (!device) { return -1; } idevice_free(device); return 0; } irecv_device_t restore_get_irecv_device(struct idevicerestore_client_t* client) { char* model = NULL; plist_t node = NULL; idevice_t device = NULL; restored_client_t restore = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; irecv_device_t irecv_device = NULL; restore_idevice_new(client, &device); if (!device) { return NULL; } restore_error = restored_client_new(device, &restore, "idevicerestore"); if (restore_error != RESTORE_E_SUCCESS) { idevice_free(device); return NULL; } if (restored_query_type(restore, NULL, NULL) != RESTORE_E_SUCCESS) { restored_client_free(restore); idevice_free(device); return NULL; } if (client->srnm == NULL) { restore_error = restored_get_value(restore, "SerialNumber", &node); if (restore_error != RESTORE_E_SUCCESS || !node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to get SerialNumber from restored\n"); restored_client_free(restore); idevice_free(device); return NULL; } plist_get_string_val(node, &client->srnm); info("INFO: device serial number is %s\n", client->srnm); plist_free(node); node = NULL; } restore_error = restored_get_value(restore, "HardwareModel", &node); restored_client_free(restore); idevice_free(device); if (restore_error != RESTORE_E_SUCCESS || !node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to get HardwareModel from restored\n"); plist_free(node); return NULL; } plist_get_string_val(node, &model); irecv_devices_get_device_by_hardware_model(model, &irecv_device); free(model); return irecv_device; } int restore_is_image4_supported(struct idevicerestore_client_t* client) { int result = 0; plist_t hwinfo = NULL; idevice_t device = NULL; restored_client_t restore = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; if (idevice_new(&device, client->udid) != IDEVICE_E_SUCCESS) { error("ERROR: Could not connect to device %s\n", client->udid); return -1; } restore_error = restored_client_new(device, &restore, "idevicerestore"); if (restore_error != RESTORE_E_SUCCESS) { idevice_free(device); return -1; } if (restored_query_type(restore, NULL, NULL) != RESTORE_E_SUCCESS) { restored_client_free(restore); idevice_free(device); return -1; } restore_error = restored_query_value(restore, "HardwareInfo", &hwinfo); if (restore_error == RESTORE_E_SUCCESS) { uint8_t b = 0; plist_t node = plist_dict_get_item(hwinfo, "SupportsImage4"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &b); result = b; } } restored_client_free(restore); idevice_free(device); return result; } int restore_reboot(struct idevicerestore_client_t* client) { if(client->restore == NULL) { if (restore_open_with_timeout(client) < 0) { error("ERROR: Unable to open device in restore mode\n"); return -1; } } mutex_lock(&client->device_event_mutex); info("Rebooting restore mode device...\n"); restored_reboot(client->restore->client); restored_client_free(client->restore->client); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 30000); if (client->mode == &idevicerestore_modes[MODE_RESTORE]) { mutex_unlock(&client->device_event_mutex); return -1; } mutex_unlock(&client->device_event_mutex); return 0; } static int restore_is_current_device(struct idevicerestore_client_t* client, const char* udid) { if (!client) { return 0; } if (!client->srnm) { error("ERROR: %s: no SerialNumber given in client data\n", __func__); return 0; } idevice_t device = NULL; idevice_error_t device_error; restored_client_t restored = NULL; restored_error_t restore_error; char *type = NULL; uint64_t version = 0; device_error = idevice_new(&device, udid); if (device_error != IDEVICE_E_SUCCESS) { debug("%s: can't open device with UDID %s\n", __func__, udid); return 0; } restore_error = restored_client_new(device, &restored, "idevicerestore"); if (restore_error != RESTORE_E_SUCCESS) { debug("%s: can't connect to restored\n", __func__); idevice_free(device); return 0; } restore_error = restored_query_type(restored, &type, &version); if ((restore_error == RESTORE_E_SUCCESS) && type && (strcmp(type, "com.apple.mobile.restored") == 0)) { debug("%s: Connected to %s, version %d\n", __func__, type, (int)version); } else { debug("%s: device %s is not in restore mode\n", __func__, udid); restored_client_free(restored); idevice_free(device); return 0; } plist_t node = NULL; restore_error = restored_get_value(restored, "SerialNumber", &node); if ((restore_error != RESTORE_E_SUCCESS) || !node || (plist_get_node_type(node) != PLIST_STRING)) { error("ERROR: %s: Unable to get SerialNumber from restored\n", __func__); restored_client_free(restored); idevice_free(device); if (node) { plist_free(node); } return 0; } restored_client_free(restored); idevice_free(device); char* this_srnm = NULL; plist_get_string_val(node, &this_srnm); plist_free(node); if (!this_srnm) { return 0; } return (strcasecmp(this_srnm, client->srnm) == 0); } int restore_open_with_timeout(struct idevicerestore_client_t* client) { int i = 0; int attempts = 180; char *type = NULL; uint64_t version = 0; idevice_t device = NULL; restored_client_t restored = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; restored_error_t restore_error = RESTORE_E_SUCCESS; // no context exists so bail if(client == NULL) { return -1; } if(client->srnm == NULL) { error("ERROR: no SerialNumber in client data!\n"); return -1; } // create our restore client if it doesn't yet exist if(client->restore == NULL) { client->restore = (struct restore_client_t*) malloc(sizeof(struct restore_client_t)); if(client->restore == NULL) { error("ERROR: Out of memory\n"); return -1; } memset(client->restore, '\0', sizeof(struct restore_client_t)); } restore_device_connected = 0; if (!restore_is_current_device(client, client->udid)) { error("ERROR: Unable to connect to device in restore mode\n"); return -1; } info("Connecting now...\n"); device_error = idevice_new(&device, client->udid); if (device_error != IDEVICE_E_SUCCESS) { return -1; } restore_error = restored_client_new(device, &restored, "idevicerestore"); if (restore_error != RESTORE_E_SUCCESS) { idevice_free(device); return -1; } restore_error = restored_query_type(restored, &type, &version); if ((restore_error == RESTORE_E_SUCCESS) && type && (strcmp(type, "com.apple.mobile.restored") == 0)) { client->restore->protocol_version = version; info("Connected to %s, version %d\n", type, (int)version); } else { error("ERROR: Unable to connect to restored, error=%d\n", restore_error); restored_client_free(restored); idevice_free(device); return -1; } client->restore->device = device; client->restore->client = restored; return 0; } const char* restore_progress_string(unsigned int operation) { switch (operation) { case CREATE_PARTITION_MAP: return "Creating partition map"; case CREATE_FILESYSTEM: return "Creating filesystem"; case RESTORE_IMAGE: return "Restoring image"; case VERIFY_RESTORE: return "Verifying restore"; case CHECK_FILESYSTEMS: return "Checking filesystems"; case MOUNT_FILESYSTEMS: return "Mounting filesystems"; case FIXUP_VAR: return "Fixing up /var"; case FLASH_FIRMWARE: return "Flashing firmware"; case UPDATE_BASEBAND: return "Updating baseband"; case SET_BOOT_STAGE: return "Setting boot stage"; case REBOOT_DEVICE: return "Rebooting device"; case SHUTDOWN_DEVICE: return "Shutdown device"; case TURN_ON_ACCESSORY_POWER: return "Turning on accessory power"; case CLEAR_BOOTARGS: return "Clearing persistent boot-args"; case MODIFY_BOOTARGS: return "Modifying persistent boot-args"; case INSTALL_ROOT: return "Installing root"; case INSTALL_KERNELCACHE: return "Installing kernelcache"; case WAIT_FOR_NAND: return "Waiting for NAND"; case UNMOUNT_FILESYSTEMS: return "Unmounting filesystems"; case SET_DATETIME: return "Setting date and time on device"; case EXEC_IBOOT: return "Executing iBEC to bootstrap update"; case FINALIZE_NAND_EPOCH_UPDATE: return "Finalizing NAND epoch update"; case CHECK_INAPPR_BOOT_PARTITIONS: return "Checking for inappropriate bootable partitions"; case CREATE_FACTORY_RESTORE_MARKER: return "Creating factory restore marker"; case LOAD_FIRMWARE: return "Loading firmware data to flash"; case REQUESTING_FUD_DATA: return "Requesting FUD data"; case REMOVING_ACTIVATION_RECORD: return "Removing activation record"; case CHECK_BATTERY_VOLTAGE: return "Checking battery voltage"; case WAIT_BATTERY_CHARGE: return "Waiting for battery to charge"; case CLOSE_MODEM_TICKETS: return "Closing modem tickets"; case MIGRATE_DATA: return "Migrating data"; case WIPE_STORAGE_DEVICE: return "Wiping storage device"; case SEND_APPLE_LOGO: return "Sending Apple logo to device"; case CHECK_LOGS: return "Checking for uncollected logs"; case CLEAR_NVRAM: return "Clearing NVRAM"; case UPDATE_GAS_GAUGE: return "Updating gas gauge software"; case PREPARE_BASEBAND_UPDATE: return "Preparing for baseband update"; case BOOT_BASEBAND: return "Booting the baseband"; case CREATE_SYSTEM_KEYBAG: return "Creating system key bag"; case UPDATE_IR_MCU_FIRMWARE: return "Updating IR MCU firmware"; case RESIZE_SYSTEM_PARTITION: return "Resizing system partition"; case COLLECTING_UPDATER_OUTPUT: return "Collecting updater output"; case PAIR_STOCKHOLM: return "Pairing Stockholm"; case UPDATE_STOCKHOLM: return "Updating Stockholm"; case UPDATE_SWDHID: return "Updating SWDHID"; case CERTIFY_SEP: return "Certifying SEP"; case UPDATE_NAND_FIRMWARE: return "Updating NAND Firmware"; case UPDATE_SE_FIRMWARE: return "Updating SE Firmware"; case UPDATE_SAVAGE: return "Updating Savage"; case INSTALLING_DEVICETREE: return "Installing DeviceTree"; case CERTIFY_SAVAGE: return "Certifying Savage"; case SUBMITTING_PROVINFO: return "Submitting Provinfo"; case CERTIFY_YONKERS: return "Certifying Yonkers"; case UPDATE_ROSE: return "Updating Rose"; case UPDATE_VERIDIAN: return "Updating Veridian"; case CREATING_PROTECTED_VOLUME: return "Creating Protected Volume"; case RESIZING_MAIN_FS_PARTITION: return "Resizing Main Filesystem Partition"; default: return "Unknown operation"; } } static int lastop = 0; static int restore_handle_previous_restore_log_msg(restored_client_t client, plist_t msg) { plist_t node = NULL; char* restorelog = NULL; node = plist_dict_get_item(msg, "PreviousRestoreLog"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse restore log from PreviousRestoreLog plist\n"); return -1; } plist_get_string_val(node, &restorelog); info("Previous Restore Log Received:\n%s\n", restorelog); free(restorelog); return 0; } int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t msg) { plist_t node = NULL; uint64_t progress = 0; uint64_t operation = 0; node = plist_dict_get_item(msg, "Operation"); if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse operation from ProgressMsg plist\n"); return -1; } plist_get_uint_val(node, &operation); node = plist_dict_get_item(msg, "Progress"); if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse progress from ProgressMsg plist \n"); return -1; } plist_get_uint_val(node, &progress); /* for restore protocol version < 14 all operation codes > 35 are 1 less so we add one */ int adapted_operation = (int)operation; if (client && client->restore && client->restore->protocol_version < 14) { if (adapted_operation > 35) { adapted_operation++; } } if ((progress > 0) && (progress <= 100)) { if ((int)operation != lastop) { info("%s (%d)\n", restore_progress_string(adapted_operation), (int)operation); } switch (adapted_operation) { case VERIFY_RESTORE: idevicerestore_progress(client, RESTORE_STEP_VERIFY_FS, progress / 100.0); break; case FLASH_FIRMWARE: idevicerestore_progress(client, RESTORE_STEP_FLASH_FW, progress / 100.0); break; case UPDATE_BASEBAND: case UPDATE_IR_MCU_FIRMWARE: idevicerestore_progress(client, RESTORE_STEP_FLASH_BB, progress / 100.0); break; case REQUESTING_FUD_DATA: idevicerestore_progress(client, RESTORE_STEP_FUD, progress / 100.0); break; case UPDATE_ROSE: case UPDATE_VERIDIAN: break; default: debug("Unhandled progress operation %d (%d)\n", adapted_operation, (int)operation); break; } } else { info("%s (%d)\n", restore_progress_string(adapted_operation), (int)operation); } lastop = (int)operation; return 0; } int restore_handle_status_msg(restored_client_t client, plist_t msg) { int result = 0; uint64_t value = 0; char* log = NULL; info("Got status message\n"); // read status code plist_t node = plist_dict_get_item(msg, "Status"); plist_get_uint_val(node, &value); switch(value) { case 0: info("Status: Restore Finished\n"); restore_finished = 1; break; case 0xFFFFFFFFFFFFFFFFLL: info("Status: Verification Error\n"); break; case 6: info("Status: Disk Failure\n"); break; case 14: info("Status: Fail\n"); break; case 27: info("Status: Failed to mount filesystems.\n"); break; case 51: info("Status: Failed to load SEP Firmware.\n"); break; case 53: info("Status: Failed to recover FDR data.\n"); break; case 1015: info("Status: X-Gold Baseband Update Failed. Defective Unit?\n"); break; default: info("Unhandled status message (%" PRIu64 ")\n", value); debug_plist(msg); break; } // read error code node = plist_dict_get_item(msg, "AMRError"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &value); result = -value; if (result > 0) { result = -result; } } // check if log is available node = plist_dict_get_item(msg, "Log"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &log); info("Log is available:\n%s\n", log); free(log); log = NULL; } return result; } static int restore_handle_bb_update_status_msg(restored_client_t client, plist_t msg) { int result = -1; plist_t node = plist_dict_get_item(msg, "Accepted"); uint8_t accepted = 0; plist_get_bool_val(node, &accepted); if (!accepted) { error("ERROR: device didn't accept BasebandData\n"); return result; } uint8_t done = 0; node = plist_access_path(msg, 2, "Output", "done"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &done); } if (done) { info("Updating Baseband completed.\n"); plist_t provisioning = plist_access_path(msg, 2, "Output", "provisioning"); if (provisioning && plist_get_node_type(provisioning) == PLIST_DICT) { char* sval = NULL; node = plist_dict_get_item(provisioning, "IMEI"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &sval); info("Provisioning:\n"); info("IMEI:%s\n", sval); free(sval); sval = NULL; } } } else { info("Updating Baseband in progress...\n"); } result = 0; return result; } static void restore_asr_progress_cb(double progress, void* userdata) { struct idevicerestore_client_t* client = (struct idevicerestore_client_t*)userdata; if (client) { idevicerestore_progress(client, RESTORE_STEP_UPLOAD_FS, progress); } } int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem) { asr_client_t asr = NULL; info("About to send filesystem...\n"); if (asr_open_with_timeout(device, &asr) < 0) { error("ERROR: Unable to connect to ASR\n"); return -1; } info("Connected to ASR\n"); asr_set_progress_callback(asr, restore_asr_progress_cb, (void*)client); // this step sends requested chunks of data from various offsets to asr so // it can validate the filesystem before installing it info("Validating the filesystem\n"); if (asr_perform_validation(asr, filesystem) < 0) { error("ERROR: ASR was unable to validate the filesystem\n"); asr_free(asr); return -1; } info("Filesystem validated\n"); // once the target filesystem has been validated, ASR then requests the // entire filesystem to be sent. info("Sending filesystem now...\n"); if (asr_send_payload(asr, filesystem) < 0) { error("ERROR: Unable to send payload to ASR\n"); asr_free(asr); return -1; } info("Done sending filesystem\n"); asr_free(asr); return 0; } int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client) { restored_error_t restore_error; plist_t dict; info("About to send RootTicket...\n"); if (client->root_ticket) { dict = plist_new_dict(); plist_dict_set_item(dict, "RootTicketData", plist_new_data((char*)client->root_ticket, client->root_ticket_len)); } else { unsigned char* data = NULL; unsigned int len = 0; if (!client->tss && !(client->flags & FLAG_CUSTOM)) { error("ERROR: Cannot send RootTicket without TSS\n"); return -1; } if (client->image4supported) { if (tss_response_get_ap_img4_ticket(client->tss, &data, &len) < 0) { error("ERROR: Unable to get ApImg4Ticket from TSS\n"); return -1; } } else { if (!(client->flags & FLAG_CUSTOM) && (tss_response_get_ap_ticket(client->tss, &data, &len) < 0)) { error("ERROR: Unable to get ticket from TSS\n"); return -1; } } dict = plist_new_dict(); if (data && (len > 0)) { plist_dict_set_item(dict, "RootTicketData", plist_new_data((char*)data, len)); } else { info("NOTE: not sending RootTicketData (no data present)\n"); } free(data); } info("Sending RootTicket now...\n"); restore_error = restored_send(restore, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send RootTicket (%d)\n", restore_error); return -1; } info("Done sending RootTicket\n"); return 0; } int restore_send_component(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, const char *component) { unsigned int size = 0; unsigned char* data = NULL; char* path = NULL; plist_t blob = NULL; plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; info("About to send %s...\n", component); if (client->tss) { if (tss_response_get_path_by_entry(client->tss, component, &path) < 0) { debug("NOTE: No path for component %s in TSS, will fetch from build identity\n", component); } } if (!path) { if (build_identity_get_component_path(build_identity, component, &path) < 0) { error("ERROR: Unable to find %s path from build identity\n", component); return -1; } } unsigned char* component_data = NULL; unsigned int component_size = 0; int ret = extract_component(client->ipsw, path, &component_data, &component_size); free(path); path = NULL; if (ret < 0) { error("ERROR: Unable to extract component %s\n", component); return -1; } ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { error("ERROR: Unable to get personalized component %s\n", component); return -1; } dict = plist_new_dict(); blob = plist_new_data((char*)data, size); char compkeyname[256]; sprintf(compkeyname, "%sFile", component); plist_dict_set_item(dict, compkeyname, blob); free(data); info("Sending %s now...\n", component); restore_error = restored_send(restore, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send kernelcache data\n"); return -1; } info("Done sending %s\n", component); return 0; } int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity) { char* llb_path = NULL; char* llb_filename = NULL; char* sep_path = NULL; char* restore_sep_path = NULL; char firmware_path[PATH_MAX - 9]; char manifest_file[PATH_MAX]; unsigned int manifest_size = 0; unsigned char* manifest_data = NULL; char firmware_filename[PATH_MAX]; unsigned int llb_size = 0; unsigned char* llb_data = NULL; plist_t dict = NULL; unsigned int nor_size = 0; unsigned char* nor_data = NULL; plist_t norimage_array = NULL; plist_t firmware_files = NULL; uint32_t i; info("About to send NORData...\n"); if (client->tss) { if (tss_response_get_path_by_entry(client->tss, "LLB", &llb_path) < 0) { debug("NOTE: Could not get LLB path from TSS data, will fetch from build identity\n"); } } if (llb_path == NULL) { if (build_identity_get_component_path(build_identity, "LLB", &llb_path) < 0) { error("ERROR: Unable to get component path for LLB\n"); return -1; } } llb_filename = strstr(llb_path, "LLB"); if (llb_filename == NULL) { error("ERROR: Unable to extract firmware path from LLB filename\n"); free(llb_path); return -1; } memset(firmware_path, '\0', sizeof(firmware_path)); memcpy(firmware_path, llb_path, (llb_filename - 1) - llb_path); info("Found firmware path %s\n", firmware_path); memset(manifest_file, '\0', sizeof(manifest_file)); snprintf(manifest_file, sizeof(manifest_file), "%s/manifest", firmware_path); firmware_files = plist_new_dict(); if (ipsw_file_exists(client->ipsw, manifest_file)) { ipsw_extract_to_memory(client->ipsw, manifest_file, &manifest_data, &manifest_size); } if (manifest_data && manifest_size > 0) { info("Getting firmware manifest from %s\n", manifest_file); char *manifest_p = (char*)manifest_data; char *filename = NULL; while ((filename = strsep(&manifest_p, "\r\n")) != NULL) { if (*filename == '\0') continue; const char *compname = get_component_name(filename); if (!compname) continue; memset(firmware_filename, '\0', sizeof(firmware_filename)); snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); plist_dict_set_item(firmware_files, compname, plist_new_string(firmware_filename)); } free(manifest_data); } else { info("Getting firmware manifest from build identity\n"); plist_dict_iter iter = NULL; plist_t build_id_manifest = plist_dict_get_item(build_identity, "Manifest"); if (build_id_manifest) { plist_dict_new_iter(build_id_manifest, &iter); } if (iter) { char *component = NULL; plist_t manifest_entry; do { component = NULL; manifest_entry = NULL; plist_dict_next_item(build_id_manifest, iter, &component, &manifest_entry); if (component && manifest_entry && plist_get_node_type(manifest_entry) == PLIST_DICT) { uint8_t is_fw = 0; plist_t is_fw_node = plist_access_path(manifest_entry, 2, "Info", "IsFirmwarePayload"); if (is_fw_node && plist_get_node_type(is_fw_node) == PLIST_BOOLEAN) { plist_get_bool_val(is_fw_node, &is_fw); } if (is_fw) { plist_t comp_path = plist_access_path(manifest_entry, 2, "Info", "Path"); if (comp_path) { plist_dict_set_item(firmware_files, component, plist_copy(comp_path)); } } } free(component); } while (manifest_entry); free(iter); } } if (plist_dict_get_size(firmware_files) == 0) { error("ERROR: Unable to get list of firmware files.\n"); return -1; } const char* component = "LLB"; unsigned char* component_data = NULL; unsigned int component_size = 0; int ret = extract_component(client->ipsw, llb_path, &component_data, &component_size); free(llb_path); if (ret < 0) { error("ERROR: Unable to extract component: %s\n", component); return -1; } ret = personalize_component(component, component_data, component_size, client->tss, &llb_data, &llb_size); free(component_data); component_data = NULL; component_size = 0; if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); return -1; } dict = plist_new_dict(); plist_dict_set_item(dict, "LlbImageData", plist_new_data((char*)llb_data, (uint64_t) llb_size)); free(llb_data); norimage_array = plist_new_array(); plist_dict_iter iter = NULL; plist_dict_new_iter(firmware_files, &iter); while (iter) { char *comp = NULL; plist_t pcomp = NULL; plist_dict_next_item(firmware_files, iter, &comp, &pcomp); if (!comp) { break; } char *comppath = NULL; plist_get_string_val(pcomp, &comppath); if (!comppath) { free(comp); continue; } component = (const char*)comp; if (!strcmp(component, "LLB") || !strcmp(component, "RestoreSEP")) { // skip LLB, it's already passed in LlbImageData // skip RestoreSEP, it's passed in RestoreSEPImageData free(comp); free(comppath); continue; } component_data = NULL; unsigned int component_size = 0; if (extract_component(client->ipsw, comppath, &component_data, &component_size) < 0) { free(iter); free(comp); free(comppath); plist_free(firmware_files); error("ERROR: Unable to extract component: %s\n", component); return -1; } if (personalize_component(component, component_data, component_size, client->tss, &nor_data, &nor_size) < 0) { free(iter); free(comp); free(comppath); free(component_data); plist_free(firmware_files); error("ERROR: Unable to get personalized component: %s\n", component); return -1; } free(component_data); component_data = NULL; component_size = 0; /* make sure iBoot is the first entry in the array */ if (!strncmp("iBoot", component, 5)) { plist_array_insert_item(norimage_array, plist_new_data((char*)nor_data, (uint64_t)nor_size), 0); } else { plist_array_append_item(norimage_array, plist_new_data((char*)nor_data, (uint64_t)nor_size)); } free(comp); free(comppath); free(nor_data); nor_data = NULL; nor_size = 0; } free(iter); plist_free(firmware_files); plist_dict_set_item(dict, "NorImageData", norimage_array); unsigned char* personalized_data = NULL; unsigned int personalized_size = 0; if (build_identity_has_component(build_identity, "RestoreSEP") && build_identity_get_component_path(build_identity, "RestoreSEP", &restore_sep_path) == 0) { component = "RestoreSEP"; ret = extract_component(client->ipsw, restore_sep_path, &component_data, &component_size); free(restore_sep_path); if (ret < 0) { error("ERROR: Unable to extract component: %s\n", component); return -1; } ret = personalize_component(component, component_data, component_size, client->tss, &personalized_data, &personalized_size); free(component_data); component_data = NULL; component_size = 0; if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); return -1; } plist_dict_set_item(dict, "RestoreSEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } if (build_identity_has_component(build_identity, "SEP") && build_identity_get_component_path(build_identity, "SEP", &sep_path) == 0) { component = "SEP"; ret = extract_component(client->ipsw, sep_path, &component_data, &component_size); free(sep_path); if (ret < 0) { error("ERROR: Unable to extract component: %s\n", component); return -1; } ret = personalize_component(component, component_data, component_size, client->tss, &personalized_data, &personalized_size); free(component_data); component_data = NULL; component_size = 0; if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); return -1; } plist_dict_set_item(dict, "SEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } if (idevicerestore_debug) debug_plist(dict); info("Sending NORData now...\n"); if (restored_send(restore, dict) != RESTORE_E_SUCCESS) { error("ERROR: Unable to send NORImageData data\n"); plist_free(dict); return -1; } info("Done sending NORData\n"); plist_free(dict); return 0; } static const char* restore_get_bbfw_fn_for_element(const char* elem) { struct bbfw_fn_elem_t { const char* element; const char* fn; }; struct bbfw_fn_elem_t bbfw_fn_elem[] = { // ICE3 firmware files { "RamPSI", "psi_ram.fls" }, { "FlashPSI", "psi_flash.fls" }, // Trek firmware files { "eDBL", "dbl.mbn" }, { "RestoreDBL", "restoredbl.mbn" }, // Phoenix/Mav4 firmware files { "DBL", "dbl.mbn" }, { "ENANDPRG", "ENPRG.mbn" }, // Mav5 firmware files { "RestoreSBL1", "restoresbl1.mbn" }, { "SBL1", "sbl1.mbn" }, // ICE16 firmware files { "RestorePSI", "restorepsi.bin" }, { "PSI", "psi_ram.bin" }, // ICE19 firmware files { "RestorePSI2", "restorepsi2.bin" }, { "PSI2", "psi_ram2.bin" }, { NULL, NULL } }; int i; for (i = 0; bbfw_fn_elem[i].element != NULL; i++) { if (strcmp(bbfw_fn_elem[i].element, elem) == 0) { return bbfw_fn_elem[i].fn; } } return NULL; } static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned char* bb_nonce) { int res = -1; // check for BBTicket in result plist_t bbticket = plist_dict_get_item(bbtss, "BBTicket"); if (!bbticket || plist_get_node_type(bbticket) != PLIST_DATA) { error("ERROR: Could not find BBTicket in Baseband TSS response\n"); return -1; } plist_t bbfw_dict = plist_dict_get_item(bbtss, "BasebandFirmware"); if (!bbfw_dict || plist_get_node_type(bbfw_dict) != PLIST_DICT) { error("ERROR: Could not find BasebandFirmware Dictionary node in Baseband TSS response\n"); return -1; } unsigned char* buffer = NULL; unsigned char* blob = NULL; unsigned char* fdata = NULL; uint64_t fsize = 0; uint64_t blob_size = 0; int zerr = 0; int zindex = -1; struct zip_stat zstat; struct zip_file* zfile = NULL; struct zip* za = NULL; struct zip_source* zs = NULL; mbn_file* mbn = NULL; fls_file* fls = NULL; za = zip_open(bbfwtmp, 0, &zerr); if (!za) { error("ERROR: Could not open ZIP archive '%s': %d\n", bbfwtmp, zerr); goto leave; } plist_dict_iter iter = NULL; plist_dict_new_iter(bbfw_dict, &iter); if (!iter) { error("ERROR: Could not create dict iter for BasebandFirmware Dictionary\n"); return -1; } int is_fls = 0; int signed_file_idxs[16]; int signed_file_count = 0; char* key = NULL; plist_t node = NULL; while (1) { plist_dict_next_item(bbfw_dict, iter, &key, &node); if (key == NULL) break; if (node && (strcmp(key + (strlen(key) - 5), "-Blob") == 0) && (plist_get_node_type(node) == PLIST_DATA)) { char *ptr = strchr(key, '-'); *ptr = '\0'; const char* signfn = restore_get_bbfw_fn_for_element(key); if (!signfn) { error("ERROR: can't match element name '%s' to baseband firmware file name.\n", key); goto leave; } char* ext = strrchr(signfn, '.'); if (!strcmp(ext, ".fls")) { is_fls = 1; } zindex = zip_name_locate(za, signfn, 0); if (zindex < 0) { error("ERROR: can't locate '%s' in '%s'\n", signfn, bbfwtmp); goto leave; } zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index failed for index %d\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index failed for index %d\n", zindex); goto leave; } buffer = (unsigned char*) malloc(zstat.size + 1); if (buffer == NULL) { error("ERROR: Out of memory\n"); goto leave; } if (zip_fread(zfile, buffer, zstat.size) != zstat.size) { error("ERROR: zip_fread: failed\n"); goto leave; } buffer[zstat.size] = '\0'; zip_fclose(zfile); zfile = NULL; if (is_fls) { fls = fls_parse(buffer, zstat.size); if (!fls) { error("ERROR: could not parse fls file\n"); goto leave; } } else { mbn = mbn_parse(buffer, zstat.size); if (!mbn) { error("ERROR: could not parse mbn file\n"); goto leave; } } free(buffer); buffer = NULL; blob = NULL; blob_size = 0; plist_get_data_val(node, (char**)&blob, &blob_size); if (!blob) { error("ERROR: could not get %s-Blob data\n", key); goto leave; } if (is_fls) { if (fls_update_sig_blob(fls, blob, (unsigned int)blob_size) != 0) { error("ERROR: could not sign %s\n", signfn); goto leave; } } else { if (mbn_update_sig_blob(mbn, blob, (unsigned int)blob_size) != 0) { error("ERROR: could not sign %s\n", signfn); goto leave; } } free(blob); blob = NULL; fsize = (is_fls ? fls->size : mbn->size); fdata = (unsigned char*)malloc(fsize); if (fdata == NULL) { error("ERROR: out of memory\n"); goto leave; } if (is_fls) { memcpy(fdata, fls->data, fsize); fls_free(fls); fls = NULL; } else { memcpy(fdata, mbn->data, fsize); mbn_free(mbn); mbn = NULL; } zs = zip_source_buffer(za, fdata, fsize, 1); if (!zs) { error("ERROR: out of memory\n"); free(fdata); goto leave; } if (zip_replace(za, zindex, zs) == -1) { error("ERROR: could not update signed '%s' in archive\n", signfn); goto leave; } if (is_fls && !bb_nonce) { if (strcmp(key, "RamPSI") == 0) { signed_file_idxs[signed_file_count++] = zindex; } } else { signed_file_idxs[signed_file_count++] = zindex; } } free(key); } free(iter); // remove everything but required files int i, j, keep, numf = zip_get_num_files(za); for (i = 0; i < numf; i++) { keep = 0; // check for signed file index for (j = 0; j < signed_file_count; j++) { if (i == signed_file_idxs[j]) { keep = 1; break; } } // check for anything but .mbn and .fls if bb_nonce is set if (bb_nonce && !keep) { const char* fn = zip_get_name(za, i, 0); if (fn) { char* ext = strrchr(fn, '.'); if (ext && (!strcmp(ext, ".fls") || !strcmp(ext, ".mbn") || !strcmp(ext, ".elf") || !strcmp(ext, ".bin"))) { keep = 1; } } } if (!keep) { zip_delete(za, i); } } if (bb_nonce) { if (is_fls) { // add BBTicket to file ebl.fls zindex = zip_name_locate(za, "ebl.fls", 0); if (zindex < 0) { error("ERROR: can't locate 'ebl.fls' in '%s'\n", bbfwtmp); goto leave; } zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index failed for index %d\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index failed for index %d\n", zindex); goto leave; } buffer = (unsigned char*) malloc(zstat.size + 1); if (buffer == NULL) { error("ERROR: Out of memory\n"); goto leave; } if (zip_fread(zfile, buffer, zstat.size) != zstat.size) { error("ERROR: zip_fread: failed\n"); goto leave; } buffer[zstat.size] = '\0'; zip_fclose(zfile); zfile = NULL; fls = fls_parse(buffer, zstat.size); free(buffer); buffer = NULL; if (!fls) { error("ERROR: could not parse fls file\n"); goto leave; } blob = NULL; blob_size = 0; plist_get_data_val(bbticket, (char**)&blob, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; } if (fls_insert_ticket(fls, blob, (unsigned int)blob_size) != 0) { error("ERROR: could not insert BBTicket to ebl.fls\n"); goto leave; } free(blob); blob = NULL; fsize = fls->size; fdata = (unsigned char*)malloc(fsize); if (!fdata) { error("ERROR: out of memory\n"); goto leave; } memcpy(fdata, fls->data, fsize); fls_free(fls); fls = NULL; zs = zip_source_buffer(za, fdata, fsize, 1); if (!zs) { error("ERROR: out of memory\n"); free(fdata); goto leave; } if (zip_replace(za, zindex, zs) == -1) { error("ERROR: could not update archive with ticketed ebl.fls\n"); goto leave; } } else { // add BBTicket as bbticket.der blob = NULL; blob_size = 0; plist_get_data_val(bbticket, (char**)&blob, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; } zs = zip_source_buffer(za, blob, blob_size, 1); if (!zs) { error("ERROR: out of memory\n"); goto leave; } blob = NULL; if (zip_add(za, "bbticket.der", zs) == -1) { error("ERROR: could not add bbticket.der to archive\n"); goto leave; } } } // this will write out the modified zip if (zip_close(za) == -1) { error("ERROR: could not close and write modified archive: %s\n", zip_strerror(za)); res = -1; } else { res = 0; } za = NULL; zs = NULL; leave: if (zfile) { zip_fclose(zfile); } if (zs) { zip_source_free(zs); } if (za) { zip_unchange_all(za); zip_close(za); } mbn_free(mbn); fls_free(fls); free(buffer); free(blob); return res; } static int restore_send_baseband_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) { int res = -1; uint64_t bb_cert_id = 0; unsigned char* bb_snum = NULL; uint64_t bb_snum_size = 0; unsigned char* bb_nonce = NULL; uint64_t bb_nonce_size = 0; uint64_t bb_chip_id = 0; plist_t response = NULL; char* buffer = NULL; char* bbfwtmp = NULL; plist_t dict = NULL; info("About to send BasebandData...\n"); // NOTE: this function is called 2 or 3 times! // setup request data plist_t arguments = plist_dict_get_item(message, "Arguments"); if (arguments && plist_get_node_type(arguments) == PLIST_DICT) { plist_t bb_chip_id_node = plist_dict_get_item(arguments, "ChipID"); if (bb_chip_id_node && plist_get_node_type(bb_chip_id_node) == PLIST_UINT) { plist_get_uint_val(bb_chip_id_node, &bb_chip_id); } plist_t bb_cert_id_node = plist_dict_get_item(arguments, "CertID"); if (bb_cert_id_node && plist_get_node_type(bb_cert_id_node) == PLIST_UINT) { plist_get_uint_val(bb_cert_id_node, &bb_cert_id); } plist_t bb_snum_node = plist_dict_get_item(arguments, "ChipSerialNo"); if (bb_snum_node && plist_get_node_type(bb_snum_node) == PLIST_DATA) { plist_get_data_val(bb_snum_node, (char**)&bb_snum, &bb_snum_size); } plist_t bb_nonce_node = plist_dict_get_item(arguments, "Nonce"); if (bb_nonce_node && plist_get_node_type(bb_nonce_node) == PLIST_DATA) { plist_get_data_val(bb_nonce_node, (char**)&bb_nonce, &bb_nonce_size); } } if ((bb_nonce == NULL) || (client->restore->bbtss == NULL)) { /* populate parameters */ plist_t parameters = plist_new_dict(); plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); if (bb_nonce) { plist_dict_set_item(parameters, "BbNonce", plist_new_data((const char*)bb_nonce, bb_nonce_size)); } plist_dict_set_item(parameters, "BbChipID", plist_new_uint(bb_chip_id)); plist_dict_set_item(parameters, "BbGoldCertId", plist_new_uint(bb_cert_id)); plist_dict_set_item(parameters, "BbSNUM", plist_new_data((const char*)bb_snum, bb_snum_size)); tss_parameters_add_from_manifest(parameters, build_identity); /* create baseband request */ plist_t request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Baseband TSS request\n"); plist_free(parameters); return -1; } /* add baseband parameters */ tss_request_add_common_tags(request, parameters, NULL); tss_request_add_baseband_tags(request, parameters, NULL); plist_t node = plist_access_path(build_identity, 2, "Info", "FDRSupport"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(node, &b); if (b) { plist_dict_set_item(request, "ApProductionMode", plist_new_bool(1)); plist_dict_set_item(request, "ApSecurityMode", plist_new_bool(1)); } } if (idevicerestore_debug) debug_plist(request); info("Sending Baseband TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); plist_free(parameters); if (response == NULL) { error("ERROR: Unable to fetch Baseband TSS\n"); return -1; } info("Received Baseband SHSH blobs\n"); if (idevicerestore_debug) debug_plist(response); } // get baseband firmware file path from build identity plist_t bbfw_path = plist_access_path(build_identity, 4, "Manifest", "BasebandFirmware", "Info", "Path"); if (!bbfw_path || plist_get_node_type(bbfw_path) != PLIST_STRING) { error("ERROR: Unable to get BasebandFirmware/Info/Path node\n"); plist_free(response); return -1; } char* bbfwpath = NULL; plist_get_string_val(bbfw_path, &bbfwpath); if (!bbfwpath) { error("ERROR: Unable to get baseband path\n"); plist_free(response); return -1; } // extract baseband firmware to temp file bbfwtmp = get_temp_filename("bbfw_"); if (!bbfwtmp) { size_t l = strlen(client->udid); bbfwtmp = malloc(l + 10); strcpy(bbfwtmp, "bbfw_"); strncpy(bbfwtmp + 5, client->udid, l); strcpy(bbfwtmp + 5 + l, ".tmp"); error("WARNING: Could not generate temporary filename, using %s in current directory\n", bbfwtmp); } if (ipsw_extract_to_file(client->ipsw, bbfwpath, bbfwtmp) != 0) { error("ERROR: Unable to extract baseband firmware from ipsw\n"); goto leave; } if (bb_nonce && !client->restore->bbtss) { // keep the response for later requests client->restore->bbtss = response; response = NULL; } res = restore_sign_bbfw(bbfwtmp, (client->restore->bbtss) ? client->restore->bbtss : response, bb_nonce); if (res != 0) { goto leave; } res = -1; size_t sz = 0; if (read_file(bbfwtmp, (void**)&buffer, &sz) < 0) { error("ERROR: could not read updated bbfw archive\n"); goto leave; } // send file dict = plist_new_dict(); plist_dict_set_item(dict, "BasebandData", plist_new_data(buffer, (uint64_t)sz)); free(buffer); buffer = NULL; info("Sending BasebandData now...\n"); if (restored_send(restore, dict) != RESTORE_E_SUCCESS) { error("ERROR: Unable to send BasebandData data\n"); goto leave; } info("Done sending BasebandData\n"); res = 0; leave: plist_free(dict); free(buffer); if (bbfwtmp) { remove(bbfwtmp); free(bbfwtmp); } plist_free(response); return res; } int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device) { restored_error_t restore_error; plist_t dict; info("About to send FDR Trust data...\n"); // FIXME: What should we send here? /* Sending an empty dict makes it continue with FDR * and this is what iTunes seems to be doing too */ dict = plist_new_dict(); info("Sending FDR Trust data now...\n"); restore_error = restored_send(restore, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: During sending FDR Trust data (%d)\n", restore_error); return -1; } info("Done sending FDR Trust Data\n"); return 0; } static int restore_send_fud_data(restored_client_t restore, struct idevicerestore_client_t *client, plist_t build_identity, plist_t message) { restored_error_t restore_error; plist_t arguments; plist_t dict; plist_t node; plist_t fud_images = NULL; plist_t fud_dict = NULL; plist_t build_id_manifest; plist_dict_iter iter = NULL; char *image_name = NULL; int want_image_list = 0; arguments = plist_dict_get_item(message, "Arguments"); want_image_list = _plist_dict_get_bool(arguments, "FUDImageList"); node = plist_dict_get_item(arguments, "ImageName"); if (node) { plist_get_string_val(node, &image_name); } if (!want_image_list && !image_name) { info("About to send FUD data...\n"); } if (want_image_list) { fud_images = plist_new_array(); } else { fud_dict = plist_new_dict(); } build_id_manifest = plist_dict_get_item(build_identity, "Manifest"); if (build_id_manifest) { plist_dict_new_iter(build_id_manifest, &iter); } if (iter) { char *component; plist_t manifest_entry; do { component = NULL; manifest_entry = NULL; plist_dict_next_item(build_id_manifest, iter, &component, &manifest_entry); if (component && manifest_entry && plist_get_node_type(manifest_entry) == PLIST_DICT) { uint8_t is_fud = 0; plist_t is_fud_node = plist_access_path(manifest_entry, 2, "Info", "IsFUDFirmware"); if (is_fud_node && plist_get_node_type(is_fud_node) == PLIST_BOOLEAN) { plist_get_bool_val(is_fud_node, &is_fud); } if (is_fud) { if (want_image_list) { info("Found FUD component '%s'\n", component); plist_array_append_item(fud_images, plist_new_string(component)); } else if (!image_name || !strcmp(image_name, component)) { char *path = NULL; unsigned char* data = NULL; unsigned int size = 0; unsigned char* component_data = NULL; unsigned int component_size = 0; int ret = -1; if (!image_name) { info("Found FUD component '%s'\n", component); } build_identity_get_component_path(build_identity, component, &path); if (path) { ret = extract_component(client->ipsw, path, &component_data, &component_size); } free(path); path = NULL; if (ret < 0) { error("ERROR: Unable to extract component: %s\n", component); } ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); } plist_dict_set_item(fud_dict, component, plist_new_data((const char*)data, size)); free(data); } } free(component); } } while (manifest_entry); free(iter); } dict = plist_new_dict(); if (want_image_list) { plist_dict_set_item(dict, "FUDImageList", fud_images); info("Sending FUD image list\n"); } else { if (image_name) { node = plist_dict_get_item(fud_dict, image_name); if (node) { plist_dict_set_item(dict, "FUDImageData", plist_copy(node)); } plist_dict_set_item(dict, "ImageName", plist_new_string(image_name)); info("Sending FUD data for %s...\n", image_name); } else { plist_dict_set_item(dict, "FUDImageData", fud_dict); info("Sending FUD data now...\n"); } } restore_error = restored_send(restore, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { if (want_image_list) { error("ERROR: Failed to send FUD image list (%d)\n", restore_error); } else { if (image_name) { error("ERROR: Failed to send FUD data for %s (%d)\n", image_name, restore_error); free(image_name); } else { error("ERROR: Failed to send FUD data (%d)\n", restore_error); } } return -1; } if (!want_image_list) { if (image_name) { free(image_name); } else { info("Done sending FUD data\n"); } } return 0; } static plist_t restore_get_se_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { const char *comp_name = NULL; char *comp_path = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; int ret; uint64_t chip_id = 0; plist_t node = plist_dict_get_item(p_info, "SE,ChipID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &chip_id); } if (chip_id == 0x20211) { comp_name = "SE,Firmware"; } else if (chip_id == 0x73 || chip_id == 0x64 || chip_id == 0xC8) { comp_name = "SE,UpdatePayload"; } else { info("WARNING: Unknown SE,ChipID 0x%" PRIx64 " detected. Restore might fail.\n", (uint64_t)chip_id); if (build_identity_has_component(build_identity, "SE,UpdatePayload")) comp_name = "SE,UpdatePayload"; else if (build_identity_has_component(build_identity, "SE,Firmware")) comp_name = "SE,Firmware"; else { error("ERROR: Neither 'SE,Firmware' nor 'SE,UpdatePayload' found in build identity.\n"); return NULL; } debug("DEBUG: %s: using %s\n", __func__, comp_name); } if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable get path for '%s' component\n", comp_name); return NULL; } ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } /* create SE request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create SE TSS request\n"); free(component_data); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, build_identity); /* add SE,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for SE TSS request */ tss_request_add_se_tags(request, parameters, NULL); plist_free(parameters); info("Sending SE TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch SE ticket\n"); free(component_data); return NULL; } if (plist_dict_get_item(response, "SE,Ticket")) { info("Received SE ticket\n"); } else { error("ERROR: No 'SE,Ticket' in TSS response, this might not work\n"); } plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_savage_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { char *comp_name = NULL; char *comp_path = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; unsigned char* component_data_tmp = NULL; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; plist_t node = NULL; int ret; /* create Savage request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Savage TSS request\n"); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, build_identity); /* add Savage,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Savage TSS request */ tss_request_add_savage_tags(request, parameters, NULL, &comp_name); plist_free(parameters); if (!comp_name) { error("ERROR: Could not determine Savage firmware component\n"); plist_free(request); return NULL; } debug("DEBUG: %s: using %s\n", __func__, comp_name); info("Sending Savage TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Savage ticket\n"); free(comp_name); return NULL; } if (plist_dict_get_item(response, "Savage,Ticket")) { info("Received Savage ticket\n"); } else { error("ERROR: No 'Savage,Ticket' in TSS response, this might not work\n"); } /* now get actual component data */ if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable get path for '%s' component\n", comp_name); free(comp_name); return NULL; } ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); free(comp_name); return NULL; } free(comp_name); comp_name = NULL; component_data_tmp = realloc(component_data, (size_t)component_size+16); if (!component_data_tmp) { free(component_data); return NULL; } component_data = component_data_tmp; memmove(component_data + 16, component_data, (size_t)component_size); memset(component_data, '\0', 16); *(uint32_t*)(component_data + 4) = htole32((uint32_t)component_size); component_size += 16; plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { char *comp_name = NULL; char *comp_path = NULL; plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; plist_t node = NULL; int ret; /* create Yonkers request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Yonkers TSS request\n"); free(component_data); free(comp_name); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, build_identity); /* add Yonkers,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Yonkers TSS request */ tss_request_add_yonkers_tags(request, parameters, NULL, &comp_name); plist_free(parameters); if (!comp_name) { error("ERROR: Could not determine Yonkers firmware component\n"); plist_free(request); return NULL; } debug("DEBUG: %s: using %s\n", __func__, comp_name); info("Sending Yonkers TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Yonkers ticket\n"); free(component_data); return NULL; } if (plist_dict_get_item(response, "Yonkers,Ticket")) { info("Received Yonkers ticket\n"); } else { error("ERROR: No 'Yonkers,Ticket' in TSS response, this might not work\n"); } if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable get path for '%s' component\n", comp_name); free(comp_name); return NULL; } /* now get actual component data */ ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); free(comp_name); return NULL; } free(comp_name); comp_name = NULL; plist_t firmware_data = plist_new_dict(); plist_dict_set_item(firmware_data, "YonkersFirmware", plist_new_data((char *)component_data, (uint64_t)component_size)); plist_dict_set_item(response, "FirmwareData", firmware_data); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { char *comp_name = NULL; char *comp_path = NULL; plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; ftab_t ftab = NULL; ftab_t rftab = NULL; uint32_t ftag = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; plist_t node = NULL; int ret; /* create Rose request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Rose TSS request\n"); free(component_data); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, build_identity); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); if (client->image4supported) { plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1)); } else { plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } /* add Rap,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Rose TSS request */ tss_request_add_rose_tags(request, parameters, NULL); plist_free(parameters); info("Sending Rose TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Rose ticket\n"); free(component_data); return NULL; } if (plist_dict_get_item(response, "Rap,Ticket")) { info("Received Rose ticket\n"); } else { error("ERROR: No 'Rap,Ticket' in TSS response, this might not work\n"); } comp_name = "Rap,RTKitOS"; if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable get path for '%s' component\n", comp_name); return NULL; } ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } if (ftab_parse(component_data, component_size, &ftab, &ftag) != 0) { free(component_data); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; } free(component_data); component_data = NULL; component_size = 0; if (ftag != 'rkos') { error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos'); } comp_name = "Rap,RestoreRTKitOS"; if (build_identity_has_component(build_identity, comp_name)) { if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { ftab_free(ftab); error("ERROR: Unable get path for '%s' component\n", comp_name); return NULL; } ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { ftab_free(ftab); error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } ftag = 0; if (ftab_parse(component_data, component_size, &rftab, &ftag) != 0) { free(component_data); ftab_free(ftab); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; } free(component_data); component_data = NULL; component_size = 0; if (ftag != 'rkos') { error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos'); } if (ftab_get_entry_ptr(rftab, 'rrko', &component_data, &component_size) == 0) { ftab_add_entry(ftab, 'rrko', component_data, component_size); } else { error("ERROR: Could not find 'rrko' entry in ftab. This will probably break things.\n"); } ftab_free(rftab); component_data = NULL; component_size = 0; } else { info("NOTE: Build identity does not have a '%s' component.\n", comp_name); } ftab_write(ftab, &component_data, &component_size); ftab_free(ftab); plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_veridian_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) { char *comp_name = "BMU,FirmwareMap"; char *comp_path = NULL; plist_t comp_node = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; plist_t node = NULL; int ret; /* create Veridian request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Veridian TSS request\n"); free(component_data); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, build_identity); /* add BMU,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Veridian TSS request */ tss_request_add_veridian_tags(request, parameters, NULL); plist_free(parameters); info("Sending Veridian TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Veridian ticket\n"); free(component_data); return NULL; } if (plist_dict_get_item(response, "BMU,Ticket")) { info("Received Veridian ticket\n"); } else { error("ERROR: No 'BMU,Ticket' in TSS response, this might not work\n"); } if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable get path for '%s' component\n", comp_name); return NULL; } /* now get actual component data */ ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); free(comp_path); comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } plist_t fw_map = NULL; if (plist_is_binary((const char*)component_data, component_size)) { plist_from_bin((const char*)component_data, component_size, &fw_map); } else { plist_from_xml((const char*)component_data, component_size, &fw_map); } free(component_data); component_data = NULL; component_size = 0; if (!fw_map) { error("ERROR: Unable to parse '%s' component data as plist\n", comp_name); return NULL; } plist_t fw_map_digest = plist_access_path(build_identity, 3, "Manifest", comp_name, "Digest"); if (!fw_map_digest) { plist_free(fw_map); error("ERROR: Unable to get Digest for '%s' component\n", comp_name); return NULL; } plist_dict_set_item(fw_map, "fw_map_digest", plist_copy(fw_map_digest)); char *bin_plist = NULL; uint32_t bin_size = 0; plist_to_bin(fw_map, &bin_plist, &bin_size); plist_free(fw_map); plist_dict_set_item(response, "FirmwareData", plist_new_data(bin_plist, (uint64_t)bin_size)); free(bin_plist); return response; } static int restore_send_firmware_updater_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) { plist_t arguments; plist_t p_type, p_updater_name, p_loop_count, p_info; plist_t loop_count_dict = NULL; char *s_type = NULL; plist_t dict = NULL; plist_t fwdict = NULL; char *s_updater_name = NULL; int restore_error; if (idevicerestore_debug) { debug("DEBUG: %s: Got FirmwareUpdaterData request:\n", __func__); debug_plist(message); } arguments = plist_dict_get_item(message, "Arguments"); if (!arguments || plist_get_node_type(arguments) != PLIST_DICT) { error("ERROR: %s: Arguments missing or has invalid type!\n", __func__); goto error_out; } p_type = plist_dict_get_item(arguments, "MessageArgType"); if (!p_type || (plist_get_node_type(p_type) != PLIST_STRING)) { error("ERROR: %s: MessageArgType missing or has invalid type!\n", __func__); goto error_out; } p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); if (!p_updater_name || (plist_get_node_type(p_updater_name) != PLIST_STRING)) { error("ERROR: %s: MessageArgUpdaterName missing or has invalid type!\n", __func__); goto error_out; } p_loop_count = plist_dict_get_item(arguments, "MessageArgUpdaterLoopCount"); if (p_loop_count) { loop_count_dict = plist_new_dict(); plist_dict_set_item(loop_count_dict, "LoopCount", plist_copy(p_loop_count)); } plist_get_string_val(p_type, &s_type); if (!s_type || strcmp(s_type, "FirmwareResponseData")) { error("ERROR: %s: MessageArgType has unexpected value '%s'\n", __func__, s_type); goto error_out; } free(s_type); s_type = NULL; p_info = plist_dict_get_item(arguments, "MessageArgInfo"); if (!p_info || (plist_get_node_type(p_info) != PLIST_DICT)) { error("ERROR: %s: MessageArgInfo missing or has invalid type!\n", __func__); goto error_out; } plist_get_string_val(p_updater_name, &s_updater_name); if (strcmp(s_updater_name, "SE") == 0) { fwdict = restore_get_se_firmware_data(restore, client, build_identity, p_info); if (fwdict == NULL) { error("ERROR: %s: Couldn't get SE firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "Savage") == 0) { const char *fwtype = "Savage"; plist_t p_info2 = plist_dict_get_item(p_info, "YonkersDeviceInfo"); if (p_info2 && plist_get_node_type(p_info2) == PLIST_DICT) { fwtype = "Yonkers"; fwdict = restore_get_yonkers_firmware_data(restore, client, build_identity, p_info2); } else { fwdict = restore_get_savage_firmware_data(restore, client, build_identity, p_info); } if (fwdict == NULL) { error("ERROR: %s: Couldn't get %s firmware data\n", __func__, fwtype); goto error_out; } } else if (strcmp(s_updater_name, "Rose") == 0) { fwdict = restore_get_rose_firmware_data(restore, client, build_identity, p_info); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Rose firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "T200") == 0) { fwdict = restore_get_veridian_firmware_data(restore, client, build_identity, p_info); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Veridian firmware data\n", __func__); goto error_out; } } else { error("ERROR: %s: Got unknown updater name '%s'.\n", __func__, s_updater_name); goto error_out; } free(s_updater_name); s_updater_name = NULL; dict = plist_new_dict(); plist_dict_set_item(dict, "FirmwareResponseData", fwdict); info("Sending FirmwareResponse data now...\n"); restore_error = restored_send(restore, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send FirmwareResponse data (%d)\n", restore_error); goto error_out; } info("Done sending FirmwareUpdater data\n"); return 0; error_out: free(s_type); free(s_updater_name); plist_free(loop_count_dict); return -1; } int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem) { char* type = NULL; plist_t node = NULL; // checks and see what kind of data restored is requests and pass // the request to its own handler node = plist_dict_get_item(message, "DataType"); if (node && PLIST_STRING == plist_get_node_type(node)) { plist_get_string_val(node, &type); // this request is sent when restored is ready to receive the filesystem if (!strcmp(type, "SystemImageData")) { if(restore_send_filesystem(client, device, filesystem) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } } // send RootTicket (== APTicket from the TSS request) else if (!strcmp(type, "RootTicket")) { if (restore_send_root_ticket(restore, client) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } // send KernelCache else if (!strcmp(type, "KernelCache")) { if (restore_send_component(restore, client, build_identity, "KernelCache") < 0) { error("ERROR: Unable to send kernelcache\n"); return -1; } } else if (!strcmp(type, "DeviceTree")) { if (restore_send_component(restore, client, build_identity, "DeviceTree") < 0) { error("ERROR: Unable to send DeviceTree\n"); return -1; } } else if (!strcmp(type, "NORData")) { if((client->flags & FLAG_EXCLUDE) == 0) { if(restore_send_nor(restore, client, build_identity) < 0) { error("ERROR: Unable to send NOR data\n"); return -1; } } else { info("Not sending NORData... Quitting...\n"); client->flags |= FLAG_QUIT; } } else if (!strcmp(type, "BasebandData")) { if(restore_send_baseband_data(restore, client, build_identity, message) < 0) { error("ERROR: Unable to send baseband data\n"); return -1; } } else if (!strcmp(type, "FDRTrustData")) { if(restore_send_fdr_trust_data(restore, device) < 0) { error("ERROR: Unable to send FDR Trust data\n"); return -1; } } else if (!strcmp(type, "FUDData")) { if(restore_send_fud_data(restore, client, build_identity, message) < 0) { error("ERROR: Unable to send FUD data\n"); return -1; } } else if (!strcmp(type, "FirmwareUpdaterData")) { if(restore_send_firmware_updater_data(restore, client, build_identity, message) < 0) { error("ERROR: Unable to send FirmwareUpdater data\n"); return -1; } } else { // Unknown DataType!! error("Unknown data request '%s' received\n", type); if (idevicerestore_debug) debug_plist(message); } } return 0; } int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem) { int err = 0; char* type = NULL; plist_t node = NULL; plist_t message = NULL; plist_t hwinfo = NULL; idevice_t device = NULL; restored_client_t restore = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; thread_t fdr_thread = (thread_t)NULL; restore_finished = 0; // open our connection to the device and verify we're in restore mode err = restore_open_with_timeout(client); if (err < 0) { error("ERROR: Unable to open device in restore mode\n"); return (err == -2) ? -1: -2; } info("Device %s has successfully entered restore mode\n", client->udid); restore = client->restore->client; device = client->restore->device; restore_error = restored_query_value(restore, "HardwareInfo", &hwinfo); if (restore_error == RESTORE_E_SUCCESS) { uint64_t i = 0; uint8_t b = 0; info("Hardware Information:\n"); node = plist_dict_get_item(hwinfo, "BoardID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &i); info("BoardID: %d\n", (int)i); } node = plist_dict_get_item(hwinfo, "ChipID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &i); info("ChipID: %d\n", (int)i); } node = plist_dict_get_item(hwinfo, "UniqueChipID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &i); info("UniqueChipID: %" PRIu64 "\n", i); } node = plist_dict_get_item(hwinfo, "ProductionMode"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &b); info("ProductionMode: %s\n", (b==1) ? "true":"false"); } plist_free(hwinfo); } restore_error = restored_query_value(restore, "SavedDebugInfo", &hwinfo); if (restore_error == RESTORE_E_SUCCESS) { char* sval = NULL; node = plist_dict_get_item(hwinfo, "PreviousExitStatus"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &sval); info("Previous restore exit status: %s\n", sval); free(sval); sval = NULL; } node = plist_dict_get_item(hwinfo, "USBLog"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &sval); info("USB log is available:\n%s\n", sval); free(sval); sval = NULL; } node = plist_dict_get_item(hwinfo, "PanicLog"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &sval); info("Panic log is available:\n%s\n", sval); free(sval); sval = NULL; } plist_free(hwinfo); } if (plist_dict_get_item(client->tss, "BBTicket")) { client->restore->bbtss = plist_copy(client->tss); } fdr_client_t fdr_control_channel = NULL; info("Starting FDR listener thread\n"); if (!fdr_connect(device, FDR_CTRL, &fdr_control_channel)) { if(thread_new(&fdr_thread, fdr_listener_thread, fdr_control_channel)) { error("ERROR: Failed to start FDR listener thread\n"); fdr_thread = (thread_t)NULL; /* undefined after failure */ } } else { error("ERROR: Failed to start FDR Ctrl channel\n"); // FIXME: We might want to return failure here as it will likely fail } plist_t opts = plist_new_dict(); // FIXME: required? //plist_dict_set_item(opts, "AuthInstallRestoreBehavior", plist_new_string("Erase")); plist_dict_set_item(opts, "AutoBootDelay", plist_new_uint(0)); if (client->preflight_info) { plist_t node; plist_t bbus = plist_copy(client->preflight_info); plist_dict_remove_item(bbus, "FusingStatus"); plist_dict_remove_item(bbus, "PkHash"); plist_dict_set_item(opts, "BBUpdaterState", bbus); node = plist_dict_get_item(client->preflight_info, "Nonce"); if (node) { plist_dict_set_item(opts, "BasebandNonce", plist_copy(node)); } } // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "BootImageType", plist_new_string("UserOrInternal")); // FIXME: required? //plist_dict_set_item(opts, "BootImageFile", plist_new_string("018-7923-347.dmg")); plist_dict_set_item(opts, "CreateFilesystemPartitions", plist_new_bool(1)); plist_dict_set_item(opts, "DFUFileType", plist_new_string("RELEASE")); plist_dict_set_item(opts, "DataImage", plist_new_bool(0)); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "DeviceTreeFile", plist_new_string("DeviceTree.k48ap.img3")); plist_dict_set_item(opts, "FirmwareDirectory", plist_new_string(".")); // FIXME: usable if false? (-x parameter) plist_dict_set_item(opts, "FlashNOR", plist_new_bool(1)); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "KernelCacheFile", plist_new_string("kernelcache.release.k48")); // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "KernelCacheType", plist_new_string("Release")); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "NORImagePath", plist_new_string(".")); // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "NORImageType", plist_new_string("production")); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "PersonalizedRestoreBundlePath", plist_new_string("/tmp/Per2.tmp")); if (client->restore_boot_args) { plist_dict_set_item(opts, "RestoreBootArgs", plist_new_string(client->restore_boot_args)); } plist_dict_set_item(opts, "RestoreBundlePath", plist_new_string("/tmp/Per2.tmp")); plist_dict_set_item(opts, "RootToInstall", plist_new_bool(0)); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "SourceRestoreBundlePath", plist_new_string("/tmp")); plist_dict_set_item(opts, "SystemImage", plist_new_bool(1)); // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "SystemImageType", plist_new_string("User")); plist_t spp = plist_access_path(build_identity, 2, "Info", "SystemPartitionPadding"); if (spp) { spp = plist_copy(spp); } else { spp = plist_new_dict(); plist_dict_set_item(spp, "128", plist_new_uint(1280)); plist_dict_set_item(spp, "16", plist_new_uint(160)); plist_dict_set_item(spp, "32", plist_new_uint(320)); plist_dict_set_item(spp, "64", plist_new_uint(640)); plist_dict_set_item(spp, "8", plist_new_uint(80)); } plist_dict_set_item(opts, "SystemPartitionPadding", spp); char* guid = generate_guid(); if (guid) { plist_dict_set_item(opts, "UUID", plist_new_string(guid)); free(guid); } // FIXME: does this have any effect actually? plist_dict_set_item(opts, "UpdateBaseband", plist_new_bool(0)); plist_t sep = plist_access_path(build_identity, 3, "Manifest", "SEP", "Info"); if (sep) { node = plist_dict_get_item(sep, "RequiredCapacity"); if (node && plist_get_node_type(node) == PLIST_STRING) { char* sval = NULL; plist_get_string_val(node, &sval); debug("TZ0RequiredCapacity: %s\n", sval); plist_dict_set_item(opts, "TZ0RequiredCapacity", plist_copy(node)); free(sval); sval = NULL; } } // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "UserLocale", plist_new_string("en_US")); /* this is mandatory on iOS 7+ to allow restore from normal mode */ plist_dict_set_item(opts, "PersonalizedDuringPreflight", plist_new_bool(1)); // start the restore process restore_error = restored_start_restore(restore, opts, client->restore->protocol_version); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to start the restore process\n"); plist_free(opts); restore_client_free(client); return -1; } plist_free(opts); idevicerestore_progress(client, RESTORE_STEP_PREPARE, 1.0); // this is the restore process loop, it reads each message in from // restored and passes that data on to it's specific handler while (!(client->flags & FLAG_QUIT)) { // finally, if any of these message handlers returned -1 then we encountered // an unrecoverable error, so we need to bail. if (err < 0) { error("ERROR: Unable to successfully restore device\n"); client->flags |= FLAG_QUIT; } restore_error = restored_receive(restore, &message); #ifdef HAVE_RESTORE_E_RECEIVE_TIMEOUT if (restore_error == RESTORE_E_RECEIVE_TIMEOUT) { debug("No data to read (timeout)\n"); message = NULL; continue; } else if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Could not read data (%d). Aborting.\n", restore_error); err = -11; break; } #else if (restore_error != RESTORE_E_SUCCESS) { debug("No data to read\n"); message = NULL; continue; } #endif // discover what kind of message has been received node = plist_dict_get_item(message, "MsgType"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Unknown message received:\n"); //if (idevicerestore_debug) debug_plist(message); plist_free(message); message = NULL; continue; } plist_get_string_val(node, &type); // data request messages are sent by restored whenever it requires // files sent to the server by the client. these data requests include // SystemImageData, RootTicket, KernelCache, NORData and BasebandData requests if (!strcmp(type, "DataRequestMsg")) { err = restore_handle_data_request_msg(client, device, restore, message, build_identity, filesystem); } // restore logs are available if a previous restore failed else if (!strcmp(type, "PreviousRestoreLogMsg")) { err = restore_handle_previous_restore_log_msg(restore, message); } // progress notification messages sent by the restored inform the client // of it's current operation and sometimes percent of progress is complete else if (!strcmp(type, "ProgressMsg")) { err = restore_handle_progress_msg(client, message); } // status messages usually indicate the current state of the restored // process or often to signal an error has been encountered else if (!strcmp(type, "StatusMsg")) { err = restore_handle_status_msg(restore, message); if (restore_finished) { plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "MsgType", plist_new_string("ReceivedFinalStatusMsg")); restored_send(restore, dict); plist_free(dict); client->flags |= FLAG_QUIT; } } // baseband update message else if (!strcmp(type, "BBUpdateStatusMsg")) { err = restore_handle_bb_update_status_msg(restore, message); } // there might be some other message types i'm not aware of, but I think // at least the "previous error logs" messages usually end up here else { debug("Unknown message type received\n"); //if (idevicerestore_debug) debug_plist(message); } free(type); plist_free(message); message = NULL; } if (thread_alive(fdr_thread)) { if (fdr_control_channel) { fdr_disconnect(fdr_control_channel); thread_join(fdr_thread); fdr_control_channel = NULL; } } restore_client_free(client); return err; } idevicerestore-1.0.0/src/restore.h000066400000000000000000000056531367173617500172000ustar00rootroot00000000000000/* * restore.h * Functions for handling idevices in restore mode * * Copyright (c) 2010-2012 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012-2015 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_RESTORE_H #define IDEVICERESTORE_RESTORE_H #ifdef __cplusplus extern "C" { #endif #include #include #include struct restore_client_t { plist_t tss; plist_t bbtss; idevice_t device; char* udid; unsigned int operation; const char* filesystem; uint64_t protocol_version; restored_client_t client; }; int restore_check_mode(struct idevicerestore_client_t* client); irecv_device_t restore_get_irecv_device(struct idevicerestore_client_t* client); int restore_client_new(struct idevicerestore_client_t* client); void restore_client_free(struct idevicerestore_client_t* client); int restore_is_image4_supported(struct idevicerestore_client_t* client); int restore_reboot(struct idevicerestore_client_t* client); const char* restore_progress_string(unsigned int operation); int restore_handle_status_msg(restored_client_t client, plist_t msg); int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t msg); int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem); int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity); int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client); int restore_send_component(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, const char *component); int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem); int restore_open_with_timeout(struct idevicerestore_client_t* client); int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem); int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device); #ifdef __cplusplus } #endif #endif idevicerestore-1.0.0/src/sha1.c000066400000000000000000000167311367173617500163430ustar00rootroot00000000000000/* SHA-1 in C By Steve Reid 100% Public Domain Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ /* #define SHA1HANDSOFF * Copies data before messing with it. */ #define SHA1HANDSOFF #include #include /* for uint32_t */ #include #include "sha1.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if BYTE_ORDER == LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #elif BYTE_ORDER == BIG_ENDIAN #define blk0(i) block->l[i] #else #error "Endianness not defined!" #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform( uint32_t state[5], const unsigned char buffer[64] ) { uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ memcpy(block, buffer, 64); #else /* The following had better never be used because it causes the * pointer-to-const buffer to be cast into a pointer to non-const. * And the result is written through. I threw a "const" in, hoping * this will cause a diagnostic. */ CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3); R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7); R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11); R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15); R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19); R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23); R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27); R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31); R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35); R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39); R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43); R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47); R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51); R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55); R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59); R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63); R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67); R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71); R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75); R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF memset(block, '\0', sizeof(block)); #endif } /* SHA1Init - Initialize new context */ void SHA1Init( SHA1_CTX * context ) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update( SHA1_CTX * context, const unsigned char *data, uint32_t len ) { uint32_t i; uint32_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1]++; context->count[1] += (len >> 29); j = (j >> 3) & 63; if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); SHA1Transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ void SHA1Final( unsigned char digest[20], SHA1_CTX * context ) { unsigned i; unsigned char finalcount[8]; unsigned char c; #if 0 /* untested "improvement" by DHR */ /* Convert context->count to a sequence of bytes * in finalcount. Second element first, but * big-endian order within element. * But we do it all backwards. */ unsigned char *fcp = &finalcount[8]; for (i = 0; i < 2; i++) { uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) *--fcp = (unsigned char) t} #else for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ } #endif c = 0200; SHA1Update(context, &c, 1); while ((context->count[0] & 504) != 448) { c = 0000; SHA1Update(context, &c, 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } /* Wipe variables */ memset(context, '\0', sizeof(*context)); memset(&finalcount, '\0', sizeof(finalcount)); } void SHA1( char *hash_out, const char *str, int len) { SHA1_CTX ctx; unsigned int ii; SHA1Init(&ctx); for (ii=0; ii 100% Public Domain */ #include "stdint.h" typedef struct { uint32_t state[5]; uint32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; void SHA1Transform( uint32_t state[5], const unsigned char buffer[64] ); void SHA1Init( SHA1_CTX * context ); void SHA1Update( SHA1_CTX * context, const unsigned char *data, uint32_t len ); void SHA1Final( unsigned char digest[20], SHA1_CTX * context ); void SHA1( char *hash_out, const char *str, int len); #endif /* SHA1_H */ idevicerestore-1.0.0/src/socket.c000066400000000000000000000201661367173617500167740ustar00rootroot00000000000000/* * socket.c * * Copyright (c) 2012 Martin Szulecki All Rights Reserved. * Copyright (c) 2012 Nikias Bassen All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #ifdef WIN32 #include #include static int wsa_init = 0; #else #include #include #include #include #include #endif #include "socket.h" #define RECV_TIMEOUT 20000 static int verbose = 0; void socket_set_verbose(int level) { verbose = level; } #ifndef WIN32 int socket_create_unix(const char *filename) { struct sockaddr_un name; int sock; size_t size; // remove if still present unlink(filename); /* Create the socket. */ sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return -1; } /* Bind a name to the socket. */ name.sun_family = AF_LOCAL; strncpy(name.sun_path, filename, sizeof(name.sun_path)); name.sun_path[sizeof(name.sun_path) - 1] = '\0'; /* The size of the address is the offset of the start of the filename, plus its length, plus one for the terminating null byte. Alternatively you can just do: size = SUN_LEN (&name); */ size = (offsetof(struct sockaddr_un, sun_path) + strlen(name.sun_path) + 1); if (bind(sock, (struct sockaddr *) &name, size) < 0) { perror("bind"); socket_close(sock); return -1; } if (listen(sock, 10) < 0) { perror("listen"); socket_close(sock); return -1; } return sock; } int socket_connect_unix(const char *filename) { struct sockaddr_un name; int sfd = -1; size_t size; struct stat fst; // check if socket file exists... if (stat(filename, &fst) != 0) { if (verbose >= 2) fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename, strerror(errno)); return -1; } // ... and if it is a unix domain socket if (!S_ISSOCK(fst.st_mode)) { if (verbose >= 2) fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__, filename); return -1; } // make a new socket if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { if (verbose >= 2) fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno)); return -1; } // and connect to 'filename' name.sun_family = AF_LOCAL; strncpy(name.sun_path, filename, sizeof(name.sun_path)); name.sun_path[sizeof(name.sun_path) - 1] = 0; size = (offsetof(struct sockaddr_un, sun_path) + strlen(name.sun_path) + 1); if (connect(sfd, (struct sockaddr *) &name, size) < 0) { socket_close(sfd); if (verbose >= 2) fprintf(stderr, "%s: connect: %s\n", __func__, strerror(errno)); return -1; } return sfd; } #endif int socket_create(uint16_t port) { int sfd = -1; int yes = 1; #ifdef WIN32 WSADATA wsa_data; if (!wsa_init) { if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { fprintf(stderr, "WSAStartup failed!\n"); ExitProcess(-1); } wsa_init = 1; } #endif struct sockaddr_in saddr; if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { perror("socket()"); return -1; } if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) { perror("setsockopt()"); socket_close(sfd); return -1; } memset((void *) &saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(port); if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) { perror("bind()"); socket_close(sfd); return -1; } if (listen(sfd, 1) == -1) { perror("listen()"); socket_close(sfd); return -1; } return sfd; } int socket_connect(const char *addr, uint16_t port) { int sfd = -1; int yes = 1; struct hostent *hp; struct sockaddr_in saddr; #ifdef WIN32 WSADATA wsa_data; if (!wsa_init) { if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { fprintf(stderr, "WSAStartup failed!\n"); ExitProcess(-1); } wsa_init = 1; } #endif if (!addr) { errno = EINVAL; return -1; } if ((hp = gethostbyname(addr)) == NULL) { if (verbose >= 2) fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr); return -1; } if (!hp->h_addr) { if (verbose >= 2) fprintf(stderr, "%s: gethostbyname returned NULL address!\n", __func__); return -1; } if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { perror("socket()"); return -1; } if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) { perror("setsockopt()"); socket_close(sfd); return -1; } memset((void *) &saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr; saddr.sin_port = htons(port); if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { perror("connect"); socket_close(sfd); return -2; } return sfd; } int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) { fd_set fds; int sret; int eagain; struct timeval to; struct timeval *pto; if (fd <= 0) { if (verbose >= 2) fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); return -1; } FD_ZERO(&fds); FD_SET(fd, &fds); if (timeout > 0) { to.tv_sec = (time_t) (timeout / 1000); to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); pto = &to; } else { pto = NULL; } sret = -1; do { eagain = 0; switch (fdm) { case FDM_READ: sret = select(fd + 1, &fds, NULL, NULL, pto); break; case FDM_WRITE: sret = select(fd + 1, NULL, &fds, NULL, pto); break; case FDM_EXCEPT: sret = select(fd + 1, NULL, NULL, &fds, pto); break; default: return -1; } if (sret < 0) { switch (errno) { case EINTR: // interrupt signal in select if (verbose >= 2) fprintf(stderr, "%s: EINTR\n", __func__); eagain = 1; break; case EAGAIN: if (verbose >= 2) fprintf(stderr, "%s: EAGAIN\n", __func__); break; default: if (verbose >= 2) fprintf(stderr, "%s: select failed: %s\n", __func__, strerror(errno)); return -1; } } } while (eagain); return sret; } int socket_accept(int fd, uint16_t port) { #ifdef WIN32 int addr_len; #else socklen_t addr_len; #endif int result; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); addr_len = sizeof(addr); result = accept(fd, (struct sockaddr*)&addr, &addr_len); return result; } int socket_shutdown(int fd, int how) { return shutdown(fd, how); } int socket_close(int fd) { #ifdef WIN32 return closesocket(fd); #else return close(fd); #endif } int socket_receive(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, 0, RECV_TIMEOUT); } int socket_peek(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); } int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) { int res; int result; // check if data is available res = socket_check_fd(fd, FDM_READ, timeout); if (res <= 0) { return res; } // if we get here, there _is_ data available result = recv(fd, data, length, flags); if (res > 0 && result == 0) { // but this is an error condition if (verbose >= 3) fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); return -EAGAIN; } if (result < 0) { return -errno; } return result; } int socket_send(int fd, void *data, size_t length) { return send(fd, data, length, 0); } idevicerestore-1.0.0/src/socket.h000066400000000000000000000035611367173617500170010ustar00rootroot00000000000000/* * socket.h * * Copyright (c) 2012 Martin Szulecki All Rights Reserved. * Copyright (c) 2012 Nikias Bassen All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SOCKET_SOCKET_H #define __SOCKET_SOCKET_H #include #include enum fd_mode { FDM_READ, FDM_WRITE, FDM_EXCEPT }; typedef enum fd_mode fd_mode; #ifdef WIN32 #include #define SHUT_RD SD_READ #define SHUT_WR SD_WRITE #define SHUT_RDWR SD_BOTH #else #include #endif #ifndef WIN32 int socket_create_unix(const char *filename); int socket_connect_unix(const char *filename); #endif int socket_create(uint16_t port); int socket_connect(const char *addr, uint16_t port); int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout); int socket_accept(int fd, uint16_t port); int socket_shutdown(int fd, int how); int socket_close(int fd); int socket_receive(int fd, void *data, size_t size); int socket_peek(int fd, void *data, size_t size); int socket_receive_timeout(int fd, void *data, size_t size, int flags, unsigned int timeout); int socket_send(int fd, void *data, size_t size); void socket_set_verbose(int level); #endif /* __SOCKET_SOCKET_H */ idevicerestore-1.0.0/src/thread.c000066400000000000000000000071021367173617500167460ustar00rootroot00000000000000/* * thread.c * * Copyright (c) 2012 Martin Szulecki All Rights Reserved. * Copyright (c) 2012 Nikias Bassen All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "thread.h" int thread_new(thread_t *thread, thread_func_t thread_func, void* data) { #ifdef WIN32 HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, data, 0, NULL); if (th == NULL) { return -1; } *thread = th; return 0; #else int res = pthread_create(thread, NULL, thread_func, data); return res; #endif } void thread_free(thread_t thread) { #ifdef WIN32 CloseHandle(thread); #endif } void thread_join(thread_t thread) { /* wait for thread to complete */ #ifdef WIN32 WaitForSingleObject(thread, INFINITE); #else pthread_join(thread, NULL); #endif } int thread_alive(thread_t thread) { if (!thread) return 0; #ifdef WIN32 return WaitForSingleObject(thread, 0) == WAIT_TIMEOUT; #else return pthread_kill(thread, 0) == 0; #endif } void mutex_init(mutex_t* mutex) { #ifdef WIN32 InitializeCriticalSection(mutex); #else pthread_mutex_init(mutex, NULL); #endif } void mutex_destroy(mutex_t* mutex) { #ifdef WIN32 DeleteCriticalSection(mutex); #else pthread_mutex_destroy(mutex); #endif } void mutex_lock(mutex_t* mutex) { #ifdef WIN32 EnterCriticalSection(mutex); #else pthread_mutex_lock(mutex); #endif } void mutex_unlock(mutex_t* mutex) { #ifdef WIN32 LeaveCriticalSection(mutex); #else pthread_mutex_unlock(mutex); #endif } void thread_once(thread_once_t *once_control, void (*init_routine)(void)) { #ifdef WIN32 while (InterlockedExchange(&(once_control->lock), 1) != 0) { Sleep(1); } if (!once_control->state) { once_control->state = 1; init_routine(); } InterlockedExchange(&(once_control->lock), 0); #else pthread_once(once_control, init_routine); #endif } void cond_init(cond_t* cond) { #ifdef WIN32 cond->sem = CreateSemaphore(NULL, 0, 32767, NULL); #else pthread_cond_init(cond, NULL); #endif } void cond_destroy(cond_t* cond) { #ifdef WIN32 CloseHandle(cond->sem); #else pthread_cond_destroy(cond); #endif } int cond_signal(cond_t* cond) { #ifdef WIN32 int result = 0; if (!ReleaseSemaphore(cond->sem, 1, NULL)) { result = -1; } return result; #else return pthread_cond_signal(cond); #endif } int cond_wait(cond_t* cond, mutex_t* mutex) { #ifdef WIN32 mutex_unlock(mutex); WaitForSingleObject(cond->sem, INFINITE); #else return pthread_cond_wait(cond, mutex); #endif } int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms) { #ifdef WIN32 mutex_unlock(mutex); WaitForSingleObject(cond->sem, timeout_ms); #else struct timespec ts; struct timeval now; gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + timeout_ms / 1000; ts.tv_nsec = now.tv_usec * 1000 + 1000 * 1000 * (timeout_ms % 1000); ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); ts.tv_nsec %= (1000 * 1000 * 1000); return pthread_cond_timedwait(cond, mutex, &ts); #endif } idevicerestore-1.0.0/src/thread.h000066400000000000000000000041331367173617500167540ustar00rootroot00000000000000/* * thread.h * * Copyright (c) 2012 Martin Szulecki All Rights Reserved. * Copyright (c) 2012 Nikias Bassen All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __THREAD_H #define __THREAD_H #ifdef WIN32 #include typedef HANDLE thread_t; typedef CRITICAL_SECTION mutex_t; typedef struct { HANDLE sem; } cond_t; typedef volatile struct { LONG lock; int state; } thread_once_t; #define THREAD_ONCE_INIT {0, 0} #define THREAD_ID GetCurrentThreadId() #else #include #include #include typedef pthread_t thread_t; typedef pthread_mutex_t mutex_t; typedef pthread_cond_t cond_t; typedef pthread_once_t thread_once_t; #define THREAD_ONCE_INIT PTHREAD_ONCE_INIT #define THREAD_ID pthread_self() #endif typedef void* (*thread_func_t)(void* data); int thread_new(thread_t* thread, thread_func_t thread_func, void* data); void thread_free(thread_t thread); void thread_join(thread_t thread); int thread_alive(thread_t thread); void mutex_init(mutex_t* mutex); void mutex_destroy(mutex_t* mutex); void mutex_lock(mutex_t* mutex); void mutex_unlock(mutex_t* mutex); void thread_once(thread_once_t *once_control, void (*init_routine)(void)); void cond_init(cond_t* cond); void cond_destroy(cond_t* cond); int cond_signal(cond_t* cond); int cond_wait(cond_t* cond, mutex_t* mutex); int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms); #endif idevicerestore-1.0.0/src/tss.c000066400000000000000000001440441367173617500163170ustar00rootroot00000000000000/* * tss.c * Functions for communicating with Apple's TSS server * * Copyright (c) 2010-2013 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "tss.h" #include "img3.h" #include "common.h" #include "idevicerestore.h" #include "endianness.h" #define TSS_CLIENT_VERSION_STRING "libauthinstall-698.0.5" #define ECID_STRSIZE 0x20 typedef struct { int length; char* content; } tss_response; char* ecid_to_string(uint64_t ecid) { char* ecid_string = malloc(ECID_STRSIZE); memset(ecid_string, '\0', ECID_STRSIZE); if (ecid == 0) { error("ERROR: Invalid ECID passed.\n"); return NULL; } snprintf(ecid_string, ECID_STRSIZE, "%"PRIu64, ecid); return ecid_string; } plist_t tss_request_new(plist_t overrides) { plist_t request = plist_new_dict(); plist_dict_set_item(request, "@Locality", plist_new_string("en_US")); plist_dict_set_item(request, "@HostPlatformInfo", #ifdef WIN32 plist_new_string("windows") #else plist_new_string("mac") #endif ); plist_dict_set_item(request, "@VersionInfo", plist_new_string(TSS_CLIENT_VERSION_STRING)); char* guid = generate_guid(); if (guid) { plist_dict_set_item(request, "@UUID", plist_new_string(guid)); free(guid); } /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return request; } int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity) { plist_t node = NULL; char* string = NULL; /* UniqueBuildID */ node = plist_dict_get_item(build_identity, "UniqueBuildID"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find UniqueBuildID node\n"); return -1; } plist_dict_set_item(parameters, "UniqueBuildID", plist_copy(node)); node = NULL; /* ApChipID */ int chip_id = 0; node = plist_dict_get_item(build_identity, "ApChipID"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApChipID node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &chip_id); plist_dict_set_item(parameters, "ApChipID", plist_new_uint(chip_id)); free(string); string = NULL; node = NULL; /* ApBoardID */ int board_id = 0; node = plist_dict_get_item(build_identity, "ApBoardID"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApBoardID node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &board_id); plist_dict_set_item(parameters, "ApBoardID", plist_new_uint(board_id)); free(string); string = NULL; node = NULL; /* ApSecurityDomain */ int security_domain = 0; node = plist_dict_get_item(build_identity, "ApSecurityDomain"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApSecurityDomain node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &security_domain); plist_dict_set_item(parameters, "ApSecurityDomain", plist_new_uint(security_domain)); free(string); string = NULL; node = NULL; /* BMU,BoardID */ node = plist_dict_get_item(build_identity, "BMU,BoardID"); if (node) { plist_dict_set_item(parameters, "BMU,BoardID", plist_copy(node)); } /* BMU,ChipID */ node = plist_dict_get_item(build_identity, "BMU,ChipID"); if (node) { plist_dict_set_item(parameters, "BMU,ChipID", plist_copy(node)); } /* BbChipID */ int bb_chip_id = 0; char* bb_chip_id_string = NULL; node = plist_dict_get_item(build_identity, "BbChipID"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &bb_chip_id_string); sscanf(bb_chip_id_string, "%x", &bb_chip_id); plist_dict_set_item(parameters, "BbChipID", plist_new_uint(bb_chip_id)); } else { debug("NOTE: Unable to find BbChipID node\n"); } node = NULL; /* BbProvisioningManifestKeyHash */ node = plist_dict_get_item(build_identity, "BbProvisioningManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbProvisioningManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbProvisioningManifestKeyHash node\n"); } node = NULL; /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ node = plist_dict_get_item(build_identity, "BbActivationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbActivationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbActivationManifestKeyHash node\n"); } node = NULL; node = plist_dict_get_item(build_identity, "BbCalibrationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbCalibrationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbCalibrationManifestKeyHash node\n"); } node = NULL; /* BbFactoryActivationManifestKeyHash */ node = plist_dict_get_item(build_identity, "BbFactoryActivationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbFactoryActivationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbFactoryActivationManifestKeyHash node\n"); } node = NULL; /* BbFDRSecurityKeyHash */ node = plist_dict_get_item(build_identity, "BbFDRSecurityKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbFDRSecurityKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbFDRSecurityKeyHash node\n"); } node = NULL; /* BbSkeyId - Used by XMM 6180/GSM */ node = plist_dict_get_item(build_identity, "BbSkeyId"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbSkeyId", plist_copy(node)); } else { debug("NOTE: Unable to find BbSkeyId node\n"); } node = NULL; /* SE,ChipID - Used for SE firmware request */ node = plist_dict_get_item(build_identity, "SE,ChipID"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "SE,ChipID", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "SE,ChipID", plist_copy(node)); } } node = NULL; /* Savage,ChipID - Used for Savage firmware request */ node = plist_dict_get_item(build_identity, "Savage,ChipID"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "Savage,ChipID", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "Savage,ChipID", plist_copy(node)); } } node = NULL; /* add Savage,PatchEpoch - Used for Savage firmware request */ node = plist_dict_get_item(build_identity, "Savage,PatchEpoch"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "Savage,PatchEpoch", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "Savage,PatchEpoch", plist_copy(node)); } } node = NULL; /* Yonkers,BoardID - Used for Yonkers firmware request */ node = plist_dict_get_item(build_identity, "Yonkers,BoardID"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "Yonkers,BoardID", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "Yonkers,BoardID", plist_copy(node)); } } node = NULL; /* Yonkers,ChipID - Used for Yonkers firmware request */ node = plist_dict_get_item(build_identity, "Yonkers,ChipID"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "Yonkers,ChipID", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "Yonkers,ChipID", plist_copy(node)); } } node = NULL; /* add Yonkers,PatchEpoch - Used for Yonkers firmware request */ node = plist_dict_get_item(build_identity, "Yonkers,PatchEpoch"); if (node) { if (plist_get_node_type(node) == PLIST_STRING) { char *strval = NULL; int intval = 0; plist_get_string_val(node, &strval); sscanf(strval, "%x", &intval); plist_dict_set_item(parameters, "Yonkers,PatchEpoch", plist_new_uint(intval)); } else { plist_dict_set_item(parameters, "Yonkers,PatchEpoch", plist_copy(node)); } } node = NULL; /* add Rap,BoardID */ node = plist_dict_get_item(build_identity, "Rap,BoardID"); if (node) { plist_dict_set_item(parameters, "Rap,BoardID", plist_copy(node)); } node = NULL; /* add Rap,ChipID */ node = plist_dict_get_item(build_identity, "Rap,ChipID"); if (node) { plist_dict_set_item(parameters, "Rap,ChipID", plist_copy(node)); } node = NULL; /* add Rap,SecurityDomain */ node = plist_dict_get_item(build_identity, "Rap,SecurityDomain"); if (node) { plist_dict_set_item(parameters, "Rap,SecurityDomain", plist_copy(node)); } node = NULL; /* add eUICC,ChipID */ node = plist_dict_get_item(build_identity, "eUICC,ChipID"); if (node) { plist_dict_set_item(parameters, "eUICC,ChipID", plist_copy(node)); } node = NULL; node = plist_dict_get_item(build_identity, "PearlCertificationRootPub"); if (node) { plist_dict_set_item(parameters, "PearlCertificationRootPub", plist_copy(node)); } node = NULL; /* add build identity manifest dictionary */ node = plist_dict_get_item(build_identity, "Manifest"); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unable to find Manifest node\n"); return -1; } plist_dict_set_item(parameters, "Manifest", plist_copy(node)); return 0; } int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters) { plist_t node = NULL; if (!parameters) { error("ERROR: Missing required AP parameters\n"); return -1; } /* ApNonce */ node = plist_dict_get_item(parameters, "ApNonce"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find required ApNonce in parameters\n"); return -1; } plist_dict_set_item(request, "ApNonce", plist_copy(node)); node = NULL; plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1)); /* ApSecurityMode */ node = plist_dict_get_item(request, "ApSecurityMode"); if (!node) { /* copy from parameters if available */ node = plist_dict_get_item(parameters, "ApSecurityMode"); if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { error("ERROR: Unable to find required ApSecurityMode in parameters\n"); return -1; } plist_dict_set_item(request, "ApSecurityMode", plist_copy(node)); node = NULL; } node = plist_dict_get_item(request, "ApProductionMode"); if (!node) { /* ApProductionMode */ node = plist_dict_get_item(parameters, "ApProductionMode"); if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { error("ERROR: Unable to find required ApProductionMode in parameters\n"); return -1; } plist_dict_set_item(request, "ApProductionMode", plist_copy(node)); node = NULL; } /* ApSepNonce */ node = plist_dict_get_item(parameters, "ApSepNonce"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find required ApSepNonce in parameters\n"); return -1; } plist_dict_set_item(request, "SepNonce", plist_copy(node)); node = NULL; /* PearlCertificationRootPub */ node = plist_dict_get_item(parameters, "PearlCertificationRootPub"); if (node) { plist_dict_set_item(request, "PearlCertificationRootPub", plist_copy(node)); } return 0; } int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters) { plist_t node = NULL; if (!parameters) { error("ERROR: Missing required AP parameters\n"); return -1; } /* ApNonce */ node = plist_dict_get_item(parameters, "ApNonce"); if (node) { if (plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find required ApNonce in parameters\n"); return -1; } plist_dict_set_item(request, "ApNonce", plist_copy(node)); node = NULL; } /* @APTicket */ plist_dict_set_item(request, "@APTicket", plist_new_bool(1)); /* ApBoardID */ node = plist_dict_get_item(request, "ApBoardID"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: Unable to find required ApBoardID in request\n"); return -1; } node = NULL; /* ApChipID */ node = plist_dict_get_item(request, "ApChipID"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: Unable to find required ApChipID in request\n"); return -1; } node = NULL; /* ApSecurityDomain */ node = plist_dict_get_item(request, "ApSecurityDomain"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: Unable to find required ApSecurityDomain in request\n"); return -1; } node = NULL; /* ApProductionMode */ node = plist_dict_get_item(parameters, "ApProductionMode"); if (!node || plist_get_node_type(node) != PLIST_BOOLEAN) { error("ERROR: Unable to find required ApProductionMode in parameters\n"); return -1; } plist_dict_set_item(request, "ApProductionMode", plist_copy(node)); node = NULL; return 0; } int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; /* ApECID */ node = plist_dict_get_item(parameters, "ApECID"); if (node) { plist_dict_set_item(request, "ApECID", plist_copy(node)); } node = NULL; /* UniqueBuildID */ node = plist_dict_get_item(parameters, "UniqueBuildID"); if (node) { plist_dict_set_item(request, "UniqueBuildID", plist_copy(node)); } node = NULL; /* ApChipID */ node = plist_dict_get_item(parameters, "ApChipID"); if (node) { plist_dict_set_item(request, "ApChipID", plist_copy(node)); } node = NULL; /* ApBoardID */ node = plist_dict_get_item(parameters, "ApBoardID"); if (node) { plist_dict_set_item(request, "ApBoardID", plist_copy(node)); } node = NULL; /* ApSecurityDomain */ node = plist_dict_get_item(parameters, "ApSecurityDomain"); if (node) { plist_dict_set_item(request, "ApSecurityDomain", plist_copy(node)); } node = NULL; /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } static void tss_entry_apply_restore_request_rules(plist_t tss_entry, plist_t parameters, plist_t rules) { if (!tss_entry || !rules) { return; } if (plist_get_node_type(tss_entry) != PLIST_DICT) { return; } if (plist_get_node_type(rules) != PLIST_ARRAY) { return; } uint32_t i; for (i = 0; i < plist_array_get_size(rules); i++) { plist_t rule = plist_array_get_item(rules, i); plist_t conditions = plist_dict_get_item(rule, "Conditions"); plist_dict_iter iter = NULL; plist_dict_new_iter(conditions, &iter); char* key = NULL; plist_t value = NULL; plist_t value2 = NULL; int conditions_fulfilled = 1; while (conditions_fulfilled) { plist_dict_next_item(conditions, iter, &key, &value); if (key == NULL) break; if (!strcmp(key, "ApRawProductionMode")) { value2 = plist_dict_get_item(parameters, "ApProductionMode"); } else if (!strcmp(key, "ApCurrentProductionMode")) { value2 = plist_dict_get_item(parameters, "ApProductionMode"); } else if (!strcmp(key, "ApRawSecurityMode")) { value2 = plist_dict_get_item(parameters, "ApSecurityMode"); } else if (!strcmp(key, "ApRequiresImage4")) { value2 = plist_dict_get_item(parameters, "ApSupportsImg4"); } else if (!strcmp(key, "ApDemotionPolicyOverride")) { value2 = plist_dict_get_item(parameters, "DemotionPolicy"); } else if (!strcmp(key, "ApInRomDFU")) { value2 = plist_dict_get_item(parameters, "ApInRomDFU"); } else { error("WARNING: Unhandled condition '%s' while parsing RestoreRequestRules\n", key); value2 = NULL; } if (value2) { conditions_fulfilled = plist_compare_node_value(value, value2); } else { conditions_fulfilled = 0; } free(key); } free(iter); iter = NULL; if (!conditions_fulfilled) { continue; } plist_t actions = plist_dict_get_item(rule, "Actions"); plist_dict_new_iter(actions, &iter); while (1) { plist_dict_next_item(actions, iter, &key, &value); if (key == NULL) break; uint8_t bv = 255; plist_get_bool_val(value, &bv); if (bv != 255) { value2 = plist_dict_get_item(tss_entry, key); if (value2) { plist_dict_remove_item(tss_entry, key); } debug("DEBUG: Adding %s=%s to TSS entry\n", key, (bv) ? "true" : "false"); plist_dict_set_item(tss_entry, key, plist_new_bool(bv)); } free(key); } } } int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) { /* loop over components from build manifest */ plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); return -1; } /* add components to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } /* do not populate BaseBandFirmware, only in basebaseband request */ if ((strcmp(key, "BasebandFirmware") == 0)) { free(key); continue; } /* FIXME: only used with diagnostics firmware */ if (strcmp(key, "Diags") == 0) { free(key); continue; } if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) { plist_t info_dict = plist_dict_get_item(manifest_entry, "Info"); if (!_plist_dict_get_bool(manifest_entry, "Trusted") && !_plist_dict_get_bool(info_dict, "IsFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsFUDFirmware")) { debug("DEBUG: %s: Skipping '%s' as it is neither firmware nor secondary firmware payload\n", __func__, key); continue; } } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove obsolete Info node */ plist_dict_remove_item(tss_entry, "Info"); /* handle RestoreRequestRules */ plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); if (rules) { debug("DEBUG: Applying restore request rules for entry %s\n", key); tss_entry_apply_restore_request_rules(tss_entry, parameters, rules); } /* Make sure we have a Digest key for Trusted items even if empty */ plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t trusted; plist_get_bool_val(node, &trusted); if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { debug("DEBUG: No Digest data, using empty value for entry %s\n", key); plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); } } /* finally add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; /* BbChipID */ uint64_t bb_chip_id = _plist_dict_get_uint(parameters, "BbChipID"); if (bb_chip_id) { plist_dict_set_item(request, "BbChipID", plist_new_uint(bb_chip_id)); } /* BbProvisioningManifestKeyHash */ node = plist_dict_get_item(parameters, "BbProvisioningManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbProvisioningManifestKeyHash", plist_copy(node)); } node = NULL; /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ node = plist_dict_get_item(parameters, "BbActivationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbActivationManifestKeyHash", plist_copy(node)); } node = NULL; node = plist_dict_get_item(parameters, "BbCalibrationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbCalibrationManifestKeyHash", plist_copy(node)); } node = NULL; /* BbFactoryActivationManifestKeyHash */ node = plist_dict_get_item(parameters, "BbFactoryActivationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbFactoryActivationManifestKeyHash", plist_copy(node)); } node = NULL; /* BbFDRSecurityKeyHash */ node = plist_dict_get_item(parameters, "BbFDRSecurityKeyHash"); if (node) { plist_dict_set_item(request, "BbFDRSecurityKeyHash", plist_copy(node)); } node = NULL; /* BbSkeyId - Used by XMM 6180/GSM */ node = plist_dict_get_item(parameters, "BbSkeyId"); if (node) { plist_dict_set_item(request, "BbSkeyId", plist_copy(node)); } node = NULL; /* BbNonce */ node = plist_dict_get_item(parameters, "BbNonce"); if (node) { plist_dict_set_item(request, "BbNonce", plist_copy(node)); } node = NULL; /* @BBTicket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); /* BbGoldCertId */ node = plist_dict_get_item(parameters, "BbGoldCertId"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: Unable to find required BbGoldCertId in parameters\n"); return -1; } node = plist_copy(node); uint64_t val; plist_get_uint_val(node, &val); int32_t bb_cert_id = (int32_t)val; plist_set_uint_val(node, bb_cert_id); plist_dict_set_item(request, "BbGoldCertId", node); node = NULL; /* BbSNUM */ node = plist_dict_get_item(parameters, "BbSNUM"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find required BbSNUM in parameters\n"); return -1; } plist_dict_set_item(request, "BbSNUM", plist_copy(node)); node = NULL; /* BasebandFirmware */ node = plist_access_path(parameters, 2, "Manifest", "BasebandFirmware"); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unable to get BasebandFirmware node\n"); return -1; } plist_t bbfwdict = plist_copy(node); node = NULL; if (plist_dict_get_item(bbfwdict, "Info")) { plist_dict_remove_item(bbfwdict, "Info"); } if (bb_chip_id == 0x68) { /* depending on the BasebandCertId remove certain nodes */ if (bb_cert_id == 0x26F3FACC || bb_cert_id == 0x5CF2EC4E || bb_cert_id == 0x8399785A) { plist_dict_remove_item(bbfwdict, "PSI2-PartialDigest"); plist_dict_remove_item(bbfwdict, "RestorePSI2-PartialDigest"); } else { plist_dict_remove_item(bbfwdict, "PSI-PartialDigest"); plist_dict_remove_item(bbfwdict, "RestorePSI-PartialDigest"); } } plist_dict_set_item(request, "BasebandFirmware", bbfwdict); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the SE,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); /* add SE,ChipID */ node = plist_dict_get_item(parameters, "SE,ChipID"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ChipID", plist_copy(node)); node = NULL; /* add SE,ID */ node = plist_dict_get_item(parameters, "SE,ID"); if (!node) { error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ID", plist_copy(node)); node = NULL; /* add SE,Nonce */ node = plist_dict_get_item(parameters, "SE,Nonce"); if (!node) { error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,Nonce", plist_copy(node)); node = NULL; /* add SE,RootKeyIdentifier */ node = plist_dict_get_item(parameters, "SE,RootKeyIdentifier"); if (!node) { error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,RootKeyIdentifier", plist_copy(node)); node = NULL; /* 'IsDev' determines whether we have Production or Development */ uint8_t is_dev = 0; node = plist_dict_get_item(parameters, "SE,IsDev"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &is_dev); } /* add SE,* components from build manifest to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { key = NULL; plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { free(key); error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } if (strncmp(key, "SE,", 3)) { free(key); continue; } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove Info node */ plist_dict_remove_item(tss_entry, "Info"); /* remove Development or Production key/hash node */ if (is_dev) { if (plist_dict_get_item(tss_entry, "ProductionCMAC")) plist_dict_remove_item(tss_entry, "ProductionCMAC"); if (plist_dict_get_item(tss_entry, "ProductionUpdatePayloadHash")) plist_dict_remove_item(tss_entry, "ProductionUpdatePayloadHash"); } else { if (plist_dict_get_item(tss_entry, "DevelopmentCMAC")) plist_dict_remove_item(tss_entry, "DevelopmentCMAC"); if (plist_dict_get_item(tss_entry, "DevelopmentUpdatePayloadHash")) plist_dict_remove_item(tss_entry, "DevelopmentUpdatePayloadHash"); } /* add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the Savage,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Savage,Ticket", plist_new_bool(1)); /* add Savage,UID */ node = plist_dict_get_item(parameters, "Savage,UID"); if (!node) { error("ERROR: %s: Unable to find required Savage,UID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,UID", plist_copy(node)); node = NULL; /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); if (!node) { error("ERROR: Unable to get SEP digest from manifest\n"); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); /* add Savage,PatchEpoch */ node = plist_dict_get_item(parameters, "Savage,PatchEpoch"); if (!node) { error("ERROR: %s: Unable to find required Savage,PatchEpoch in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,PatchEpoch", plist_copy(node)); node = NULL; /* add Savage,ChipID */ node = plist_dict_get_item(parameters, "Savage,ChipID"); if (!node) { error("ERROR: %s: Unable to find required Savage,ChipID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ChipID", plist_copy(node)); node = NULL; /* add Savage,AllowOfflineBoot */ node = plist_dict_get_item(parameters, "Savage,AllowOfflineBoot"); if (!node) { error("ERROR: %s: Unable to find required Savage,AllowOfflineBoot in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,AllowOfflineBoot", plist_copy(node)); node = NULL; /* add Savage,ReadFWKey */ node = plist_dict_get_item(parameters, "Savage,ReadFWKey"); if (!node) { error("ERROR: %s: Unable to find required Savage,ReadFWKey in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ReadFWKey", plist_copy(node)); node = NULL; /* add Savage,ProductionMode */ node = plist_dict_get_item(parameters, "Savage,ProductionMode"); if (!node) { error("ERROR: %s: Unable to find required Savage,ProductionMode in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ProductionMode", plist_copy(node)); const char *comp_name = NULL; uint8_t isprod = 0; plist_get_bool_val(node, &isprod); node = NULL; /* get the right component name */ comp_name = (isprod) ? "Savage,B0-Prod-Patch" : "Savage,B0-Dev-Patch"; node = plist_dict_get_item(parameters, "Savage,Revision"); if (node && (plist_get_node_type(node) == PLIST_DATA)) { unsigned char *savage_rev = NULL; uint64_t savage_rev_len = 0; plist_get_data_val(node, (char**)&savage_rev, &savage_rev_len); if (savage_rev_len > 0) { if (((savage_rev[0] | 0x10) & 0xF0) == 0x30) { comp_name = (isprod) ? "Savage,B2-Prod-Patch" : "Savage,B2-Dev-Patch"; } else if ((savage_rev[0] & 0xF0) == 0xA0) { comp_name = (isprod) ? "Savage,BA-Prod-Patch" : "Savage,BA-Dev-Patch"; } } free(savage_rev); } /* add Savage,B?-*-Patch */ node = plist_dict_get_item(manifest_node, comp_name); if (!node) { error("ERROR: Unable to get %s entry from manifest\n", comp_name); return -1; } dict = plist_copy(node); plist_dict_remove_item(dict, "Info"); plist_dict_set_item(request, comp_name, dict); if (component_name) { *component_name = strdup(comp_name); } /* add Savage,Nonce */ node = plist_dict_get_item(parameters, "Savage,Nonce"); if (!node) { error("ERROR: %s: Unable to find required Savage,Nonce in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,Nonce", plist_copy(node)); node = NULL; /* add Savage,ReadECKey */ node = plist_dict_get_item(parameters, "Savage,ReadECKey"); if (!node) { error("ERROR: %s: Unable to find required Savage,ReadECKey in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ReadECKey", plist_copy(node)); node = NULL; /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the Savage,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Yonkers,Ticket", plist_new_bool(1)); /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); if (!node) { error("ERROR: Unable to get SEP digest from manifest\n"); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); { static const char *keys[] = {"Yonkers,AllowOfflineBoot", "Yonkers,BoardID", "Yonkers,ChipID", "Yonkers,ECID", "Yonkers,Nonce", "Yonkers,PatchEpoch", "Yonkers,ProductionMode", "Yonkers,ReadECKey", "Yonkers,ReadFWKey", }; int i; for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); ++i) { node = plist_dict_get_item(parameters, keys[i]); if (!node) { error("ERROR: %s: Unable to find required %s in parameters\n", __func__, keys[i]); } plist_dict_set_item(request, keys[i], plist_copy(node)); node = NULL; } } char *comp_name = NULL; plist_t comp_node = NULL; uint8_t isprod = 1; uint64_t fabrevision = (uint64_t)-1; node = plist_dict_get_item(parameters, "Yonkers,ProductionMode"); if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { plist_get_bool_val(node, &isprod); } node = plist_dict_get_item(parameters, "Yonkers,FabRevision"); if (node && (plist_get_node_type(node) == PLIST_UINT)) { plist_get_uint_val(node, &fabrevision); } plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (iter) { node = NULL; comp_name = NULL; plist_dict_next_item(manifest_node, iter, &comp_name, &node); if (comp_name == NULL) { node = NULL; break; } if (strncmp(comp_name, "Yonkers,", 8) == 0) { int target_node = 1; plist_t sub_node; if ((sub_node = plist_dict_get_item(node, "EPRO")) != NULL && plist_get_node_type(sub_node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(sub_node, &b); target_node &= ((isprod) ? b : !b); } if ((sub_node = plist_dict_get_item(node, "FabRevision")) != NULL && plist_get_node_type(sub_node) == PLIST_UINT) { uint64_t v = 0; plist_get_uint_val(sub_node, &v); target_node &= (v == fabrevision); } if (target_node) { comp_node = node; break; } } free(comp_name); } free(iter); if (comp_name == NULL) { error("ERROR: No Yonkers node for %s/%lu\n", (isprod) ? "Production" : "Development", (unsigned long)fabrevision); return -1; } /* add Yonkers,SysTopPatch* */ if (comp_node != NULL) { plist_t comp_dict = plist_copy(comp_node); plist_dict_remove_item(comp_dict, "Info"); plist_dict_set_item(request, comp_name, comp_dict); } if (component_name) { *component_name = comp_name; } else { free(comp_name); } /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the eUICC,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@eUICC,Ticket", plist_new_bool(1)); node = plist_dict_get_item(parameters, "eUICC,ChipID"); if (node) { plist_dict_set_item(request, "eUICC,ChipID", plist_copy(node)); } node = plist_dict_get_item(parameters, "eUICC,EID"); if (node) { plist_dict_set_item(request, "eUICC,EID", plist_copy(node)); } node = plist_dict_get_item(parameters, "eUICC,RootKeyIdentifier"); if (node) { plist_dict_set_item(request, "eUICC,RootKeyIdentifier", plist_copy(node)); } /* set Nonce for eUICC,Gold component */ node = plist_dict_get_item(parameters, "EUICCGoldNonce"); if (node) { plist_t n = plist_dict_get_item(request, "eUICC,Gold"); if (n) { plist_dict_set_item(n, "Nonce", plist_copy(node)); } } /* set Nonce for eUICC,Main component */ node = plist_dict_get_item(parameters, "EUICCMainNonce"); if (node) { plist_t n = plist_dict_get_item(request, "eUICC,Main"); if (n) { plist_dict_set_item(n, "Nonce", plist_copy(node)); } } /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the Rap,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Rap,Ticket", plist_new_bool(1)); uint64_t u64val = 0; uint8_t bval = 0; u64val = _plist_dict_get_uint(parameters, "Rap,BoardID"); plist_dict_set_item(request, "Rap,BoardID", plist_new_uint(u64val)); u64val = _plist_dict_get_uint(parameters, "Rap,ChipID"); plist_dict_set_item(request, "Rap,ChipID", plist_new_uint(u64val)); u64val = _plist_dict_get_uint(parameters, "Rap,ECID"); plist_dict_set_item(request, "Rap,ECID", plist_new_uint(u64val)); node = plist_dict_get_item(parameters, "Rap,Nonce"); if (node) { plist_dict_set_item(request, "Rap,Nonce", plist_copy(node)); } bval = _plist_dict_get_bool(parameters, "Rap,ProductionMode"); plist_dict_set_item(request, "Rap,ProductionMode", plist_new_bool(bval)); u64val = _plist_dict_get_uint(parameters, "Rap,SecurityDomain"); plist_dict_set_item(request, "Rap,SecurityDomain", plist_new_uint(u64val)); bval = _plist_dict_get_bool(parameters, "Rap,SecurityMode"); plist_dict_set_item(request, "Rap,SecurityMode", plist_new_bool(bval)); char *comp_name = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (iter) { node = NULL; comp_name = NULL; plist_dict_next_item(manifest_node, iter, &comp_name, &node); if (comp_name == NULL) { node = NULL; break; } if (strncmp(comp_name, "Rap,", 4) == 0) { plist_t manifest_entry = plist_copy(node); /* handle RestoreRequestRules */ plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); if (rules) { debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); } /* Make sure we have a Digest key for Trusted items even if empty */ plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t trusted; plist_get_bool_val(node, &trusted); if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); } } plist_dict_remove_item(manifest_entry, "Info"); /* finally add entry to request */ plist_dict_set_item(request, comp_name, manifest_entry); } free(comp_name); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); return -1; } /* add tags indicating we want to get the Rap,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@BMU,Ticket", plist_new_bool(1)); uint64_t u64val = 0; uint8_t bval = 0; u64val = _plist_dict_get_uint(parameters, "BMU,BoardID"); plist_dict_set_item(request, "BMU,BoardID", plist_new_uint(u64val)); u64val = _plist_dict_get_uint(parameters, "ChipID"); plist_dict_set_item(request, "BMU,ChipID", plist_new_uint(u64val)); node = plist_dict_get_item(parameters, "Nonce"); if (node) { plist_dict_set_item(request, "BMU,Nonce", plist_copy(node)); } bval = _plist_dict_get_bool(parameters, "ProductionMode"); plist_dict_set_item(request, "BMU,ProductionMode", plist_new_bool(bval)); u64val = _plist_dict_get_uint(parameters, "UniqueID"); plist_dict_set_item(request, "BMU,UniqueID", plist_new_uint(u64val)); char *comp_name = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (iter) { node = NULL; comp_name = NULL; plist_dict_next_item(manifest_node, iter, &comp_name, &node); if (comp_name == NULL) { node = NULL; break; } if (strncmp(comp_name, "BMU,", 4) == 0) { plist_t manifest_entry = plist_copy(node); /* handle RestoreRequestRules */ plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); if (rules) { debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); } /* Make sure we have a Digest key for Trusted items even if empty */ plist_t node = plist_dict_get_item(manifest_entry, "Trusted"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t trusted; plist_get_bool_val(node, &trusted); if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) { debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); } } plist_dict_remove_item(manifest_entry, "Info"); /* finally add entry to request */ plist_dict_set_item(request, comp_name, manifest_entry); } free(comp_name); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; } static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response) { size_t total = size * nmemb; if (total != 0) { response->content = realloc(response->content, response->length + total + 1); memcpy(response->content + response->length, data, total); response->content[response->length + total] = '\0'; response->length += total; } return total; } plist_t tss_request_send(plist_t tss_request, const char* server_url_string) { if (idevicerestore_debug) { debug_plist(tss_request); } char* request = NULL; int status_code = -1; int retry = 0; int max_retries = 15; unsigned int size = 0; char curl_error_message[CURL_ERROR_SIZE]; const char* urls[6] = { "https://gs.apple.com/TSS/controller?action=2", "https://17.171.36.30/TSS/controller?action=2", "https://17.151.36.30/TSS/controller?action=2", "http://gs.apple.com/TSS/controller?action=2", "http://17.171.36.30/TSS/controller?action=2", "http://17.151.36.30/TSS/controller?action=2" }; plist_to_xml(tss_request, &request, &size); tss_response* response = NULL; memset(curl_error_message, '\0', CURL_ERROR_SIZE); while (retry++ < max_retries) { response = NULL; CURL* handle = curl_easy_init(); if (handle == NULL) { break; } struct curl_slist* header = NULL; header = curl_slist_append(header, "Cache-Control: no-cache"); header = curl_slist_append(header, "Content-type: text/xml; charset=\"utf-8\""); header = curl_slist_append(header, "Expect:"); response = malloc(sizeof(tss_response)); if (response == NULL) { fprintf(stderr, "Unable to allocate sufficent memory\n"); return NULL; } response->length = 0; response->content = malloc(1); response->content[0] = '\0'; /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&tss_write_callback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request); curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING); curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request)); if (server_url_string) { curl_easy_setopt(handle, CURLOPT_URL, server_url_string); } else { int url_index = (retry - 1) % 6; curl_easy_setopt(handle, CURLOPT_URL, urls[url_index]); info("Request URL set to %s\n", urls[url_index]); } info("Sending TSS request attempt %d... ", retry); curl_easy_perform(handle); curl_slist_free_all(header); curl_easy_cleanup(handle); if (strstr(response->content, "MESSAGE=SUCCESS")) { status_code = 0; info("response successfully received\n"); break; } if (response->length > 0) { error("TSS server returned: %s\n", response->content); } char* status = strstr(response->content, "STATUS="); if (status) { sscanf(status+7, "%d&%*s", &status_code); } if (status_code == -1) { error("%s\n", curl_error_message); // no status code in response. retry free(response->content); free(response); response = NULL; sleep(2); continue; } else if (status_code == 8) { // server error (invalid bb request?) break; } else if (status_code == 49) { // server error (invalid bb data, e.g. BbSNUM?) break; } else if (status_code == 69 || status_code == 94) { // This device isn't eligible for the requested build. break; } else if (status_code == 100) { // server error, most likely the request was malformed break; } else if (status_code == 126) { // An internal error occured, most likely the request was malformed break; } else { error("ERROR: tss_send_request: Unhandled status code %d\n", status_code); } } if (status_code != 0) { if (response && strstr(response->content, "MESSAGE=") != NULL) { char* message = strstr(response->content, "MESSAGE=") + strlen("MESSAGE="); error("ERROR: TSS request failed (status=%d, message=%s)\n", status_code, message); } else { error("ERROR: TSS request failed: %s (status=%d)\n", curl_error_message, status_code); } free(request); if (response) free(response->content); if (response) free(response); return NULL; } char* tss_data = strstr(response->content, "content); free(response); return NULL; } uint32_t tss_size = 0; plist_t tss_response = NULL; tss_size = response->length - (tss_data - response->content); plist_from_xml(tss_data, tss_size, &tss_response); free(response->content); free(response); if (idevicerestore_debug) { debug_plist(tss_response); } free(request); return tss_response; } static int tss_response_get_data_by_key(plist_t response, const char* name, unsigned char** buffer, unsigned int* length) { plist_t node = plist_dict_get_item(response, name); if (!node || plist_get_node_type(node) != PLIST_DATA) { debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, name); return -1; } char *data = NULL; uint64_t len = 0; plist_get_data_val(node, &data, &len); if (data) { *length = (unsigned int)len; *buffer = (unsigned char*)data; return 0; } else { error("ERROR: Unable to get %s data from TSS response\n", name); return -1; } } int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { return tss_response_get_data_by_key(response, "ApImg4Ticket", ticket, length); } int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { return tss_response_get_data_by_key(response, "APTicket", ticket, length); } int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length) { return tss_response_get_data_by_key(response, "BBTicket", ticket, length); } int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path) { char* path_string = NULL; plist_t path_node = NULL; plist_t entry_node = NULL; *path = NULL; entry_node = plist_dict_get_item(response, entry); if (!entry_node || plist_get_node_type(entry_node) != PLIST_DICT) { debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry); return -1; } path_node = plist_dict_get_item(entry_node, "Path"); if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) { debug("NOTE: Unable to find %s path in TSS entry\n", entry); return -1; } plist_get_string_val(path_node, &path_string); *path = path_string; return 0; } int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob) { uint32_t i = 0; uint32_t tss_size = 0; uint64_t blob_size = 0; char* entry_key = NULL; char* blob_data = NULL; char* entry_path = NULL; plist_t tss_entry = NULL; plist_t blob_node = NULL; plist_t path_node = NULL; plist_dict_iter iter = NULL; *blob = NULL; plist_dict_new_iter(tss, &iter); tss_size = plist_dict_get_size(tss); for (i = 0; i < tss_size; i++) { plist_dict_next_item(tss, iter, &entry_key, &tss_entry); if (entry_key == NULL) break; if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) { continue; } path_node = plist_dict_get_item(tss_entry, "Path"); if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) { error("ERROR: Unable to find TSS path node in entry %s\n", entry_key); free(iter); return -1; } plist_get_string_val(path_node, &entry_path); if (strcmp(path, entry_path) == 0) { blob_node = plist_dict_get_item(tss_entry, "Blob"); if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) { error("ERROR: Unable to find TSS blob node in entry %s\n", entry_key); free(iter); return -1; } plist_get_data_val(blob_node, &blob_data, &blob_size); break; } free(entry_key); } free(iter); if (blob_data == NULL || blob_size <= 0) { return -1; } *blob = (unsigned char*)blob_data; return 0; } int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob) { uint64_t blob_size = 0; char* blob_data = NULL; plist_t blob_node = NULL; plist_t tss_entry = NULL; *blob = NULL; tss_entry = plist_dict_get_item(response, entry); if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) { debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry); return -1; } blob_node = plist_dict_get_item(tss_entry, "Blob"); if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) { error("ERROR: Unable to find blob in %s entry\n", entry); return -1; } plist_get_data_val(blob_node, &blob_data, &blob_size); *blob = (unsigned char*)blob_data; return 0; } idevicerestore-1.0.0/src/tss.h000066400000000000000000000057231367173617500163240ustar00rootroot00000000000000/* * tss.h * Definitions for communicating with Apple's TSS server. * * Copyright (c) 2013 Martin Szulecki. All Rights Reserved. * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. * Copyright (c) 2010 Joshua Hill. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef IDEVICERESTORE_TSS_H #define IDEVICERESTORE_TSS_H #ifdef __cplusplus extern "C" { #endif #include /* parameters */ int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity); /* request */ plist_t tss_request_new(plist_t overrides); int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name); int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name); int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides); int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters); int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters); /* i/o */ plist_t tss_request_send(plist_t request, const char* server_url_string); /* response */ int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length); int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length); int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length); int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path); int tss_response_get_blob_by_path(plist_t response, const char* path, unsigned char** blob); int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob); /* helpers */ char* ecid_to_string(uint64_t ecid); #ifdef __cplusplus } #endif #endif