summaryrefslogtreecommitdiffstats
path: root/src/dfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dfu.c')
-rw-r--r--src/dfu.c267
1 files changed, 206 insertions, 61 deletions
diff --git a/src/dfu.c b/src/dfu.c
index 60eeaf1..efe0a40 100644
--- a/src/dfu.c
+++ b/src/dfu.c
@@ -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;
}