diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/dfu.c | 28 | ||||
-rw-r--r-- | src/dfu.h | 29 | ||||
-rw-r--r-- | src/idevicerestore.c | 623 | ||||
-rw-r--r-- | src/normal.c | 28 | ||||
-rw-r--r-- | src/normal.h | 29 | ||||
-rw-r--r-- | src/recovery.c | 269 | ||||
-rw-r--r-- | src/recovery.h | 37 | ||||
-rw-r--r-- | src/restore.c | 405 | ||||
-rw-r--r-- | src/restore.h | 36 |
10 files changed, 871 insertions, 615 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 540b262..6840a0c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,6 @@ AM_LDFLAGS =\ bin_PROGRAMS = idevicerestore -idevicerestore_SOURCES = idevicerestore.c img3.c ipsw.c tss.c +idevicerestore_SOURCES = idevicerestore.c dfu.c tss.c img3.c ipsw.c normal.c restore.c recovery.c idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS)
\ No newline at end of file diff --git a/src/dfu.c b/src/dfu.c new file mode 100644 index 0000000..5e13f38 --- /dev/null +++ b/src/dfu.c @@ -0,0 +1,28 @@ +/* + * dfu.c + * Functions for handling idevices in DFU 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 <stdint.h> + +#include "dfu.h" + +int dfu_get_ecid(uint64_t* ecid) { + return 0; +} diff --git a/src/dfu.h b/src/dfu.h new file mode 100644 index 0000000..ef9d911 --- /dev/null +++ b/src/dfu.h @@ -0,0 +1,29 @@ +/* + * 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 DFU_H +#define DFU_H + +#include <stdint.h> + +int dfu_get_ecid(uint64_t* ecid); + +#endif diff --git a/src/idevicerestore.c b/src/idevicerestore.c index fa685bd..aaff4d6 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -29,17 +29,20 @@ #include <libimobiledevice/lockdown.h> #include <libimobiledevice/libimobiledevice.h> +#include "dfu.h" #include "tss.h" #include "img3.h" #include "ipsw.h" +#include "normal.h" +#include "restore.h" +#include "recovery.h" #include "idevicerestore.h" #define UNKNOWN_MODE 0 -#define NORMAL_MODE 1 -#define RECOVERY_MODE 2 -#define RESTORE_MODE 3 - -#define ASR_PORT 12345 +#define DFU_MODE 1 +#define NORMAL_MODE 2 +#define RECOVERY_MODE 3 +#define RESTORE_MODE 4 int idevicerestore_debug = 0; static int idevicerestore_mode = 0; @@ -48,11 +51,6 @@ static int idevicerestore_custom = 0; void usage(int argc, char* argv[]); int write_file(const char* filename, char* data, int size); -int recovery_send_ibec(char* ipsw, plist_t tss); -int recovery_send_applelogo(char* ipsw, plist_t tss); -int recovery_send_devicetree(char* ipsw, plist_t tss); -int recovery_send_ramdisk(char* ipsw, plist_t tss); -int recovery_send_kernelcache(char* ipsw, plist_t tss); int get_tss_data_by_name(plist_t tss, const char* entry, char** path, char** blob); int get_tss_data_by_path(plist_t tss, const char* path, char** name, char** blob); void device_callback(const idevice_event_t* event, void *user_data); @@ -379,7 +377,7 @@ int main(int argc, char* argv[]) { free(kernelcache_data); } else if (!strcmp(datatype, "NORData")) { - send_nor_data(restore, ipsw, tss_response); + restore_send_nor_data(restore, ipsw, tss_response); } else { // Unknown DataType!! @@ -435,375 +433,6 @@ void usage(int argc, char* argv[]) { exit(1); } -int restore_handle_progress_msg(restored_client_t client, plist_t msg) { - const char operation_name[][35] = { - "Unknown 1", - "Unknown 2", - "Unknown 3", - "Unknown 4", - "Unknown 5", - "Unknown 6", - "Unknown 7", - "Unknown 8", - "Unknown 9", - "Unknown 10", - "Unknown 11", - "Creating partition map", - "Creating filesystem", - "Restoring image", - "Verifying restore", - "Checking filesystems", - "Mounting filesystems", - "Unknown 18", - "Flashing NOR", - "Updating baseband", - "Finalizing NAND epoch update", - "Unknown 22", - "Unknown 23", - "Unknown 24", - "Unknown 25", - "Modifying persistent boot-args", - "Unknown 27", - "Unknown 28", - "Waiting for NAND", - "Unmounting filesystems", - "Unknown 31", - "Unknown 32", - "Waiting for Device...", - "Unknown 34", - "Unknown 35", - "Loading NOR data to flash" - }; - - plist_t node = NULL; - uint64_t operation = 0; - uint64_t uprogress = 0; - int progress = 0; - - node = plist_dict_get_item(msg, "Operation"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &operation); - } else { - debug("Failed to parse operation from ProgressMsg plist\n"); - return 0; - } - - node = plist_dict_get_item(msg, "Progress"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &uprogress); - progress = (int) uprogress; - } else { - debug("Failed to parse progress from ProgressMsg plist \n"); - return 0; - } - - if ((progress > 0) && (progress < 100)) - info("%s - Progress: %llu%\n", operation_name[operation], progress); - else - info("%s\n", operation_name[operation]); - - return 0; -} - -int restore_handle_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")) { - asr_send_system_image_data_from_file(device, client, filesystem); - } else if (!strcmp(datatype, "KernelCache")) { - restore_send_kernelcache(client, kernel); - } else if (!strcmp(datatype, "NORData")) { - send_nor_data(device, client); - } else { - // Unknown DataType!! - error("Unknown DataType\n"); - return -1; - } - } - return 0; -} - -int restore_handle_status_msg(restored_client_t client, plist_t msg) { - info("Got status message\n"); - return 0; -} - -int asr_send_system_image_data_from_file(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; - } - info("Received %d bytes\n", recv_bytes); - info("%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); - - 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; - } - - 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); - } - - if (i % (1450 * 1000) == 0) { - info("."); - } - } - - info("Done sending filesystem\n"); - fclose(fd); - ret = idevice_disconnect(connection); - return ret; -} - -int restore_send_kernelcache(restored_client_t client, char *kernel_data, int len) { - info("Sending kernelcache\n"); - - 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("ERROR: Unable to send kernelcache data\n"); - plist_free(dict); - return -1; - } - - info("Done sending kernelcache\n"); - plist_free(dict); - return 0; -} - -int send_nor_data(restored_client_t client, char* ipsw, plist_t tss) { - char* llb_path = NULL; - char* llb_blob = NULL; - if (get_tss_data_by_name(tss, "LLB", &llb_path, &llb_blob) < 0) { - error("ERROR: Unable to get LLB info from TSS response\n"); - return -1; - } - - char* llb_filename = strstr(llb_path, "LLB"); - if (llb_filename == NULL) { - error("ERROR: Unable to extrac firmware path from LLB filename\n"); - free(llb_path); - free(llb_blob); - return -1; - } - - char firmware_path[256]; - memset(firmware_path, '\0', sizeof(firmware_path)); - memcpy(firmware_path, llb_path, (llb_filename - 1) - llb_path); - info("Found firmware path %s\n", firmware_path); - - char manifest_file[256]; - memset(manifest_file, '\0', sizeof(manifest_file)); - snprintf(manifest_file, sizeof(manifest_file), "%s/manifest", firmware_path); - info("Getting firmware manifest %s\n", manifest_file); - - int manifest_size = 0; - char* manifest_data = NULL; - if (ipsw_extract_to_memory(ipsw, manifest_file, &manifest_data, &manifest_size) < 0) { - error("ERROR: Unable to extract firmware manifest from ipsw\n"); - free(llb_path); - free(llb_blob); - return -1; - } - - char firmware_filename[256]; - memset(firmware_filename, '\0', sizeof(firmware_filename)); - - int llb_size = 0; - char* llb_data = NULL; - plist_t dict = plist_new_dict(); - char* filename = strtok(manifest_data, "\n"); - if (filename != NULL) { - memset(firmware_filename, '\0', sizeof(firmware_filename)); - snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); - if (get_signed_component_by_path(ipsw, tss, firmware_filename, &llb_data, &llb_size) < 0) { - error("ERROR: Unable to get signed LLB\n"); - return -1; - } - - plist_dict_insert_item(dict, "LlbImageData", plist_new_data(llb_data, (uint64_t) llb_size)); - } - - int nor_size = 0; - char* nor_data = NULL; - filename = strtok(NULL, "\n"); - plist_t norimage_array = plist_new_array(); - while (filename != NULL) { - memset(firmware_filename, '\0', sizeof(firmware_filename)); - snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); - if (get_signed_component_by_path(ipsw, tss, firmware_filename, &nor_data, &nor_size) < 0) { - error("ERROR: Unable to get signed firmware %s\n", firmware_filename); - break; - } - - plist_array_append_item(norimage_array, plist_new_data(nor_data, (uint64_t) nor_size)); - free(nor_data); - nor_data = NULL; - nor_size = 0; - filename = strtok(NULL, "\n"); - } - 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); - - restored_error_t ret = restored_send(client, dict); - if (ret != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send kernelcache data\n"); - plist_free(dict); - return -1; - } - - plist_free(dict); - return 0; -} - int write_file(const char* filename, char* data, int size) { debug("Writing data to %s\n", filename); FILE* file = fopen(filename, "wb"); @@ -1058,237 +687,3 @@ int get_signed_component_by_path(char* ipsw, plist_t tss, char* path, char** pda *psize = size; return 0; } - -static int recovery_send_signed_component(irecv_client_t client, char* ipsw, plist_t tss, char* component) { - int size = 0; - char* data = NULL; - char* path = NULL; - char* blob = NULL; - img3_file* img3 = NULL; - irecv_error_t error = 0; - - if (get_signed_component_by_name(ipsw, tss, component, &data, &size) < 0) { - error("ERROR: Unable to get signed component: %s\n", component); - return -1; - } - - info("Sending %s...\n", component); - error = irecv_send_buffer(client, data, size); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to send IMG3: %s\n", path); - img3_free(img3); - free(data); - free(path); - return -1; - } - - if (data) { - free(data); - data = NULL; - } - - return 0; -} - -static irecv_error_t recovery_open_with_timeout(irecv_client_t* client) { - int i = 0; - irecv_error_t error = 0; - for (i = 10; i > 0; i--) { - error = irecv_open(client); - if (error == IRECV_E_SUCCESS) { - return error; - } - - sleep(2); - info("Retrying connection...\n"); - } - - error("ERROR: Unable to connect to recovery device.\n"); - return error; -} - -int recovery_send_ibec(char* ipsw, plist_t tss) { - irecv_error_t error = 0; - irecv_client_t client = NULL; - char* component = "iBEC"; - - error = recovery_open_with_timeout(&client); - if (error != IRECV_E_SUCCESS) { - return -1; - } - - error = irecv_send_command(client, "setenv auto-boot true"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to set auto-boot environmental variable\n"); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "saveenv"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to save environmental variable\n"); - irecv_close(client); - client = NULL; - return -1; - } - - if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { - error("ERROR: Unable to send %s to device.\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "go"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to execute %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - if (client) { - irecv_close(client); - client = NULL; - } - return 0; -} - -int recovery_send_applelogo(char* ipsw, plist_t tss) { - irecv_error_t error = 0; - irecv_client_t client = NULL; - char* component = "AppleLogo"; - - info("Sending %s...\n", component); - - error = recovery_open_with_timeout(&client); - if (error != IRECV_E_SUCCESS) { - return -1; - } - - if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { - error("ERROR: Unable to send %s to device.\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "setpicture 1"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to set %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "bgcolor 0 0 0"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to display %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - if (client) { - irecv_close(client); - client = NULL; - } - return 0; -} - -int recovery_send_devicetree(char* ipsw, plist_t tss) { - irecv_error_t error = 0; - irecv_client_t client = NULL; - char *component = "RestoreDeviceTree"; - - error = recovery_open_with_timeout(&client); - if (error != IRECV_E_SUCCESS) { - return -1; - } - - if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { - error("ERROR: Unable to send %s to device.\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "devicetree"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to execute %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - if (client) { - irecv_close(client); - client = NULL; - } - return 0; -} - -int recovery_send_ramdisk(char* ipsw, plist_t tss) { - irecv_error_t error = 0; - irecv_client_t client = NULL; - char *component = "RestoreRamDisk"; - - error = recovery_open_with_timeout(&client); - if (error != IRECV_E_SUCCESS) { - return -1; - } - - if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { - error("ERROR: Unable to send %s to device.\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "ramdisk"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to execute %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - if (client) { - irecv_close(client); - client = NULL; - } - return 0; -} - -int recovery_send_kernelcache(char* ipsw, plist_t tss) { - irecv_error_t error = 0; - irecv_client_t client = NULL; - char *component = "RestoreKernelCache"; - - error = recovery_open_with_timeout(&client); - if (error != IRECV_E_SUCCESS) { - return -1; - } - - if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { - error("ERROR: Unable to send %s to device.\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - error = irecv_send_command(client, "bootx"); - if (error != IRECV_E_SUCCESS) { - error("ERROR: Unable to execute %s\n", component); - irecv_close(client); - client = NULL; - return -1; - } - - if (client) { - irecv_close(client); - client = NULL; - } - return 0; -} diff --git a/src/normal.c b/src/normal.c new file mode 100644 index 0000000..c7baefd --- /dev/null +++ b/src/normal.c @@ -0,0 +1,28 @@ +/* + * normal.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 <stdint.h> + +#include "normal.h" + +int normal_get_ecid(uint64_t* ecid) { + return 0; +} diff --git a/src/normal.h b/src/normal.h new file mode 100644 index 0000000..3e2868d --- /dev/null +++ b/src/normal.h @@ -0,0 +1,29 @@ +/* + * normal.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 NORMAL_H +#define NORMAL_H + +#include <stdint.h> + +int normal_get_ecid(uint64_t* ecid); + +#endif diff --git a/src/recovery.c b/src/recovery.c new file mode 100644 index 0000000..4e2e7ad --- /dev/null +++ b/src/recovery.c @@ -0,0 +1,269 @@ +/* + * recovery.c + * Functions for handling idevices in recovery 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 <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <libirecovery.h> + +#include "tss.h" +#include "img3.h" +#include "recovery.h" +#include "idevicerestore.h" + +int recovery_send_signed_component(irecv_client_t client, char* ipsw, plist_t tss, char* component) { + int size = 0; + char* data = NULL; + char* path = NULL; + char* blob = NULL; + img3_file* img3 = NULL; + irecv_error_t error = 0; + + if (get_signed_component_by_name(ipsw, tss, component, &data, &size) < 0) { + error("ERROR: Unable to get signed component: %s\n", component); + return -1; + } + + info("Sending %s...\n", component); + error = irecv_send_buffer(client, data, size); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to send IMG3: %s\n", path); + img3_free(img3); + free(data); + free(path); + return -1; + } + + if (data) { + free(data); + data = NULL; + } + + return 0; +} + +irecv_error_t recovery_open_with_timeout(irecv_client_t* client) { + int i = 0; + irecv_error_t error = 0; + for (i = 10; i > 0; i--) { + error = irecv_open(client); + if (error == IRECV_E_SUCCESS) { + return error; + } + + sleep(2); + info("Retrying connection...\n"); + } + + error("ERROR: Unable to connect to recovery device.\n"); + return error; +} + +int recovery_send_ibec(char* ipsw, plist_t tss) { + irecv_error_t error = 0; + irecv_client_t client = NULL; + char* component = "iBEC"; + + error = recovery_open_with_timeout(&client); + if (error != IRECV_E_SUCCESS) { + return -1; + } + + error = irecv_send_command(client, "setenv auto-boot true"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to set auto-boot environmental variable\n"); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "saveenv"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to save environmental variable\n"); + irecv_close(client); + client = NULL; + return -1; + } + + if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { + error("ERROR: Unable to send %s to device.\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "go"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to execute %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + if (client) { + irecv_close(client); + client = NULL; + } + return 0; +} + +int recovery_send_applelogo(char* ipsw, plist_t tss) { + irecv_error_t error = 0; + irecv_client_t client = NULL; + char* component = "AppleLogo"; + + info("Sending %s...\n", component); + + error = recovery_open_with_timeout(&client); + if (error != IRECV_E_SUCCESS) { + return -1; + } + + if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { + error("ERROR: Unable to send %s to device.\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "setpicture 1"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to set %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "bgcolor 0 0 0"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to display %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + if (client) { + irecv_close(client); + client = NULL; + } + return 0; +} + +int recovery_send_devicetree(char* ipsw, plist_t tss) { + irecv_error_t error = 0; + irecv_client_t client = NULL; + char *component = "RestoreDeviceTree"; + + error = recovery_open_with_timeout(&client); + if (error != IRECV_E_SUCCESS) { + return -1; + } + + if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { + error("ERROR: Unable to send %s to device.\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "devicetree"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to execute %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + if (client) { + irecv_close(client); + client = NULL; + } + return 0; +} + +int recovery_send_ramdisk(char* ipsw, plist_t tss) { + irecv_error_t error = 0; + irecv_client_t client = NULL; + char *component = "RestoreRamDisk"; + + error = recovery_open_with_timeout(&client); + if (error != IRECV_E_SUCCESS) { + return -1; + } + + if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { + error("ERROR: Unable to send %s to device.\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "ramdisk"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to execute %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + if (client) { + irecv_close(client); + client = NULL; + } + return 0; +} + +int recovery_send_kernelcache(char* ipsw, plist_t tss) { + irecv_error_t error = 0; + irecv_client_t client = NULL; + char *component = "RestoreKernelCache"; + + error = recovery_open_with_timeout(&client); + if (error != IRECV_E_SUCCESS) { + return -1; + } + + if (recovery_send_signed_component(client, ipsw, tss, component) < 0) { + error("ERROR: Unable to send %s to device.\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + error = irecv_send_command(client, "bootx"); + if (error != IRECV_E_SUCCESS) { + error("ERROR: Unable to execute %s\n", component); + irecv_close(client); + client = NULL; + return -1; + } + + if (client) { + irecv_close(client); + client = NULL; + } + return 0; +} + + +int recovery_get_ecid(uint64_t* ecid) { + return 0; +} diff --git a/src/recovery.h b/src/recovery.h new file mode 100644 index 0000000..5495638 --- /dev/null +++ b/src/recovery.h @@ -0,0 +1,37 @@ +/* + * recovery.h + * Functions for handling idevices in recovery 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 RECOVERY_H +#define RECOVERY_H + +#include <stdint.h> +#include <plist/plist.h> + +int recovery_send_signed_component(irecv_client_t client, char* ipsw, plist_t tss, char* component); +irecv_error_t recovery_open_with_timeout(irecv_client_t* client); +int recovery_send_ibec(char* ipsw, plist_t tss); +int recovery_send_applelogo(char* ipsw, plist_t tss); +int recovery_send_devicetree(char* ipsw, plist_t tss); +int recovery_send_ramdisk(char* ipsw, plist_t tss); +int recovery_send_kernelcache(char* ipsw, plist_t tss); +int recovery_get_ecid(uint64_t* ecid); + +#endif diff --git a/src/restore.c b/src/restore.c new file mode 100644 index 0000000..485df9b --- /dev/null +++ b/src/restore.c @@ -0,0 +1,405 @@ +/* + * restore.c + * Functions for handling idevices in restore 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libimobiledevice/restore.h> + +#include "restore.h" +#include "idevicerestore.h" + +#define ASR_PORT 12345 + +#define CREATE_PARTITION_MAP 12 +#define CREATE_FILESYSTEM 13 +#define RESTORE_IMAGE 14 +#define VERIFY_RESTORE 15 +#define CHECK_FILESYSTEM 16 +#define MOUNT_FILESYSTEM 17 +#define FLASH_NOR 19 +#define UPDATE_BASEBAND 20 +#define FINIALIZE_NAND 21 +#define MODIFY_BOOTARGS 26 +#define WAIT_FOR_NAND 29 +#define UNMOUNT_FILESYSTEM 30 +#define WAIT_FOR_DEVICE 33 +#define LOAD_NOR 36 + +const char* restore_progress_string(unsigned int operation) { + switch(operation) { + case CREATE_PARTITION_MAP: + return "Creating partition map"; + + case CREATE_FILESYSTEM: + return "Creating filesystem"; + + case RESTORE_IMAGE: + return "Restoring image"; + + case VERIFY_RESTORE: + return "Verifying restore"; + + case CHECK_FILESYSTEM: + return "Checking filesystems"; + + case MOUNT_FILESYSTEM: + return "Mounting filesystems"; + + case FLASH_NOR: + return "Flashing NOR"; + + case UPDATE_BASEBAND: + return "Updating baseband"; + + case FINIALIZE_NAND: + return "Finalizing NAND epoch update"; + + case MODIFY_BOOTARGS: + return "Modifying persistent boot-args"; + + case UNMOUNT_FILESYSTEM: + return "Unmounting filesystems"; + + case WAIT_FOR_NAND: + return "Waiting for NAND..."; + + case WAIT_FOR_DEVICE: + return "Waiting for Device..."; + + case LOAD_NOR: + return "Loading NOR data to flash"; + + default: + return "Unknown 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; + uint32_t progress = 0; + + node = plist_dict_get_item(msg, "Operation"); + if (node && PLIST_UINT == plist_get_node_type(node)) { + plist_get_uint_val(node, &operation); + } else { + debug("Failed to parse operation from ProgressMsg plist\n"); + return 0; + } + + node = plist_dict_get_item(msg, "Progress"); + if (node && PLIST_UINT == plist_get_node_type(node)) { + plist_get_uint_val(node, &uprogress); + progress = (uint32_t) uprogress; + } else { + debug("Failed to parse progress from ProgressMsg plist \n"); + return 0; + } + + if ((progress > 0) && (progress < 100)) + info("%s - Progress: %ul%%\n", restore_progress_string(operation), progress); + 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"); + return 0; +} + +int asr_send_system_image_data_from_file(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; + } + info("Received %d bytes\n", recv_bytes); + info("%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); + + 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; + } + + 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); + } + + if (i % (1450 * 1000) == 0) { + info("."); + } + } + + info("Done sending filesystem\n"); + fclose(fd); + ret = idevice_disconnect(connection); + return ret; +} + +int restore_send_kernelcache(restored_client_t client, char *kernel_data, int len) { + info("Sending kernelcache\n"); + + 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("ERROR: Unable to send kernelcache data\n"); + plist_free(dict); + return -1; + } + + info("Done sending kernelcache\n"); + plist_free(dict); + return 0; +} + +int restore_send_nor_data(restored_client_t client, char* ipsw, plist_t tss) { + char* llb_path = NULL; + char* llb_blob = NULL; + if (get_tss_data_by_name(tss, "LLB", &llb_path, &llb_blob) < 0) { + error("ERROR: Unable to get LLB info from TSS response\n"); + return -1; + } + + char* llb_filename = strstr(llb_path, "LLB"); + if (llb_filename == NULL) { + error("ERROR: Unable to extrac firmware path from LLB filename\n"); + free(llb_path); + free(llb_blob); + return -1; + } + + char firmware_path[256]; + memset(firmware_path, '\0', sizeof(firmware_path)); + memcpy(firmware_path, llb_path, (llb_filename - 1) - llb_path); + info("Found firmware path %s\n", firmware_path); + + char manifest_file[256]; + memset(manifest_file, '\0', sizeof(manifest_file)); + snprintf(manifest_file, sizeof(manifest_file), "%s/manifest", firmware_path); + info("Getting firmware manifest %s\n", manifest_file); + + int manifest_size = 0; + char* manifest_data = NULL; + if (ipsw_extract_to_memory(ipsw, manifest_file, &manifest_data, &manifest_size) < 0) { + error("ERROR: Unable to extract firmware manifest from ipsw\n"); + free(llb_path); + free(llb_blob); + return -1; + } + + char firmware_filename[256]; + memset(firmware_filename, '\0', sizeof(firmware_filename)); + + int llb_size = 0; + char* llb_data = NULL; + plist_t dict = plist_new_dict(); + char* filename = strtok(manifest_data, "\n"); + if (filename != NULL) { + memset(firmware_filename, '\0', sizeof(firmware_filename)); + snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); + if (get_signed_component_by_path(ipsw, tss, firmware_filename, &llb_data, &llb_size) < 0) { + error("ERROR: Unable to get signed LLB\n"); + return -1; + } + + plist_dict_insert_item(dict, "LlbImageData", plist_new_data(llb_data, (uint64_t) llb_size)); + } + + int nor_size = 0; + char* nor_data = NULL; + filename = strtok(NULL, "\n"); + plist_t norimage_array = plist_new_array(); + while (filename != NULL) { + memset(firmware_filename, '\0', sizeof(firmware_filename)); + snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); + if (get_signed_component_by_path(ipsw, tss, firmware_filename, &nor_data, &nor_size) < 0) { + error("ERROR: Unable to get signed firmware %s\n", firmware_filename); + break; + } + + plist_array_append_item(norimage_array, plist_new_data(nor_data, (uint64_t) nor_size)); + free(nor_data); + nor_data = NULL; + nor_size = 0; + filename = strtok(NULL, "\n"); + } + 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); + + restored_error_t ret = restored_send(client, dict); + if (ret != RESTORE_E_SUCCESS) { + error("ERROR: Unable to send kernelcache data\n"); + plist_free(dict); + return -1; + } + + plist_free(dict); + return 0; +} diff --git a/src/restore.h b/src/restore.h new file mode 100644 index 0000000..644658a --- /dev/null +++ b/src/restore.h @@ -0,0 +1,36 @@ +/* + * restore.h + * Functions for handling idevices in restore 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 RESTORED_H +#define RESTORED_H + +#include <libimobiledevice/restore.h> + +#include "restore.h" + +int restore_handle_progress_msg(restored_client_t client, plist_t msg); +int restore_handle_status_msg(restored_client_t client, plist_t msg); +int asr_send_system_image_data_from_file(idevice_t device, restored_client_t client, const char *filesystem); +int restore_send_kernelcache(restored_client_t client, char *kernel_data, int len); +int restore_send_nor_data(restored_client_t client, char* ipsw, plist_t tss); +const char* restore_progress_string(unsigned int operation); + +#endif |