From 9308e665a2741690b4fa50fa494d7568dda95a28 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Wed, 20 Nov 2013 11:32:26 +0100 Subject: refactor component personalization and add support for Image4 stitching --- src/Makefile.am | 2 +- src/dfu.c | 16 +++++- src/idevicerestore.c | 79 +++++++++++++++-------------- src/idevicerestore.h | 4 +- src/img3.c | 16 +++--- src/img3.h | 2 +- src/img4.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/img4.h | 35 +++++++++++++ src/recovery.c | 31 +++++++++--- src/restore.c | 57 ++++++++++++++++++--- 10 files changed, 318 insertions(+), 64 deletions(-) create mode 100644 src/img4.c create mode 100644 src/img4.h diff --git a/src/Makefile.am b/src/Makefile.am index 680e43f..f0e85c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ AM_LDADD = $(AC_LDADD) bin_PROGRAMS = idevicerestore -idevicerestore_SOURCES = idevicerestore.c common.c tss.c fls.c mbn.c img3.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c limera1n.c download.c locking.c +idevicerestore_SOURCES = idevicerestore.c common.c tss.c fls.c mbn.c img3.c img4.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c limera1n.c download.c locking.c idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) idevicerestore_LDADD = $(AM_LDADD) diff --git a/src/dfu.c b/src/dfu.c index 5fdbfd5..6050ed0 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -179,11 +179,23 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide } } - if (ipsw_get_component_by_path(client->ipsw, client->tss, component, path, &data, &size) < 0) { - error("ERROR: Unable to get component: %s\n", component); + unsigned char* component_data = NULL; + unsigned int component_size = 0; + + if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(path); + return -1; + } + + if (personalize_component(component, component_data, component_size, client->tss, &data, &size) < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + free(component_data); free(path); return -1; } + free(component_data); + component_data = NULL; if (!(client->flags & FLAG_CUSTOM) && (strcmp(component, "iBEC") == 0)) { unsigned char* ticket = NULL; diff --git a/src/idevicerestore.c b/src/idevicerestore.c index c47e805..6eb25a9 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -34,6 +34,7 @@ #include "dfu.h" #include "tss.h" #include "img3.h" +#include "img4.h" #include "ipsw.h" #include "common.h" #include "normal.h" @@ -788,9 +789,11 @@ int idevicerestore_start(struct idevicerestore_client_t* client) client->mode = &idevicerestore_modes[MODE_RECOVERY]; } else { if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) { - /* send ApTicket */ - if (recovery_send_ticket(client) < 0) { - error("WARNING: Unable to send APTicket\n"); + if (!client->image4supported) { + /* send ApTicket */ + if (recovery_send_ticket(client) < 0) { + error("WARNING: Unable to send APTicket\n"); + } } } @@ -1520,13 +1523,12 @@ int build_manifest_get_identity_count(plist_t build_manifest) { return plist_array_get_size(build_identities_array); } -int ipsw_get_component_by_path(const char* ipsw, plist_t tss, const char* component, const char* path, unsigned char** data, unsigned int* size) { - unsigned int component_size = 0; - unsigned char* component_data = NULL; - unsigned char* component_blob = NULL; +int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) +{ char* component_name = NULL; - unsigned char* stitched_component = NULL; - unsigned int stitched_component_size = 0; + if (!ipsw || !path || !component_data || !component_size) { + return -1; + } component_name = strrchr(path, '/'); if (component_name != NULL) @@ -1535,50 +1537,51 @@ int ipsw_get_component_by_path(const char* ipsw, plist_t tss, const char* compon component_name = (char*) path; info("Extracting %s...\n", component_name); - - if (ipsw_extract_to_memory(ipsw, path, &component_data, &component_size) < 0) { + if (ipsw_extract_to_memory(ipsw, path, component_data, component_size) < 0) { error("ERROR: Unable to extract %s from %s\n", component_name, ipsw); return -1; } - if (tss) { - /* try to get blob for current component from tss response */ - if (component) { - if (tss_response_get_blob_by_entry(tss, component, &component_blob) < 0) { - debug("NOTE: No SHSH blob found for TSS entry %s from component %s\n", component_name, component); - } + return 0; +} + +int personalize_component(const char *component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size) { + unsigned char* component_blob = NULL; + unsigned int component_blob_size = 0; + unsigned char* stitched_component = NULL; + unsigned int stitched_component_size = 0; + + if (tss_response) { + if (tss_response_get_ap_img4_ticket(tss_response, &component_blob, &component_blob_size) == 0) { + /* stitch ApImg4Ticket into IMG4 file */ + img4_stitch_component(component_name, component_data, component_size, component_blob, component_blob_size, &stitched_component, &stitched_component_size); } else { - if (tss_response_get_blob_by_path(tss, path, &component_blob) < 0) { - debug("NOTE: No SHSH blob found for TSS entry %s from path %s\n", component_name, path); + /* try to get blob for current component from tss response */ + if (tss_response_get_blob_by_entry(tss_response, component_name, &component_blob) < 0) { + debug("NOTE: No SHSH blob found for component %s\n", component_name); } - } - if (component_blob != NULL) { - info("Personalizing component %s...\n", component_name); - - if (img3_stitch_component(component_data, component_size, component_blob, 64, &stitched_component, &stitched_component_size) < 0) { - error("ERROR: Unable to replace IMG3 signature\n"); - free(component_blob); - return -1; + if (component_blob != NULL) { + if (img3_stitch_component(component_name, component_data, component_size, component_blob, 64, &stitched_component, &stitched_component_size) < 0) { + error("ERROR: Unable to replace %s IMG3 signature\n", component_name); + free(component_blob); + return -1; + } + } else { + info("Not personalizing component %s...\n", component_name); } - free(component_data); - component_data = stitched_component; - component_size = stitched_component_size; - } else { - info("Not personalizing component %s...\n", component_name); + if (component_blob) + free(component_blob); } - - if (component_blob) - free(component_blob); } if (idevicerestore_debug) { - write_file(component_name, component_data, component_size); + write_file(component_name, stitched_component, stitched_component_size); } - *data = component_data; - *size = component_size; + *personalized_component = stitched_component; + *personalized_component_size = stitched_component_size; return 0; } diff --git a/src/idevicerestore.h b/src/idevicerestore.h index 148c78d..b6ca084 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -88,7 +88,9 @@ int build_manifest_get_build_count(plist_t build_manifest); void build_identity_print_information(plist_t build_identity); int build_identity_get_component_path(plist_t build_identity, const char* component, char** path); int ipsw_extract_filesystem(const char* ipsw, plist_t build_identity, char** filesystem); -int ipsw_get_component_by_path(const char* ipsw, plist_t tss, const char* component, const char* path, unsigned char** data, unsigned int* size); +int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size); +int personalize_component(const char *component, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size); + const char* get_component_name(const char* filename); #ifdef __cplusplus diff --git a/src/img3.c b/src/img3.c index ed7646d..1c70df2 100644 --- a/src/img3.c +++ b/src/img3.c @@ -402,39 +402,41 @@ static int img3_get_data(img3_file* image, unsigned char** pdata, unsigned int* return 0; } -int img3_stitch_component(const unsigned char* component, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size) +int img3_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size) { img3_file *img3 = NULL; unsigned char* outbuf = NULL; unsigned int outsize = 0; - if (!component || component_size == 0 || !blob || blob_size == 0 || !img3_data || !img3_size) { + if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img3_data || !img3_size) { return -1; } + + info("Personalizing IMG3 component %s...\n", component_name); /* parse current component as img3 */ - img3 = img3_parse_file(component, component_size); + img3 = img3_parse_file(component_data, component_size); if (img3 == NULL) { - error("ERROR: Unable to parse IMG3 file\n"); + error("ERROR: Unable to parse %s IMG3 file\n", component_name); return -1; } if (((img3_element_header*)blob)->full_size != blob_size) { - error("ERROR: the size %d embedded in the blob does not match the passed size of %d\n", ((img3_element_header*)blob)->full_size, blob_size); + error("ERROR: Invalid blob passed for %s IMG3: The size %d embedded in the blob does not match the passed size of %d\n", component_name, ((img3_element_header*)blob)->full_size, blob_size, component_name); img3_free(img3); return -1; } /* personalize the component using the blob */ if (img3_replace_signature(img3, blob) < 0) { - error("ERROR: Unable to replace IMG3 signature\n"); + error("ERROR: Unable to replace %s IMG3 signature\n", component_name); img3_free(img3); return -1; } /* get the img3 file as data */ if (img3_get_data(img3, &outbuf, &outsize) < 0) { - error("ERROR: Unable to reconstruct IMG3\n"); + error("ERROR: Unable to reconstruct %s IMG3\n", component_name); img3_free(img3); return -1; } diff --git a/src/img3.h b/src/img3.h index d1119a6..4a088b7 100644 --- a/src/img3.h +++ b/src/img3.h @@ -95,7 +95,7 @@ typedef struct { img3_element* unkn_element;*/ } img3_file; -int img3_stitch_component(const unsigned char* component, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size); +int img3_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img3_data, unsigned int *img3_size); #ifdef __cplusplus }s diff --git a/src/img4.c b/src/img4.c new file mode 100644 index 0000000..e18b419 --- /dev/null +++ b/src/img4.c @@ -0,0 +1,140 @@ +/* + * img4.c + * Functions for handling the new IMG4 format + * + * Copyright (c) 2013 Nikias Bassen. 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 "img4.h" + +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_SEQUENCE 0x10 +#define ASN1_CONTEXT_SPECIFIC 0x80 +#define ASN1_IA5_STRING 0x16 + +#define IMG4_MAGIC "IMG4" +#define IMG4_MAGIC_SIZE 4 + +static unsigned char* asn1_create_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size) +{ + unsigned char buf[5]; + unsigned int off = 0; + + if (!type || size == 0 || !data || !data_size) { + return NULL; + } + + buf[off++] = type; + + // first, calculate the size + if (size >= 0x1000000) { + // 1+4 bytes length + buf[off++] = 0x84; + buf[off++] = (size >> 24) & 0xFF; + buf[off++] = (size >> 16) & 0xFF; + buf[off++] = (size >> 8) & 0xFF; + buf[off++] = size & 0xFF; + } else if (size >= 0x10000) { + // 1+3 bytes length + buf[off++] = 0x83; + buf[off++] = (size >> 16) & 0xFF; + buf[off++] = (size >> 8) & 0xFF; + buf[off++] = size & 0xFF; + } else if (size >= 0x100) { + // 1+2 bytes length + buf[off++] = 0x82; + buf[off++] = (size >> 8) & 0xFF; + buf[off++] = (size & 0xFF); + } else if (size >= 0x80) { + // 1+1 byte length + buf[off++] = 0x81; + buf[off++] = (size & 0xFF); + } else { + // 1 byte length + buf[off++] = size & 0xFF; + } + + *data = malloc(off); + memcpy(*data, buf, off); + *data_size = off; + + return *data; +} + +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size) +{ + unsigned char* magic_header = NULL; + unsigned int magic_header_size = 0; + unsigned char* blob_header = NULL; + unsigned int blob_header_size = 0; + unsigned char* img4header = NULL; + unsigned int img4header_size = 0; + unsigned int content_size; + unsigned char* outbuf; + unsigned char* p; + + if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img4_data || !img4_size) { + return -1; + } + + /* first we need check if we have to change the tag for the given component */ + // FIXME: write proper ASN1 handling code for this + if (strcmp(component_name, "RestoreKernelCache") == 0) { + memcpy((char*)component_data+0xD, "rkrn", 4); + } else if (strcmp(component_name, "RestoreDeviceTree") == 0) { + memcpy((char*)component_data+0xD, "rdtr", 4); + } + + // create element header for the "IMG4" magic + asn1_create_element_header(ASN1_IA5_STRING, IMG4_MAGIC_SIZE, &magic_header, &magic_header_size); + // create element header for the blob (ApImg4Ticket) + asn1_create_element_header(ASN1_CONTEXT_SPECIFIC|ASN1_CONSTRUCTED, blob_size, &blob_header, &blob_header_size); + + // calculate the size for the final IMG4 file (asn1 sequence) + content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size; + + // create element header for the final IMG4 asn1 blob + asn1_create_element_header(ASN1_SEQUENCE|ASN1_CONSTRUCTED, content_size, &img4header, &img4header_size); + + outbuf = (unsigned char*)malloc(img4header_size + content_size); + if (!outbuf) { + return -1; + } + p = outbuf; + + // now put everything together + memcpy(p, img4header, img4header_size); + p += img4header_size; + memcpy(p, magic_header, magic_header_size); + p += magic_header_size; + memcpy(p, IMG4_MAGIC, IMG4_MAGIC_SIZE); + p += IMG4_MAGIC_SIZE; + memcpy(p, component_data, component_size); + p += component_size; + memcpy(p, blob_header, blob_header_size); + p += blob_header_size; + memcpy(p, blob, blob_size); + p += blob_size; + + *img4_data = outbuf; + *img4_size = (p - outbuf); + + return 0; +} diff --git a/src/img4.h b/src/img4.h new file mode 100644 index 0000000..97a61aa --- /dev/null +++ b/src/img4.h @@ -0,0 +1,35 @@ +/* + * img4.h + * Functions for handling the new IMG4 format + * + * Copyright (c) 2013 Nikias Bassen. 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_IMG4_H +#define IDEVICERESTORE_IMG4_H + +#ifdef __cplusplus +extern "C" { +#endif + +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size); + +#ifdef __cplusplus +}s +#endif + +#endif diff --git a/src/recovery.c b/src/recovery.c index 518b0b8..e866531 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -158,10 +158,12 @@ int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build } if ((client->build_major > 8) && !(client->flags & FLAG_CUSTOM)) { - /* send ApTicket */ - if (recovery_send_ticket(client) < 0) { - error("ERROR: Unable to send APTicket\n"); - return -1; + if (!client->image4supported) { + /* send ApTicket */ + if (recovery_send_ticket(client) < 0) { + error("ERROR: Unable to send APTicket\n"); + return -1; + } } } @@ -280,11 +282,23 @@ int recovery_send_component(struct idevicerestore_client_t* client, plist_t buil } } - if (ipsw_get_component_by_path(client->ipsw, client->tss, component, path, &data, &size) < 0) { - error("ERROR: Unable to get component: %s\n", component); + unsigned char* component_data = NULL; + unsigned int component_size = 0; + + if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(path); + return -1; + } + + if (personalize_component(component, component_data, component_size, client->tss, &data, &size) < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + free(component_data); free(path); return -1; } + free(component_data); + component_data = NULL; info("Sending %s (%d bytes)...\n", component, size); @@ -342,7 +356,7 @@ int recovery_send_applelogo(struct idevicerestore_client_t* client, plist_t buil return -1; } - recovery_error = irecv_send_command(client->recovery->client, "setpicture 0"); + recovery_error = irecv_send_command(client->recovery->client, "setpicture 2"); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to set %s\n", component); return -1; @@ -391,6 +405,9 @@ int recovery_send_ramdisk(struct idevicerestore_client_t* client, plist_t build_ } } + irecv_send_command(client->recovery->client, "getenv ramdisk-size"); + irecv_receive(client->recovery->client); + if (recovery_send_component(client, build_identity, component) < 0) { error("ERROR: Unable to send %s to device.\n", component); return -1; diff --git a/src/restore.c b/src/restore.c index 845a670..ed3006b 100644 --- a/src/restore.c +++ b/src/restore.c @@ -787,11 +787,25 @@ int restore_send_kernelcache(restored_client_t restore, struct idevicerestore_cl } } - if (ipsw_get_component_by_path(client->ipsw, client->tss, "KernelCache", path, &data, &size) < 0) { - error("ERROR: Unable to get kernelcache file\n"); + const char* component = "KernelCache"; + unsigned char* component_data = NULL; + unsigned int component_size = 0; + + if (extract_component(client->ipsw, path, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(path); return -1; } + if (personalize_component(component, component_data, component_size, client->tss, &data, &size) < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + free(component_data); + free(path); + return -1; + } + free(component_data); + component_data = NULL; + dict = plist_new_dict(); blob = plist_new_data((char*)data, size); plist_dict_insert_item(dict, "KernelCacheFile", blob); @@ -864,11 +878,25 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - if (ipsw_get_component_by_path(client->ipsw, client->tss, "LLB", llb_path, &llb_data, &llb_size) < 0) { - error("ERROR: Unable to get personalized LLB\n"); + const char* component = "LLB"; + unsigned char* component_data = NULL; + unsigned int component_size = 0; + + if (extract_component(client->ipsw, llb_path, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(llb_path); return -1; } + if (personalize_component(component, component_data, component_size, client->tss, &llb_data, &llb_size) < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + free(component_data); + free(llb_path); + return -1; + } + free(component_data); + component_data = NULL; + dict = plist_new_dict(); plist_dict_insert_item(dict, "LlbImageData", plist_new_data((char*)llb_data, (uint64_t) llb_size)); @@ -883,10 +911,25 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* } memset(firmware_filename, '\0', sizeof(firmware_filename)); snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); - if (ipsw_get_component_by_path(client->ipsw, client->tss, get_component_name(filename), firmware_filename, &nor_data, &nor_size) < 0) { - error("ERROR: Unable to get personalized firmware file %s\n", firmware_filename); - break; + + component = get_component_name(filename); + component_data = NULL; + unsigned int component_size = 0; + + if (extract_component(client->ipsw, firmware_filename, &component_data, &component_size) < 0) { + error("ERROR: Unable to extract component: %s\n", component); + free(llb_path); + return -1; + } + + if (personalize_component(component, component_data, component_size, client->tss, &nor_data, &nor_size) < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + free(component_data); + free(llb_path); + return -1; } + free(component_data); + component_data = NULL; plist_array_append_item(norimage_array, plist_new_data((char*)nor_data, (uint64_t)nor_size)); free(nor_data); -- cgit v1.1-32-gdbae