diff options
-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"); |