diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/idevicerestore.c | 394 | 
1 files changed, 385 insertions, 9 deletions
| diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 7e9a03d..d6551a4 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -25,6 +25,7 @@  #include <unistd.h>  #include <plist/plist.h>  #include <libirecovery.h> +#include <libimobiledevice/restore.h>  #include <libimobiledevice/lockdown.h>  #include <libimobiledevice/libimobiledevice.h> @@ -34,10 +35,14 @@  #include "idevicerestore.h"  #define UNKNOWN_MODE   0 -#define RECOVERY_MODE  1 -#define NORMAL_MODE    2 +#define NORMAL_MODE    1 +#define RECOVERY_MODE  2 +#define RESTORE_MODE   3 + +#define ASR_PORT       12345  int idevicerestore_debug = 0; +static int idevicerestore_mode = 0;  void usage(int argc, char* argv[]);  int write_file(const char* filename, char* data, int size); @@ -47,10 +52,10 @@ int send_devicetree(char* ipsw, plist_t tss);  int send_ramdisk(char* ipsw, plist_t tss);  int send_kernelcache(char* ipsw, plist_t tss);  int get_tss_data(plist_t tss, const char* entry, char** path, char** blob); +void device_callback(const idevice_event_t* event, void *user_data);  int main(int argc, char* argv[]) {  	int opt = 0; -	int mode = 0;  	char* ipsw = NULL;  	char* uuid = NULL;  	uint64_t ecid = 0; @@ -104,14 +109,14 @@ int main(int argc, char* argv[]) {  			return -1;  		}  		info("Found device in recovery mode\n"); -		mode = RECOVERY_MODE; +		idevicerestore_mode = RECOVERY_MODE;  	} else {  		info("Found device in normal mode\n"); -		mode = NORMAL_MODE; +		idevicerestore_mode = NORMAL_MODE;  	} -	if (mode == NORMAL_MODE) { +	if (idevicerestore_mode == NORMAL_MODE) {  		lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");  		if (lockdown_error != LOCKDOWN_E_SUCCESS) {  			error("ERROR: Unable to connect to lockdownd\n"); @@ -140,7 +145,8 @@ int main(int argc, char* argv[]) {  		idevice_free(device);  		lockdown = NULL;  		device = NULL; -	} else if (mode == RECOVERY_MODE) { + +	} else if (idevicerestore_mode == RECOVERY_MODE) {  		recovery_error = irecv_get_ecid(recovery, &ecid);  		if (recovery_error != IRECV_E_SUCCESS) {  			error("ERROR: Unable to get device ECID\n"); @@ -185,10 +191,34 @@ int main(int argc, char* argv[]) {  		plist_free(tss_request);  		return -1;  	} -	plist_free(tss_request);  	info("Got TSS response\n"); -	if (mode == NORMAL_MODE) { +	// Get name of filesystem DMG in IPSW +	char* filesystem = NULL; +	plist_t filesystem_node = plist_dict_get_item(tss_request, "OS"); +	if(!filesystem_node || plist_get_node_type(filesystem_node) != PLIST_DICT) { +		error("ERROR: Unable to find OS filesystem\n"); +		plist_free(tss_request); +		return -1; +	} + +	plist_t filesystem_info_node = plist_dict_get_item(filesystem_node, "Info"); +	if(!filesystem_info_node || plist_get_node_type(filesystem_info_node) != PLIST_DICT) { +		error("ERROR: Unable to find filesystem info node\n"); +		plist_free(tss_request); +		return -1; +	} + +	plist_t filesystem_info_path_node = plist_dict_get_item(filesystem_info_node, "Path"); +	if(!filesystem_info_path_node || plist_get_node_type(filesystem_info_path_node) != PLIST_STRING) { +		error("ERROR: Unable to find filesystem info path node\n"); +		plist_free(tss_request); +		return -1; +	} +	plist_get_string_val(filesystem_info_path_node, &filesystem); +	plist_free(tss_request); + +	if (idevicerestore_mode == NORMAL_MODE) {  		// Place the device in recovery mode  		info("Entering recovery mode...\n");  		device_error = idevice_new(&device, uuid); @@ -256,10 +286,107 @@ int main(int argc, char* argv[]) {  		return -1;  	} +	idevice_event_subscribe(&device_callback, NULL); +	info("Waiting for device to enter restore mode\n"); +	while(idevicerestore_mode != RESTORE_MODE) sleep(1); +	device_error = idevice_new(&device, uuid); +	if (device_error != IDEVICE_E_SUCCESS) { +		error("ERROR: Unable to open device\n"); +		plist_free(tss_response); +		return -1; +	} + +	restored_client_t restore = NULL; +	restored_error_t restore_error = restored_client_new(device, &restore, "idevicerestore"); +	if(restore_error != RESTORE_E_SUCCESS) { +		error("ERROR: Unable to start restored client\n"); +		plist_free(tss_response); +		idevice_free(device); +		return -1; +	} + +	char* type = NULL; +	uint64_t version = 0; +	if (restored_query_type(restore, &type, &version) != RESTORE_E_SUCCESS) { +		printf("ERROR: Device is not in restore mode. QueryType returned \"%s\"\n", type); +		plist_free(tss_response); +		restored_client_free(restore); +		idevice_free(device); +		return -1; +	} +	info("Device has successfully entered restore mode\n"); + +	/* start restored service and retrieve port */ +	int quit_flag = 0; +	char* kernelcache = NULL; +	printf("Restore protocol version is %llu.\n", version); +	restore_error = restored_start_restore(restore); +	if (restore_error == RESTORE_E_SUCCESS) { +		while (!quit_flag) { +			plist_t message = NULL; +			restore_error = restored_receive(restore, &message); +			plist_t msgtype_node = plist_dict_get_item(message, "MsgType"); +			if (msgtype_node && PLIST_STRING == plist_get_node_type(msgtype_node)) { +				char *msgtype = NULL; +				plist_get_string_val(msgtype_node, &msgtype); +				if(!strcmp(msgtype, "ProgressMsg")) { +					restore_error = progress_msg(restore, message); + +				} +				else if(!strcmp(msgtype, "DataRequestMsg")) { +					//restore_error = data_request_msg(device, restore, message, filesystem); +					plist_t datatype_node = plist_dict_get_item(message, "DataType"); +					if (datatype_node && PLIST_STRING == plist_get_node_type(datatype_node)) { +						char *datatype = NULL; +						plist_get_string_val(datatype_node, &datatype); +						if(!strcmp(datatype, "SystemImageData")) { +							send_system_data(device, restore, filesystem); +						} +						else if(!strcmp(datatype, "KernelCache")) { +							send_kernel_data(device, restore, kernelcache); +						} +						else if(!strcmp(datatype, "NORData")) { +							send_nor_data(device, restore); +						} +						else { +							// Unknown DataType!! +							error("Unknown DataType\n"); +							return -1; +						} +					} + +				} +				else if(!strcmp(msgtype, "StatusMsg")) { +					restore_error = status_msg(restore, message); + +				} +				else { +					printf("Received unknown message type: %s\n", msgtype); +				} +			} + +			if (RESTORE_E_SUCCESS != restore_error) { +				printf("Invalid return status %d\n", restore_error); +			} + +			plist_free(message); +		} +	} else { +		printf("ERROR: Could not start restore. %d\n", restore_error); +	} + +	restored_client_free(restore); +	idevice_free(device);  	plist_free(tss_response);  	return 0;  } +void device_callback(const idevice_event_t* event, void *user_data) { +	if(event->event == IDEVICE_DEVICE_ADD) { +		idevicerestore_mode = RESTORE_MODE; +	} +} +  void usage(int argc, char* argv[]) {  	char *name = strrchr(argv[0], '/');  	printf("Usage: %s [OPTIONS]\n", (name ? name + 1 : argv[0])); @@ -274,6 +401,255 @@ void usage(int argc, char* argv[]) {  	exit(1);  } +int progress_msg(restored_client_t client, plist_t msg) { +	info("Got progress message\n"); +	return 0; +} + +int data_request_msg(idevice_t device, restored_client_t client, plist_t msg, const char *filesystem, const char *kernel) { +	plist_t datatype_node = plist_dict_get_item(msg, "DataType"); +	if (datatype_node && PLIST_STRING == plist_get_node_type(datatype_node)) { +		char *datatype = NULL; +		plist_get_string_val(datatype_node, &datatype); +		if(!strcmp(datatype, "SystemImageData")) { +			send_system_data(device, client, filesystem); +		} +		else if(!strcmp(datatype, "KernelCache")) { +			send_kernel_data(device, client, kernel); +		} +		else if(!strcmp(datatype, "NORData")) { +			send_nor_data(device, client); +		} +		else { +			// Unknown DataType!! +			error("Unknown DataType\n"); +			return -1; +		} +	} +	return 0; +} + +int status_msg(restored_client_t client, plist_t msg) { +	info("Got status message\n"); +	return 0; +} + +int send_system_data(idevice_t device, restored_client_t client, const char *filesystem) { +	int i = 0; +	char buffer[0x1000]; +	uint32_t recv_bytes = 0; +	memset(buffer, '\0', 0x1000); +	idevice_connection_t connection = NULL; +	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; + +	for(i = 0; i < 5; i++) { +		ret = idevice_connect(device, ASR_PORT, &connection); +		if(ret == IDEVICE_E_SUCCESS) +			break; + +		else +			sleep(1); +	} + +	if(ret != IDEVICE_E_SUCCESS) +		return ret; + +	memset(buffer, '\0', 0x1000); +	ret = idevice_connection_receive(connection,  buffer, 0x1000, &recv_bytes); +	if(ret != IDEVICE_E_SUCCESS) { +		idevice_disconnect(connection); +		return ret; +	} +	printf("Received %d bytes\n", recv_bytes); +	printf("%s", buffer); + +	FILE* fd = fopen(filesystem, "rb"); +	if(fd == NULL) { +		idevice_disconnect(connection); +		return ret; +	} + +	fseek(fd, 0, SEEK_END); +	uint64_t len = ftell(fd); +	fseek(fd, 0, SEEK_SET); + +	printf("Connected to ASR\n"); +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "FEC Slice Stride", plist_new_uint(40)); +	plist_dict_insert_item(dict, "Packet Payload Size", plist_new_uint(1450)); +	plist_dict_insert_item(dict, "Packets Per FEC", plist_new_uint(25)); + +	plist_t payload = plist_new_dict(); +	plist_dict_insert_item(payload, "Port", plist_new_uint(1)); +	plist_dict_insert_item(payload, "Size", plist_new_uint(len)); +	plist_dict_insert_item(dict, "Payload", payload); + +	plist_dict_insert_item(dict, "Stream ID", plist_new_uint(1)); +	plist_dict_insert_item(dict, "Version", plist_new_uint(1)); + +	char* xml = NULL; +	unsigned int dict_size = 0; +	unsigned int sent_bytes = 0; +	plist_to_xml(dict, &xml, &dict_size); + +	ret = idevice_connection_send(connection, xml, dict_size, &sent_bytes); +	if(ret != IDEVICE_E_SUCCESS) { +		idevice_disconnect(connection); +		return ret; +	} + +	printf("Sent %d bytes\n", sent_bytes); +	printf("%s", xml); +	plist_free(dict); +	free(xml); + +	char* command = NULL; +	do { +		memset(buffer, '\0', 0x1000); +		ret = idevice_connection_receive(connection, buffer, 0x1000, &recv_bytes); +		if(ret != IDEVICE_E_SUCCESS) { +			idevice_disconnect(connection); +			return ret; +		} +		info("Received %d bytes\n", recv_bytes); +		info("%s", buffer); + +		plist_t request = NULL; +		plist_from_xml(buffer, recv_bytes, &request); +		plist_t command_node = plist_dict_get_item(request, "Command"); +		if (command_node && PLIST_STRING == plist_get_node_type(command_node)) { +			plist_get_string_val(command_node, &command); +			if(!strcmp(command, "OOBData")) { +				plist_t oob_length_node = plist_dict_get_item(request, "OOB Length"); +				if (!oob_length_node || PLIST_UINT != plist_get_node_type(oob_length_node)) { +					printf("Error fetching OOB Length\n"); +					idevice_disconnect(connection); +					return IDEVICE_E_UNKNOWN_ERROR; +				} +				uint64_t oob_length = 0; +				plist_get_uint_val(oob_length_node, &oob_length); + +				plist_t oob_offset_node = plist_dict_get_item(request, "OOB Offset"); +				if (!oob_offset_node || PLIST_UINT != plist_get_node_type(oob_offset_node)) { +					error("Error fetching OOB Offset\n"); +					idevice_disconnect(connection); +					return IDEVICE_E_UNKNOWN_ERROR; +				} +				uint64_t oob_offset = 0; +				plist_get_uint_val(oob_offset_node, &oob_offset); + +				char* oob_data = (char*) malloc(oob_length); +				if(oob_data == NULL) { +					error("Out of memory\n"); +					idevice_disconnect(connection); +					return IDEVICE_E_UNKNOWN_ERROR; +				} + +				fseek(fd, oob_offset, SEEK_SET); +				if(fread(oob_data, 1, oob_length, fd) != oob_length) { +					error("Unable to read filesystem offset\n"); +					idevice_disconnect(connection); +					free(oob_data); +					return ret; +				} + +				ret = idevice_connection_send(connection, oob_data, oob_length, &sent_bytes); +				if(sent_bytes != oob_length || ret != IDEVICE_E_SUCCESS) { +					printf("Unable to send %d bytes to asr\n", sent_bytes); +					idevice_disconnect(connection); +					free(oob_data); +					return ret; +				} +				plist_free(request); +				free(oob_data); +			} +		} + +	} while(strcmp(command, "Payload")); + +	fseek(fd, 0, SEEK_SET); +	char data[1450]; +	for(i = len; i > 0; i -= 1450) { +		int size = 1450; +		if(i < 1450) { +			size = i; +		} + +		if(fread(data, 1, size, fd) != (unsigned int)size) { +			fclose(fd); +			idevice_disconnect(connection); +			printf("Error reading filesystem\n"); +			return IDEVICE_E_UNKNOWN_ERROR; +		} + +		ret = idevice_connection_send(connection, data, size, &sent_bytes); +		if(ret != IDEVICE_E_SUCCESS) { +			fclose(fd); +		} + +		if(i % (1450*1000) == 0) { +			printf("."); +		} +	} + +	printf("Done sending filesystem\n"); +	fclose(fd); +	ret = idevice_disconnect(connection); +	return ret; +} + +int send_kernel_data(idevice_t device, restored_client_t client, const char *kernel) { +	printf("Sending kernelcache\n"); +	FILE* fd = fopen(kernel, "rb"); +	if(fd == NULL) { +		info("Unable to open kernelcache"); +		return -1; +	} + +	fseek(fd, 0, SEEK_END); +	uint64_t len = ftell(fd); +	fseek(fd, 0, SEEK_SET); + +	char* kernel_data = (char*) malloc(len); +	if(kernel_data == NULL) { +		error("Unable to allocate memory for kernel data"); +		fclose(fd); +		return -1; +	} + +	if(fread(kernel_data, 1, len, fd) != len) { +		error("Unable to read kernel data\n"); +		free(kernel_data); +		fclose(fd); +		return -1; +	} +	fclose(fd); + +	plist_t kernelcache_node = plist_new_data(kernel_data, len); + +	plist_t dict = plist_new_dict(); +	plist_dict_insert_item(dict, "KernelCacheFile", kernelcache_node); + +	restored_error_t ret = restored_send(client, dict); +	if(ret != RESTORE_E_SUCCESS) { +		error("Unable to send kernelcache data\n"); +		free(kernel_data); +		plist_free(dict); +		return -1; +	} + +	info("Done sending kernelcache\n"); +	free(kernel_data); +	plist_free(dict); +	return 0; +} + + +int send_nor_data(idevice_t device, restored_client_t client) { +	info("Not implemented\n"); +	return 0; +} +  int write_file(const char* filename, char* data, int size) {  	debug("Writing data to %s\n", filename);  	FILE* file = fopen(filename, "wb"); | 
