From 02773da6a2daa0d00780cac4707917360b81b6c9 Mon Sep 17 00:00:00 2001 From: Joshua Hill Date: Tue, 8 Jun 2010 16:14:29 +0800 Subject: Added a new asr.c file to stick all stuff related to filesystem and abstract the restore process to allow for easier porting --- TODO | 27 +-- src/Makefile.am | 2 +- src/asr.c | 275 ++++++++++++++++++++++++++++++ src/asr.h | 35 ++++ src/idevicerestore.c | 62 +++++-- src/idevicerestore.h | 29 +++- src/ipsw.c | 2 +- src/restore.c | 464 ++++++++++++++++++++++----------------------------- src/restore.h | 2 +- 9 files changed, 601 insertions(+), 297 deletions(-) create mode 100644 src/asr.c create mode 100644 src/asr.h diff --git a/TODO b/TODO index 9e77cef..c631123 100644 --- a/TODO +++ b/TODO @@ -1,16 +1,17 @@ TODO List ------------------------------------------------ -1) Instead of assumeing upgrade or restore, the program should query all avaiable build identitiys and find which ones are valid, if more then one is valid then display a menu asking whether to resore or upgrade. (This would also benifit users by cacheing both upgrade and restore SHSH blobs to sauriks server so users aren't stuck with only being able to upgrade or restore.) -2) Also img3 should be updated to be able to fetch tags from each image such as the VERS tag to be able to compare with the current iBoot version. -3) Many of idevicerestore options can probably be compiled into one flags structure. -4) need to implement restore_reboot(). -5) Need to update restore progress notifications to use print_progress_bar(). -6) Need to add 8900 file support into img3.c. -7) Cacheing TSS and activation info locally so the program doesn't need to query apple/cydia servers everytime would be nice. -8) Need to add support to handle and display status messages correctly. -9) Writing restore logs to an external file for debugging would be a great help. -10) Need to change some info progresses to only display the component name rather then the full path which can be messy. -11) Would be nice to add options to keep some connections open during similar operations rather then reopening and closing them each time. -12) Extracting and signing all firmware files at one time rather then extracting and signing each one as we need it could speed things up as well. -13) restore.c is still very ugly and can be cleaned up \ No newline at end of file +o) Instead of assumeing upgrade or restore, the program should query all avaiable build identitiys and find which ones are valid, if more then one is valid then display a menu asking whether to resore or upgrade. (This would also benifit users by cacheing both upgrade and restore SHSH blobs to sauriks server so users aren't stuck with only being able to upgrade or restore.) +o) Also img3 should be updated to be able to fetch tags from each image such as the VERS tag to be able to compare with the current iBoot version. +o) Many of idevicerestore options can probably be compiled into one flags structure. +o) Need to add 8900 file support into img3.c. +o) Cacheing TSS and activation info locally so the program doesn't need to query apple/cydia servers everytime would be nice. +o) Need to add support to handle and display status messages correctly. +o) Writing restore logs to an external file for debugging would be a great help. +o) Need to change some info progresses to only display the component name rather then the full path which can be messy. +o) Would be nice to add options to keep some connections open during similar operations rather then reopening and closing them each time. +o) Extracting and signing all firmware files at one time rather then extracting and signing each one as we need it could speed things up as well. +o) Clean up restore_send_kernelcache +o) Clean up restore_send_nor +o) Finishing cleaning up asr_perform_validation +o) Double check to make sure asr_send_payload looks clean \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index b641d00..a44640f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,6 @@ AM_LDFLAGS =\ bin_PROGRAMS = idevicerestore -idevicerestore_SOURCES = idevicerestore.c dfu.c tss.c img3.c ipsw.c normal.c restore.c recovery.c activate.c +idevicerestore_SOURCES = idevicerestore.c dfu.c asr.c tss.c img3.c ipsw.c normal.c restore.c recovery.c activate.c idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) \ No newline at end of file diff --git a/src/asr.c b/src/asr.c new file mode 100644 index 0000000..658f03f --- /dev/null +++ b/src/asr.c @@ -0,0 +1,275 @@ +/* + * dfu.h + * Functions for handling idevices in normal mode + * + * Copyright (c) 2010 Joshua Hill. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "asr.h" +#include "idevicerestore.h" + +#define ASR_PORT 12345 +#define ASR_BUFFER_SIZE 65536 + +int asr_open_with_timeout(idevice_t device, idevice_connection_t* asr) { + int i = 0; + int attempts = 10; + idevice_connection_t connection = NULL; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + + *asr = NULL; + + if (device == NULL) { + return -1; + } + + debug("Connecting to ASR\n"); + for (i = 1; i <= attempts; i++) { + device_error = idevice_connect(device, ASR_PORT, &connection); + if (device_error == IDEVICE_E_SUCCESS) { + break; + } + + if (i >= attempts) { + error("ERROR: Unable to connect to ASR client\n"); + return -1; + } + + sleep(2); + debug("Retrying connection...\n"); + } + + *asr = connection; + return 0; +} + +int asr_receive(idevice_connection_t asr, plist_t* data) { + uint32_t size = 0; + char* buffer = NULL; + plist_t request = NULL; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + + *data = NULL; + + buffer = (char*) malloc(ASR_BUFFER_SIZE); + if (buffer == NULL) { + error("ERROR: Unable to allocate memory for ASR receive buffer\n"); + return -1; + } + memset(buffer, '\0', ASR_BUFFER_SIZE); + + device_error = idevice_connection_receive(asr, buffer, ASR_BUFFER_SIZE, &size); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: Unable to receive data from ASR\n"); + free(buffer); + return -1; + } + plist_from_xml(buffer, size, &request); + + *data = request; + + debug("Received %d bytes:\n%s\n", size, buffer); + free(buffer); + return 0; +} + +int asr_send(idevice_connection_t asr, plist_t* data) { + uint32_t size = 0; + char* buffer = NULL; + + plist_to_xml(data, &buffer, &size); + if (asr_send_buffer(asr, buffer, size) < 0) { + error("ERROR: Unable to send plist to ASR\n"); + free(buffer); + return -1; + } + + free(buffer); + return 0; +} + +int asr_send_buffer(idevice_connection_t asr, const char* data, uint32_t size) { + uint32_t bytes = 0; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + + device_error = idevice_connection_send(asr, data, size, &bytes); + if (device_error != IDEVICE_E_SUCCESS || bytes != size) { + error("ERROR: Unable to send data to ASR\n"); + return -1; + } + + debug("Sent %d bytes:\n%s", bytes, data); + return 0; +} + +void asr_close(idevice_connection_t asr) { + if (asr != NULL) { + idevice_disconnect(asr); + asr = NULL; + } +} + +int asr_perform_validation(idevice_connection_t asr, const char* filesystem) { + FILE* file = NULL; + uint64_t length = 0; + char* command = NULL; + plist_t node = NULL; + plist_t packet = NULL; + plist_t packet_info = NULL; + plist_t payload_info = NULL; + + file = fopen(filesystem, "rb"); + if (file == NULL) { + return -1; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + payload_info = plist_new_dict(); + plist_dict_insert_item(payload_info, "Port", plist_new_uint(1)); + plist_dict_insert_item(payload_info, "Size", plist_new_uint(length)); + + packet_info = plist_new_dict(); + plist_dict_insert_item(packet_info, "FEC Slice Stride", plist_new_uint(40)); + plist_dict_insert_item(packet_info, "Packet Payload Size", plist_new_uint(1450)); + plist_dict_insert_item(packet_info, "Packets Per FEC", plist_new_uint(25)); + plist_dict_insert_item(packet_info, "Payload", payload_info); + plist_dict_insert_item(packet_info, "Stream ID", plist_new_uint(1)); + plist_dict_insert_item(packet_info, "Version", plist_new_uint(1)); + + if (asr_send(asr, packet_info)) { + error("ERROR: Unable to sent packet information to ASR\n"); + plist_free(packet_info); + return -1; + } + plist_free(packet_info); + + char* oob_data = NULL; + uint64_t oob_offset = 0; + uint64_t oob_length = 0; + plist_t oob_length_node = NULL; + plist_t oob_offset_node = NULL; + do { + if (asr_receive(asr, &packet) < 0) { + error("ERROR: Unable to receive validation packet\n"); + return -1; + } + + node = plist_dict_get_item(packet, "Command"); + if (!node || plist_get_node_type(node) != PLIST_STRING) { + error("ERROR: Unable to find command node in validation request\n"); + return -1; + } + plist_get_string_val(node, &command); + + if (!strcmp(command, "OOBData")) { + oob_length_node = plist_dict_get_item(packet, "OOB Length"); + if (!oob_length_node || PLIST_UINT != plist_get_node_type(oob_length_node)) { + error("ERROR: Unable to find OOB data length\n"); + return -1; + } + plist_get_uint_val(oob_length_node, &oob_length); + + oob_offset_node = plist_dict_get_item(packet, "OOB Offset"); + if (!oob_offset_node || PLIST_UINT != plist_get_node_type(oob_offset_node)) { + error("ERROR: Unable to find OOB data offset\n"); + return -1; + } + plist_get_uint_val(oob_offset_node, &oob_offset); + + oob_data = (char*) malloc(oob_length); + if (oob_data == NULL) { + error("ERROR: Out of memory\n"); + plist_free(packet); + return -1; + } + + fseek(file, oob_offset, SEEK_SET); + if (fread(oob_data, 1, oob_length, file) != oob_length) { + error("ERROR: Unable to read OOB data from filesystem offset\n"); + plist_free(packet); + free(oob_data); + return -1; + } + + if (asr_send_buffer(asr, oob_data, oob_length) < 0) { + error("ERROR: Unable to send OOB data to ASR\n"); + plist_free(packet); + free(oob_data); + return -1; + } + + plist_free(packet); + free(oob_data); + + } + + } while (strcmp(packet, "Payload")); +} + +int asr_send_payload(idevice_connection_t asr, const char* filesystem) { + int i = 0; + char data[1450]; + FILE* file = NULL; + uint32_t bytes = 0; + uint32_t count = 0; + uint32_t length = 0; + double progress = 0; + + file = fopen(filesystem, "rb"); + if (file == NULL) { + return -1; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + for(i = length; i > 0; i -= 1450) { + int size = 1450; + if (i < 1450) { + size = i; + } + + if (fread(data, 1, size, file) != (unsigned int) size) { + error("Error reading filesystem\n"); + fclose(file); + return -1; + } + + if (asr_send_buffer(asr, data, size) < 0) { + error("ERROR: Unable to send filesystem payload\n"); + fclose(file); + return -1; + } + + bytes += size; + progress = ((double) bytes/ (double) length) * 100.0; + print_progress_bar("Extracting", progress); + + } + + fclose(file); + return 0; +} diff --git a/src/asr.h b/src/asr.h new file mode 100644 index 0000000..151c1d0 --- /dev/null +++ b/src/asr.h @@ -0,0 +1,35 @@ +/* + * dfu.h + * Functions for handling idevices in normal mode + * + * Copyright (c) 2010 Joshua Hill. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IDEVICERESTORE_ASR_H +#define IDEVICERESTORE_ASR_H + +#include + +int asr_open_with_timeout(idevice_t device, idevice_connection_t* asr); +int asr_send(idevice_connection_t asr, plist_t* data); +int asr_receive(idevice_connection_t asr, plist_t* data); +int asr_send_buffer(idevice_connection_t asr, const char* data, uint32_t size); +void asr_close(idevice_connection_t asr); +int asr_perform_validation(idevice_connection_t asr, const char* filesystem); +int asr_send_payload(idevice_connection_t asr, const char* filesystem); + +#endif diff --git a/src/idevicerestore.c b/src/idevicerestore.c index cc97e2c..69363fb 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -602,23 +602,28 @@ int get_signed_component(const char* ipsw, plist_t tss, const char* path, char** uint32_t component_size = 0; char* component_data = NULL; char* component_blob = NULL; + char* component_name = NULL; - info("Extracting %s from %s\n", path, ipsw); + component_name = strrchr(path, '/'); + if (component_name != NULL) component_name++; + else component_name = (char*) path; + + info("Extracting %s\n", component_name); if (ipsw_extract_to_memory(ipsw, path, &component_data, &component_size) < 0) { - error("ERROR: Unable to extract %s from %s\n", path, ipsw); + error("ERROR: Unable to extract %s from %s\n", component_name, ipsw); return -1; } img3 = img3_parse_file(component_data, component_size); if (img3 == NULL) { - error("ERROR: Unable to parse IMG3: %s\n", path); + error("ERROR: Unable to parse IMG3: %s\n", component_name); free(component_data); return -1; } free(component_data); if (tss_get_blob_by_path(tss, path, &component_blob) < 0) { - error("ERROR: Unable to get SHSH blob for TSS %s entry\n", path); + error("ERROR: Unable to get SHSH blob for TSS %s entry\n", component_name); img3_free(img3); return -1; } @@ -641,13 +646,7 @@ int get_signed_component(const char* ipsw, plist_t tss, const char* path, char** img3_free(img3); if (idevicerestore_debug) { - char* out = strrchr(path, '/'); - if (out != NULL) { - out++; - } else { - out = (char*) path; - } - write_file(out, component_data, component_size); + write_file(component_name, component_data, component_size); } *data = component_data; @@ -676,3 +675,44 @@ int write_file(const char* filename, const void* data, size_t size) { return size; } + +int read_file(const char* filename, char** data, uint32_t* size) { + size_t bytes = 0; + size_t length = 0; + FILE* file = NULL; + char* buffer = NULL; + debug("Reading data from %s\n", filename); + + *size = 0; + *data = NULL; + + file = fopen(filename, "rb"); + if (file == NULL) { + error("read_file: File %s not found\n", filename); + return -1; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + rewind(file); + + buffer = (char*) malloc(length); + if(buffer == NULL) { + error("ERROR: Out of memory\n"); + fclose(file); + return -1; + } + bytes = fread(buffer, 1, length, file); + fclose(file); + + if(bytes != length) { + error("ERROR: Unable to read entire file\n"); + free(buffer); + return -1; + } + + *size = length; + *data = buffer; + return 0; +} + diff --git a/src/idevicerestore.h b/src/idevicerestore.h index e4186c9..20578a9 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -63,6 +63,16 @@ #define DEVICE_IPOD3G 5 #define DEVICE_IPAD1G 6 +typedef struct idevicerestore_entry { + char* name; + char* path; + char* filename; + char* blob_data; + uint32_t blob_size; + struct idevicerestore_entry* next; + struct idevicerestore_entry* prev; +} idevicerestore_entry_t; + typedef struct { int device_id; const char* product; @@ -70,7 +80,21 @@ typedef struct { int board_id; int chip_id; } idevicerestore_device_t; - +/* +typedef struct { + int mode; + idevice_t device; + irecv_client_t recovery; + restored_client_t restore; + lockdownd_client_t lockdown; + int erase; // 1 + int custom; // 2 + int exclude; // 4 + int verbose; // 8 + idevicerestore_device_t* idevicerestore_device; + idevicerestore_entry_t** entries; +} idevicerestore_context_t; +*/ static idevicerestore_device_t idevicerestore_devices[] = { { 0, "iPhone1,1", "M68AP", 0, 8900 }, { 1, "iPod1,1", "N45AP", 2, 8900 }, @@ -79,6 +103,7 @@ static idevicerestore_device_t idevicerestore_devices[] = { { 4, "iPhone2,1", "N88AP", 0, 8920 }, { 5, "iPod3,1", "N18AP", 2, 8922 }, { 6, "iPad1,1", "K48AP", 2, 8930 }, + { 6, "iPhone4,1", "XXXAP", 0, 0 }, { -1, NULL, NULL, -1, -1 } }; @@ -90,6 +115,7 @@ extern int idevicerestore_custom; extern int idevicerestore_exclude; extern int idevicerestore_verbose; extern idevicerestore_device_t* idevicerestore_device; +extern idevicerestore_entry_t** idevicerestore_entries; int check_mode(const char* uuid); int check_device(const char* uuid); @@ -100,6 +126,7 @@ int get_cpid(const char* uuid, uint32_t* cpid); int extract_buildmanifest(const char* ipsw, plist_t* buildmanifest); plist_t get_build_identity(plist_t buildmanifest, uint32_t identity); int write_file(const char* filename, const void* data, size_t size); +int read_file(const char* filename, char** data, uint32_t* size); int get_shsh_blobs(uint64_t ecid, plist_t build_identity, plist_t* tss); int extract_filesystem(const char* ipsw, plist_t buildmanifest, char** filesystem); int get_signed_component(const char* ipsw, plist_t tss, const char* path, char** data, uint32_t* size); diff --git a/src/ipsw.c b/src/ipsw.c index 7ded33e..adbaea0 100644 --- a/src/ipsw.c +++ b/src/ipsw.c @@ -98,7 +98,7 @@ int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfi int bytes = 0; int count = 0; double progress = 0; - for (i = zstat.size; i > 0; i -= count) { + for(i = zstat.size; i > 0; i -= count) { if (i < BUFSIZE) size = i; else diff --git a/src/restore.c b/src/restore.c index b5eea66..fc22b75 100644 --- a/src/restore.c +++ b/src/restore.c @@ -24,12 +24,11 @@ #include #include +#include "asr.h" #include "tss.h" #include "restore.h" #include "idevicerestore.h" -#define ASR_PORT 12345 - #define CREATE_PARTITION_MAP 12 #define CREATE_FILESYSTEM 13 #define RESTORE_IMAGE 14 @@ -110,7 +109,7 @@ int restore_check_device(const char* uuid) { } restore_error = restored_get_value(restore, "HardwareModel", &node); - if(restore_error != RESTORE_E_SUCCESS) { + if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to get HardwareModel from restored\n"); restored_client_free(restore); idevice_free(device); @@ -124,13 +123,14 @@ int restore_check_device(const char* uuid) { if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to get HardwareModel information\n"); - if(node) plist_free(node); + if (node) + plist_free(node); return -1; } plist_get_string_val(node, &model); - for(i = 0; idevicerestore_devices[i].model != NULL; i++) { - if(!strcasecmp(model, idevicerestore_devices[i].model)) { + for (i = 0; idevicerestore_devices[i].model != NULL; i++) { + if (!strcasecmp(model, idevicerestore_devices[i].model)) { break; } } @@ -147,11 +147,31 @@ void restore_device_callback(const idevice_event_t* event, void* user_data) { } } +int restore_reboot(const char* uuid) { + idevice_t device = NULL; + restored_client_t restore = NULL; + restored_error_t restore_error = RESTORE_E_SUCCESS; + if (restore_open_with_timeout(uuid, &device, &restore) < 0) { + error("ERROR: Unable to open device in restore mode\n"); + return -1; + } + + restore_error = restored_reboot(restore); + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Unable to reboot the device from restore mode\n"); + restore_close(device, restore); + return -1; + } + + restore_close(device, restore); + restore = NULL; + device = NULL; + return 0; +} + int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_client_t* restore) { int i = 0; - int attempt = 10; - char* type = NULL; - uint64_t version = 0; + int attempts = 10; idevice_t context = NULL; restored_client_t client = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; @@ -166,12 +186,12 @@ int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_clie return -1; } - for (i = 1; i <= attempt; i++) { + for (i = 1; i <= attempts; i++) { if (restore_device_connected == 1) { break; } - if (i == attempt) { + if (i == attempts) { error("ERROR: Unable to connect to device in restore mode\n"); } @@ -190,7 +210,7 @@ int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_clie return -1; } - restore_error = restored_query_type(client, &type, &version); + restore_error = restored_query_type(client, NULL, NULL); if (restore_error != RESTORE_E_SUCCESS) { restored_client_free(client); idevice_event_unsubscribe(); @@ -208,7 +228,6 @@ void restore_close(idevice_t device, restored_client_t restore) { restored_client_free(restore); if (device) idevice_free(device); - //idevice_event_unsubscribe(); } const char* restore_progress_string(unsigned int operation) { @@ -262,202 +281,83 @@ const char* restore_progress_string(unsigned int operation) { int restore_handle_progress_msg(restored_client_t client, plist_t msg) { plist_t node = NULL; - uint64_t operation = 0; - uint64_t uprogress = 0; uint64_t progress = 0; + uint64_t operation = 0; node = plist_dict_get_item(msg, "Operation"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &operation); - } else { + if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse operation from ProgressMsg plist\n"); - return 0; + return -1; } + plist_get_uint_val(node, &operation); node = plist_dict_get_item(msg, "Progress"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &uprogress); - progress = uprogress; - } else { + if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse progress from ProgressMsg plist \n"); - return 0; + return -1; } + plist_get_uint_val(node, &progress); + + if ((progress > 0) && (progress < 100)) { + print_progress_bar(restore_progress_string(operation), (double) progress); - if ((progress > 0) && (progress < 100)) - info("%s - Progress: %llu%%\n", restore_progress_string(operation), progress); - else + } else { info("%s\n", restore_progress_string(operation)); + } return 0; } int restore_handle_status_msg(restored_client_t client, plist_t msg) { info("Got status message\n"); + debug_plist(msg); return 0; } -int restore_send_filesystem(idevice_t device, restored_client_t client, const char* filesystem) { +int restore_send_filesystem(idevice_t device, 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; + FILE* file = NULL; + plist_t data = NULL; + idevice_connection_t asr = NULL; + idevice_error_t device_error = IDEVICE_E_UNKNOWN_ERROR; - 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; - } - info("Received %d bytes\n", recv_bytes); - info("%s", buffer); - - FILE* fd = fopen(filesystem, "rb"); - if (fd == NULL) { - idevice_disconnect(connection); - return ret; + if (asr_open_with_timeout(device, &asr) < 0) { + error("ERROR: Unable to connect to ASR\n"); + return -1; } - - fseek(fd, 0, SEEK_END); - uint64_t len = ftell(fd); - fseek(fd, 0, SEEK_SET); - info("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; + // we don't really need to do anything with this, + // we're just clearing the output buffer + if (asr_receive(asr, &data) < 0) { + error("ERROR: Unable to receive data from ASR\n"); + asr_close(asr); + return -1; } + plist_free(data); - info("Sent %d bytes\n", sent_bytes); - info("%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)) { - error("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) { - error("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); - error("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); - } + // this step sends requested chunks of data from various offsets to asr so + // it can validate the filesystem before installing it + debug("Preparing to validate the filesystem\n"); + if (asr_perform_validation(asr, filesystem) < 0) { + error("ERROR: ASR was unable to validate the filesystem\n"); + asr_close(asr); + return -1; + } + info("Filesystem validated\n"); - if (i % (1450 * 1000) == 0) { - info("."); - } + // once the target filesystem has been validated, ASR then requests the + // entire filesystem to be sent. + debug("Preparing to send filesystem\n"); + if (asr_send_payload(asr, filesystem) < 0) { + error("ERROR: Unable to send payload to ASR\n"); + asr_close(asr); + return -1; } + info("Filesystem finished\n"); - info("Done sending filesystem\n"); - fclose(fd); - ret = idevice_disconnect(connection); - return ret; + asr_close(asr); + return 0; } int restore_send_kernelcache(restored_client_t client, char* kernel_data, int len) { @@ -550,11 +450,7 @@ int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss) { } plist_dict_insert_item(dict, "NorImageData", norimage_array); - int sz = 0; - char* xml = NULL; - plist_to_xml(dict, &xml, &sz); - debug("%s", xml); - free(xml); + debug_plist(dict); restored_error_t ret = restored_send(client, dict); if (ret != RESTORE_E_SUCCESS) { @@ -567,105 +463,135 @@ int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss) { return 0; } -int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem) { - idevice_t device = NULL; - idevice_error_t device_error = idevice_new(&device, uuid); - if (device_error != IDEVICE_E_SUCCESS) { - error("ERROR: Unable to open device\n"); - plist_free(tss); - return -1; - } +int restore_handle_data_request_msg(idevice_t device, restored_client_t restore, plist_t message, plist_t tss, const char* ipsw, const char* filesystem) { + char* type = NULL; + plist_t node = NULL; - 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); - idevice_free(device); - return -1; + // checks and see what kind of data restored is requests and pass + // the request to its own handler + node = plist_dict_get_item(message, "DataType"); + if (node && PLIST_STRING == plist_get_node_type(node)) { + plist_get_string_val(node, &type); + + // this request is sent when restored is ready to receive the filesystem + if (!strcmp(type, "SystemImageData")) { + restore_send_filesystem(device, filesystem); + + } + + else if (!strcmp(type, "KernelCache")) { + int kernelcache_size = 0; + char* kernelcache_data = NULL; + char* kernelcache_path = NULL; + if (tss_get_entry_path(tss, "KernelCache", &kernelcache_path) < 0) { + error("ERROR: Unable to find kernelcache path\n"); + return -1; + } + + if (get_signed_component(ipsw, tss, kernelcache_path, &kernelcache_data, &kernelcache_size) < 0) { + error("ERROR: Unable to get kernelcache file\n"); + return -1; + } + restore_send_kernelcache(restore, kernelcache_data, kernelcache_size); + free(kernelcache_data); + + } + + else if (!strcmp(type, "NORData")) { + restore_send_nor(restore, ipsw, tss); + + } else { + // Unknown DataType!! + debug("Unknown data request received\n"); + } } + return 0; +} +int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem) { + int error = 0; char* type = NULL; - uint64_t version = 0; - if (restored_query_type(restore, &type, &version) != RESTORE_E_SUCCESS) { - error("ERROR: Device is not in restore mode. QueryType returned \"%s\"\n", type); - plist_free(tss); - restored_client_free(restore); - idevice_free(device); + char* kernel = NULL; + plist_t node = NULL; + plist_t message = NULL; + idevice_t device = NULL; + restored_client_t restore = NULL; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + restored_error_t restore_error = RESTORE_E_SUCCESS; + + // open our connection to the device and verify we're in restore mode + if (restore_open_with_timeout(uuid, &device, &restore) < 0) { + error("ERROR: Unable to open device in restore mode\n"); return -1; } info("Device has successfully entered restore mode\n"); - /* start restore process */ - char* kernelcache = NULL; - info("Restore protocol version is %llu.\n", version); + // start the restore process restore_error = restored_start_restore(restore); - if (restore_error == RESTORE_E_SUCCESS) { - while (!idevicerestore_quit) { - 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 = restore_handle_progress_msg(restore, message); - - } else if (!strcmp(msgtype, "DataRequestMsg")) { - // device is requesting data to be sent - 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")) { - restore_send_filesystem(device, restore, filesystem); - - } else if (!strcmp(datatype, "KernelCache")) { - int kernelcache_size = 0; - char* kernelcache_data = NULL; - char* kernelcache_path = NULL; - if (tss_get_entry_path(tss, "KernelCache", &kernelcache_path) < 0) { - error("ERROR: Unable to find kernelcache path\n"); - return -1; - } - - if (get_signed_component(ipsw, tss, kernelcache_path, &kernelcache_data, &kernelcache_size) < 0) { - error("ERROR: Unable to get kernelcache file\n"); - return -1; - } - restore_send_kernelcache(restore, kernelcache_data, kernelcache_size); - free(kernelcache_data); - - } else if (!strcmp(datatype, "NORData")) { - restore_send_nor(restore, ipsw, tss); - - } else { - // Unknown DataType!! - error("Unknown DataType\n"); - return -1; - } - } - - } else if (!strcmp(msgtype, "StatusMsg")) { - restore_error = restore_handle_status_msg(restore, message); - - } else { - info("Received unknown message type: %s\n", msgtype); - } - } + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Unable to start the restore process\n"); + restore_close(device, restore); + return -1; + } - if (RESTORE_E_SUCCESS != restore_error) { - error("Invalid return status %d\n", restore_error); - //idevicerestore_quit = 1; - } + // this is the restore process loop, it reads each message in from + // restored and passes that data on to it's specific handler + while (!idevicerestore_quit) { + restore_error = restored_receive(restore, &message); + if (restore_error != RESTORE_E_SUCCESS) { + debug("No data to read\n"); + message = NULL; + continue; + } + // discover what kind of message has been received + node = plist_dict_get_item(message, "MsgType"); + if (!node || plist_get_node_type(node) != PLIST_STRING) { + debug("Unknown message received\n"); + debug_plist(message); plist_free(message); + message = NULL; + continue; } - } else { - error("ERROR: Could not start restore. %d\n", restore_error); + plist_get_string_val(node, &type); + + // data request messages are sent by restored whenever it requires + // files sent to the server by the client. these data requests include + // SystemImageData, KernelCache, and NORData requests + if (!strcmp(type, "DataRequestMsg")) { + error = restore_handle_data_request_msg(device, restore, message, tss, ipsw, filesystem); + } + + // progress notification messages sent by the restored inform the client + // of it's current operation and sometimes percent of progress is complete + else if (!strcmp(type, "ProgressMsg")) { + error = restore_handle_progress_msg(restore, message); + } + + // status messages usually indicate the current state of the restored + // process or often to signal an error has been encountered + else if (!strcmp(type, "StatusMsg")) { + error = restore_handle_status_msg(restore, message); + } + + // there might be some other message types i'm not aware of, but I think + // at least the "previous error logs" messages usually end up here + else { + debug("Unknown message type received\n"); + debug_plist(message); + } + + // finally, if any of these message handlers returned -1 then we encountered + // an unrecoverable error, so we need to bail. + if (error < 0) { + error("ERROR: Unable to successfully restore device\n"); + idevicerestore_quit = 1; + } + + plist_free(message); + message = NULL; } - restored_client_free(restore); - idevice_free(device); + restore_close(device, restore); return 0; } diff --git a/src/restore.h b/src/restore.h index 7a2e27e..cf9cf51 100644 --- a/src/restore.h +++ b/src/restore.h @@ -37,6 +37,6 @@ int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss); int restore_send_kernelcache(restored_client_t client, char *kernel_data, int len); int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem); int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_client_t* client); -int restore_send_filesystem(idevice_t device, restored_client_t client, const char *filesystem); +int restore_send_filesystem(idevice_t device, const char* filesystem); #endif -- cgit v1.1-32-gdbae