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;  	} | 
