diff options
-rw-r--r-- | .github/FUNDING.yml | 3 | ||||
-rw-r--r-- | .github/workflows/build.yml | 19 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | NEWS | 47 | ||||
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | configure.ac | 35 | ||||
-rw-r--r-- | include/libirecovery.h | 10 | ||||
-rw-r--r-- | src/libirecovery.c | 562 | ||||
-rw-r--r-- | tools/Makefile.am | 5 | ||||
-rw-r--r-- | tools/irecovery.c | 67 |
10 files changed, 585 insertions, 181 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e995b30 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: nikias +patreon: nikias +custom: ["https://www.paypal.me/NikiasBassen"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d36f2a..600e9c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,14 +17,14 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{env.target_triplet}} repo: libimobiledevice/libplist - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -49,7 +49,7 @@ jobs: run: | mkdir -p dest DESTDIR=`pwd`/dest make install - tar -C dest -cf libirecovery.tar lib usr + tar -C dest -cf libirecovery.tar --strip-components 1 . - name: publish artifact uses: actions/upload-artifact@v4 with: @@ -67,14 +67,14 @@ jobs: fi shell: bash - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_macOS repo: libimobiledevice/libplist - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -109,14 +109,14 @@ jobs: run: | mkdir -p dest DESTDIR=`pwd`/dest make install - tar -C dest -cf libirecovery.tar usr + tar -C dest -cf libirecovery.tar --strip-components 1 . - name: publish artifact uses: actions/upload-artifact@v4 with: name: libirecovery-latest_macOS path: libirecovery.tar build-windows: - runs-on: windows-2019 + runs-on: windows-latest defaults: run: shell: msys2 {0} @@ -137,6 +137,7 @@ jobs: base-devel git mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-pkg-config make libtool autoconf @@ -147,14 +148,14 @@ jobs: echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libplist - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml diff --git a/Makefile.am b/Makefile.am index 6c48faf..80b0eae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,6 +7,7 @@ EXTRA_DIST = \ git-version-gen dist-hook: + @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi echo $(VERSION) > $(distdir)/.tarball-version DISTCHECK_CONFIGURE_FLAGS = \ @@ -1,3 +1,50 @@ +Version 1.3.1 +~~~~~~~~~~~~~ + +* Device database changes: + - Support iPad Pro M5 family devices + - Support Apple Vision Pro M5 + - Support MacBook Pro 14-inch M5 + +* Bug Fixes: + Fix: array initialization compatibility with MSVC + + +Version 1.3.0 +~~~~~~~~~~~~~ + +* Changes: + - Switch to better initializer strategy + - Allow building without readline support for the irecovery tool + - Added support for iOS 1 and iOS 2 recovery mode (Linux, macOS) + +* Device database changes: + - Support iPhone 17 family devices + - Support Watch 11 / SE3 / Ultra3 models + - Support March 2025 iPad and Mac models + - Support iPhone 16e + - Support November 2024 Mac models + - Support iPad mini (A17 Pro) + +* Bug Fixes: + - IOKit: Fix race condition when trying to delete runloop before it even started + + +Version 1.2.1 +~~~~~~~~~~~~~ + +* Changes: + - Make sure IRECV_DEVICE_REMOVE event has the mode set the device was in + - KIS: Add some retry loops around open/set config/set interface operations + +* Device database changes: + - Support Apple Watch Series 10 and iPhone 16 models + - Add iPad Air (M2) and iPad Pro (M4) models + +* Bug Fixes: + - Windows: Fix crash due to access to uninitialized data + + Version 1.2.0 ~~~~~~~~~~~~~ @@ -39,8 +39,9 @@ have common build steps across different platforms. Only the prerequisites differ and they are described in this section. libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue). +Some platforms already provide it as a package. Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building) -section of the README on how to build it. Note that some platforms might have it as a package. +section of the README on how to build it. #### Linux (Debian/Ubuntu based) @@ -68,12 +69,12 @@ section of the README on how to build it. Note that some platforms might have it Using MacPorts: ```shell - sudo port install libtool autoconf automake pkgconfig + sudo port install libtool autoconf automake pkgconfig libimobiledevice-glue ``` Using Homebrew: ```shell - brew install libtool autoconf automake pkg-config + brew install libtool autoconf automake pkg-config libimobiledevice-glue ``` #### Windows @@ -91,7 +92,9 @@ section of the README on how to build it. Note that some platforms might have it libtool \ autoconf \ automake-wrapper \ - pkg-config + pkg-config \ + mingw-w64-x86_64-libimobiledevice-glue \ + mingw-w64-x86_64-readline ``` NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. @@ -236,8 +239,8 @@ Please make sure your contribution adheres to: ## Links * Homepage: https://libimobiledevice.org/ -* Repository: https://git.libimobiledevice.org/libirecovery.git -* Repository (Mirror): https://github.com/libimobiledevice/libirecovery.git +* Repository: https://github.com/libimobiledevice/libirecovery.git +* Repository (Mirror): https://git.libimobiledevice.org/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 @@ -255,4 +258,4 @@ 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: 2024-03-23 +README Updated on: 2025-09-10 diff --git a/configure.ac b/configure.ac index 94bb793..52a28b5 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ dnl libtool versioning # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE -LIBIRECOVERY_SO_VERSION=5:0:0 +LIBIRECOVERY_SO_VERSION=6:2:1 dnl Minimum package versions LIBUSB_VERSION=1.0.3 @@ -77,35 +77,21 @@ case ${host_os} in esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) -# Check if the C compiler supports __attribute__((constructor)) -AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes], - ac_cv_attribute_constructor, [ - ac_cv_attribute_constructor=no - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[ - static void __attribute__((constructor)) test_constructor(void) { - } - static void __attribute__((destructor)) test_destructor(void) { - } - ]], [])], - [ac_cv_attribute_constructor=yes] - )] -) -if test "$ac_cv_attribute_constructor" = "yes"; then - AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes]) -fi - AC_ARG_WITH([tools], - [AS_HELP_STRING([--with-tools], [Build irecovery tools. (requires readline) [default=yes]])], + [AS_HELP_STRING([--with-tools], [Build irecovery tools. [default=yes]])], [], [with_tools=yes]) AS_IF([test "x$with_tools" = "xyes"], [ + have_readline=no AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools]) - AC_CHECK_HEADERS([readline/readline.h], [], - [AC_MSG_ERROR([Please install readline development headers])] - )] -) + AC_CHECK_HEADERS([readline/readline.h], + [AC_DEFINE(HAVE_READLINE, 1, [Define if readline is available]) + have_readline=yes], + [AC_MSG_NOTICE([NOTE: Building without readline support. If you want readline support, install its development package.])] + ) + AM_CONDITIONAL(HAVE_READLINE, test "x$have_readline" = "xyes") +]) AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes") AC_ARG_WITH([dummy], @@ -224,6 +210,7 @@ Configuration for $PACKAGE $VERSION: Install prefix: .........: $prefix USB backend: ............: $USB_BACKEND + Build tools: ............: $with_tools Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. diff --git a/include/libirecovery.h b/include/libirecovery.h index ec9255b..e37b060 100644 --- a/include/libirecovery.h +++ b/include/libirecovery.h @@ -106,6 +106,13 @@ struct irecv_device_info { unsigned char* sep_nonce; unsigned int sep_nonce_size; uint16_t pid; + unsigned int have_cpid : 1; + unsigned int have_cprv : 1; + unsigned int have_cpfm : 1; + unsigned int have_scep : 1; + unsigned int have_bdid : 1; + unsigned int have_ecid : 1; + unsigned int have_ibfl : 1; }; typedef enum { @@ -132,8 +139,6 @@ enum { /* library */ IRECV_API void irecv_set_debug_level(int level); IRECV_API const char* irecv_strerror(irecv_error_t error); -IRECV_API void irecv_init(void); /* deprecated: libirecovery has constructor now */ -IRECV_API void irecv_exit(void); /* deprecated: libirecovery has destructor now */ IRECV_API const char* irecv_version(); @@ -156,6 +161,7 @@ IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int c IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface); 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); IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); +IRECV_API int irecv_usb_interrupt_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); diff --git a/src/libirecovery.c b/src/libirecovery.c index a60782c..bf9a0d6 100644 --- a/src/libirecovery.c +++ b/src/libirecovery.c @@ -36,7 +36,7 @@ #include <libimobiledevice-glue/thread.h> #ifndef USE_DUMMY -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT #include <libusb.h> #if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102)) @@ -71,6 +71,33 @@ #include "libirecovery.h" +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) +#endif + struct irecv_client_private { int debug; int usb_config; @@ -80,7 +107,7 @@ struct irecv_client_private { int isKIS; struct irecv_device_info device_info; #ifndef USE_DUMMY -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT libusb_device_handle* handle; #else @@ -122,7 +149,7 @@ struct irecv_client_private { static int libirecovery_debug = 0; #ifndef USE_DUMMY -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT static libusb_context* libirecovery_context = NULL; #endif @@ -187,6 +214,15 @@ static struct irecv_device irecv_devices[] = { { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" }, { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" }, { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" }, + { "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" }, + { "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" }, + { "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" }, + { "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" }, + { "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" }, + { "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" }, + { "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" }, + { "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" }, + { "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" }, /* iPod */ { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" }, { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" }, @@ -279,6 +315,26 @@ static struct irecv_device irecv_devices[] = { { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" }, { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" }, { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" }, + { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" }, + { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" }, + { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" }, + { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" }, + { "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" }, + { "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" }, + { "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" }, + { "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" }, + { "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" }, + { "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" }, + { "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" }, + { "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" }, + { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" }, + { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" }, + { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" }, + { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" }, + { "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" }, + { "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" }, + { "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" }, + { "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" }, /* Apple TV */ { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" }, { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" }, @@ -337,6 +393,19 @@ static struct irecv_device irecv_devices[] = { { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" }, { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" }, { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" }, + { "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" }, + { "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" }, + { "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" }, + { "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" }, + { "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" }, + { "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" }, + { "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" }, + { "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" }, + { "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" }, + { "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" }, + { "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" }, + { "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" }, + { "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" }, /* Apple Silicon Macs */ { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" }, { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" }, @@ -373,6 +442,20 @@ static struct irecv_device irecv_devices[] = { { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" }, { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" }, { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" }, + { "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" }, + { "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" }, + { "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" }, + { "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" }, + { "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" }, + { "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" }, + { "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" }, + { "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" }, + { "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" }, + { "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" }, + { "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" }, + { "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" }, + { "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" }, + { "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" }, /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */ { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" }, /* Apple T2 Coprocessor */ @@ -396,6 +479,7 @@ static struct irecv_device irecv_devices[] = { { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" }, /* Apple Vision Pro */ { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" }, + { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" }, { NULL, NULL, -1, -1, NULL } }; @@ -470,7 +554,7 @@ static unsigned int crc32_lookup_t1[256] = { #define crc32_step(a,b) \ a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8)) -#ifdef WIN32 +#ifdef _WIN32 #pragma pack(1) typedef struct { uint16_t vid; @@ -563,12 +647,30 @@ typedef struct { #pragma pack() #endif +#pragma pack(1) +typedef struct { + uint16_t cmdcode; +#define MSG_VERSION_QUERY 0x000 +#define MSG_ECHO 0x801 +#define MSG_DUMP_BUFFER 0x802 +#define MSG_SEND_COMMAND 0x803 +#define MSG_READ_FILE 0x804 +#define MSG_SEND_FILE 0x805 +#define MSG_CRC 0x807 +#define MSG_ACK 0x808 +#define MSG_REJECT 0x809 + uint16_t magic; // always 0x1234 + uint32_t size; + uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files +} legacyCMD; +#pragma pack() + 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 +#ifndef _WIN32 #ifdef HAVE_IOKIT static CFRunLoopRef iokit_runloop = NULL; #else @@ -576,28 +678,10 @@ 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 _WIN32 #ifndef HAVE_IOKIT if (libirecovery_context != NULL) { libusb_exit(libirecovery_context); @@ -610,43 +694,24 @@ static void _irecv_deinit(void) #endif } -static thread_once_t init_once = THREAD_ONCE_INIT; -static thread_once_t deinit_once = THREAD_ONCE_INIT; - -#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR - #if defined(__llvm__) || defined(__GNUC__) - #define HAVE_ATTRIBUTE_CONSTRUCTOR - #endif -#endif - -#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR -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); -} -#elif defined(WIN32) -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) +INITIALIZER(_irecv_init) { - 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; + char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL"); + if (dbglvl) { + libirecovery_debug = strtol(dbglvl, NULL, 0); + irecv_set_debug_level(libirecovery_debug); } - return 1; -} -#else -#warning No compiler support for constructor/destructor attributes, some features might not be available. +#ifndef USE_DUMMY +#ifndef _WIN32 +#ifndef HAVE_IOKIT + libusb_init(&libirecovery_context); +#endif +#endif + collection_init(&listeners); + mutex_init(&listener_mutex); #endif + atexit(_irecv_deinit); +} #ifdef HAVE_IOKIT static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) @@ -699,7 +764,7 @@ static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) { -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size); #else @@ -710,10 +775,10 @@ static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc unsigned short langid = 0; unsigned char data[256]; int di, si; - memset(data, 0, 256); + memset(data, 0, sizeof(data)); memset(buffer, 0, size); - ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, 255, USB_TIMEOUT); + ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT); if (ret < 0) return ret; if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR; @@ -749,21 +814,28 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons ptr = strstr(iboot_string, "CPID:"); if (ptr != NULL) { sscanf(ptr, "CPID:%x", &client->device_info.cpid); + client->device_info.have_cpid = 1; + } else { + // early iOS 1 doesn't identify itself + client->device_info.cpid = 0x8900; } ptr = strstr(iboot_string, "CPRV:"); if (ptr != NULL) { sscanf(ptr, "CPRV:%x", &client->device_info.cprv); + client->device_info.have_cprv = 1; } ptr = strstr(iboot_string, "CPFM:"); if (ptr != NULL) { sscanf(ptr, "CPFM:%x", &client->device_info.cpfm); + client->device_info.have_cpfm = 1; } ptr = strstr(iboot_string, "SCEP:"); if (ptr != NULL) { sscanf(ptr, "SCEP:%x", &client->device_info.scep); + client->device_info.have_scep = 1; } ptr = strstr(iboot_string, "BDID:"); @@ -771,16 +843,19 @@ static void irecv_load_device_info_from_iboot_string(irecv_client_t client, cons uint64_t bdid = 0; sscanf(ptr, "BDID:%" SCNx64, &bdid); client->device_info.bdid = (unsigned int)bdid; + client->device_info.have_bdid = 1; } ptr = strstr(iboot_string, "ECID:"); if (ptr != NULL) { sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid); + client->device_info.have_ecid = 1; } ptr = strstr(iboot_string, "IBFL:"); if (ptr != NULL) { sscanf(ptr, "IBFL:%x", &client->device_info.ibfl); + client->device_info.have_ibfl = 1; } char tmp[256]; @@ -900,8 +975,8 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un *nonce = NULL; *nonce_size = 0; - memset(buf, 0, 256); - len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*) buf, 255); + memset(buf, 0, sizeof(buf)); + len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1); if (len < 0) { debug("%s: got length: %d\n", __func__, len); return; @@ -912,7 +987,7 @@ static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, un irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf); } -#ifndef WIN32 +#ifndef _WIN32 static irecv_error_t irecv_kis_request_init(KIS_req_header *hdr, uint8_t portal, uint16_t index, size_t argCount, size_t payloadSize, size_t rplWords) { if (argCount > UINT8_MAX) { @@ -1045,7 +1120,7 @@ static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, siz static irecv_error_t irecv_kis_init(irecv_client_t client) { -#ifndef WIN32 +#ifndef _WIN32 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL); if (err != IRECV_E_SUCCESS) { debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err); @@ -1066,7 +1141,7 @@ static irecv_error_t irecv_kis_init(irecv_client_t client) static irecv_error_t irecv_kis_load_device_info(irecv_client_t client) { debug("Loading device info in KIS mode...\n"); -#ifdef WIN32 +#ifdef _WIN32 KIS_device_info kisInfo; DWORD transferred = 0; int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL); @@ -1134,7 +1209,7 @@ static irecv_error_t irecv_kis_load_device_info(irecv_client_t client) return IRECV_E_SUCCESS; } -#ifdef WIN32 +#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}}; static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}}; @@ -1307,20 +1382,6 @@ static int check_context(irecv_client_t client) } #endif -void irecv_init(void) -{ -#ifndef USE_DUMMY - thread_once(&init_once, _irecv_init); -#endif -} - -void irecv_exit(void) -{ -#ifndef USE_DUMMY - thread_once(&deinit_once, _irecv_deinit); -#endif -} - #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) @@ -1361,7 +1422,7 @@ int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, u #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; #else -#ifndef WIN32 +#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 @@ -1481,6 +1542,74 @@ static int iokit_usb_bulk_transfer(irecv_client_t client, return IRECV_E_USB_INTERFACE; } + +static int iokit_usb_interrupt_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 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0; + UInt8 numEndpoints; + + if (!intf) return IRECV_E_USB_INTERFACE; + + result = (*intf)->GetNumEndpoints(intf, &numEndpoints); + + if (result != kIOReturnSuccess) + return IRECV_E_USB_INTERFACE; + + for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) { + UInt8 direction = 0; + UInt8 number = 0; + UInt8 transferType = 0; + UInt16 maxPacketSize = 0; + UInt8 interval = 0; + + result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval); + if (result != kIOReturnSuccess) + continue; + + if (direction == 3) + direction = isUSBIn; + + if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn) + continue; + + // 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 (isUSBIn) { + result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout); + if (result != kIOReturnSuccess) + return IRECV_E_PIPE; + *transferred = size; + + return IRECV_E_SUCCESS; + } + else { + 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 @@ -1496,7 +1625,7 @@ int irecv_usb_bulk_transfer(irecv_client_t client, #else int ret; -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout); #else @@ -1518,6 +1647,36 @@ int irecv_usb_bulk_transfer(irecv_client_t client, #endif } +IRECV_API int irecv_usb_interrupt_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_interrupt_transfer(client, endpoint, data, length, transferred, timeout); +#else + ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout); + if (ret < 0) { + libusb_clear_halt(client->handle, endpoint); + } +#endif +#else + // win32 + return IRECV_E_UNSUPPORTED; +#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) @@ -1686,7 +1845,7 @@ static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid } #endif -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_client_t *pclient, struct libusb_device_handle *usb_handle, struct libusb_device_descriptor *usb_descriptor, uint64_t ecid) { @@ -1703,8 +1862,8 @@ static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_clien if (client->mode != KIS_PRODUCT_ID) { char serial_str[256]; - memset(serial_str, 0, 256); - irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, 255); + memset(serial_str, 0, sizeof(serial_str)); + irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1); irecv_load_device_info_from_iboot_string(client, serial_str); } @@ -1797,7 +1956,7 @@ irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid) if (libirecovery_debug) { irecv_set_debug_level(libirecovery_debug); } -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT error = iokit_open_with_ecid(pclient, ecid); #else @@ -1880,7 +2039,7 @@ irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configurati if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; -#ifndef WIN32 +#ifndef _WIN32 debug("Setting to configuration %d\n", configuration); #ifdef HAVE_IOKIT @@ -1999,7 +2158,7 @@ irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, return IRECV_E_NO_DEVICE; debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface); -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) { return IRECV_E_USB_INTERFACE; @@ -2037,7 +2196,7 @@ irecv_error_t irecv_reset(irecv_client_t client) if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT IOReturn result; @@ -2176,7 +2335,7 @@ struct irecv_usb_device_info { int alive; }; -#ifdef WIN32 +#ifdef _WIN32 struct irecv_win_dev_ctx { PSP_DEVICE_INTERFACE_DETAIL_DATA_A details; uint32_t location; @@ -2194,7 +2353,7 @@ static int _irecv_is_recovery_device(void *device) { uint16_t vendor_id = 0; uint16_t product_id = 0; -#ifdef WIN32 +#ifdef _WIN32 const char *path = (const char*)device; unsigned int vendor = 0; unsigned int product = 0; @@ -2261,8 +2420,8 @@ static void* _irecv_handle_device_add(void *userdata) irecv_error_t error = 0; irecv_client_t client = NULL; - memset(serial_str, 0, 256); -#ifdef WIN32 + memset(serial_str, 0, sizeof(serial_str)); +#ifdef _WIN32 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata; PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details; LPSTR result = (LPSTR)details->DevicePath; @@ -2294,6 +2453,11 @@ static void* _irecv_handle_device_add(void *userdata) if (product_id == KIS_PRODUCT_ID) { client = (irecv_client_t)malloc(sizeof(struct irecv_client_private)); + if (client == NULL) { + debug("%s: Failed to allocate memory\n", __func__); + return NULL; + } + memset(client, '\0', sizeof(struct irecv_client_private)); client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (client->handle == INVALID_HANDLE_VALUE) { debug("%s: Failed to open device path %s\n", __func__, result); @@ -2317,7 +2481,7 @@ static void* _irecv_handle_device_add(void *userdata) } } -#else /* !WIN32 */ +#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; @@ -2349,13 +2513,19 @@ static void* _irecv_handle_device_add(void *userdata) if (product_id == KIS_PRODUCT_ID) { IOObjectRetain(device); - - error = iokit_usb_open_service(&client, device); + int i = 0; + for (i = 0; i < 10; i++) { + error = iokit_usb_open_service(&client, device); + if (error == IRECV_E_SUCCESS) { + break; + } + debug("%s: Could not open KIS device, retrying...\n", __func__); + usleep(500000); + } if (error != IRECV_E_SUCCESS) { debug("%s: ERROR: could not open KIS device!\n", __func__); return NULL; } - product_id = client->mode; } else { CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0); @@ -2397,7 +2567,7 @@ static void* _irecv_handle_device_add(void *userdata) product_id = client->mode; } else { - libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, 255); + libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1); if (libusb_error < 0) { debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error)); return 0; @@ -2405,17 +2575,32 @@ static void* _irecv_handle_device_add(void *userdata) libusb_close(usb_handle); } #endif /* !HAVE_IOKIT */ -#endif /* !WIN32 */ +#endif /* !_WIN32 */ memset(&client_loc, '\0', sizeof(client_loc)); if (product_id == KIS_PRODUCT_ID) { - error = irecv_usb_set_configuration(client, 1); + int i = 0; + for (i = 0; i < 10; i++) { + error = irecv_usb_set_configuration(client, 1); + if (error == IRECV_E_SUCCESS) { + break; + } + debug("Failed to set configuration, error %d, retrying...\n", error); + usleep(500000); + } if (error != IRECV_E_SUCCESS) { debug("Failed to set configuration, error %d\n", error); irecv_close(client); return NULL; } - error = irecv_usb_set_interface(client, 0, 0); + for (i = 0; i < 10; i++) { + error = irecv_usb_set_interface(client, 0, 0); + if (error == IRECV_E_SUCCESS) { + break; + } + debug("Failed to set interface, error %d, retrying...\n", error); + usleep(500000); + } if (error != IRECV_E_SUCCESS) { debug("Failed to set interface, error %d\n", error); irecv_close(client); @@ -2473,7 +2658,7 @@ 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.mode = devinfo->mode; dev_event.device_info = &(devinfo->device_info); mutex_lock(&listener_mutex); FOREACH(struct irecv_device_event_context* context, &listeners) { @@ -2493,7 +2678,7 @@ static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo) free(devinfo); } -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT static void iokit_device_added(void *refcon, io_iterator_t iterator) { @@ -2590,7 +2775,7 @@ static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, lib } #endif /* HAVE_LIBUSB_HOTPLUG_API */ #endif /* !HAVE_IOKIT */ -#endif /* !WIN32 */ +#endif /* !_WIN32 */ struct _irecv_event_handler_info { cond_t startup_cond; @@ -2600,7 +2785,7 @@ struct _irecv_event_handler_info { static void *_irecv_event_handler(void* data) { struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data; -#ifdef WIN32 +#ifdef _WIN32 struct collection newDevices; const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL }; int running = 1; @@ -2744,7 +2929,7 @@ static void *_irecv_event_handler(void* data) } while (running); collection_free(&newDevices); -#else /* !WIN32 */ +#else /* !_WIN32 */ #ifdef HAVE_IOKIT kern_return_t kr; @@ -2869,7 +3054,7 @@ static void *_irecv_event_handler(void* data) } while (running); #endif /* !HAVE_LIBUSB_HOTPLUG_API */ #endif /* !HAVE_IOKIT */ -#endif /* !WIN32 */ +#endif /* !_WIN32 */ return NULL; } #endif /* !USE_DUMMY */ @@ -2898,7 +3083,7 @@ irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context struct _irecv_event_handler_info info; cond_init(&info.startup_cond); mutex_init(&info.startup_mutex); -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT libusb_init(&irecv_hotplug_ctx); #endif @@ -2948,6 +3133,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) { #ifdef HAVE_IOKIT if (iokit_runloop) { + while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420); CFRunLoopStop(iokit_runloop); iokit_runloop = NULL; } @@ -2970,7 +3156,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex collection_free(&devices); mutex_unlock(&device_mutex); mutex_destroy(&device_mutex); -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT libusb_exit(irecv_hotplug_ctx); irecv_hotplug_ctx = NULL; @@ -2984,7 +3170,7 @@ irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t contex #endif } -irecv_error_t irecv_close(irecv_client_t client) +static irecv_error_t irecv_cleanup(irecv_client_t client) { #ifdef USE_DUMMY return IRECV_E_UNSUPPORTED; @@ -2998,7 +3184,7 @@ irecv_error_t irecv_close(irecv_client_t client) event.type = IRECV_DISCONNECTED; client->disconnected_callback(client, &event); } -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_IOKIT if (client->usbInterface) { (*client->usbInterface)->USBInterfaceClose(client->usbInterface); @@ -3028,20 +3214,28 @@ irecv_error_t irecv_close(irecv_client_t client) 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_error_t irecv_close(irecv_client_t client) +{ + irecv_error_t ret = IRECV_E_SUCCESS; + if (client) { + ret = irecv_cleanup(client); + free(client); + client = NULL; + } + return ret; +} + void irecv_set_debug_level(int level) { libirecovery_debug = level; #ifndef USE_DUMMY -#ifndef WIN32 +#ifndef _WIN32 #ifndef HAVE_IOKIT if (libirecovery_context) { #if LIBUSB_API_VERSION >= 0x01000106 @@ -3072,6 +3266,35 @@ static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* c return IRECV_E_INVALID_INPUT; } + if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) { + int bytes = 0; + irecv_error_t error = 0; +#ifdef DEBUG + uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be + if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error; + if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR; +#endif + char cmdstr[0x100] = {0}; + if (length & 0xf) { + length &= ~0xf; + length += 0x10; + } + snprintf(cmdstr, sizeof(cmdstr), "%s\n",command); + legacyCMD cmd = { + MSG_SEND_COMMAND, + 0x1234, //magic + (uint32_t)length, //zero terminated? + 0x0 + }; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR; + if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error; + sleep(1); //go easy on this old device + return IRECV_E_SUCCESS; + } + if (length > 0) { irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT); } @@ -3179,8 +3402,8 @@ static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* statu } unsigned char buffer[6]; - memset(buffer, '\0', 6); - if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, USB_TIMEOUT) != 6) { + memset(buffer, '\0', sizeof(buffer)); + if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) { *status = 0; return IRECV_E_USB_STATUS; } @@ -3205,7 +3428,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* if (toUpload > 0x4000) toUpload = 0x4000; -#ifdef WIN32 +#ifdef _WIN32 memcpy(chunk->data, buffer, toUpload); chunk->size = toUpload; chunk->address = address; @@ -3222,7 +3445,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* memcpy(chunk->data, buffer, toUpload); #endif -#ifdef WIN32 +#ifdef _WIN32 DWORD transferred = 0; int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL); irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD; @@ -3255,7 +3478,7 @@ static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* free(chunk); if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) { -#ifdef WIN32 +#ifdef _WIN32 DWORD amount = (DWORD)origLen; DWORD transferred = 0; int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL); @@ -3283,6 +3506,30 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un irecv_error_t error = 0; int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE)); + int legacy_recovery_mode = 0; + int isiOS2 = 0; + + if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) { +#ifdef DEBUG + uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be + int bytes = 0; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error; + if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR; +#endif + legacy_recovery_mode = 1; + } + + if (recovery_mode && !legacy_recovery_mode) { + // we are in recovery mode and we are not dealing with iOS 1.x + if ((client->device_info.cpid == 0x8900 || client->device_info.cpid == 0x8720) && !client->device_info.have_ibfl) { + // iOS 2.x doesn't have IBFL tag, but iOS 3 does + // Also, to avoid false activation of this codepath, restrict it to the only two CPID which can run iOS 2 + recovery_mode = 0; // iOS 2 recovery mode works same as DFU mode + isiOS2 = 1; + options |= IRECV_SEND_OPT_DFU_NOTIFY_FINISH; + } + } if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; @@ -3291,6 +3538,7 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10}; int dfu_crc = 1; int packet_size = recovery_mode ? 0x8000 : 0x800; + if (legacy_recovery_mode) packet_size = 0x200; if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) { packet_size = 0x40; dfu_crc = 0; @@ -3305,7 +3553,24 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un } /* initiate transfer */ - if (recovery_mode) { + if (legacy_recovery_mode) { + int bytes = 0; + uint32_t loadaddr0x8900 = 0x09000000; + const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR"); + if (ios1_overwrite_loadaddr) { + sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900); + debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900); + } + legacyCMD cmd = { + MSG_SEND_FILE, + 0x1234, //magic + (uint32_t)length, + loadaddr0x8900 + }; + if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error; + if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR; + } else if (recovery_mode) { error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT); } else { uint8_t state = 0; @@ -3318,6 +3583,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un case 2: /* DFU IDLE */ break; + case 8: + /* DFU WAIT RESET */ + if (!isiOS2) { + debug("Unexpected state %d in non-iOS2 mode!, issuing ABORT\n", state); + irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT); + error = IRECV_E_USB_UPLOAD; + } + break; case 10: debug("DFU ERROR, issuing CLRSTATUS\n"); irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT); @@ -3342,10 +3615,14 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un 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) { + if (legacy_recovery_mode) { + // Use interrupt transfer for legacy devices + error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); + } else if (recovery_mode) { + // Use bulk transfer for recovery mode error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT); } else { + // Use control transfer for DFU and WTF mode if (dfu_crc) { int j; for (j = 0; j < size; j++) { @@ -3451,6 +3728,17 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un } irecv_reset(client); + + if (isiOS2) { + irecv_reconnect(client, 0); + } + } + + if (legacy_recovery_mode) { + irecv_reconnect(client, 0); + char cmdstr[0x100] = {0}; + snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length); + irecv_send_command(client, cmdstr); } return IRECV_E_SUCCESS; @@ -3508,6 +3796,11 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v return IRECV_E_INVALID_INPUT; } + if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) { + debug("iOS 1 doesn't support getenv\n"); + return IRECV_E_UNSUPPORTED; + } + memset(command, '\0', sizeof(command)); snprintf(command, sizeof(command)-1, "getenv %s", variable); irecv_error_t error = irecv_send_command_raw(client, command, 0); @@ -3519,13 +3812,14 @@ irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** v return error; } - char* response = (char*) malloc(256); + int rsize = 256; + char* response = (char*) malloc(rsize); 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); + memset(response, '\0', rsize); + irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT); *value = response; @@ -3543,13 +3837,14 @@ irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) *value = 0; - char* response = (char*) malloc(256); + int rsize = 256; + char* response = (char*) malloc(rsize); 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); + memset(response, '\0', rsize); + irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT); *value = (unsigned int) *response; @@ -3992,7 +4287,7 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) uint64_t ecid = client->device_info.ecid; if (check_context(client) == IRECV_E_SUCCESS) { - irecv_close(client); + irecv_cleanup(client); } if (initial_pause > 0) { @@ -4012,6 +4307,11 @@ irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) new_client->postcommand_callback = postcommand_callback; new_client->disconnected_callback = disconnected_callback; + // keep old handle valid + memcpy(client, new_client, sizeof(*client)); + free(new_client); + new_client = client; + if (new_client->connected_callback != NULL) { irecv_event_t event; event.size = 0; diff --git a/tools/Makefile.am b/tools/Makefile.am index ebb085c..15e2eb9 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -2,12 +2,15 @@ if BUILD_TOOLS AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS) -AM_LDFLAGS = $(libusb_LIBS) -lreadline +AM_LDFLAGS = $(libusb_LIBS) bin_PROGRAMS = irecovery irecovery_SOURCES = irecovery.c irecovery_CFLAGS = $(AM_CFLAGS) irecovery_LDFLAGS = $(AM_LDFLAGS) +if HAVE_READLINE +irecovery_LDFLAGS += -lreadline +endif irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la endif diff --git a/tools/irecovery.c b/tools/irecovery.c index 61d053a..b293324 100644 --- a/tools/irecovery.c +++ b/tools/irecovery.c @@ -31,16 +31,22 @@ #include <string.h> #include <getopt.h> #include <inttypes.h> +#include <ctype.h> #include <libirecovery.h> +#ifdef HAVE_READLINE #include <readline/readline.h> #include <readline/history.h> +#else +#ifndef _WIN32 +#include <termios.h> +#endif +#endif -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> -#ifndef sleep +#include <conio.h> #define sleep(n) Sleep(1000 * n) #endif -#endif #define FILE_HISTORY_PATH ".irecovery" #define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__) @@ -271,14 +277,48 @@ static void parse_command(irecv_client_t client, unsigned char* command, unsigne static void load_command_history() { +#ifdef HAVE_READLINE read_history(FILE_HISTORY_PATH); +#endif } -static void append_command_to_history(char* cmd) +static void append_command_to_history(const char* cmd) { +#ifdef HAVE_READLINE add_history(cmd); write_history(FILE_HISTORY_PATH); +#endif +} + +#ifndef HAVE_READLINE +#ifdef _WIN32 +#define BS_CC '\b' +#else +#define BS_CC 0x7f +#define getch getchar +#endif +static void get_input(char *buf, int maxlen) +{ + int len = 0; + int c; + + while ((c = getch())) { + if ((c == '\r') || (c == '\n')) { + break; + } + if (isprint(c)) { + if (len < maxlen-1) + buf[len++] = c; + } else if (c == BS_CC) { + if (len > 0) { + fputs("\b \b", stdout); + len--; + } + } + } + buf[len] = 0; } +#endif static void init_shell(irecv_client_t client) { @@ -294,8 +334,15 @@ static void init_shell(irecv_client_t client) debug("%s\n", irecv_strerror(error)); break; } - +#ifdef HAVE_READLINE char* cmd = readline("> "); +#else + char cmdbuf[4096]; + const char* cmd = &cmdbuf[0]; + printf("> "); + fflush(stdout); + get_input(cmdbuf, sizeof(cmdbuf)); +#endif if (cmd && *cmd) { if (_is_breq_command(cmd)) { error = irecv_send_command_breq(client, cmd, 1); @@ -307,8 +354,10 @@ static void init_shell(irecv_client_t client) } append_command_to_history(cmd); - free(cmd); } +#ifdef HAVE_READLINE + free(cmd); +#endif } } @@ -544,7 +593,11 @@ int main(int argc, char* argv[]) return 0; case 'V': - printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); + printf("%s %s", TOOL_NAME, PACKAGE_VERSION); +#ifdef HAVE_READLINE + printf(" (readline)"); +#endif + printf("\n"); return 0; default: |