diff options
| -rw-r--r-- | src/common.h | 2 | ||||
| -rw-r--r-- | src/dfu.c | 267 | ||||
| -rw-r--r-- | src/dfu.h | 2 | 
3 files changed, 209 insertions, 62 deletions
| diff --git a/src/common.h b/src/common.h index 87b91aa..9ef8a15 100644 --- a/src/common.h +++ b/src/common.h @@ -126,6 +126,8 @@ struct idevicerestore_client_t {  	int flags;  	plist_t tss;  	uint64_t ecid; +	unsigned char* nonce; +	int nonce_size;  	const char* uuid;  	const char* ipsw;  	const char* filesystem; @@ -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;  } @@ -35,7 +35,7 @@ struct dfu_client_t {  	plist_t tss;  }; -int dfu_client_new(struct idevicerestore_client_t* client, uint32_t timeout); +int dfu_client_new(struct idevicerestore_client_t* client);  void dfu_client_free(struct idevicerestore_client_t* client);  int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); | 
