diff options
Diffstat (limited to 'src/dfu.c')
-rw-r--r-- | src/dfu.c | 267 |
1 files changed, 206 insertions, 61 deletions
@@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <libirecovery.h> #include "dfu.h" @@ -34,118 +35,262 @@ int dfu_progress_callback(irecv_client_t client, const irecv_event_t* event) { return 0; } -int dfu_client_new(struct idevicerestore_client_t* client, uint32_t timeout) { - struct dfu_client_t* dfu = NULL; - if(client == NULL) { - return -1; - } +int dfu_client_new(struct idevicerestore_client_t* client) { + int i = 0; + int attempts = 10; + irecv_client_t dfu = NULL; + irecv_error_t dfu_error = IRECV_E_UNKNOWN_ERROR; - if(client->dfu) { - dfu_client_free(client); + if (client->dfu == NULL) { + client->dfu = (struct dfu_client_t*)malloc(sizeof(struct dfu_client_t)); + memset(client->dfu, 0, sizeof(struct dfu_client_t)); + if (client->dfu == NULL) { + error("ERROR: Out of memory\n"); + return -1; + } } - dfu = (struct dfu_client_t*) malloc(sizeof(struct dfu_client_t)); - if (dfu == NULL) { - error("ERROR: Out of memory\n"); - return -1; - } + for (i = 1; i <= attempts; i++) { + dfu_error = irecv_open(&dfu); + if (dfu_error == IRECV_E_SUCCESS) { + break; + } - if (dfu_open_with_timeout(dfu, timeout) < 0) { - dfu_client_free(client); - return -1; - } + if (i >= attempts) { + error("ERROR: Unable to connect to device in DFU mode\n"); + return -1; + } - if(dfu->client->mode != kDfuMode) { - dfu_client_free(client); - return -1; + sleep(1); + debug("Retrying connection...\n"); } - client->dfu = dfu; + irecv_event_subscribe(dfu, IRECV_PROGRESS, &dfu_progress_callback, NULL); + client->dfu->client = dfu; return 0; } void dfu_client_free(struct idevicerestore_client_t* client) { - struct dfu_client_t* dfu = NULL; if(client != NULL) { - dfu = client->dfu; - if (dfu != NULL) { - if(dfu->client != NULL) { - irecv_close(dfu->client); - dfu->client = NULL; + if (client->dfu != NULL) { + if(client->dfu->client != NULL) { + irecv_close(client->dfu->client); + client->dfu->client = NULL; } - free(dfu); + free(client->dfu); } client->dfu = NULL; } } -int dfu_open_with_timeout(struct idevicerestore_client_t* client, uint32_t timeout) { - int i = 0; - irecv_client_t recovery = NULL; - irecv_error_t recovery_error = IRECV_E_UNKNOWN_ERROR; +int dfu_check_mode() { + irecv_client_t dfu = NULL; + irecv_error_t dfu_error = IRECV_E_SUCCESS; - for (i = 1; i <= timeout; i++) { - recovery_error = irecv_open(&recovery); - if (recovery_error == IRECV_E_SUCCESS) { - break; + irecv_init(); + dfu_error=irecv_open(&dfu); + + if (dfu_error != IRECV_E_SUCCESS) { + return -1; + } + + if (dfu->mode != kDfuMode) { + irecv_close(dfu); + return -1; + } + + irecv_close(dfu); + + 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; + char* path = NULL; + char* blob = NULL; + irecv_error_t error = 0; + + if (client->tss) { + if (tss_get_entry_path(client->tss, component, &path) < 0) { + debug("NOTE: No path for component %s in TSS, will fetch from build_identity\n", component); + } + } + if (!path) { + if (build_identity_get_component_path(build_identity, component, &path) < 0) { + error("ERROR: Unable to get path for component '%s'\n", component); + if (path) + free(path); + return -1; } + } - if (i == timeout) { - error("ERROR: Unable to connect to device in DFU mode\n"); + if (client->tss) + info("%s will be signed\n", component); + + if (ipsw_get_component_by_path(client->ipsw, client->tss, path, &data, &size) < 0) { + error("ERROR: Unable to get component: %s\n", component); + free(path); + return -1; + } + + if (!(client->flags & FLAG_CUSTOM) && (strcmp(component, "iBEC") == 0)) { + char* ticket = NULL; + uint32_t tsize = 0; + if (tss_get_ticket(client->tss, &ticket, &tsize) < 0) { + error("ERROR: Unable to get ApTicket from TSS request\n"); return -1; } + uint32_t fillsize = 0; + if ((tsize % 0x100) != 0) { + fillsize = ((tsize / 0x100) + 1) * 0x100; + } + debug("ticket size = %d\nfillsize = %d\n", tsize, fillsize); + char* newdata = (char*)malloc(size + fillsize); + memcpy(newdata, ticket, tsize); + memset(newdata+tsize, '\xFF', fillsize - tsize); + memcpy(newdata+fillsize, data, size); + free(data); + data = newdata; + size += fillsize; + } - sleep(1); - debug("Retrying connection...\n"); + info("Sending %s (%d bytes)...\n", component, size); + + // FIXME: Did I do this right???? + error = irecv_send_buffer(client->dfu->client, data, size, 1); + free(path); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(error)); + free(data); + return -1; } - irecv_event_subscribe(recovery, IRECV_PROGRESS, &dfu_progress_callback, NULL); - client->dfu->client = recovery; + free(data); return 0; } -int dfu_check_mode() { - return -1; +int dfu_get_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) { + irecv_error_t dfu_error = IRECV_E_SUCCESS; + + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + dfu_error = irecv_get_nonce(client->dfu->client, nonce, nonce_size); + if (dfu_error != IRECV_E_SUCCESS) { + return -1; + } + + return 0; } int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity) { - irecv_client_t dfu = NULL; - const char* component = "iBSS"; irecv_error_t dfu_error = IRECV_E_SUCCESS; - if (recovery_client_new(client) < 0 || dfu->mode != kDfuMode) { + if (dfu_client_new(client) < 0) { error("ERROR: Unable to connect to DFU device\n"); - if (dfu) - irecv_close(dfu); return -1; } - if (recovery_send_component(client, build_identity, component) < 0) { - error("ERROR: Unable to send %s to device\n", component); - irecv_close(dfu); + if (client->dfu->client->mode != kDfuMode) { + info("NOTE: device is not in DFU mode, assuming recovery mode.\n"); + client->mode = &idevicerestore_modes[MODE_RECOVERY]; + return 0; + } + + if (dfu_send_component(client, build_identity, "iBSS") < 0) { + error("ERROR: Unable to send iBSS to device\n"); + irecv_close(client->dfu->client); return -1; } dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { error("ERROR: Unable to reset device\n"); - irecv_close(dfu); + irecv_close(client->dfu->client); return -1; } - irecv_close(client->dfu->client); - client->dfu->client = NULL; + + if (client->build[0] > '8') { + /* reconnect */ + dfu_client_free(client); + sleep(1); + dfu_client_new(client); + + /* get nonce */ + unsigned char* nonce = NULL; + int nonce_size = 0; + int nonce_changed = 0; + if (dfu_get_nonce(client, &nonce, &nonce_size) < 0) { + error("ERROR: Unable to get nonce from device!\n"); + return -1; + } + + if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { + nonce_changed = 1; + if (client->nonce) { + free(client->nonce); + } + client->nonce = nonce; + client->nonce_size = nonce_size; + } else { + free(nonce); + } + + info("Nonce: "); + int i; + for (i = 0; i < client->nonce_size; i++) { + info("%02x ", client->nonce[i]); + } + info("\n"); + + if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { + // Welcome iOS5. We have to re-request the TSS with our nonce. + plist_free(client->tss); + if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { + error("ERROR: Unable to get SHSH blobs for this device\n"); + return -1; + } + if (!client->tss) { + error("ERROR: can't continue without TSS\n"); + return -1; + } + fixup_tss(client->tss); + } + + if (irecv_set_configuration(client->dfu->client, 1) < 0) { + error("ERROR: set configuration failed\n"); + } + + /* send iBEC */ + if (dfu_send_component(client, build_identity, "iBEC") < 0) { + error("ERROR: Unable to send iBEC to device\n"); + irecv_close(client->dfu->client); + return -1; + } + + dfu_error = irecv_reset(client->dfu->client); + if (dfu_error != IRECV_E_SUCCESS) { + error("ERROR: Unable to reset device\n"); + irecv_close(client->dfu->client); + return -1; + } + } + + dfu_client_free(client); + + sleep(7); // Reconnect to device, but this time make sure we're not still in DFU mode - if (recovery_client_new(client) < 0 || client->mode->index != kDfuMode) { + if (recovery_client_new(client) < 0 || client->recovery->client->mode == kDfuMode) { error("ERROR: Unable to connect to recovery device\n"); - if (client->dfu->client) - irecv_close(client->dfu->client); + if (client->recovery->client) + irecv_close(client->recovery->client); return -1; } - - client->mode = &idevicerestore_modes[MODE_RECOVERY]; - irecv_close(client->dfu->client); - client->dfu->client = NULL; return 0; } |