summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2019-08-29 10:00:09 +0200
committerGravatar Nikias Bassen2019-08-29 10:00:09 +0200
commitc09c07b33038cc333daf1e15f3abbbe522b1de62 (patch)
tree7435920f6e99696d32345c63a82fc2b3fdf40078 /src
parentc40c94562779c1973ef7e425752a2ac7b1ff0ab8 (diff)
downloadidevicerestore-c09c07b33038cc333daf1e15f3abbbe522b1de62.tar.gz
idevicerestore-c09c07b33038cc333daf1e15f3abbbe522b1de62.tar.bz2
img4: Add new function img4_create_local_manifest()
Diffstat (limited to 'src')
-rw-r--r--src/img4.c502
-rw-r--r--src/img4.h5
2 files changed, 482 insertions, 25 deletions
diff --git a/src/img4.c b/src/img4.c
index db33f15..9bc2bf8 100644
--- a/src/img4.c
+++ b/src/img4.c
@@ -1,8 +1,8 @@
/*
* img4.c
- * Functions for handling the new IMG4 format
+ * Functions for handling the IMG4 format
*
- * Copyright (c) 2013 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2013-2019 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
@@ -25,53 +25,104 @@
#include "common.h"
#include "img4.h"
+#define ASN1_PRIVATE 0xc0
+#define ASN1_PRIMITIVE_TAG 0x1f
#define ASN1_CONSTRUCTED 0x20
#define ASN1_SEQUENCE 0x10
+#define ASN1_SET 0x11
#define ASN1_CONTEXT_SPECIFIC 0x80
#define ASN1_IA5_STRING 0x16
+#define ASN1_OCTET_STRING 0x04
+#define ASN1_INTEGER 0x02
+#define ASN1_BOOLEAN 0x01
#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)
+static int asn1_calc_int_size(uint64_t value)
{
- unsigned char buf[6];
- unsigned int off = 0;
+ int i = 1;
+ while ((value >>= 7) != 0) i++;
+ return i;
+}
- if (!type || size == 0 || !data || !data_size) {
- return NULL;
+static void asn1_write_int_value(unsigned char **p, uint64_t value, int size)
+{
+ int value_size = (size > 0) ? size : asn1_calc_int_size(value);
+ int i = 0;
+ for (i = 1; i <= value_size; i++) {
+ (*p)[value_size-i] = value & 0xFF;
+ value >>= 8;
}
+ *p += value_size;
+}
- buf[off++] = type;
+static void asn1_write_size(unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+ unsigned int off = 0;
// 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;
+ (*data)[off++] = 0x84;
+ (*data)[off++] = (size >> 24) & 0xFF;
+ (*data)[off++] = (size >> 16) & 0xFF;
+ (*data)[off++] = (size >> 8) & 0xFF;
+ (*data)[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;
+ (*data)[off++] = 0x83;
+ (*data)[off++] = (size >> 16) & 0xFF;
+ (*data)[off++] = (size >> 8) & 0xFF;
+ (*data)[off++] = size & 0xFF;
} else if (size >= 0x100) {
// 1+2 bytes length
- buf[off++] = 0x82;
- buf[off++] = (size >> 8) & 0xFF;
- buf[off++] = (size & 0xFF);
+ (*data)[off++] = 0x82;
+ (*data)[off++] = (size >> 8) & 0xFF;
+ (*data)[off++] = (size & 0xFF);
} else if (size >= 0x80) {
// 1+1 byte length
- buf[off++] = 0x81;
- buf[off++] = (size & 0xFF);
+ (*data)[off++] = 0x81;
+ (*data)[off++] = (size & 0xFF);
} else {
// 1 byte length
- buf[off++] = size & 0xFF;
+ (*data)[off++] = size & 0xFF;
+ }
+
+ *data += off;
+ *data_size += off;
+}
+
+static void asn1_write_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+ unsigned int off = 0;
+
+ if (!type || size == 0 || !data || !data_size) {
+ return;
+ }
+
+ (*data)[off++] = type;
+ *data += off;
+
+ asn1_write_size(size, data, &off);
+
+ *data_size += off;
+}
+
+static unsigned char* asn1_create_element_header(unsigned char type, unsigned int size, unsigned char** data, unsigned int *data_size)
+{
+ unsigned char buf[6];
+ unsigned int off = 0;
+
+ if (!type || size == 0 || !data || !data_size) {
+ return NULL;
}
+ buf[off++] = type;
+
+ unsigned char* p = &buf[off];
+ asn1_write_size(size, &p, &off);
+
*data = malloc(off);
memcpy(*data, buf, off);
*data_size = off;
@@ -79,6 +130,82 @@ static unsigned char* asn1_create_element_header(unsigned char type, unsigned in
return *data;
}
+static void asn1_write_priv_element(unsigned char **p, unsigned int *length, unsigned int value)
+{
+ int i = 0;
+ int ttag = 0;
+ int tag = value;
+
+ i = ASN1_CONSTRUCTED;
+ i |= (0xFF & ASN1_PRIVATE);
+
+ (*p)[0] = i | ASN1_PRIMITIVE_TAG;
+ *p += 1;
+ *length += 1;
+
+ for (i = 0, ttag = tag; ttag > 0; i++)
+ ttag >>= 7;
+ ttag = i;
+ while (i-- > 0) {
+ (*p)[i] = tag & 0x7f;
+ if (i != (ttag - 1))
+ (*p)[i] |= 0x80;
+ tag >>= 7;
+ }
+ *p += ttag;
+ *length += ttag;
+}
+
+static void asn1_write_element(unsigned char **p, unsigned int *length, unsigned char type, void *data, int data_len)
+{
+ unsigned int this_len = 0;
+ switch (type) {
+ case ASN1_IA5_STRING: {
+ char *str = (char*)data;
+ size_t len = (data_len < 0) ? strlen(str) : data_len;
+ asn1_write_element_header(type, len, p, &this_len);
+ *length += this_len;
+ memcpy(*p, str, len);
+ *p += len;
+ *length += len;
+ } break;
+ case ASN1_OCTET_STRING: {
+ asn1_write_element_header(type, data_len, p, &this_len);
+ *length += this_len;
+ memcpy(*p, data, data_len);
+ *p += data_len;
+ *length += data_len;
+ } break;
+ case ASN1_INTEGER: {
+ uint64_t value = *(uint64_t*)data;
+ int value_size = asn1_calc_int_size(value);
+ asn1_write_element_header(type, value_size, p, &this_len);
+ *length += this_len;
+ asn1_write_int_value(p, value, value_size);
+ *length += value_size;
+ } break;
+ case ASN1_BOOLEAN: {
+ unsigned int value = *(unsigned int*)data;
+ asn1_write_element_header(type, 1, p, &this_len);
+ *length += this_len;
+ asn1_write_int_value(p, value ? 0xFF : 0x00, 1);
+ *length += 1;
+ } break;
+ case (ASN1_SET | ASN1_CONSTRUCTED): {
+ asn1_write_element_header(type, data_len, p, &this_len);
+ *length += this_len;
+ if (data && data_len > 0) {
+ memcpy(*p, data, data_len);
+ *p += data_len;
+ *length += data_len;
+ }
+ } break;
+ default:
+ fprintf(stderr, "ERROR: %s: type %02x is not implemented", __func__, type);
+ return;
+ }
+}
+
static unsigned int asn1_get_element(const unsigned char* data, unsigned char* type, unsigned char* size)
{
unsigned int off = 0;
@@ -137,6 +264,97 @@ static const unsigned char *asn1_find_element(unsigned int index, unsigned char
return &data[off];
}
+const char *_img4_get_component_tag(const char *compname)
+{
+ struct comp_tags {
+ const char *comp;
+ const char *tag;
+ };
+ const struct comp_tags component_tags[] = {
+ { "ACIBT", "acib" },
+ { "ACIWIFI", "aciw" },
+ { "Alamo", "almo" },
+ { "ANE", "anef" },
+ { "AOP", "aopf" },
+ { "Ap,HapticAssets", "hpas" },
+ { "AppleLogo", "logo" },
+ { "AudioCodecFirmware", "acfw" },
+ { "AVE", "avef" },
+ { "BatteryCharging", "glyC" },
+ { "BatteryCharging0", "chg0" },
+ { "BatteryCharging1", "chg1" },
+ { "BatteryFull", "batF" },
+ { "BatteryLow0", "bat0" },
+ { "BatteryLow1", "bat1" },
+ { "BatteryPlugin", "glyP" },
+ { "CFELoader", "cfel" },
+ { "Dali", "dali" },
+ { "DCP", "dcpf" },
+ { "DeviceTree", "dtre" },
+ { "Diags", "diag" },
+ { "EngineeringTrustCache", "dtrs" },
+ { "ftap", "ftap" },
+ { "ftsp", "ftsp" },
+ { "GFX", "gfxf" },
+ { "Hamm", "hamf" },
+ { "Homer", "homr" },
+ { "iBEC", "ibec" },
+ { "iBoot", "ibot" },
+ { "iBootData", "ibdt" },
+ { "iBootTest", "itst" },
+ { "iBSS", "ibss" },
+ { "InputDevice", "ipdf" },
+ { "ISP", "ispf" },
+ { "KernelCache", "krnl" },
+ { "LeapHaptics", "lphp" },
+ { "Liquid", "liqd" },
+ { "LLB", "illb" },
+ { "LoadableTrustCache", "ltrs" },
+ { "LowPowerWallet0", "lpw0" },
+ { "LowPowerWallet1", "lpw1" },
+ { "LowPowerWallet2", "lpw2" },
+ { "MacEFI", "mefi" },
+ { "Multitouch", "mtfw" },
+ { "NeedService", "nsrv" },
+ { "OS", "OS\0\0" },
+ { "OSRamdisk", "osrd" },
+ { "PersonalizedDMG", "pdmg" },
+ { "PEHammer", "hmmr" },
+ { "PERTOS", "pert" },
+ { "PHLEET", "phlt" },
+ { "PMP", "pmpf" },
+ { "RBM", "rmbt" },
+ { "RecoveryMode", "recm" },
+ { "RestoreDCP", "rdcp" },
+ { "RestoreDeviceTree", "rdtr" },
+ { "RestoreKernelCache", "rkrn" },
+ { "RestoreLogo", "rlgo" },
+ { "RestoreRamDisk", "rdsk" },
+ { "RestoreSEP", "rsep" },
+ { "RestoreTrustCache", "rtsc" },
+ { "rfta", "rfta" },
+ { "rfts", "rfts" },
+ { "RTP", "rtpf" },
+ { "SCE", "scef" },
+ { "SEP", "sepi" },
+ { "SIO", "siof" },
+ { "StaticTrustCache", "trst" },
+ { "SystemLocker", "lckr" },
+ { "WCHFirmwareUpdater", "wchf" },
+ { NULL, NULL }
+ };
+ int i = 0;
+
+ while (component_tags[i].comp) {
+ if (!strcmp(component_tags[i].comp, compname)) {
+ return component_tags[i].tag;
+ }
+ i++;
+ }
+
+ return NULL;
+}
+
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;
@@ -227,3 +445,241 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo
return 0;
}
+
+#ifndef __bswap_32
+#define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \
+ | (((x) & 0x00FF0000) >> 8) \
+ | (((x) & 0x0000FF00) << 8) \
+ | (((x) & 0x000000FF) << 24))
+#endif
+
+void _manifest_write_key_value(unsigned char **p, unsigned int *length, const char *tag, int type, void *value, int size)
+{
+ uint32_t utag = __bswap_32(*(uint32_t*)tag);
+ asn1_write_priv_element(p, length, utag);
+
+ unsigned char *start = *p;
+ unsigned char *outer_start = *p + 5;
+ unsigned char *inner_start = *p + 5 + 6;
+ unsigned int inner_length = 0;
+ asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1);
+ asn1_write_element(&inner_start, &inner_length, type, value, size);
+
+ unsigned int outer_length = 0;
+ unsigned int this_length = 0;
+ if (!value && size > 0) {
+ asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length + size, &outer_start, &outer_length);
+ asn1_write_size(outer_length + inner_length + size, &start, &this_length);
+ } else {
+ asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length);
+ asn1_write_size(outer_length + inner_length, &start, &this_length);
+ }
+
+ memmove(start, outer_start - outer_length, outer_length);
+ outer_start = start + outer_length;
+ *length += this_length;
+ *length += outer_length;
+
+ memmove(outer_start, inner_start - inner_length, inner_length);
+ *length += inner_length;
+
+ *p += this_length + outer_length + inner_length;
+}
+
+void _manifest_write_component(unsigned char **p, unsigned int *length, const char *tag, plist_t comp)
+{
+ uint32_t utag = __bswap_32(*(uint32_t*)tag);
+ asn1_write_priv_element(p, length, utag);
+
+ unsigned char *start = *p;
+ unsigned char *outer_start = *p + 5;
+ unsigned char *inner_start = *p + 5 + 6;
+ unsigned int inner_length = 0;
+ asn1_write_element(&inner_start, &inner_length, ASN1_IA5_STRING, (void*)tag, -1);
+
+ unsigned char tmp_[256] = { 0, };
+ unsigned int tmp_len = 0;
+ unsigned char *tmp = &tmp_[0];
+
+ plist_t node = NULL;
+ uint8_t boolval = 0;
+
+ node = plist_dict_get_item(comp, "Digest");
+ if (node) {
+ char *digest = NULL;
+ uint64_t digest_len = 0;
+ plist_get_data_val(node, &digest, &digest_len);
+ if (digest_len > 0) {
+ _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, digest, digest_len);
+ }
+ free(digest);
+ }
+
+ node = plist_dict_get_item(comp, "Trusted");
+ if (node) {
+ boolval = 0;
+ plist_get_bool_val(node, &boolval);
+ unsigned int int_bool_val = boolval;
+ _manifest_write_key_value(&tmp, &tmp_len, "EKEY", ASN1_BOOLEAN, &int_bool_val, -1);
+ }
+
+ node = plist_dict_get_item(comp, "EPRO");
+ if (node) {
+ boolval = 0;
+ plist_get_bool_val(node, &boolval);
+ unsigned int int_bool_val = boolval;
+ _manifest_write_key_value(&tmp, &tmp_len, "EPRO", ASN1_BOOLEAN, &int_bool_val, -1);
+ }
+
+ node = plist_dict_get_item(comp, "ESEC");
+ if (node) {
+ boolval = 0;
+ plist_get_bool_val(node, &boolval);
+ unsigned int int_bool_val = boolval;
+ _manifest_write_key_value(&tmp, &tmp_len, "ESEC", ASN1_BOOLEAN, &int_bool_val, -1);
+ }
+
+ asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, tmp_len, &inner_start, &inner_length);
+ memcpy(inner_start, tmp_, tmp_len);
+ inner_start += tmp_len;
+ inner_length += tmp_len;
+
+ unsigned int outer_length = 0;
+ asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_length, &outer_start, &outer_length);
+
+ unsigned int this_length = 0;
+ asn1_write_size(outer_length + inner_length, &start, &this_length);
+
+ memmove(start, outer_start - outer_length, outer_length);
+
+ outer_start = start + outer_length;
+ *length += this_length;
+ *length += outer_length;
+
+ memmove(outer_start, inner_start - inner_length, inner_length);
+
+ *length += inner_length;
+
+ *p += this_length + outer_length + inner_length;
+}
+
+int img4_create_local_manifest(plist_t request, plist_t* manifest)
+{
+ if (!request || !manifest) {
+ return -1;
+ }
+
+ unsigned char *buf = calloc(1, 65536);
+ unsigned char *p = buf;
+ unsigned int length = 0;
+ uint64_t uintval = 0;
+ unsigned int boolval = 0;
+ char *strval = NULL;
+ plist_t node = NULL;
+
+ unsigned char tmp_[1024];
+ unsigned char *tmp = &tmp_[0];
+ unsigned int tmp_len = 0;
+
+ /* write manifest properties */
+ uintval = _plist_dict_get_uint(request, "ApBoardID");
+ _manifest_write_key_value(&tmp, &tmp_len, "BORD", ASN1_INTEGER, &uintval, -1);
+
+ uintval = 0;
+ _manifest_write_key_value(&tmp, &tmp_len, "CEPO", ASN1_INTEGER, &uintval, -1);
+
+ uintval = _plist_dict_get_uint(request, "ApChipID");
+ _manifest_write_key_value(&tmp, &tmp_len, "CHIP", ASN1_INTEGER, &uintval, -1);
+
+ boolval = _plist_dict_get_bool(request, "ApProductionMode");
+ _manifest_write_key_value(&tmp, &tmp_len, "CPRO", ASN1_BOOLEAN, &boolval, -1);
+
+ boolval = 0;
+ _manifest_write_key_value(&tmp, &tmp_len, "CSEC", ASN1_BOOLEAN, &boolval, -1);
+
+ uintval = _plist_dict_get_uint(request, "ApSecurityDomain");
+ _manifest_write_key_value(&tmp, &tmp_len, "SDOM", ASN1_INTEGER, &uintval, -1);
+
+ /* create manifest properties set */
+ _manifest_write_key_value(&p, &length, "MANP", ASN1_SET | ASN1_CONSTRUCTED, tmp_, tmp_len);
+
+ /* now write the components */
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(request, &iter);
+ char *key = NULL;
+ plist_t val = NULL;
+ do {
+ plist_dict_next_item(request, iter, &key, &val);
+ if (val && plist_get_node_type(val) == PLIST_DICT) {
+ const char *comp = _img4_get_component_tag(key);
+ if (!comp) {
+ error("ERROR: %s: Unhandled component '%s' - can't create manifest\n", __func__, key);
+ free(iter);
+ free(buf);
+ return -1;
+ }
+ debug("DEBUG: found component %s (%s)\n", comp, key);
+ _manifest_write_component(&p, &length, comp, val);
+ }
+ free(key);
+ } while (val);
+ free(iter);
+
+ /* write manifest body header */
+ unsigned char manb_[32];
+ unsigned char *manb = &manb_[0];
+ unsigned int manb_len = 0;
+ _manifest_write_key_value(&manb, &manb_len, "MANB", ASN1_SET | ASN1_CONSTRUCTED, NULL, length);
+
+ /* write inner set */
+ unsigned char inner_set_[8];
+ unsigned char *inner_set = &inner_set_[0];
+ unsigned int inner_set_len = 0;
+ asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, length + manb_len, &inner_set, &inner_set_len);
+
+ /* write header values */
+ unsigned char hdrdata_[16];
+ unsigned char *hdrdata = &hdrdata_[0];
+ unsigned int hdrdata_len = 0;
+ asn1_write_element(&hdrdata, &hdrdata_len, ASN1_IA5_STRING, (void*)"IM4M", -1);
+ uint64_t intval = 0;
+ asn1_write_element(&hdrdata, &hdrdata_len, ASN1_INTEGER, &intval, -1);
+
+ /* write outer sequence now that we know the entire size */
+ unsigned char seq_[8];
+ unsigned char *seq = &seq_[0];
+ unsigned int seq_len = 0;
+ asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_set_len + length + manb_len + hdrdata_len, &seq, &seq_len);
+
+ unsigned int header_len = seq_len + hdrdata_len + inner_set_len + manb_len;
+
+ /* now put everything together */
+ memmove(buf + header_len, buf, length);
+
+ unsigned char *hdr = buf;
+ unsigned int hdr_len = 0;
+
+ memcpy(hdr, seq_, seq_len);
+ hdr += seq_len;
+ hdr_len += seq_len;
+
+ memcpy(hdr, hdrdata_, hdrdata_len);
+ hdr += hdrdata_len;
+ hdr_len += hdrdata_len;
+
+ memcpy(hdr, inner_set_, inner_set_len);
+ hdr += inner_set_len;
+ hdr_len += inner_set_len;
+
+ memcpy(hdr, manb_, manb_len);
+ hdr += manb_len;
+ hdr_len += manb_len;
+
+ length += hdr_len;
+
+ *manifest = plist_new_data((char*)buf, length);
+
+ free(buf);
+
+ return 0;
+}
diff --git a/src/img4.h b/src/img4.h
index b83f1ca..ffbd57d 100644
--- a/src/img4.h
+++ b/src/img4.h
@@ -1,8 +1,8 @@
/*
* img4.h
- * Functions for handling the new IMG4 format
+ * Functions for handling the IMG4 format
*
- * Copyright (c) 2013 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2013-2019 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
@@ -27,6 +27,7 @@ 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);
+int img4_create_local_manifest(plist_t request, plist_t* manifest);
#ifdef __cplusplus
}