pax_global_header00006660000000000000000000000064136717356120014525gustar00rootroot0000000000000052 comment=1b9d9c3cdd3ef2f38198a21c356352f13641482d libirecovery-1.0.0/000077500000000000000000000000001367173561200142215ustar00rootroot00000000000000libirecovery-1.0.0/.gitignore000066400000000000000000000005261367173561200162140ustar00rootroot00000000000000*.[oa] *~ *.po *.lo *.la autom4te.cache/* *.in */.deps/* m4/* *.dll *.so *.dylib *.patch aclocal.m4 config.h config.log config.sub config.guess config.status configure depcomp install-sh compile main ltmain.sh missing mkinstalldirs libtool *Makefile stamp-h1 src/.libs *.pc tools/.libs/* tools/irecovery .irecovery udev/39-libirecovery.rules libirecovery-1.0.0/COPYING000066400000000000000000000636411367173561200152660ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. 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 not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the 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 specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it!libirecovery-1.0.0/Makefile.am000066400000000000000000000003051367173561200162530ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src include tools udev EXTRA_DIST = \ README.md DISTCHECK_CONFIGURE_FLAGS = \ --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) libirecovery-1.0.0/NEWS000066400000000000000000000042061367173561200147220ustar00rootroot00000000000000Version 1.0.0 ~~~~~~~~~~~~~ * Changes: - Output basic device information after connecting - Remove obsolete "in-tree" copy of libusb-1.0 - Improve source code directory structure - Clean up and update of build system files - Major code refactoring - Add getters to retrieve device model information - Change exploit related wording to more accurate limera1n - Various improvements/fixes for win32 build - Add support for latest device models - Fix some memory leaks - Add requirement for autoconf 2.64 - Support IOKit on OSX (removes dependency on libusb) - Add DFU mode error handling - Add udev rules to allow non-root device access - Support ECID in hex or decimal format - Fix various compiler warnings - Add device add/remove event subscription interface - Convert README to markdown - Print PWND string if present - Add support for Apple T2 processors - Allow compiling without USB functionality - Support checkra1n DFU mode devices - Allow toggling debug level using "LIBIRECOVERY_DEBUG_LEVEL" environment variable - Add long argument name variants to irecovery - Add new "--version" argument to irecovery - Add support for Apple Watch 1st gen devices - Add support for missing iPad4,3 model and fix wrong device information iPad7 variants - Improve README.md with project description, installation, contributing and usage sections - Rename library and all related files by adding an API version resulting in "libirecovery-1.0" Version 0.1.1 ~~~~~~~~~~~~~ * Changes: - Add serial number and imei getters - Improve USB communication stability - Add support for WTF mode - Add option to target device by ECID - Add nonce getter - Improve win32 device detection and mingw compatibility - Add support for new device models - Switch to autotools build system instead of plain Makefile - Expose control and bulk transfer methods in public interface - Improve maintainability of device model information - Change license to LGPL 2.1 Version 0.1.0 ~~~~~~~~~~~~~ * Changes: - Implement initial interface and device communication - Add basic irecovery tool - Setup build system libirecovery-1.0.0/README.md000066400000000000000000000065321367173561200155060ustar00rootroot00000000000000# libirecovery *The libirecovery library allows communication with iBoot/iBSS of iOS devices via USB.* ## Features libirecovery is a cross-platform library which implements communication to iBoot/iBSS found on Apple's iOS devices via USB. A command-line utility named `irecovery` is also provided. This is a fork of an older version from former openjailbreak.org and is meant to be used with [idevicerestore](https://github.com/libimobiledevice/idevicerestore.git/) from the [libimobiledevice](https://github.com/libimobiledevice/) project. ## Installation / Getting started ### Debian / Ubuntu Linux First install all required dependencies and build tools: ```shell sudo apt-get install \ build-essential \ checkinstall \ git \ autoconf \ automake \ libtool-bin \ libreadline-dev \ libusb-1.0-0-dev ``` Then clone the actual project repository: ```shell git clone https://github.com/libimobiledevice/libirecovery.git cd libirecovery ``` Now you can build and install it: ```shell ./autogen.sh make sudo make install ``` ## Usage First of all attach your device to your machine. Make sure your device is not in normal mode. You can use the `ideviceenterrecovery` application from [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice.git/) to let your device boot into recovery mode if you need it. Then simply run: ```shell irecovery --shell ``` This connects to your device and opens a simple shell to interact with the device. For instance to make your device boot into normal mode again use: ```shell setenv auto-boot true reboot ``` Please consult the usage information or manual page for a full documentation of available command line options: ```shell irecovery --help man irecovery ``` ## Contributing We welcome contributions from anyone and are grateful for every pull request! If you'd like to contribute, please fork the `master` branch, change, commit and send a pull request for review. Once approved it can be merged into the main code base. If you plan to contribute larger changes or a major refactoring, please create a ticket first to discuss the idea upfront to ensure less effort for everyone. Please make sure your contribution adheres to: * Try to follow the code style of the project * Commit messages should describe the change well without being to short * Try to split larger changes into individual commits of a common domain * Use your real name and a valid email address for your commits We are still working on the guidelines so bear with us! ## Links * Homepage: https://libimobiledevice.org/ * Repository: https://git.libimobiledevice.org/libirecovery.git * Repository (Mirror): https://github.com/libimobiledevice/libirecovery.git * Issue Tracker: https://github.com/libimobiledevice/libirecovery/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 v2.1](https://www.gnu.org/licenses/lgpl-2.1.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 library and has not been authorized, sponsored, or otherwise approved by Apple Inc. README Updated on: 2020-06-14 libirecovery-1.0.0/autogen.sh000077500000000000000000000005551367173561200162270ustar00rootroot00000000000000#!/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 libirecovery-1.0.0/configure.ac000066400000000000000000000136561367173561200165220ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.64) AC_INIT([libirecovery], [1.0.0], [https://github.com/libimobiledevice/libirecovery/issues],, [https://libimobiledevice.org]) AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) dnl libtool versioning # +1 : 0 : +1 == adds new functions to the interface # +1 : 0 : 0 == changes or removes functions (changes include both # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE LIBIRECOVERY_SO_VERSION=3:0:0 dnl Minimum package versions LIBUSB_VERSION=1.0.3 AC_SUBST(LIBIRECOVERY_SO_VERSION) # Checks for programs. AC_PROG_CC AC_PROG_CXX AM_PROG_CC_C_O AC_PROG_LIBTOOL # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdint.h stdlib.h string.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_CHECK_FUNCS([strdup strerror strndup malloc realloc calloc]) # Checks for libraries. AC_CHECK_HEADERS([readline/readline.h], [], [AC_MSG_ERROR([Please install readline development headers])] ) # Check additional platform flags AC_MSG_CHECKING([for platform-specific build settings]) case ${host_os} in darwin*) AC_MSG_RESULT([${host_os}]) AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [ AC_CHECK_HEADER(IOKit/usb/IOUSBLib.h, [ GLOBAL_LDFLAGS+=" -framework IOKit -framework CoreFoundation" have_iokit=yes ], []) ], []) AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])]) ;; mingw32*) AC_MSG_RESULT([${host_os}]) GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lmsvcrt -lsetupapi" win32=true ;; cygwin*) AC_MSG_RESULT([${host_os}]) CC=gcc-3 CFLAGS+=" -mno-cygwin" GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lmsvcrt -lsetupapi" win32=true ;; *) AC_MSG_RESULT([${host_os}]) AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build $PACKAGE])]) ;; esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) AC_ARG_WITH([dummy], [AS_HELP_STRING([--with-dummy], [Use no USB driver at all [default=no]. This is only useful if you just want to query the device list by product type or hardware model. All other operations are no-ops or will return IRECV_E_UNSUPPORTED.])], [], [with_dummy=no]) AS_IF([test "x$have_iokit" = "xyes"], [ AC_ARG_WITH([iokit], [AS_HELP_STRING([--with-iokit], [Use IOKit instead of libusb on OS X [default=yes]])], [], [with_iokit=yes]) ] ) AS_IF([test "x$with_dummy" = "xyes"], [ AC_DEFINE(USE_DUMMY, 1, [Define if we are using dummy USB driver]) USB_BACKEND="dummy" ], [ AS_IF([test "x$with_iokit" = "xyes" && test "x$have_iokit" = "xyes"], [ AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit]) USB_BACKEND="IOKit" ], [ AS_IF([test "x$win32" = "xtrue"], [ USB_BACKEND="win32 native (setupapi)" ], [ PKG_CHECK_MODULES(libusb, libusb-1.0 >= $LIBUSB_VERSION) USB_BACKEND="libusb `$PKG_CONFIG --modversion libusb-1.0`" LIBUSB_REQUIRED="libusb-1.0 >= $LIBUSB_VERSION" AC_SUBST(LIBUSB_REQUIRED) ]) ]) ]) AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden $PTHREAD_CFLAGS") GLOBAL_LDFLAGS+=" $PTHREAD_LIBS" AC_SUBST(GLOBAL_CFLAGS) AC_SUBST(GLOBAL_LDFLAGS) case "$GLOBAL_CFLAGS" in *-fvisibility=hidden*) AC_DEFINE([HAVE_FVISIBILITY], [1], [Define if compiled with -fvisibility=hidden]) esac # check for large file support AC_SYS_LARGEFILE AC_ARG_WITH([udev], AS_HELP_STRING([--with-udev], [Configure and install udev rules file for DFU/Recovery mode devices]), [], [if $($PKG_CONFIG --exists udev); then with_udev=yes; else with_udev=no; fi]) AC_ARG_WITH([udevrulesdir], AS_HELP_STRING([--with-udevrulesdir=DIR], [Directory for udev rules (implies --with-udev)]), [with_udev=yes], [with_udevrulesdir=auto]) AC_ARG_WITH([udevrule], AS_HELP_STRING([--with-udevrule="RULE"], [udev activation rule (implies --with-udev)]), [with_udev=yes], [with_udevrule=auto]) if test "x$with_udev" = "xyes"; then if test "x$with_udevrule" = "xauto"; then for I in plugdev storage disk staff; do if grep $I /etc/group >/dev/null; then USEGROUP=$I break fi done if test "x$USEGROUP" != "x"; then if ! groups |grep $USEGROUP >/dev/null; then AC_MSG_WARN([The group '$USEGROUP' was determined to be used for the udev rule, but the current user is not member of this group.]) fi else AC_MSG_ERROR([Could not determine an appropriate user group for the udev activation rule. Please manually specify a udev activation rule using --with-udevrule= Example: --with-udevrule="OWNER=\\"root\\", GROUP=\\"myusergroup\\", MODE=\\"0660\\""]) fi with_udevrule="OWNER=\"root\", GROUP=\"$USEGROUP\", MODE=\"0660\"" fi if test "x$with_udevrulesdir" = "xauto"; then udevdir=$($PKG_CONFIG --silence-errors --variable=udevdir udev) if test "x$udevdir" != "x"; then with_udevrulesdir=$udevdir"/rules.d" else with_udevrulesdir="\${prefix}/lib/udev/rules.d" AC_MSG_WARN([Could not determine default udev rules directory. Using $with_udevrulesdir.]) fi fi AC_SUBST([udev_activation_rule], [$with_udevrule]) AC_SUBST([udevrulesdir], [$with_udevrulesdir]) fi AM_CONDITIONAL(WITH_UDEV, test "x$with_udev" = "xyes") m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_OUTPUT([ Makefile src/Makefile src/libirecovery-1.0.pc include/Makefile tools/Makefile udev/Makefile ]) echo " Configuration for $PACKAGE $VERSION: ------------------------------------------- Install prefix: .........: $prefix USB backend: ............: $USB_BACKEND Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. " libirecovery-1.0.0/include/000077500000000000000000000000001367173561200156445ustar00rootroot00000000000000libirecovery-1.0.0/include/Makefile.am000066400000000000000000000000541367173561200176770ustar00rootroot00000000000000nobase_dist_include_HEADERS = libirecovery.hlibirecovery-1.0.0/include/libirecovery.h000066400000000000000000000145361367173561200205240ustar00rootroot00000000000000/* * libirecovery.h * Communication to iBoot/iBSS on Apple iOS devices via USB * * Copyright (c) 2012-2019 Nikias Bassen * Copyright (c) 2012-2013 Martin Szulecki * Copyright (c) 2010 Chronic-Dev Team * Copyright (c) 2010 Joshua Hill * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * 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. */ #ifndef LIBIRECOVERY_H #define LIBIRECOVERY_H #ifdef __cplusplus extern "C" { #endif #include enum irecv_mode { IRECV_K_RECOVERY_MODE_1 = 0x1280, IRECV_K_RECOVERY_MODE_2 = 0x1281, IRECV_K_RECOVERY_MODE_3 = 0x1282, IRECV_K_RECOVERY_MODE_4 = 0x1283, IRECV_K_WTF_MODE = 0x1222, IRECV_K_DFU_MODE = 0x1227 }; typedef enum { IRECV_E_SUCCESS = 0, IRECV_E_NO_DEVICE = -1, IRECV_E_OUT_OF_MEMORY = -2, IRECV_E_UNABLE_TO_CONNECT = -3, IRECV_E_INVALID_INPUT = -4, IRECV_E_FILE_NOT_FOUND = -5, IRECV_E_USB_UPLOAD = -6, IRECV_E_USB_STATUS = -7, IRECV_E_USB_INTERFACE = -8, IRECV_E_USB_CONFIGURATION = -9, IRECV_E_PIPE = -10, IRECV_E_TIMEOUT = -11, IRECV_E_UNSUPPORTED = -254, IRECV_E_UNKNOWN_ERROR = -255 } irecv_error_t; typedef enum { IRECV_RECEIVED = 1, IRECV_PRECOMMAND = 2, IRECV_POSTCOMMAND = 3, IRECV_CONNECTED = 4, IRECV_DISCONNECTED = 5, IRECV_PROGRESS = 6 } irecv_event_type; typedef struct { int size; const char* data; double progress; irecv_event_type type; } irecv_event_t; struct irecv_device { const char* product_type; const char* hardware_model; unsigned int board_id; unsigned int chip_id; const char* display_name; }; typedef struct irecv_device* irecv_device_t; struct irecv_device_info { unsigned int cpid; unsigned int cprv; unsigned int cpfm; unsigned int scep; unsigned int bdid; unsigned long long ecid; unsigned int ibfl; char* srnm; char* imei; char* srtg; char* serial_string; unsigned char* ap_nonce; unsigned int ap_nonce_size; unsigned char* sep_nonce; unsigned int sep_nonce_size; }; typedef enum { IRECV_DEVICE_ADD = 1, IRECV_DEVICE_REMOVE = 2 } irecv_device_event_type; typedef struct { irecv_device_event_type type; enum irecv_mode mode; struct irecv_device_info *device_info; } irecv_device_event_t; typedef struct irecv_client_private irecv_client_private; typedef irecv_client_private* irecv_client_t; /* library */ void irecv_set_debug_level(int level); const char* irecv_strerror(irecv_error_t error); void irecv_init(void); /* deprecated: libirecovery has constructor now */ void irecv_exit(void); /* deprecated: libirecovery has destructor now */ /* device connectivity */ irecv_error_t irecv_open_with_ecid(irecv_client_t* client, unsigned long long ecid); irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, unsigned long long ecid, int attempts); irecv_error_t irecv_reset(irecv_client_t client); irecv_error_t irecv_close(irecv_client_t client); irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause); /* misc */ irecv_error_t irecv_receive(irecv_client_t client); irecv_error_t irecv_execute_script(irecv_client_t client, const char* script); irecv_error_t irecv_reset_counters(irecv_client_t client); irecv_error_t irecv_finish_transfer(irecv_client_t client); irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client); /* usb helpers */ irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration); irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface); int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout); int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); /* events */ typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data); typedef struct irecv_device_event_context* irecv_device_event_context_t; irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data); irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context); typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event); irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data); irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type); /* I/O */ irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfu_notify_finished); irecv_error_t irecv_send_command(irecv_client_t client, const char* command); irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfu_notify_finished); irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length); /* commands */ irecv_error_t irecv_saveenv(irecv_client_t client); irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value); irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value); irecv_error_t irecv_reboot(irecv_client_t client); irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value); /* device information */ irecv_error_t irecv_get_mode(irecv_client_t client, int* mode); const struct irecv_device_info* irecv_get_device_info(irecv_client_t client); /* device database queries */ irecv_device_t irecv_devices_get_all(void); irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device); irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device); irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device); #ifdef __cplusplus } #endif #endif libirecovery-1.0.0/m4/000077500000000000000000000000001367173561200145415ustar00rootroot00000000000000libirecovery-1.0.0/m4/as-compiler-flag.m4000066400000000000000000000027371367173561200201360ustar00rootroot00000000000000dnl as-compiler-flag.m4 0.1.0 dnl autostars m4 macro for detection of compiler flags dnl David Schleef dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $ dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) dnl Tries to compile with the given CFLAGS. dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, dnl and ACTION-IF-NOT-ACCEPTED otherwise. AC_DEFUN([AS_COMPILER_FLAG], [ AC_MSG_CHECKING([to see if compiler understands $1]) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then m4_ifvaln([$2],[$2]) true else m4_ifvaln([$3],[$3]) true fi AC_MSG_RESULT([$flag_ok]) ]) dnl AS_COMPILER_FLAGS(VAR, FLAGS) dnl Tries to compile with the given CFLAGS. AC_DEFUN([AS_COMPILER_FLAGS], [ list=$2 flags_supported="" flags_unsupported="" AC_MSG_CHECKING([for supported compiler flags]) for each in $list do save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $each" AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then flags_supported="$flags_supported $each" else flags_unsupported="$flags_unsupported $each" fi done AC_MSG_RESULT([$flags_supported]) if test "X$flags_unsupported" != X ; then AC_MSG_WARN([unsupported compiler flags: $flags_unsupported]) fi $1="$$1 $flags_supported" ]) libirecovery-1.0.0/m4/ax_pthread.m4000066400000000000000000000505201367173561200171240ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 23 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD libirecovery-1.0.0/src/000077500000000000000000000000001367173561200150105ustar00rootroot00000000000000libirecovery-1.0.0/src/Makefile.am000066400000000000000000000010641367173561200170450ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = \ $(GLOBAL_CFLAGS) \ $(LFS_CFLAGS) \ $(libusb_CFLAGS) AM_LDFLAGS = \ $(GLOBAL_LDFLAGS) \ $(libusb_LIBS) lib_LTLIBRARIES = libirecovery-1.0.la libirecovery_1_0_la_CFLAGS = $(AM_CFLAGS) libirecovery_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIRECOVERY_SO_VERSION) -no-undefined libirecovery_1_0_la_SOURCES = \ libirecovery.c \ utils.c utils.h \ thread.c thread.h if WIN32 libirecovery_1_0_la_LDFLAGS += -avoid-version endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libirecovery-1.0.pc libirecovery-1.0.0/src/libirecovery-1.0.pc.in000066400000000000000000000004561367173561200207400ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: @PACKAGE_NAME@ Description: A library to communicate with iBoot/iBSS on iOS devices via USB Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lirecovery-1.0 Cflags: -I${includedir} Requires.private: @LIBUSB_REQUIRED@ libirecovery-1.0.0/src/libirecovery.c000066400000000000000000002650531367173561200176650ustar00rootroot00000000000000/* * libirecovery.c * Communication to iBoot/iBSS on Apple iOS devices via USB * * Copyright (c) 2011-2020 Nikias Bassen * Copyright (c) 2012-2020 Martin Szulecki * Copyright (c) 2010 Chronic-Dev Team * Copyright (c) 2010 Joshua Hill * Copyright (c) 2008-2011 Nicolas Haunold * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * 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. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT #include #include #if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102)) #define HAVE_LIBUSB_HOTPLUG_API 1 #endif #else #include #include #include #include #endif #else #define WIN32_LEAN_AND_MEAN #include #include #ifndef sleep #define sleep(n) Sleep(1000 * n) #endif #endif #endif #ifdef WIN32 #define IRECV_API __declspec( dllexport ) #else #ifdef HAVE_FVISIBILITY #define IRECV_API __attribute__((visibility("default"))) #else #define IRECV_API #endif #endif #include "libirecovery.h" #include "utils.h" #include "thread.h" struct irecv_client_private { int debug; int usb_config; int usb_interface; int usb_alt_interface; unsigned int mode; struct irecv_device_info device_info; #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT libusb_device_handle* handle; #else IOUSBDeviceInterface320 **handle; IOUSBInterfaceInterface300 **usbInterface; #endif #else HANDLE handle; HANDLE hDFU; HANDLE hIB; LPSTR iBootPath; LPSTR DfuPath; #endif irecv_event_cb_t progress_callback; irecv_event_cb_t received_callback; irecv_event_cb_t connected_callback; irecv_event_cb_t precommand_callback; irecv_event_cb_t postcommand_callback; irecv_event_cb_t disconnected_callback; #endif }; #define USB_TIMEOUT 10000 #define APPLE_VENDOR_ID 0x05AC #define BUFFER_SIZE 0x1000 #define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) static int libirecovery_debug = 0; #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT static libusb_context* libirecovery_context = NULL; #endif #endif #endif static struct irecv_device irecv_devices[] = { /* iPhone */ { "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" }, { "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" }, { "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" }, { "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" }, { "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" }, { "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" }, { "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" }, { "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" }, { "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" }, { "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" }, { "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" }, { "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" }, { "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" }, { "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" }, { "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" }, { "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" }, { "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" }, { "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" }, { "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" }, { "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE" }, { "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE" }, { "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" }, { "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" }, { "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" }, { "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" }, { "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" }, { "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" }, { "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" }, { "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" }, { "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" }, { "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" }, { "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" }, { "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" }, { "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" }, { "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" }, { "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" }, { "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" }, { "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" }, { "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2020)" }, /* iPod */ { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" }, { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" }, { "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" }, { "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" }, { "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" }, { "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" }, { "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" }, /* iPad */ { "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" }, { "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" }, { "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" }, { "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" }, { "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" }, { "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad Mini (WiFi)" }, { "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad Mini (GSM)" }, { "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad Mini (Global)" }, { "iPad3,1", "j1ap", 0x00, 0x8945, "iPad 3 (WiFi)" }, { "iPad3,2", "j2ap", 0x02, 0x8945, "iPad 3 (CDMA)" }, { "iPad3,3", "j2aap", 0x04, 0x8945, "iPad 3 (GSM)" }, { "iPad3,4", "p101ap", 0x00, 0x8955, "iPad 4 (WiFi)" }, { "iPad3,5", "p102ap", 0x02, 0x8955, "iPad 4 (GSM)" }, { "iPad3,6", "p103ap", 0x04, 0x8955, "iPad 4 (Global)" }, { "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" }, { "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" }, { "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" }, { "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad Mini 2 (WiFi)" }, { "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad Mini 2 (Cellular)" }, { "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad Mini 2 (China)" }, { "iPad4,7", "j85map", 0x32, 0x8960, "iPad Mini 3 (WiFi)" }, { "iPad4,8", "j86map", 0x34, 0x8960, "iPad Mini 3 (Cellular)" }, { "iPad4,9", "j87map", 0x36, 0x8960, "iPad Mini 3 (China)" }, { "iPad5,1", "j96ap", 0x08, 0x7000, "iPad Mini 4 (WiFi)" }, { "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad Mini 4 (Cellular)" }, { "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" }, { "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" }, { "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7in (WiFi)" }, { "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7in (Cellular)" }, { "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9in (WiFi)" }, { "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9in (Cellular)" }, { "iPad6,11", "j71sap", 0x10, 0x8000, "iPad 5 (WiFi)" }, { "iPad6,11", "j71tap", 0x10, 0x8003, "iPad 5 (WiFi)" }, { "iPad6,12", "j72sap", 0x12, 0x8000, "iPad 5 (Cellular)" }, { "iPad6,12", "j72tap", 0x12, 0x8003, "iPad 5 (Cellular)" }, { "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 2 12.9in (WiFi)" }, { "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 2 12.9in (Cellular)" }, { "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5in (WiFi)" }, { "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5in (Cellular)" }, { "iPad7,5", "j71bap", 0x18, 0x8010, "iPad 6 (WiFi)" }, { "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad 6 (Cellular)" }, { "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad 7 (WiFi)" }, { "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad 7 (Cellular)" }, { "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 3 11in (WiFi)" }, { "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 3 11in (WiFi, 1TB)" }, { "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 3 11in (Cellular)" }, { "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 3 11in (Cellular, 1TB)" }, { "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 3 12.9in (WiFi)" }, { "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 3 12.9in (WiFi, 1TB)" }, { "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 3 12.9in (Cellular)" }, { "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 3 12.9in (Cellular, 1TB)" }, { "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 4 11in (WiFi)" }, { "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 4 11in (Cellular)" }, { "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 4 12.9in (WiFi)" }, { "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 4 12.9in (Cellular)" }, { "iPad11,1", "j210ap", 0x14, 0x8020, "iPad Mini 5 (WiFi)" }, { "iPad11,2", "j211ap", 0x16, 0x8020, "iPad Mini 5 (Cellular)" }, { "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air 3 (WiFi)" }, { "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air 3 (Celluar)" }, /* Apple TV */ { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" }, { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" }, { "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" }, { "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" }, { "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" }, /* Apple Watch */ { "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" }, { "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" }, /* Apple T2 Coprocessor */ { "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" }, { "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" }, { "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" }, { "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" }, { "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" }, { "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" }, { "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" }, { "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" }, { "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" }, { "iBridge2,14", "j152f", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" }, { NULL, NULL, -1, -1, NULL } }; #ifndef USE_DUMMY static unsigned int crc32_lookup_t1[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, }; #define crc32_step(a,b) \ a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8)) static THREAD_T th_event_handler = THREAD_T_NULL; struct collection listeners; static mutex_t listener_mutex; struct collection devices; static mutex_t device_mutex; #ifndef WIN32 #ifdef HAVE_IOKIT static CFRunLoopRef iokit_runloop = NULL; #else static libusb_context* irecv_hotplug_ctx = NULL; #endif #endif static void _irecv_init(void) { char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL"); if (dbglvl) { libirecovery_debug = strtol(dbglvl, NULL, 0); irecv_set_debug_level(libirecovery_debug); } #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT libusb_init(&libirecovery_context); #endif #endif collection_init(&listeners); mutex_init(&listener_mutex); #endif } static void _irecv_deinit(void) { #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT if (libirecovery_context != NULL) { libusb_exit(libirecovery_context); libirecovery_context = NULL; } #endif #endif collection_free(&listeners); mutex_destroy(&listener_mutex); #endif } static thread_once_t init_once = THREAD_ONCE_INIT; static thread_once_t deinit_once = THREAD_ONCE_INIT; #ifdef WIN32 BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: thread_once(&init_once, _irecv_init); break; case DLL_PROCESS_DETACH: thread_once(&deinit_once, _irecv_deinit); break; default: break; } return 1; } #else static void __attribute__((constructor)) libirecovery_initialize(void) { thread_once(&init_once, _irecv_init); } static void __attribute__((destructor)) libirecovery_deinitialize(void) { thread_once(&deinit_once, _irecv_deinit); } #endif #ifdef HAVE_IOKIT static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { IOReturn result; IOUSBDevRequest request; unsigned char descriptor[256]; request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); request.bRequest = kUSBRqGetDescriptor; request.wValue = (kUSBStringDesc << 8); // | desc_index; request.wIndex = 0; // All languages 0x409; // language request.wLength = sizeof(descriptor) - 1; request.pData = descriptor; request.wLenDone = 0; result = (*client->handle)->DeviceRequest(client->handle, &request); if (result == kIOReturnNoDevice) return IRECV_E_NO_DEVICE; if (result == kIOReturnNotOpen) return IRECV_E_USB_STATUS; if (result != kIOReturnSuccess) return IRECV_E_UNKNOWN_ERROR; if (descriptor[0] >= 4) { // && descriptor[2] == 0x9 && descriptor[3] == 0x4) { request.wValue = (kUSBStringDesc << 8) | desc_index; request.wIndex = descriptor[2] + (descriptor[3] << 8); request.wLenDone = 0; result = (*client->handle)->DeviceRequest(client->handle, &request); if (result == kIOReturnNoDevice) return IRECV_E_NO_DEVICE; if (result == kIOReturnNotOpen) return IRECV_E_USB_STATUS; if (result != kIOReturnSuccess) return IRECV_E_UNKNOWN_ERROR; int i = 2, j = 0; for ( ; i < descriptor[0]; i += 2, j += 1) { buffer[j] = descriptor[i]; } buffer[j] = 0; return request.wLenDone; } return IRECV_E_UNKNOWN_ERROR; } #endif static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { #ifndef WIN32 #ifdef HAVE_IOKIT return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size); #else return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size); #endif #else irecv_error_t ret; unsigned short langid = 0; unsigned char data[255]; int di, si; memset(data, 0, sizeof(data)); memset(buffer, 0, size); ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), USB_TIMEOUT); if (ret < 0) return ret; if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR; for (di = 0, si = 2; si < data[0]; si += 2) { if (di >= (size - 1)) break; if (data[si + 1]) { /* high byte */ buffer[di++] = '?'; } else { buffer[di++] = data[si]; } } buffer[di] = 0; return di; #endif } static void irecv_load_device_info_from_iboot_string(irecv_client_t client, const char* iboot_string) { if (!client || !iboot_string) { return; } memset(&client->device_info, '\0', sizeof(struct irecv_device_info)); client->device_info.serial_string = strdup(iboot_string); char* ptr; ptr = strstr(iboot_string, "CPID:"); if (ptr != NULL) { sscanf(ptr, "CPID:%x", &client->device_info.cpid); } ptr = strstr(iboot_string, "CPRV:"); if (ptr != NULL) { sscanf(ptr, "CPRV:%x", &client->device_info.cprv); } ptr = strstr(iboot_string, "CPFM:"); if (ptr != NULL) { sscanf(ptr, "CPFM:%x", &client->device_info.cpfm); } ptr = strstr(iboot_string, "SCEP:"); if (ptr != NULL) { sscanf(ptr, "SCEP:%x", &client->device_info.scep); } ptr = strstr(iboot_string, "BDID:"); if (ptr != NULL) { sscanf(ptr, "BDID:%x", &client->device_info.bdid); } ptr = strstr(iboot_string, "ECID:"); if (ptr != NULL) { sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid); } ptr = strstr(iboot_string, "IBFL:"); if (ptr != NULL) { sscanf(ptr, "IBFL:%x", &client->device_info.ibfl); } char tmp[256]; tmp[0] = '\0'; ptr = strstr(iboot_string, "SRNM:["); if(ptr != NULL) { sscanf(ptr, "SRNM:[%s]", tmp); ptr = strrchr(tmp, ']'); if(ptr != NULL) { *ptr = '\0'; } client->device_info.srnm = strdup(tmp); } tmp[0] = '\0'; ptr = strstr(iboot_string, "IMEI:["); if(ptr != NULL) { sscanf(ptr, "IMEI:[%s]", tmp); ptr = strrchr(tmp, ']'); if(ptr != NULL) { *ptr = '\0'; } client->device_info.imei = strdup(tmp); } tmp[0] = '\0'; ptr = strstr(iboot_string, "SRTG:["); if(ptr != NULL) { sscanf(ptr, "SRTG:[%s]", tmp); ptr = strrchr(tmp, ']'); if(ptr != NULL) { *ptr = '\0'; } client->device_info.srtg = strdup(tmp); } } static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, unsigned char** nonce, unsigned int* nonce_size) { if (!client || !tag) { return; } char buf[255]; int len; *nonce = NULL; *nonce_size = 0; len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255); if (len < 0) { debug("%s: got length: %d\n", __func__, len); return; } buf[len] = 0; int taglen = strlen(tag); int nlen = 0; char* nonce_string = NULL; char* p = buf; char* colon = NULL; do { colon = strchr(p, ':'); if (!colon) break; if (colon-taglen < p) { break; } char *space = strchr(colon, ' '); if (strncmp(colon-taglen, tag, taglen) == 0) { p = colon+1; if (!space) { nlen = strlen(p); } else { nlen = space-p; } nonce_string = p; nlen/=2; break; } else { if (!space) { break; } else { p = space+1; } } } while (colon); if (nlen == 0) { debug("%s: WARNING: couldn't find tag %s in string %s\n", __func__, tag, buf); return; } unsigned char *nn = malloc(nlen); if (!nn) { return; } int i = 0; for (i = 0; i < nlen; i++) { int val = 0; if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) { nn[i] = (unsigned char)val; } else { debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2)); break; } } if (i != nlen) { debug("%s: ERROR: unable to parse nonce\n", __func__); free(nn); return; } *nonce = nn; *nonce_size = nlen; } #ifdef WIN32 static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}}; static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; typedef struct usb_control_request { uint8_t bmRequestType; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; char data[]; } usb_control_request; irecv_error_t mobiledevice_openpipes(irecv_client_t client); void mobiledevice_closepipes(irecv_client_t client); irecv_error_t mobiledevice_connect(irecv_client_t* client, unsigned long long ecid); irecv_error_t mobiledevice_connect(irecv_client_t* client, unsigned long long ecid) { int found = 0; SP_DEVICE_INTERFACE_DATA currentInterface; HDEVINFO usbDevices; DWORD i; irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private)); memset(_client, 0, sizeof(struct irecv_client_private)); /* get DFU paths */ usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) { free(_client->DfuPath); _client->DfuPath = NULL; _client->handle = NULL; DWORD requiredSize = 0; PSP_DEVICE_INTERFACE_DETAIL_DATA details; SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { free(details); continue; } else { LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); free(details); _client->DfuPath = result; if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { mobiledevice_closepipes(_client); continue; } if (ecid == IRECV_K_WTF_MODE) { if (_client->mode != IRECV_K_WTF_MODE) { /* special ecid case, ignore !IRECV_K_WTF_MODE */ continue; } else { ecid = 0; } } if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) { /* we can't get ecid in WTF mode */ mobiledevice_closepipes(_client); continue; } char serial_str[256]; char *p = result + strlen(result) - 1; while (p-- && p > result) { if (*p == '\\' && (strncmp(p, "\\usb", 4) == 0)) { break; } } serial_str[0] = '\0'; if (!p || (sscanf(p, "\\usb#vid_%*04x&pid_%*04x#%s", serial_str) != 1) || (serial_str[0] == '\0')) { mobiledevice_closepipes(_client); continue; } p = strchr(serial_str, '#'); if (p) { *p = '\0'; } unsigned int j; for (j = 0; j < strlen(serial_str); j++) { if (serial_str[j] == '_') { serial_str[j] = ' '; } else { serial_str[j] = toupper(serial_str[j]); } } irecv_load_device_info_from_iboot_string(_client, serial_str); irecv_copy_nonce_with_tag(_client, "NONC", &_client->device_info.ap_nonce, &_client->device_info.ap_nonce_size); irecv_copy_nonce_with_tag(_client, "SNON", &_client->device_info.sep_nonce, &_client->device_info.sep_nonce_size); if (ecid != 0) { if (_client->device_info.ecid != ecid) { mobiledevice_closepipes(_client); continue; } debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid); } found = 1; break; } } SetupDiDestroyDeviceInfoList(usbDevices); if (found) { *client = _client; return IRECV_E_SUCCESS; } /* get iBoot path */ usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for(i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) { free(_client->iBootPath); _client->iBootPath = NULL; _client->handle = NULL; DWORD requiredSize = 0; PSP_DEVICE_INTERFACE_DETAIL_DATA details; SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) { free(details); continue; } else { LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD)); memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD)); free(details); _client->iBootPath = result; if (mobiledevice_openpipes(_client) != IRECV_E_SUCCESS) { mobiledevice_closepipes(_client); continue; } if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) { /* we can't get ecid in WTF mode */ mobiledevice_closepipes(_client); continue; } char serial_str[256]; char *p = result + strlen(result) - 1; while (p-- && p > result) { if (*p == '\\' && (strncmp(p, "\\usb", 4) == 0)) { break; } } serial_str[0] = '\0'; if (!p || (sscanf(p, "\\usb#vid_%*04x&pid_%*04x#%s", serial_str) != 1) || (serial_str[0] == '\0')) { mobiledevice_closepipes(_client); continue; } p = strchr(serial_str, '#'); if (p) { *p = '\0'; } unsigned int j; for (j = 0; j < strlen(serial_str); j++) { if (serial_str[j] == '_') { serial_str[j] = ' '; } else { serial_str[j] = toupper(serial_str[j]); } } irecv_load_device_info_from_iboot_string(_client, serial_str); irecv_copy_nonce_with_tag(_client, "NONC", &_client->device_info.ap_nonce, &_client->device_info.ap_nonce_size); irecv_copy_nonce_with_tag(_client, "SNON", &_client->device_info.sep_nonce, &_client->device_info.sep_nonce_size); if (ecid != 0) { if (_client->device_info.ecid != ecid) { mobiledevice_closepipes(_client); continue; } debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid); } found = 1; break; } } SetupDiDestroyDeviceInfoList(usbDevices); if (!found) { irecv_close(_client); return IRECV_E_UNABLE_TO_CONNECT; } *client = _client; return IRECV_E_SUCCESS; } irecv_error_t mobiledevice_openpipes(irecv_client_t client) { if (client->iBootPath && !(client->hIB = CreateFile(client->iBootPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { irecv_close(client); return IRECV_E_UNABLE_TO_CONNECT; } if (client->DfuPath && !(client->hDFU = CreateFile(client->DfuPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) { irecv_close(client); return IRECV_E_UNABLE_TO_CONNECT; } client->mode = 0; if (client->iBootPath == NULL) { if (strncmp(client->DfuPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { sscanf(client->DfuPath+21, "%x#", &client->mode); } client->handle = client->hDFU; } else { if (strncmp(client->iBootPath, "\\\\?\\usb#vid_05ac&pid_", 21) == 0) { sscanf(client->iBootPath+21, "%x#", &client->mode); } client->handle = client->hIB; } if (client->mode == 0) { irecv_close(client); return IRECV_E_UNABLE_TO_CONNECT; } return IRECV_E_SUCCESS; } void mobiledevice_closepipes(irecv_client_t client) { if (client->hDFU!=NULL) { CloseHandle(client->hDFU); client->hDFU = NULL; } if (client->hIB!=NULL) { CloseHandle(client->hIB); client->hIB = NULL; } } #endif #ifdef HAVE_IOKIT static void iokit_cfdictionary_set_short(CFMutableDictionaryRef dict, const void *key, SInt16 value) { CFNumberRef numberRef; numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &value); if (numberRef) { CFDictionarySetValue(dict, key, numberRef); CFRelease(numberRef); } } #endif static int check_context(irecv_client_t client) { if (client == NULL || client->handle == NULL) { return IRECV_E_NO_DEVICE; } return IRECV_E_SUCCESS; } #endif IRECV_API void irecv_init(void) { thread_once(&init_once, _irecv_init); } IRECV_API void irecv_exit(void) { thread_once(&deinit_once, _irecv_deinit); } #ifndef USE_DUMMY #ifdef HAVE_IOKIT static int iokit_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout) { IOReturn result; IOUSBDevRequestTO req; bzero(&req, sizeof(req)); req.bmRequestType = bm_request_type; req.bRequest = b_request; req.wValue = OSSwapLittleToHostInt16(w_value); req.wIndex = OSSwapLittleToHostInt16(w_index); req.wLength = OSSwapLittleToHostInt16(w_length); req.pData = data; req.noDataTimeout = timeout; req.completionTimeout = timeout; result = (*client->handle)->DeviceRequestTO(client->handle, &req); switch (result) { case kIOReturnSuccess: return req.wLenDone; case kIOReturnTimeout: return IRECV_E_TIMEOUT; case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT; case kIOReturnNotResponding: return IRECV_E_NO_DEVICE; case kIOReturnNoDevice: return IRECV_E_NO_DEVICE; default: return IRECV_E_UNKNOWN_ERROR; } } #else #ifdef __APPLE__ void dummy_callback(void) { } #endif #endif #endif IRECV_API int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else #ifndef WIN32 #ifdef HAVE_IOKIT return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout); #else return libusb_control_transfer(client->handle, bm_request_type, b_request, w_value, w_index, data, w_length, timeout); #endif #else DWORD count = 0; BOOL bRet; OVERLAPPED overlapped; if (data == NULL) w_length = 0; usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + w_length); packet->bmRequestType = bm_request_type; packet->bRequest = b_request; packet->wValue = w_value; packet->wIndex = w_index; packet->wLength = w_length; if (bm_request_type < 0x80 && w_length > 0) { memcpy(packet->data, data, w_length); } memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + w_length, packet, sizeof(usb_control_request) + w_length, NULL, &overlapped); WaitForSingleObject(overlapped.hEvent, timeout); bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE); CloseHandle(overlapped.hEvent); if (!bRet) { CancelIo(client->handle); free(packet); return -1; } count -= sizeof(usb_control_request); if (count > 0) { if (bm_request_type >= 0x80) { memcpy(data, packet->data, count); } } free(packet); return count; #endif #endif } #ifndef USE_DUMMY #ifdef HAVE_IOKIT static int iokit_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout) { IOReturn result; IOUSBInterfaceInterface300 **intf = client->usbInterface; UInt32 size = length; UInt8 transferDirection = endpoint & kUSBbEndpointDirectionMask; UInt8 numEndpoints; UInt8 pipeRef = 1; if (!intf) return IRECV_E_USB_INTERFACE; result = (*intf)->GetNumEndpoints(intf, &numEndpoints); if (result != kIOReturnSuccess || pipeRef > numEndpoints) return IRECV_E_USB_INTERFACE; // Just because result = (*intf)->GetPipeStatus(intf, pipeRef); switch (result) { case kIOReturnSuccess: break; case kIOReturnNoDevice: return IRECV_E_NO_DEVICE; case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT; default: return IRECV_E_USB_STATUS; } // Do the transfer if (transferDirection == kUSBEndpointDirectionIn) { result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout); if (result != kIOReturnSuccess) return IRECV_E_PIPE; *transferred = size; return IRECV_E_SUCCESS; } else { // IOUSBInterfaceClass::interfaceWritePipe (intf?, pipeRef==1, data, size=0x8000) result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout); if (result != kIOReturnSuccess) return IRECV_E_PIPE; *transferred = size; return IRECV_E_SUCCESS; } return IRECV_E_USB_INTERFACE; } #endif #endif IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int ret; #ifndef WIN32 #ifdef HAVE_IOKIT return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout); #else ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout); if (ret < 0) { libusb_clear_halt(client->handle, endpoint); } #endif #else if (endpoint==0x4) { ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL); } else { ret = 0; } ret = (ret==0) ? -1 : 0; #endif return ret; #endif } #ifndef USE_DUMMY #ifdef HAVE_IOKIT static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service) { IOReturn result; irecv_error_t error; irecv_client_t client; SInt32 score; UInt16 mode; UInt32 locationID; IOCFPlugInInterface **plug = NULL; CFStringRef serialString; client = (irecv_client_t) calloc( 1, sizeof(struct irecv_client_private)); // Create the plug-in result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score); if (result != kIOReturnSuccess) { IOObjectRelease(service); free(client); return IRECV_E_UNKNOWN_ERROR; } // Cache the serial string before discarding the service. The service object // has a cached copy, so a request to the hardware device is not required. char serial_str[256]; serial_str[0] = '\0'; serialString = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); if (serialString) { CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8); CFRelease(serialString); } irecv_load_device_info_from_iboot_string(client, serial_str); IOObjectRelease(service); // Create the device interface result = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&(client->handle)); IODestroyPlugInInterface(plug); if (result != kIOReturnSuccess) { free(client); return IRECV_E_UNKNOWN_ERROR; } (*client->handle)->GetDeviceProduct(client->handle, &mode); (*client->handle)->GetLocationID(client->handle, &locationID); client->mode = mode; debug("opening device %04x:%04x @ %#010x...\n", kAppleVendorID, client->mode, locationID); result = (*client->handle)->USBDeviceOpenSeize(client->handle); if (result != kIOReturnSuccess) { (*client->handle)->Release(client->handle); free(client); return IRECV_E_UNABLE_TO_CONNECT; } irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size); irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size); error = irecv_usb_set_configuration(client, 1); if (error != IRECV_E_SUCCESS) { free(client); return error; } // DFU mode has no endpoints, so no need to open the interface if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_WTF_MODE) { error = irecv_usb_set_interface(client, 0, 0); if (error != IRECV_E_SUCCESS) { free(client); return error; } } else { error = irecv_usb_set_interface(client, 0, 0); if (error != IRECV_E_SUCCESS) { free(client); return error; } if (client->mode > IRECV_K_RECOVERY_MODE_2) { error = irecv_usb_set_interface(client, 1, 1); if (error != IRECV_E_SUCCESS) { free(client); return error; } } } *pclient = client; return IRECV_E_SUCCESS; } static io_iterator_t iokit_usb_get_iterator_for_pid(UInt16 pid) { IOReturn result; io_iterator_t iterator; CFMutableDictionaryRef matchingDict; matchingDict = IOServiceMatching(kIOUSBDeviceClassName); iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID); iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pid); result = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iterator); if (result != kIOReturnSuccess) return IO_OBJECT_NULL; return iterator; } static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, unsigned long long ecid) { io_service_t service, ret_service; io_iterator_t iterator; CFStringRef usbSerial = NULL; CFStringRef ecidString = NULL; CFRange range; UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0}; UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, 0 }; UInt16 *pids = all_pids; int i; if (pclient == NULL) { debug("%s: pclient parameter is null\n", __func__); return IRECV_E_INVALID_INPUT; } if (ecid == IRECV_K_WTF_MODE) { /* special ecid case, ignore !IRECV_K_WTF_MODE */ pids = wtf_pids; ecid = 0; } if (ecid > 0) { ecidString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%llX"), ecid); if (ecidString == NULL) { debug("%s: failed to create ECID string\n", __func__); return IRECV_E_UNABLE_TO_CONNECT; } } *pclient = NULL; ret_service = IO_OBJECT_NULL; for (i = 0; (pids[i] > 0 && ret_service == IO_OBJECT_NULL) ; i++) { iterator = iokit_usb_get_iterator_for_pid(pids[i]); if (iterator) { while ((service = IOIteratorNext(iterator))) { if (ecid == 0) { ret_service = service; break; } usbSerial = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); if (usbSerial == NULL) { debug("%s: failed to create USB serial string property\n", __func__); IOObjectRelease(service); continue; } range = CFStringFind(usbSerial, ecidString, kCFCompareCaseInsensitive); if (range.location == kCFNotFound) { IOObjectRelease(service); } else { ret_service = service; break; } } if (usbSerial) { CFRelease(usbSerial); usbSerial = NULL; } IOObjectRelease(iterator); } } if (ecidString) CFRelease(ecidString); if (ret_service == IO_OBJECT_NULL) return IRECV_E_UNABLE_TO_CONNECT; return iokit_usb_open_service(pclient, ret_service); } #endif #endif IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, unsigned long long ecid) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int ret = IRECV_E_UNABLE_TO_CONNECT; if(libirecovery_debug) { irecv_set_debug_level(libirecovery_debug); } #ifndef WIN32 #ifdef HAVE_IOKIT ret = iokit_open_with_ecid(pclient, ecid); #else int i = 0; struct libusb_device* usb_device = NULL; struct libusb_device** usb_device_list = NULL; struct libusb_device_descriptor usb_descriptor; *pclient = NULL; irecv_error_t error = IRECV_E_SUCCESS; int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); for (i = 0; i < usb_device_count; i++) { usb_device = usb_device_list[i]; libusb_get_device_descriptor(usb_device, &usb_descriptor); if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { /* verify this device is in a mode we understand */ if (usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_1 || usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_2 || usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_3 || usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_4 || usb_descriptor.idProduct == IRECV_K_WTF_MODE || usb_descriptor.idProduct == IRECV_K_DFU_MODE) { if (ecid == IRECV_K_WTF_MODE) { if (usb_descriptor.idProduct != IRECV_K_WTF_MODE) { /* special ecid case, ignore !IRECV_K_WTF_MODE */ continue; } else { ecid = 0; } } if ((ecid != 0) && (usb_descriptor.idProduct == IRECV_K_WTF_MODE)) { /* we can't get ecid in WTF mode */ continue; } debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); struct libusb_device_handle* usb_handle = NULL; int libusb_error = libusb_open(usb_device, &usb_handle); if (usb_handle == NULL || libusb_error != 0) { debug("%s: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error)); libusb_close(usb_handle); if (ecid != 0) { continue; } libusb_free_device_list(usb_device_list, 1); return IRECV_E_UNABLE_TO_CONNECT; } irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client_private)); if (client == NULL) { libusb_free_device_list(usb_device_list, 1); libusb_close(usb_handle); return IRECV_E_OUT_OF_MEMORY; } memset(client, '\0', sizeof(struct irecv_client_private)); client->usb_interface = 0; client->handle = usb_handle; client->mode = usb_descriptor.idProduct; char serial_str[256]; irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*)serial_str, 255); irecv_load_device_info_from_iboot_string(client, serial_str); irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size); irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size); if (ecid != 0) { if (client->device_info.ecid != ecid) { irecv_close(client); continue; } debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid); } error = irecv_usb_set_configuration(client, 1); if (error != IRECV_E_SUCCESS) { libusb_free_device_list(usb_device_list, 1); irecv_close(client); return error; } if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)) { error = irecv_usb_set_interface(client, 0, 0); if (client->mode > IRECV_K_RECOVERY_MODE_2) { error = irecv_usb_set_interface(client, 1, 1); } } else { error = irecv_usb_set_interface(client, 0, 0); } if (error != IRECV_E_SUCCESS) { libusb_free_device_list(usb_device_list, 1); irecv_close(client); return error; } *pclient = client; libusb_free_device_list(usb_device_list, 1); ret = IRECV_E_SUCCESS; } } } #endif #else ret = mobiledevice_connect(pclient, ecid); if (ret == IRECV_E_SUCCESS) { irecv_client_t client = *pclient; int error = IRECV_E_SUCCESS; if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)) { error = irecv_usb_set_interface(client, 0, 0); if (client->mode > IRECV_K_RECOVERY_MODE_2) { error = irecv_usb_set_interface(client, 1, 1); } } else { error = irecv_usb_set_interface(client, 0, 0); } if (error != IRECV_E_SUCCESS) { debug("WARNING: set interface failed, error %d\n", error); } } #endif if (ret == IRECV_E_SUCCESS) { if ((*pclient)->connected_callback != NULL) { irecv_event_t event; event.size = 0; event.data = NULL; event.progress = 0; event.type = IRECV_CONNECTED; (*pclient)->connected_callback(*pclient, &event); } } return ret; #endif } IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; #ifndef WIN32 debug("Setting to configuration %d\n", configuration); #ifdef HAVE_IOKIT IOReturn result; result = (*client->handle)->SetConfiguration(client->handle, configuration); if (result != kIOReturnSuccess) { debug("error setting configuration: %#x\n", result); return IRECV_E_USB_CONFIGURATION; } #else int current = 0; libusb_get_configuration(client->handle, ¤t); if (current != configuration) { if (libusb_set_configuration(client->handle, configuration) < 0) { return IRECV_E_USB_CONFIGURATION; } } #endif client->usb_config = configuration; #endif return IRECV_E_SUCCESS; #endif } #ifndef USE_DUMMY #ifdef HAVE_IOKIT static IOReturn iokit_usb_get_interface(IOUSBDeviceInterface320 **device, uint8_t ifc, io_service_t *usbInterfacep) { IOUSBFindInterfaceRequest request; uint8_t current_interface; kern_return_t kresult; io_iterator_t interface_iterator; *usbInterfacep = IO_OBJECT_NULL; request.bInterfaceClass = kIOUSBFindInterfaceDontCare; request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; request.bAlternateSetting = kIOUSBFindInterfaceDontCare; kresult = (*device)->CreateInterfaceIterator(device, &request, &interface_iterator); if (kresult) return kresult; for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) { *usbInterfacep = IOIteratorNext(interface_iterator); if (current_interface != ifc) (void) IOObjectRelease (*usbInterfacep); } IOObjectRelease(interface_iterator); return kIOReturnSuccess; } static irecv_error_t iokit_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface) { IOReturn result; io_service_t interface_service = IO_OBJECT_NULL; IOCFPlugInInterface **plugInInterface = NULL; SInt32 score; // Close current interface if (client->usbInterface) { result = (*client->usbInterface)->USBInterfaceClose(client->usbInterface); result = (*client->usbInterface)->Release(client->usbInterface); client->usbInterface = NULL; } result = iokit_usb_get_interface(client->handle, usb_interface, &interface_service); if (result != kIOReturnSuccess) { debug("failed to find requested interface: %d\n", usb_interface); return IRECV_E_USB_INTERFACE; } result = IOCreatePlugInInterfaceForService(interface_service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); IOObjectRelease(interface_service); if (result != kIOReturnSuccess) { debug("error creating plug-in interface: %#x\n", result); return IRECV_E_USB_INTERFACE; } result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300), (LPVOID)&client->usbInterface); IODestroyPlugInInterface(plugInInterface); if (result != kIOReturnSuccess) { debug("error creating interface interface: %#x\n", result); return IRECV_E_USB_INTERFACE; } result = (*client->usbInterface)->USBInterfaceOpen(client->usbInterface); if (result != kIOReturnSuccess) { debug("error opening interface: %#x\n", result); return IRECV_E_USB_INTERFACE; } if (usb_interface == 1) { result = (*client->usbInterface)->SetAlternateInterface(client->usbInterface, usb_alt_interface); if (result != kIOReturnSuccess) { debug("error setting alternate interface: %#x\n", result); return IRECV_E_USB_INTERFACE; } } return IRECV_E_SUCCESS; } #endif #endif IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface); #ifndef WIN32 #ifdef HAVE_IOKIT if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) { return IRECV_E_USB_INTERFACE; } #else if (libusb_claim_interface(client->handle, usb_interface) < 0) { return IRECV_E_USB_INTERFACE; } if (usb_interface == 1) { if (libusb_set_interface_alt_setting(client->handle, usb_interface, usb_alt_interface) < 0) { return IRECV_E_USB_INTERFACE; } } #endif #else if (irecv_usb_control_transfer(client, 0, 0x0B, usb_alt_interface, usb_interface, NULL, 0, USB_TIMEOUT) < 0) { return IRECV_E_USB_INTERFACE; } #endif client->usb_interface = usb_interface; client->usb_alt_interface = usb_alt_interface; return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_reset(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; #ifndef WIN32 #ifdef HAVE_IOKIT IOReturn result; result = (*client->handle)->ResetDevice(client->handle); if (result != kIOReturnSuccess && result != kIOReturnNotResponding) { debug("error sending device reset: %#x\n", result); return IRECV_E_UNKNOWN_ERROR; } result = (*client->handle)->USBDeviceReEnumerate(client->handle, 0); if (result != kIOReturnSuccess && result != kIOReturnNotResponding) { debug("error re-enumerating device: %#x (ignored)\n", result); } #else libusb_reset_device(client->handle); #endif #else DWORD count; DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL); #endif return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, unsigned long long ecid, int attempts) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int i; for (i = 0; i < attempts; i++) { if(*pclient) { irecv_close(*pclient); *pclient = NULL; } if (irecv_open_with_ecid(pclient, ecid) != IRECV_E_SUCCESS) { debug("Connection failed. Waiting 1 sec before retry.\n"); sleep(1); } else { return IRECV_E_SUCCESS; } } return IRECV_E_UNABLE_TO_CONNECT; #endif } IRECV_API irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else switch(type) { case IRECV_RECEIVED: client->received_callback = callback; break; case IRECV_PROGRESS: client->progress_callback = callback; break; case IRECV_CONNECTED: client->connected_callback = callback; break; case IRECV_PRECOMMAND: client->precommand_callback = callback; break; case IRECV_POSTCOMMAND: client->postcommand_callback = callback; break; case IRECV_DISCONNECTED: client->disconnected_callback = callback; break; default: return IRECV_E_UNKNOWN_ERROR; } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else switch(type) { case IRECV_RECEIVED: client->received_callback = NULL; break; case IRECV_PROGRESS: client->progress_callback = NULL; break; case IRECV_CONNECTED: client->connected_callback = NULL; break; case IRECV_PRECOMMAND: client->precommand_callback = NULL; break; case IRECV_POSTCOMMAND: client->postcommand_callback = NULL; break; case IRECV_DISCONNECTED: client->disconnected_callback = NULL; break; default: return IRECV_E_UNKNOWN_ERROR; } return IRECV_E_SUCCESS; #endif } #ifndef USE_DUMMY struct irecv_device_event_context { irecv_device_event_cb_t callback; void *user_data; }; struct irecv_usb_device_info { struct irecv_device_info device_info; uint32_t location; int alive; }; #ifdef WIN32 struct irecv_win_dev_ctx { PSP_DEVICE_INTERFACE_DETAIL_DATA details; uint32_t location; }; #else #ifdef HAVE_IOKIT struct irecv_iokit_dev_ctx { io_service_t device; IOUSBDeviceInterface **dev; }; #endif #endif static int _irecv_is_recovery_device(void *device) { uint16_t vendor_id = 0; uint16_t product_id = 0; #ifdef WIN32 const char *path = (const char*)device; unsigned int vendor = 0; unsigned int product = 0; if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) { return 0; } vendor_id = (uint16_t)vendor; product_id = (uint16_t)product; #else #ifdef HAVE_IOKIT kern_return_t kr; IOUSBDeviceInterface **dev = device; kr = (*dev)->GetDeviceVendor(dev, &vendor_id); kr = (*dev)->GetDeviceProduct(dev, &product_id); #else libusb_device *device_ = (libusb_device*)device; struct libusb_device_descriptor devdesc; int libusb_error; libusb_error = libusb_get_device_descriptor(device_, &devdesc); if (libusb_error != 0) { debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error)); return 0; } vendor_id = devdesc.idVendor; product_id = devdesc.idProduct; #endif #endif if (vendor_id != APPLE_VENDOR_ID) { return 0; } switch (product_id) { case IRECV_K_DFU_MODE: case IRECV_K_WTF_MODE: case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: case IRECV_K_RECOVERY_MODE_4: break; default: return 0; } return 1; } static void* _irecv_handle_device_add(void *userdata) { struct irecv_client_private client_loc; char serial_str[256]; uint32_t location = 0; uint16_t product_id = 0; serial_str[0] = '\0'; #ifdef WIN32 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata; PSP_DEVICE_INTERFACE_DETAIL_DATA details = win_ctx->details; LPSTR result = (LPSTR)details->DevicePath; location = win_ctx->location; char *p = result + strlen(result) - 1; while (p-- && p > result) { if (*p == '\\' && (strncmp(p, "\\usb", 4) == 0)) { break; } } unsigned int pid = 0; if (!p || (sscanf(p, "\\usb#vid_%*04x&pid_%04x#%s", &pid, serial_str) != 2) || (serial_str[0] == '\0')) { debug("%s: ERROR: failed to parse DevicePath?!\n", __func__); return NULL; } if (!_irecv_is_recovery_device(p)) { return NULL; } p = strchr(serial_str, '#'); if (p) { *p = '\0'; } unsigned int j; for (j = 0; j < strlen(serial_str); j++) { if (serial_str[j] == '_') { serial_str[j] = ' '; } else { serial_str[j] = toupper(serial_str[j]); } } product_id = (uint16_t)pid; #else /* !WIN32 */ #ifdef HAVE_IOKIT struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata; io_service_t device = iokit_ctx->device; IOUSBDeviceInterface **dev = iokit_ctx->dev; if (!device) { debug("%s: ERROR: no device?!\n", __func__); return NULL; } if (!dev) { debug("%s: ERROR: no device interface?!\n", __func__); return NULL; } (*dev)->GetDeviceProduct(dev, &product_id); if (!product_id) { debug("%s: ERROR: could not get product id?!\n", __func__); return NULL; } CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); if (locationNum) { CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location); CFRelease(locationNum); } if (!location) { debug("%s: ERROR: could not get locationID?!\n", __func__); return NULL; } CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); if (serialString) { CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8); CFRelease(serialString); } #else /* !HAVE_IOKIT */ libusb_device *device = (libusb_device*)userdata; struct libusb_device_descriptor devdesc; struct libusb_device_handle* usb_handle = NULL; int libusb_error; libusb_error = libusb_get_device_descriptor(device, &devdesc); if (libusb_error != 0) { debug("%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error)); return NULL; } product_id = devdesc.idProduct; uint8_t bus = libusb_get_bus_number(device); uint8_t address = libusb_get_device_address(device); location = (bus << 16) | address; libusb_error = libusb_open(device, &usb_handle); if (usb_handle == NULL || libusb_error != 0) { debug("%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error)); libusb_close(usb_handle); return 0; } libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255); if (libusb_error < 0) { debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error)); return 0; } libusb_close(usb_handle); #endif /* !HAVE_IOKIT */ #endif /* !WIN32 */ memset(&client_loc, '\0', sizeof(client_loc)); irecv_load_device_info_from_iboot_string(&client_loc, serial_str); client_loc.mode = product_id; struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info)); memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info)); usb_dev_info->location = location; usb_dev_info->alive = 1; collection_add(&devices, usb_dev_info); irecv_device_event_t dev_event; dev_event.type = IRECV_DEVICE_ADD; dev_event.mode = client_loc.mode; dev_event.device_info = &(usb_dev_info->device_info); mutex_lock(&listener_mutex); FOREACH(struct irecv_device_event_context* context, &listeners) { context->callback(&dev_event, context->user_data); } ENDFOREACH mutex_unlock(&listener_mutex); return NULL; } static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo) { irecv_device_event_t dev_event; dev_event.type = IRECV_DEVICE_REMOVE; dev_event.mode = 0; dev_event.device_info = &(devinfo->device_info); mutex_lock(&listener_mutex); FOREACH(struct irecv_device_event_context* context, &listeners) { context->callback(&dev_event, context->user_data); } ENDFOREACH mutex_unlock(&listener_mutex); free(devinfo->device_info.srnm); devinfo->device_info.srnm = NULL; free(devinfo->device_info.imei); devinfo->device_info.imei = NULL; free(devinfo->device_info.srtg); devinfo->device_info.srtg = NULL; free(devinfo->device_info.serial_string); devinfo->device_info.serial_string = NULL; devinfo->alive = 0; collection_remove(&devices, devinfo); free(devinfo); } #ifndef WIN32 #ifdef HAVE_IOKIT static void iokit_device_added(void *refcon, io_iterator_t iterator) { kern_return_t kr; io_service_t device; IOCFPlugInInterface **plugInInterface = NULL; IOUSBDeviceInterface **dev = NULL; HRESULT result; SInt32 score; while ((device = IOIteratorNext(iterator))) { kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); if ((kIOReturnSuccess != kr) || !plugInInterface) { debug("%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr); kr = IOObjectRelease(device); continue; } result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&dev); (*plugInInterface)->Release(plugInInterface); if (result || !dev) { debug("%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result); kr = IOObjectRelease(device); continue; } if (!_irecv_is_recovery_device(dev)) { (void) (*dev)->Release(dev); kr = IOObjectRelease(device); continue; } struct irecv_iokit_dev_ctx idev; idev.device = device; idev.dev = dev; _irecv_handle_device_add(&idev); (void) (*dev)->Release(dev); kr = IOObjectRelease(device); } } static void iokit_device_removed(void *refcon, io_iterator_t iterator) { io_service_t device; while ((device = IOIteratorNext(iterator))) { uint32_t location = 0; CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); if (locationNum) { CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location); CFRelease(locationNum); } IOObjectRelease(device); if (!location) { continue; } FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (devinfo->location == location) { _irecv_handle_device_remove(devinfo); break; } } ENDFOREACH } } #else /* !HAVE_IOKIT */ #ifdef HAVE_LIBUSB_HOTPLUG_API static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) { if (!_irecv_is_recovery_device(device)) { return 0; } if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { THREAD_T th_device; if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) { debug("%s: FATAL: failed to create thread to handle device add\n", __func__); return 0; } thread_detach(th_device); } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { uint8_t bus = libusb_get_bus_number(device); uint8_t address = libusb_get_device_address(device); uint32_t location = (bus << 16) | address; FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (devinfo->location == location) { _irecv_handle_device_remove(devinfo); break; } } ENDFOREACH } return 0; } #endif /* HAVE_LIBUSB_HOTPLUG_API */ #endif /* !HAVE_IOKIT */ #endif /* !WIN32 */ static void *_irecv_event_handler(void* unused) { #ifdef WIN32 const GUID *guids[] = { &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; int running = 1; do { SP_DEVICE_INTERFACE_DATA currentInterface; HDEVINFO usbDevices; DWORD i; int k; for (k = 0; guids[k]; k++) { usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (!usbDevices) { debug("%s: ERROR: SetupDiGetClassDevs failed\n", __func__); return NULL; } FOREACH(struct irecv_usb_device_info *devinfo, &devices) { devinfo->alive = 0; } ENDFOREACH memset(¤tInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA)); currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, ¤tInterface); i++) { DWORD requiredSize = 0; PSP_DEVICE_INTERFACE_DETAIL_DATA details; SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); SP_DEVINFO_DATA devinfodata; devinfodata.cbSize = sizeof(SP_DEVINFO_DATA); if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, &devinfodata)) { free(details); continue; } DWORD sz = REG_SZ; char driver[256]; driver[0] = '\0'; if (!SetupDiGetDeviceRegistryProperty(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) { debug("%s: ERROR: Failed to get driver key\n", __func__); free(details); continue; } char *p = strrchr(driver, '\\'); if (!p) { debug("%s: ERROR: Failed to parse device location\n", __func__); free(details); continue; } uint32_t location = strtoul(p+1, NULL, 10); int found = 0; FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (devinfo->location == location) { devinfo->alive = 1; found = 1; break; } } ENDFOREACH if (!found) { struct irecv_win_dev_ctx win_ctx; win_ctx.details = details; win_ctx.location = location; _irecv_handle_device_add(&win_ctx); } free(details); } SetupDiDestroyDeviceInfoList(usbDevices); } FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (!devinfo->alive) { _irecv_handle_device_remove(devinfo); } } ENDFOREACH mutex_lock(&listener_mutex); if (collection_count(&listeners) == 0) { running = 0; } mutex_unlock(&listener_mutex); Sleep(500); } while (running); #else /* !WIN32 */ #ifdef HAVE_IOKIT kern_return_t kr; IONotificationPortRef notifyPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort); iokit_runloop = CFRunLoopGetCurrent(); CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode); uint16_t pids[7] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, 0 }; int i = 0; while (pids[i] > 0) { CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID); iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]); matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict); io_iterator_t devAddedIter; kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter); iokit_device_added(NULL, devAddedIter); io_iterator_t devRemovedIter; kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter); iokit_device_removed(NULL, devRemovedIter); i++; } CFRunLoopRun(); #else /* !HAVE_IOKIT */ #ifdef HAVE_LIBUSB_HOTPLUG_API static libusb_hotplug_callback_handle usb_hotplug_cb_handle; libusb_hotplug_register_callback(irecv_hotplug_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, APPLE_VENDOR_ID, LIBUSB_HOTPLUG_MATCH_ANY, 0, _irecv_usb_hotplug_cb, NULL, &usb_hotplug_cb_handle); int running = 1; do { struct timeval tv; tv.tv_sec = tv.tv_usec = 0; libusb_handle_events_timeout(irecv_hotplug_ctx, &tv); mutex_lock(&listener_mutex); if (collection_count(&listeners) == 0) { running = 0; } mutex_unlock(&listener_mutex); usleep(100000); } while (running); libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle); #else /* !HAVE_LIBUSB_HOTPLUG_API */ int i, cnt; libusb_device **devs; int running = 1; do { cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs); if (cnt < 0) { debug("%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt)); return NULL; } FOREACH(struct irecv_usb_device_info *devinfo, &devices) { devinfo->alive = 0; } ENDFOREACH for (i = 0; i < cnt; i++) { libusb_device *dev = devs[i]; if (!_irecv_is_recovery_device(dev)) { continue; } uint8_t bus = libusb_get_bus_number(dev); uint8_t address = libusb_get_device_address(dev); uint32_t location = (bus << 16) | address; int found = 0; FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (devinfo->location == location) { devinfo->alive = 1; found = 1; break; } } ENDFOREACH if (!found) { _irecv_handle_device_add(dev); } } FOREACH(struct irecv_usb_device_info *devinfo, &devices) { if (!devinfo->alive) { _irecv_handle_device_remove(devinfo); } } ENDFOREACH libusb_free_device_list(devs, 1); mutex_lock(&listener_mutex); if (collection_count(&listeners) == 0) { running = 0; } mutex_unlock(&listener_mutex); if (!running) break; usleep(500000); } while (running); #endif /* !HAVE_LIBUSB_HOTPLUG_API */ #endif /* !HAVE_IOKIT */ #endif /* !WIN32 */ return NULL; } #endif /* !USE_DUMMY */ IRECV_API irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (!context || !callback) return IRECV_E_INVALID_INPUT; struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context)); if (!_context) { return IRECV_E_OUT_OF_MEMORY; } _context->callback = callback; _context->user_data = user_data; mutex_lock(&listener_mutex); collection_add(&listeners, _context); mutex_unlock(&listener_mutex); if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) { #ifndef WIN32 #ifndef HAVE_IOKIT libusb_init(&irecv_hotplug_ctx); #endif #endif collection_init(&devices); mutex_init(&device_mutex); thread_new(&th_event_handler, _irecv_event_handler, NULL); } *context = _context; return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (!context) return IRECV_E_INVALID_INPUT; mutex_lock(&listener_mutex); collection_remove(&listeners, context); int num = collection_count(&listeners); mutex_unlock(&listener_mutex); if (num == 0) { #ifdef HAVE_IOKIT if (iokit_runloop) { CFRunLoopStop(iokit_runloop); } #endif thread_join(th_event_handler); thread_free(th_event_handler); th_event_handler = THREAD_T_NULL; mutex_lock(&device_mutex); FOREACH(struct irecv_usb_device_info *devinfo, &devices) { free(devinfo->device_info.srnm); devinfo->device_info.srnm = NULL; free(devinfo->device_info.imei); devinfo->device_info.imei = NULL; free(devinfo->device_info.srtg); devinfo->device_info.srtg = NULL; free(devinfo->device_info.serial_string); devinfo->device_info.serial_string = NULL; free(devinfo); } ENDFOREACH collection_free(&devices); mutex_unlock(&device_mutex); mutex_destroy(&device_mutex); #ifndef WIN32 #ifndef HAVE_IOKIT libusb_exit(irecv_hotplug_ctx); irecv_hotplug_ctx = NULL; #endif #endif } free(context); return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_close(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (client != NULL) { if(client->disconnected_callback != NULL) { irecv_event_t event; event.size = 0; event.data = NULL; event.progress = 0; event.type = IRECV_DISCONNECTED; client->disconnected_callback(client, &event); } #ifndef WIN32 #ifdef HAVE_IOKIT if (client->usbInterface) { (*client->usbInterface)->USBInterfaceClose(client->usbInterface); (*client->usbInterface)->Release(client->usbInterface); client->usbInterface = NULL; } if (client->handle) { (*client->handle)->USBDeviceClose(client->handle); (*client->handle)->Release(client->handle); client->handle = NULL; } #else if (client->handle != NULL) { if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)) { libusb_release_interface(client->handle, client->usb_interface); } libusb_close(client->handle); client->handle = NULL; } #endif #else free(client->iBootPath); client->iBootPath = NULL; free(client->DfuPath); client->DfuPath = NULL; mobiledevice_closepipes(client); #endif free(client->device_info.srnm); free(client->device_info.imei); free(client->device_info.srtg); free(client->device_info.serial_string); free(client->device_info.ap_nonce); free(client->device_info.sep_nonce); free(client); client = NULL; } return IRECV_E_SUCCESS; #endif } IRECV_API void irecv_set_debug_level(int level) { libirecovery_debug = level; #ifndef USE_DUMMY #ifndef WIN32 #ifndef HAVE_IOKIT if(libirecovery_context) { #if LIBUSB_API_VERSION >= 0x01000106 libusb_set_option(libirecovery_context, LIBUSB_OPTION_LOG_LEVEL, libirecovery_debug > 2 ? 1: 0); #else libusb_set_debug(libirecovery_context, libirecovery_debug > 2 ? 1: 0); #endif } #endif #endif #endif } #ifndef USE_DUMMY static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* command) { unsigned int length = strlen(command); if (length >= 0x100) { length = 0xFF; } if (length > 0) { irecv_usb_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); } return IRECV_E_SUCCESS; } #endif IRECV_API irecv_error_t irecv_send_command(irecv_client_t client, const char* command) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else irecv_error_t error = 0; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; unsigned int length = strlen(command); if (length >= 0x100) { length = 0xFF; } irecv_event_t event; if(client->precommand_callback != NULL) { event.size = length; event.data = command; event.type = IRECV_PRECOMMAND; if(client->precommand_callback(client, &event)) { return IRECV_E_SUCCESS; } } error = irecv_send_command_raw(client, command); if (error != IRECV_E_SUCCESS) { debug("Failed to send command %s\n", command); if (error != IRECV_E_PIPE) return error; } if(client->postcommand_callback != NULL) { event.size = length; event.data = command; event.type = IRECV_POSTCOMMAND; if(client->postcommand_callback(client, &event)) { return IRECV_E_SUCCESS; } } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfu_notify_finished) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; FILE* file = fopen(filename, "rb"); if (file == NULL) { return IRECV_E_FILE_NOT_FOUND; } struct stat fst; if (fstat(fileno(file), &fst) < 0) { return IRECV_E_UNKNOWN_ERROR; } size_t length = fst.st_size; char* buffer = (char*)malloc(length); if (buffer == NULL) { fclose(file); return IRECV_E_OUT_OF_MEMORY; } size_t bytes = fread(buffer, 1, length, file); fclose(file); if (bytes != length) { free(buffer); return IRECV_E_UNKNOWN_ERROR; } irecv_error_t error = irecv_send_buffer(client, (unsigned char*)buffer, length, dfu_notify_finished); free(buffer); return error; #endif } #ifndef USE_DUMMY static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { if (check_context(client) != IRECV_E_SUCCESS) { *status = 0; return IRECV_E_NO_DEVICE; } unsigned char buffer[6]; memset(buffer, '\0', 6); if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) { *status = 0; return IRECV_E_USB_STATUS; } *status = (unsigned int) buffer[4]; return IRECV_E_SUCCESS; } #endif IRECV_API irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfu_notify_finished) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else irecv_error_t error = 0; int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; unsigned int h1 = 0xFFFFFFFF; unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; int packet_size = recovery_mode ? 0x8000 : 0x800; int last = length % packet_size; int packets = length / packet_size; if (last != 0) { packets++; } else { last = packet_size; } /* initiate transfer */ if (recovery_mode) { error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); } else { uint8_t state = 0; if (irecv_usb_control_transfer(client, 0xa1, 5, 0, 0, (unsigned char*)&state, 1, USB_TIMEOUT) == 1) { error = IRECV_E_SUCCESS; } else { return IRECV_E_USB_UPLOAD; } switch (state) { case 2: /* DFU IDLE */ break; case 10: debug("DFU ERROR, issuing CLRSTATUS\n"); irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT); error = IRECV_E_USB_UPLOAD; break; default: debug("Unexpected state %d, issuing ABORT\n", state); irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT); error = IRECV_E_USB_UPLOAD; break; } } if (error != IRECV_E_SUCCESS) { return error; } int i = 0; unsigned long count = 0; unsigned int status = 0; int bytes = 0; for (i = 0; i < packets; i++) { int size = (i + 1) < packets ? packet_size : last; /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ if (recovery_mode) { error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); } else { int j; for (j = 0; j < size; j++) { crc32_step(h1, buffer[i*packet_size + j]); } if (i+1 == packets) { if (size+16 > packet_size) { bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT); if (bytes != size) { return IRECV_E_USB_UPLOAD; } count += size; size = 0; } for (j = 0; j < 2; j++) { crc32_step(h1, dfu_xbuf[j*6 + 0]); crc32_step(h1, dfu_xbuf[j*6 + 1]); crc32_step(h1, dfu_xbuf[j*6 + 2]); crc32_step(h1, dfu_xbuf[j*6 + 3]); crc32_step(h1, dfu_xbuf[j*6 + 4]); crc32_step(h1, dfu_xbuf[j*6 + 5]); } char* newbuf = (char*)malloc(size + 16); if (size > 0) { memcpy(newbuf, &buffer[i * packet_size], size); } memcpy(newbuf+size, dfu_xbuf, 12); newbuf[size+12] = h1 & 0xFF; newbuf[size+13] = (h1 >> 8) & 0xFF; newbuf[size+14] = (h1 >> 16) & 0xFF; newbuf[size+15] = (h1 >> 24) & 0xFF; size += 16; bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, (unsigned char*)newbuf, size, USB_TIMEOUT); free(newbuf); } else { bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT); } } if (bytes != size) { return IRECV_E_USB_UPLOAD; } if (!recovery_mode) { error = irecv_get_status(client, &status); } if (error != IRECV_E_SUCCESS) { return error; } if (!recovery_mode && status != 5) { int retry = 0; while (retry++ < 20) { irecv_get_status(client, &status); if (status == 5) { break; } sleep(1); } if (status != 5) { return IRECV_E_USB_UPLOAD; } } count += size; if(client->progress_callback != NULL) { irecv_event_t event; event.progress = ((double) count/ (double) length) * 100.0; event.type = IRECV_PROGRESS; event.data = (char*)"Uploading"; event.size = count; client->progress_callback(client, &event); } else { debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); } } if (dfu_notify_finished && !recovery_mode) { irecv_usb_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT); for (i = 0; i < 2; i++) { error = irecv_get_status(client, &status); if (error != IRECV_E_SUCCESS) { return error; } } if (dfu_notify_finished == 2) { /* we send a pseudo ZLP here just in case */ irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT); } irecv_reset(client); } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_receive(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else char buffer[BUFFER_SIZE]; memset(buffer, '\0', BUFFER_SIZE); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; int bytes = 0; while (1) { irecv_usb_set_interface(client, 1, 1); int r = irecv_usb_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500); irecv_usb_set_interface(client, 0, 0); if (r != 0) { break; } if (bytes > 0) { if (client->received_callback != NULL) { irecv_event_t event; event.size = bytes; event.data = buffer; event.type = IRECV_RECEIVED; if (client->received_callback(client, &event) != 0) { break; } } } else break; } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else char command[256]; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; *value = NULL; if(variable == NULL) { return IRECV_E_INVALID_INPUT; } memset(command, '\0', sizeof(command)); snprintf(command, sizeof(command)-1, "getenv %s", variable); irecv_error_t error = irecv_send_command_raw(client, command); if(error == IRECV_E_PIPE) { return IRECV_E_SUCCESS; } if(error != IRECV_E_SUCCESS) { return error; } char* response = (char*) malloc(256); if (response == NULL) { return IRECV_E_OUT_OF_MEMORY; } memset(response, '\0', 256); irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); *value = response; return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; *value = 0; char* response = (char*) malloc(256); if (response == NULL) { return IRECV_E_OUT_OF_MEMORY; } memset(response, '\0', 256); irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, USB_TIMEOUT); *value = (unsigned int) *response; return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_get_mode(irecv_client_t client, int* mode) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; *mode = client->mode; return IRECV_E_SUCCESS; #endif } IRECV_API const struct irecv_device_info* irecv_get_device_info(irecv_client_t client) { #ifdef USE_DUMMY return NULL; #else if (check_context(client) != IRECV_E_SUCCESS) return NULL; return &client->device_info; #endif } #ifndef USE_DUMMY #ifdef HAVE_IOKIT static void *iokit_limera1n_usb_submit_request(void *argv) { void **args = argv; IOUSBDeviceInterface320 **dev = args[0]; IOUSBDevRequest *req = args[1]; IOReturn result = (*dev)->DeviceRequest(dev, req); if (result != kIOReturnSuccess) debug("%s result: %#x\n", __func__, result); return NULL; } #endif #endif IRECV_API irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; #ifdef HAVE_IOKIT IOReturn result; IOUSBDevRequestTO req; bzero(&req, sizeof(req)); req.bmRequestType = 0x21; req.bRequest = 2; req.wValue = 0; req.wIndex = 0; req.wLength = 0; req.pData = NULL; req.noDataTimeout = USB_TIMEOUT; req.completionTimeout = USB_TIMEOUT; // The original version uses an async request, but we don't have an async event // source set up. The hack relies on aborting the transaction before it times out, // which can be accomplished by sending on another thread. void *args[2] = { client->handle, &req }; pthread_t thread; pthread_create(&thread, NULL, iokit_limera1n_usb_submit_request, args); usleep(5 * 1000); result = (*client->handle)->USBDeviceAbortPipeZero(client->handle); if (result != kIOReturnSuccess) debug("USBDeviceAbortPipeZero returned %#x\n", result); switch (result) { case kIOReturnSuccess: return req.wLenDone; case kIOReturnTimeout: return IRECV_E_TIMEOUT; case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT; case kIOReturnNotResponding: return IRECV_E_NO_DEVICE; case kIOReturnNoDevice: return IRECV_E_NO_DEVICE; default: return IRECV_E_UNKNOWN_ERROR; } #else irecv_usb_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT); #endif return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_execute_script(irecv_client_t client, const char* script) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else irecv_error_t error = IRECV_E_SUCCESS; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; char* body = strdup(script); char* line = strtok(body, "\n"); while(line != NULL) { if(line[0] != '#') { error = irecv_send_command(client, line); if(error != IRECV_E_SUCCESS) { break; } error = irecv_receive(client); if(error != IRECV_E_SUCCESS) { break; } } line = strtok(NULL, "\n"); } free(body); return error; #endif } IRECV_API irecv_error_t irecv_saveenv(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else irecv_error_t error = irecv_send_command_raw(client, "saveenv"); if(error != IRECV_E_SUCCESS) { return error; } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else char command[256]; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; if(variable == NULL || value == NULL) { return IRECV_E_UNKNOWN_ERROR; } memset(command, '\0', sizeof(command)); snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); irecv_error_t error = irecv_send_command_raw(client, command); if(error != IRECV_E_SUCCESS) { return error; } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_reboot(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else irecv_error_t error = irecv_send_command_raw(client, "reboot"); if(error != IRECV_E_SUCCESS) { return error; } return IRECV_E_SUCCESS; #endif } IRECV_API const char* irecv_strerror(irecv_error_t error) { switch (error) { case IRECV_E_SUCCESS: return "Command completed successfully"; case IRECV_E_NO_DEVICE: return "Unable to find device"; case IRECV_E_OUT_OF_MEMORY: return "Out of memory"; case IRECV_E_UNABLE_TO_CONNECT: return "Unable to connect to device"; case IRECV_E_INVALID_INPUT: return "Invalid input"; case IRECV_E_FILE_NOT_FOUND: return "File not found"; case IRECV_E_USB_UPLOAD: return "Unable to upload data to device"; case IRECV_E_USB_STATUS: return "Unable to get device status"; case IRECV_E_USB_INTERFACE: return "Unable to set device interface"; case IRECV_E_USB_CONFIGURATION: return "Unable to set device configuration"; case IRECV_E_PIPE: return "Broken pipe"; case IRECV_E_TIMEOUT: return "Timeout talking to device"; case IRECV_E_UNSUPPORTED: return "Operation unsupported by driver"; default: return "Unknown error"; } return NULL; } IRECV_API irecv_error_t irecv_reset_counters(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; if ((client->mode == IRECV_K_DFU_MODE) || (client->mode == IRECV_K_WTF_MODE)) { irecv_usb_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT); } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; int packet_size = recovery_mode ? 0x2000: 0x800; int last = length % packet_size; int packets = length / packet_size; if (last != 0) { packets++; } else { last = packet_size; } int i = 0; int bytes = 0; unsigned long count = 0; for (i = 0; i < packets; i++) { unsigned short size = (i+1) < packets ? packet_size : last; bytes = irecv_usb_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char*)&buffer[i * packet_size], size, USB_TIMEOUT); if (bytes != size) { return IRECV_E_USB_UPLOAD; } count += size; if(client->progress_callback != NULL) { irecv_event_t event; event.progress = ((double) count/ (double) length) * 100.0; event.type = IRECV_PROGRESS; event.data = (char*)"Downloading"; event.size = count; client->progress_callback(client, &event); } else { debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length); } } return IRECV_E_SUCCESS; #endif } IRECV_API irecv_error_t irecv_finish_transfer(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int i = 0; unsigned int status = 0; if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT); for(i = 0; i < 3; i++){ irecv_get_status(client, &status); } irecv_reset(client); return IRECV_E_SUCCESS; #endif } IRECV_API irecv_device_t irecv_devices_get_all(void) { return irecv_devices; } IRECV_API irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else int i = 0; *device = NULL; if (client->device_info.cpid == 0) { return IRECV_E_UNKNOWN_ERROR; } for (i = 0; irecv_devices[i].hardware_model != NULL; i++) { if (irecv_devices[i].chip_id == client->device_info.cpid && irecv_devices[i].board_id == client->device_info.bdid) { *device = &irecv_devices[i]; return IRECV_E_SUCCESS; } } return IRECV_E_NO_DEVICE; #endif } IRECV_API irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device) { int i = 0; *device = NULL; for (i = 0; irecv_devices[i].product_type != NULL; i++) { if (!strcmp(product_type, irecv_devices[i].product_type)) { *device = &irecv_devices[i]; return IRECV_E_SUCCESS; } } return IRECV_E_NO_DEVICE; } IRECV_API irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device) { int i = 0; *device = NULL; /* lowercase hardware_model string for proper lookup */ char model[8]; strcpy(model, hardware_model); char *p = model; for (; *p; ++p) *p = tolower(*p); for (i = 0; irecv_devices[i].hardware_model != NULL; i++) { if (!strcmp(model, irecv_devices[i].hardware_model)) { *device = &irecv_devices[i]; return IRECV_E_SUCCESS; } } return IRECV_E_NO_DEVICE; } IRECV_API irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) { #ifdef USE_DUMMY return NULL; #else irecv_error_t error = 0; irecv_client_t new_client = NULL; irecv_event_cb_t progress_callback = client->progress_callback; irecv_event_cb_t received_callback = client->received_callback; irecv_event_cb_t connected_callback = client->connected_callback; irecv_event_cb_t precommand_callback = client->precommand_callback; irecv_event_cb_t postcommand_callback = client->postcommand_callback; irecv_event_cb_t disconnected_callback = client->disconnected_callback; unsigned long long ecid = client->device_info.ecid; if (check_context(client) == IRECV_E_SUCCESS) { irecv_close(client); } if (initial_pause > 0) { debug("Waiting %d seconds for the device to pop up...\n", initial_pause); sleep(initial_pause); } error = irecv_open_with_ecid_and_attempts(&new_client, ecid, 10); if(error != IRECV_E_SUCCESS) { return NULL; } new_client->progress_callback = progress_callback; new_client->received_callback = received_callback; new_client->connected_callback = connected_callback; new_client->precommand_callback = precommand_callback; new_client->postcommand_callback = postcommand_callback; new_client->disconnected_callback = disconnected_callback; if (new_client->connected_callback != NULL) { irecv_event_t event; event.size = 0; event.data = NULL; event.progress = 0; event.type = IRECV_CONNECTED; new_client->connected_callback(new_client, &event); } return new_client; #endif } libirecovery-1.0.0/src/thread.c000066400000000000000000000054321367173561200164270ustar00rootroot00000000000000/* * thread.c * * Copyright (c) 2012-2019 Nikias Bassen, All Rights Reserved. * Copyright (c) 2012 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 */ #ifdef HAVE_CONFIG_H #include #endif #include "thread.h" int thread_new(THREAD_T *thread, thread_func_t thread_func, void* data) { #ifdef WIN32 HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, data, 0, NULL); if (th == NULL) { return -1; } *thread = th; return 0; #else int res = pthread_create(thread, NULL, thread_func, data); return res; #endif } void thread_detach(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); #else pthread_detach(thread); #endif } void thread_free(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); #endif } int thread_join(THREAD_T thread) { /* wait for thread to complete */ #ifdef WIN32 return (int)WaitForSingleObject(thread, INFINITE); #else return pthread_join(thread, NULL); #endif } int thread_alive(THREAD_T thread) { #ifdef WIN32 return WaitForSingleObject(thread, 0) == WAIT_TIMEOUT; #else return pthread_kill(thread, 0) == 0; #endif } int thread_cancel(THREAD_T thread) { #ifdef WIN32 return -1; #else #ifdef HAVE_PTHREAD_CANCEL return pthread_cancel(thread); #else return -1; #endif #endif } void mutex_init(mutex_t* mutex) { #ifdef WIN32 InitializeCriticalSection(mutex); #else pthread_mutex_init(mutex, NULL); #endif } void mutex_destroy(mutex_t* mutex) { #ifdef WIN32 DeleteCriticalSection(mutex); #else pthread_mutex_destroy(mutex); #endif } void mutex_lock(mutex_t* mutex) { #ifdef WIN32 EnterCriticalSection(mutex); #else pthread_mutex_lock(mutex); #endif } void mutex_unlock(mutex_t* mutex) { #ifdef WIN32 LeaveCriticalSection(mutex); #else pthread_mutex_unlock(mutex); #endif } void thread_once(thread_once_t *once_control, void (*init_routine)(void)) { #ifdef WIN32 while (InterlockedExchange(&(once_control->lock), 1) != 0) { Sleep(1); } if (!once_control->state) { once_control->state = 1; init_routine(); } InterlockedExchange(&(once_control->lock), 0); #else pthread_once(once_control, init_routine); #endif } libirecovery-1.0.0/src/thread.h000066400000000000000000000043271367173561200164360ustar00rootroot00000000000000/* * thread.h * * Copyright (c) 2012-2019 Nikias Bassen, All Rights Reserved. * Copyright (c) 2012 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 */ #ifndef __THREAD_H #define __THREAD_H #include #ifdef WIN32 #include typedef HANDLE THREAD_T; typedef CRITICAL_SECTION mutex_t; typedef volatile struct { LONG lock; int state; } thread_once_t; #define THREAD_ONCE_INIT {0, 0} #define THREAD_ID GetCurrentThreadId() #define THREAD_T_NULL (THREAD_T)NULL #else #include #include typedef pthread_t THREAD_T; typedef pthread_mutex_t mutex_t; typedef pthread_once_t thread_once_t; #define THREAD_ONCE_INIT PTHREAD_ONCE_INIT #define THREAD_ID pthread_self() #define THREAD_T_NULL (THREAD_T)NULL #endif typedef void* (*thread_func_t)(void* data); int thread_new(THREAD_T* thread, thread_func_t thread_func, void* data); void thread_detach(THREAD_T thread); void thread_free(THREAD_T thread); int thread_join(THREAD_T thread); int thread_alive(THREAD_T thread); int thread_cancel(THREAD_T thread); #ifdef WIN32 #undef HAVE_THREAD_CLEANUP #else #ifdef HAVE_PTHREAD_CANCEL #define HAVE_THREAD_CLEANUP 1 #define thread_cleanup_push(routine, arg) pthread_cleanup_push(routine, arg) #define thread_cleanup_pop(execute) pthread_cleanup_pop(execute) #endif #endif void mutex_init(mutex_t* mutex); void mutex_destroy(mutex_t* mutex); void mutex_lock(mutex_t* mutex); void mutex_unlock(mutex_t* mutex); void thread_once(thread_once_t *once_control, void (*init_routine)(void)); #endif libirecovery-1.0.0/src/utils.c000066400000000000000000000044721367173561200163230ustar00rootroot00000000000000/* * utils.c * * Copyright (C) 2009 Hector Martin * Copyright (C) 2009 Nikias Bassen * Copyright (c) 2013 Federico Mena Quintero * * 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; 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 "utils.h" #define CAPACITY_STEP 8 void collection_init(struct collection *col) { col->list = malloc(sizeof(void *) * CAPACITY_STEP); memset(col->list, 0, sizeof(void *) * CAPACITY_STEP); col->capacity = CAPACITY_STEP; } void collection_free(struct collection *col) { free(col->list); col->list = NULL; col->capacity = 0; } void collection_add(struct collection *col, void *element) { int i; for(i=0; icapacity; i++) { if(!col->list[i]) { col->list[i] = element; return; } } col->list = realloc(col->list, sizeof(void*) * (col->capacity + CAPACITY_STEP)); memset(&col->list[col->capacity], 0, sizeof(void *) * CAPACITY_STEP); col->list[col->capacity] = element; col->capacity += CAPACITY_STEP; } void collection_remove(struct collection *col, void *element) { int i; for(i=0; icapacity; i++) { if(col->list[i] == element) { col->list[i] = NULL; return; } } } int collection_count(struct collection *col) { int i, cnt = 0; for(i=0; icapacity; i++) { if(col->list[i]) cnt++; } return cnt; } void collection_copy(struct collection *dest, struct collection *src) { if (!dest || !src) return; dest->capacity = src->capacity; dest->list = malloc(sizeof(void*) * src->capacity); memcpy(dest->list, src->list, sizeof(void*) * src->capacity); } libirecovery-1.0.0/src/utils.h000066400000000000000000000032311367173561200163200ustar00rootroot00000000000000/* * utils.h * * Copyright (C) 2009 Hector Martin * Copyright (C) 2009 Nikias Bassen * * 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef UTILS_H #define UTILS_H struct collection { void **list; int capacity; }; void collection_init(struct collection *col); void collection_add(struct collection *col, void *element); void collection_remove(struct collection *col, void *element); int collection_count(struct collection *col); void collection_free(struct collection *col); void collection_copy(struct collection *dest, struct collection *src); #define MERGE_(a,b) a ## _ ## b #define LABEL_(a,b) MERGE_(a, b) #define UNIQUE_VAR(a) LABEL_(a, __LINE__) #define FOREACH(var, col) \ do { \ int UNIQUE_VAR(_iter); \ for(UNIQUE_VAR(_iter)=0; UNIQUE_VAR(_iter)<(col)->capacity; UNIQUE_VAR(_iter)++) { \ if(!(col)->list[UNIQUE_VAR(_iter)]) continue; \ var = (col)->list[UNIQUE_VAR(_iter)]; #define ENDFOREACH \ } \ } while(0); #endif libirecovery-1.0.0/tools/000077500000000000000000000000001367173561200153615ustar00rootroot00000000000000libirecovery-1.0.0/tools/Makefile.am000066400000000000000000000004631367173561200174200ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS) AM_LDFLAGS = $(libusb_LIBS) -lreadline bin_PROGRAMS = irecovery irecovery_SOURCES = irecovery.c irecovery_CFLAGS = $(AM_CFLAGS) irecovery_LDFLAGS = $(AM_LDFLAGS) irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la libirecovery-1.0.0/tools/irecovery.c000066400000000000000000000345151367173561200175440ustar00rootroot00000000000000/* * irecovery.c * Software frontend for iBoot/iBSS communication with iOS devices * * Copyright (c) 2012-2020 Nikias Bassen * Copyright (c) 2012-2015 Martin Szulecki * Copyright (c) 2010-2011 Chronic-Dev Team * Copyright (c) 2010-2011 Joshua Hill * Copyright (c) 2008-2011 Nicolas Haunold * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define TOOL_NAME "irecovery" #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #ifndef sleep #define sleep(n) Sleep(1000 * n) #endif #endif #define FILE_HISTORY_PATH ".irecovery" #define debug(...) if(verbose) fprintf(stderr, __VA_ARGS__) enum { kNoAction, kResetDevice, kStartShell, kSendCommand, kSendFile, kSendExploit, kSendScript, kShowMode, kRebootToNormalMode, kQueryInfo }; static unsigned int quit = 0; static unsigned int verbose = 0; void print_progress_bar(double progress); int received_cb(irecv_client_t client, const irecv_event_t* event); int progress_cb(irecv_client_t client, const irecv_event_t* event); int precommand_cb(irecv_client_t client, const irecv_event_t* event); int postcommand_cb(irecv_client_t client, const irecv_event_t* event); static void shell_usage() { printf("Usage:\n"); printf(" /upload FILE\t\tsend FILE to device\n"); printf(" /limera1n [FILE]\trun limera1n exploit and send optional payload from FILE\n"); printf(" /deviceinfo\t\tprint device information (ECID, IMEI, etc.)\n"); printf(" /help\t\t\tshow this help\n"); printf(" /exit\t\t\texit interactive shell\n"); } static const char* mode_to_str(int mode) { switch (mode) { case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: case IRECV_K_RECOVERY_MODE_4: return "Recovery"; break; case IRECV_K_DFU_MODE: return "DFU"; break; case IRECV_K_WTF_MODE: return "WTF"; break; default: return "Unknown"; break; } } static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) { FILE *f; uint64_t size; *length = 0; f = fopen(filename, "rb"); if (!f) { return; } fseek(f, 0, SEEK_END); size = ftell(f); rewind(f); if (size == 0) { fclose(f); return; } *buffer = (char*)malloc(sizeof(char)*(size+1)); fread(*buffer, sizeof(char), size, f); fclose(f); *length = size; } static void print_hex(unsigned char *buf, size_t len) { size_t i; for (i = 0; i < len; i++) { printf("%02x", buf[i]); } } static void print_device_info(irecv_client_t client) { int ret, mode; const struct irecv_device_info *devinfo = irecv_get_device_info(client); if (devinfo) { printf("CPID: 0x%04x\n", devinfo->cpid); printf("CPRV: 0x%02x\n", devinfo->cprv); printf("BDID: 0x%02x\n", devinfo->bdid); printf("ECID: 0x%016" PRIx64 "\n", devinfo->ecid); printf("CPFM: 0x%02x\n", devinfo->cpfm); printf("SCEP: 0x%02x\n", devinfo->scep); printf("IBFL: 0x%02x\n", devinfo->ibfl); printf("SRTG: %s\n", (devinfo->srtg) ? devinfo->srtg : "N/A"); printf("SRNM: %s\n", (devinfo->srnm) ? devinfo->srnm : "N/A"); printf("IMEI: %s\n", (devinfo->imei) ? devinfo->imei : "N/A"); printf("NONC: "); if (devinfo->ap_nonce) { print_hex(devinfo->ap_nonce, devinfo->ap_nonce_size); } else { printf("N/A"); } printf("\n"); printf("SNON: "); if (devinfo->sep_nonce) { print_hex(devinfo->sep_nonce, devinfo->sep_nonce_size); } else { printf("N/A"); } printf("\n"); char* p = strstr(devinfo->serial_string, "PWND:["); if (p) { p+=6; char* pend = strchr(p, ']'); if (pend) { printf("PWND: %.*s\n", (int)(pend-p), p); } } } else { printf("Could not get device info?!\n"); } ret = irecv_get_mode(client, &mode); if (ret == IRECV_E_SUCCESS) { printf("MODE: %s\n", mode_to_str(mode)); } } static void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) { char* cmd = strdup((char*)command); char* action = strtok(cmd, " "); if (!strcmp(cmd, "/exit")) { quit = 1; } else if (!strcmp(cmd, "/help")) { shell_usage(); } else if (!strcmp(cmd, "/upload")) { char* filename = strtok(NULL, " "); debug("Uploading file %s\n", filename); if (filename != NULL) { irecv_send_file(client, filename, 0); } } else if (!strcmp(cmd, "/deviceinfo")) { print_device_info(client); } else if (!strcmp(cmd, "/limera1n")) { char* filename = strtok(NULL, " "); debug("Sending limera1n payload %s\n", filename); if (filename != NULL) { irecv_send_file(client, filename, 0); } irecv_trigger_limera1n_exploit(client); } else if (!strcmp(cmd, "/execute")) { char* filename = strtok(NULL, " "); debug("Executing script %s\n", filename); if (filename != NULL) { char* buffer = NULL; uint64_t buffer_length = 0; buffer_read_from_filename(filename, &buffer, &buffer_length); if (buffer) { buffer[buffer_length] = '\0'; irecv_execute_script(client, buffer); free(buffer); } else { printf("Could not read file '%s'\n", filename); } } } else { printf("Unsupported command %s. Use /help to get a list of available commands.\n", cmd); } free(action); } static void load_command_history() { read_history(FILE_HISTORY_PATH); } static void append_command_to_history(char* cmd) { add_history(cmd); write_history(FILE_HISTORY_PATH); } static void init_shell(irecv_client_t client) { irecv_error_t error = 0; load_command_history(); irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL); irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL); irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL); while (!quit) { error = irecv_receive(client); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } char* cmd = readline("> "); if (cmd && *cmd) { error = irecv_send_command(client, cmd); if (error != IRECV_E_SUCCESS) { quit = 1; } append_command_to_history(cmd); free(cmd); } } } int received_cb(irecv_client_t client, const irecv_event_t* event) { if (event->type == IRECV_RECEIVED) { int i = 0; int size = event->size; const char* data = event->data; for (i = 0; i < size; i++) { printf("%c", data[i]); } } return 0; } int precommand_cb(irecv_client_t client, const irecv_event_t* event) { if (event->type == IRECV_PRECOMMAND) { if (event->data[0] == '/') { parse_command(client, (unsigned char*)event->data, event->size); return -1; } } return 0; } int postcommand_cb(irecv_client_t client, const irecv_event_t* event) { char* value = NULL; char* action = NULL; char* command = NULL; char* argument = NULL; irecv_error_t error = IRECV_E_SUCCESS; if (event->type == IRECV_POSTCOMMAND) { command = strdup(event->data); action = strtok(command, " "); if (!strcmp(action, "getenv")) { argument = strtok(NULL, " "); error = irecv_getenv(client, argument, &value); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); free(command); return error; } printf("%s\n", value); free(value); } if (!strcmp(action, "reboot")) { quit = 1; } } free(command); return 0; } int progress_cb(irecv_client_t client, const irecv_event_t* event) { if (event->type == IRECV_PROGRESS) { print_progress_bar(event->progress); } return 0; } void print_progress_bar(double progress) { int i = 0; if(progress < 0) { return; } if(progress > 100) { progress = 100; } printf("\r["); for(i = 0; i < 50; i++) { if(i < progress / 2) { printf("="); } else { printf(" "); } } printf("] %3.1f%%", progress); fflush(stdout); if(progress == 100) { printf("\n"); } } static void print_usage(int argc, char **argv) { char *name = NULL; name = strrchr(argv[0], '/'); printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); printf("\n"); printf("Interact with an iOS device in DFU or recovery mode.\n"); printf("\n"); printf("OPTIONS:\n"); printf(" -i, --ecid ECID\tconnect to specific device by its ECID\n"); printf(" -c, --command CMD\trun CMD on device\n"); printf(" -m, --mode\t\tprint current device mode\n"); printf(" -f, --file FILE\tsend file to device\n"); printf(" -k, --payload FILE\tsend limera1n usb exploit payload from FILE\n"); printf(" -r, --reset\t\treset client\n"); printf(" -n, --normal\t\treboot device into normal mode (exit recovery loop)\n"); printf(" -e, --script FILE\texecutes recovery script from FILE\n"); printf(" -s, --shell\t\tstart an interactive shell\n"); printf(" -q, --query\t\tquery device info\n"); printf(" -v, --verbose\t\tenable verbose output, repeat for higher verbosity\n"); printf(" -h, --help\t\tprints this usage information\n"); printf(" -V, --version\t\tprints version information\n"); printf("\n"); printf("Homepage: <" PACKAGE_URL ">\n"); printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n"); } int main(int argc, char* argv[]) { static struct option longopts[] = { { "ecid", required_argument, NULL, 'i' }, { "command", required_argument, NULL, 'c' }, { "mode", no_argument, NULL, 'm' }, { "file", required_argument, NULL, 'f' }, { "payload", required_argument, NULL, 'k' }, { "reset", no_argument, NULL, 'r' }, { "normal", no_argument, NULL, 'n' }, { "script", required_argument, NULL, 'e' }, { "shell", no_argument, NULL, 's' }, { "query", no_argument, NULL, 'q' }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; int i = 0; int opt = 0; int action = kNoAction; unsigned long long ecid = 0; int mode = -1; char* argument = NULL; irecv_error_t error = 0; char* buffer = NULL; uint64_t buffer_length = 0; if (argc == 1) { print_usage(argc, argv); return 0; } while ((opt = getopt_long(argc, argv, "i:vVhrsmnc:f:e:k:q", longopts, NULL)) > 0) { switch (opt) { case 'i': if (optarg) { char* tail = NULL; ecid = strtoull(optarg, &tail, 0); if (tail && (tail[0] != '\0')) { ecid = 0; } if (ecid == 0) { fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg); return -1; } } break; case 'v': verbose += 1; break; case 'h': print_usage(argc, argv); return 0; case 'm': action = kShowMode; break; case 'n': action = kRebootToNormalMode; break; case 'r': action = kResetDevice; break; case 's': action = kStartShell; break; case 'f': action = kSendFile; argument = optarg; break; case 'c': action = kSendCommand; argument = optarg; break; case 'k': action = kSendExploit; argument = optarg; break; case 'e': action = kSendScript; argument = optarg; break; case 'q': action = kQueryInfo; break; case 'V': printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); return 0; default: fprintf(stderr, "Unknown argument\n"); return -1; } } if (action == kNoAction) { fprintf(stderr, "ERROR: Missing action option\n"); print_usage(argc, argv); return -1; } if (verbose) irecv_set_debug_level(verbose); irecv_client_t client = NULL; for (i = 0; i <= 5; i++) { debug("Attempting to connect... \n"); irecv_error_t err = irecv_open_with_ecid(&client, ecid); if (err == IRECV_E_UNSUPPORTED) { fprintf(stderr, "ERROR: %s\n", irecv_strerror(err)); return -1; } else if (err != IRECV_E_SUCCESS) sleep(1); else break; if (i == 5) { fprintf(stderr, "ERROR: %s\n", irecv_strerror(err)); return -1; } } irecv_device_t device = NULL; irecv_devices_get_device_by_client(client, &device); if (device) debug("Connected to %s, model %s, cpid 0x%04x, bdid 0x%02x\n", device->product_type, device->hardware_model, device->chip_id, device->board_id); switch (action) { case kResetDevice: irecv_reset(client); break; case kSendFile: irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 1); debug("%s\n", irecv_strerror(error)); break; case kSendCommand: error = irecv_send_command(client, argument); debug("%s\n", irecv_strerror(error)); break; case kSendExploit: if (argument != NULL) { irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); error = irecv_send_file(client, argument, 0); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } } error = irecv_trigger_limera1n_exploit(client); debug("%s\n", irecv_strerror(error)); break; case kStartShell: init_shell(client); break; case kSendScript: buffer_read_from_filename(argument, &buffer, &buffer_length); if (buffer) { buffer[buffer_length] = '\0'; error = irecv_execute_script(client, buffer); if(error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); } free(buffer); } else { fprintf(stderr, "Could not read file '%s'\n", argument); } break; case kShowMode: irecv_get_mode(client, &mode); printf("%s Mode\n", mode_to_str(mode)); break; case kRebootToNormalMode: error = irecv_setenv(client, "auto-boot", "true"); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } error = irecv_saveenv(client); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); break; } error = irecv_reboot(client); if (error != IRECV_E_SUCCESS) { debug("%s\n", irecv_strerror(error)); } else { debug("%s\n", irecv_strerror(error)); } break; case kQueryInfo: print_device_info(client); break; default: fprintf(stderr, "Unknown action\n"); break; } irecv_close(client); return 0; } libirecovery-1.0.0/udev/000077500000000000000000000000001367173561200151645ustar00rootroot00000000000000libirecovery-1.0.0/udev/39-libirecovery.rules.in000066400000000000000000000006331367173561200215760ustar00rootroot00000000000000# Handle iOS devices in DFU and Recovery mode - for use with libirecovery # Change group and permissions of iOS devices in DFU, legacy WTF, and Recovery mode ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="122[27]|128[0-3]", @udev_activation_rule@ # Handle checkra1n DFU mode ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="1338", @udev_activation_rule@ libirecovery-1.0.0/udev/Makefile.am000066400000000000000000000005411367173561200172200ustar00rootroot00000000000000if WITH_UDEV edit = \ $(SED) -r \ -e 's|@udev_activation_rule[@]|$(udev_activation_rule)|g' \ < $< > $@ || rm $@ udevrules_DATA = \ 39-libirecovery.rules 39-libirecovery.rules: 39-libirecovery.rules.in $(edit) EXTRA_DIST = \ 39-libirecovery.rules.in MAINTAINERCLEANFILES = \ 39-libirecovery.rules CLEANFILES = \ 39-libirecovery.rules endif