diff options
-rw-r--r-- | src/common.h | 18 | ||||
-rw-r--r-- | src/dfu.c | 45 | ||||
-rw-r--r-- | src/dfu.h | 5 | ||||
-rw-r--r-- | src/idevicerestore.c | 74 | ||||
-rw-r--r-- | src/libirecovery.c | 33 | ||||
-rw-r--r-- | src/libirecovery.h | 1 | ||||
-rw-r--r-- | src/recovery.c | 2 |
7 files changed, 159 insertions, 19 deletions
diff --git a/src/common.h b/src/common.h index 19ce881..6e1441a 100644 --- a/src/common.h +++ b/src/common.h @@ -84,10 +84,11 @@ extern "C" { #define DEVICE_IPHONE4S 14 #define MODE_UNKNOWN -1 -#define MODE_DFU 0 -#define MODE_RECOVERY 1 -#define MODE_RESTORE 2 -#define MODE_NORMAL 3 +#define MODE_WTF 0 +#define MODE_DFU 1 +#define MODE_RECOVERY 2 +#define MODE_RESTORE 3 +#define MODE_NORMAL 4 #define FLAG_QUIT 1 #define FLAG_DEBUG 2 @@ -149,10 +150,11 @@ struct idevicerestore_client_t { }; static struct idevicerestore_mode_t idevicerestore_modes[] = { - { 0, "DFU" }, - { 1, "Recovery" }, - { 2, "Restore" }, - { 3, "Normal" }, + { 0, "WTF" }, + { 1, "DFU" }, + { 2, "Recovery" }, + { 3, "Restore" }, + { 4, "Normal" }, { -1, NULL } }; @@ -83,7 +83,7 @@ void dfu_client_free(struct idevicerestore_client_t* client) { } } -int dfu_check_mode() { +int dfu_check_mode(int* mode) { irecv_client_t dfu = NULL; irecv_error_t dfu_error = IRECV_E_SUCCESS; @@ -94,16 +94,40 @@ int dfu_check_mode() { return -1; } - if (dfu->mode != kDfuMode) { + if ((dfu->mode != kDfuMode) && (dfu->mode != kWTFMode)) { irecv_close(dfu); return -1; } + *mode = (dfu->mode == kWTFMode) ? MODE_WTF : MODE_DFU; + irecv_close(dfu); return 0; } +int dfu_send_buffer(struct idevicerestore_client_t* client, char* buffer, uint32_t size) +{ + irecv_error_t error = 0; + + info("Sending data (%d bytes)...\n", size); + + error = irecv_send_buffer(client->dfu->client, buffer, size, 1); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to send data: %s\n", irecv_strerror(error)); + return -1; + } + + error = irecv_reset(client->dfu->client); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to reset device\n"); + irecv_close(client->dfu->client); + return -1; + } + + return 0; +} + int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { uint32_t size = 0; char* data = NULL; @@ -170,6 +194,23 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide return 0; } +int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) { + irecv_error_t dfu_error = IRECV_E_SUCCESS; + + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + dfu_error = irecv_get_cpid(client->dfu->client, cpid); + if (dfu_error != IRECV_E_SUCCESS) { + return -1; + } + + return 0; +} + int dfu_get_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { irecv_error_t dfu_error = IRECV_E_SUCCESS; @@ -37,6 +37,11 @@ struct dfu_client_t { int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); +int dfu_check_mode(int* mode); +int dfu_send_buffer(struct idevicerestore_client_t* client, char* buffer, uint32_t size); +int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); +int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); +int dfu_get_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 93c6766..4efc0d4 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -217,6 +217,75 @@ int main(int argc, char* argv[]) { } info("Found device in %s mode\n", client->mode->string); + if (client->mode->index == MODE_WTF) { + int cpid = 0; + + if (dfu_client_new(client) != 0) { + error("ERROR: Could not open device in WTF mode\n"); + return -1; + } + if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { + error("ERROR: Could not get CPID for WTF mode device\n"); + dfu_client_free(client); + return -1; + } + + char* s_wtfurl = NULL; + plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); + if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { + plist_get_string_val(wtfurl, &s_wtfurl); + } + if (!s_wtfurl) { + info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); + s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); + } + + // make a local file name + char* fnpart = strrchr(s_wtfurl, '/'); + if (!fnpart) { + fnpart = "x12220000_5_Recovery.ipsw"; + } else { + fnpart++; + } + struct stat fst; + char wtfipsw[256]; + sprintf(wtfipsw, "cache/%s", fnpart); + if (stat(wtfipsw, &fst) != 0) { + __mkdir("cache", 0755); + download_to_file(s_wtfurl, wtfipsw); + } + + char wtfname[256]; + sprintf(wtfname, "Firmware/dfu/WTF.s5l%04dxall.RELEASE.dfu", cpid); + char* wtftmp = NULL; + uint32_t wtfsize = 0; + ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); + if (!wtftmp) { + error("ERROR: Could not extract WTF\n"); + } + + char xbuf[16] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10, 0xcb, 0x55, 0xa4, 0x05}; + char *wtfbuf = (char*)malloc(wtfsize + 16); + if (!wtfbuf) { + error("ERROR: Out of Memory\n"); + return -1; + } + memcpy(wtfbuf, wtftmp, wtfsize); + free(wtftmp); + memcpy(wtfbuf+wtfsize, xbuf, 16); + wtfsize += 16; + + if (dfu_send_buffer(client, wtfbuf, wtfsize) != 0) { + error("ERROR: Could not send WTF...\n"); + } + dfu_client_free(client); + + sleep(1); + + free(wtfbuf); + client->mode = &idevicerestore_modes[MODE_DFU]; + } + // discover the device type if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) { error("ERROR: Unable to discover device type\n"); @@ -657,13 +726,14 @@ int main(int argc, char* argv[]) { int check_mode(struct idevicerestore_client_t* client) { int mode = MODE_UNKNOWN; + int dfumode = MODE_UNKNOWN; if (recovery_check_mode() == 0) { mode = MODE_RECOVERY; } - else if (dfu_check_mode() == 0) { - mode = MODE_DFU; + else if (dfu_check_mode(&dfumode) == 0) { + mode = dfumode; } else if (normal_check_mode(client->uuid) == 0) { diff --git a/src/libirecovery.c b/src/libirecovery.c index 45efa85..7160669 100644 --- a/src/libirecovery.c +++ b/src/libirecovery.c @@ -338,6 +338,7 @@ irecv_error_t irecv_open(irecv_client_t* pclient) { usb_descriptor.idProduct == kRecoveryMode2 || usb_descriptor.idProduct == kRecoveryMode3 || usb_descriptor.idProduct == kRecoveryMode4 || + usb_descriptor.idProduct == kWTFMode || usb_descriptor.idProduct == kDfuMode) { debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); @@ -369,7 +370,7 @@ irecv_error_t irecv_open(irecv_client_t* pclient) { return error; } - if (client->mode != kDfuMode) { + if ((client->mode != kDfuMode) && (client->mode != kWTFMode)) { error = irecv_set_interface(client, 0, 0); if (client->mode > kRecoveryMode2) { error = irecv_set_interface(client, 1, 1); @@ -549,7 +550,7 @@ irecv_error_t irecv_close(irecv_client_t client) { } #ifndef WIN32 if (client->handle != NULL) { - if (client->mode != kDfuMode) { + if ((client->mode != kDfuMode) && (client->mode != kWTFMode)) { libusb_release_interface(client->handle, client->interface); } libusb_close(client->handle); @@ -683,7 +684,7 @@ irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) { irecv_error_t error = 0; - int recovery_mode = (client->mode != kDfuMode); + int recovery_mode = ((client->mode != kDfuMode) && (client->mode != kWTFMode)); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; int packet_size = recovery_mode ? 0x8000 : 0x800; @@ -738,7 +739,17 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un } if (!recovery_mode && status != 5) { - return IRECV_E_USB_UPLOAD; + 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; @@ -844,6 +855,16 @@ irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) { irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; + if (client->mode == kWTFMode) { + char s_cpid[8] = {0,}; + strncpy(s_cpid, client->serial, 4); + if (sscanf(s_cpid, "%d", cpid) != 1) { + *cpid = 0; + return IRECV_E_UNKNOWN_ERROR; + } + return IRECV_E_SUCCESS; + } + char* cpid_string = strstr(client->serial, "CPID:"); if (cpid_string == NULL) { *cpid = 0; @@ -1142,7 +1163,7 @@ int irecv_read_file(const char* filename, char** data, uint32_t* size) { irecv_error_t irecv_reset_counters(irecv_client_t client) { if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; - if (client->mode == kDfuMode) { + if ((client->mode == kDfuMode) || (client->mode == kWTFMode)) { irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT); } return IRECV_E_SUCCESS; @@ -1150,7 +1171,7 @@ irecv_error_t irecv_reset_counters(irecv_client_t client) { irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) { irecv_error_t error = 0; - int recovery_mode = (client->mode != kDfuMode); + int recovery_mode = ((client->mode != kDfuMode) && (client->mode != kWTFMode)); if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE; diff --git a/src/libirecovery.h b/src/libirecovery.h index a4e3aa1..efd5ee8 100644 --- a/src/libirecovery.h +++ b/src/libirecovery.h @@ -90,6 +90,7 @@ enum { kRecoveryMode2 = 0x1281, kRecoveryMode3 = 0x1282, kRecoveryMode4 = 0x1283, + kWTFMode = 0x1222, kDfuMode = 0x1227 }; diff --git a/src/recovery.c b/src/recovery.c index 9e86cc5..279fe50 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -99,7 +99,7 @@ int recovery_check_mode() { return -1; } - if (recovery->mode == kDfuMode) { + if ((recovery->mode == kDfuMode) || (recovery->mode == kWTFMode)) { irecv_close(recovery); return -1; } |