Skip to content

Commit

Permalink
Fix iPhone 16 restore from normal mode and add support for RecoveryOS
Browse files Browse the repository at this point in the history
  • Loading branch information
nikias committed Oct 15, 2024
1 parent f4a18ee commit 151c680
Show file tree
Hide file tree
Showing 10 changed files with 578 additions and 104 deletions.
3 changes: 3 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ struct idevicerestore_client_t {
int nonce_size;
int image4supported;
plist_t build_manifest;
plist_t firmware_preflight_info;
plist_t preflight_info;
plist_t parameters;
char* udid;
char* srnm;
ipsw_archive_t ipsw;
Expand All @@ -131,6 +133,7 @@ struct idevicerestore_client_t {
cond_t device_event_cond;
int ignore_device_add_events;
plist_t macos_variant;
plist_t recovery_variant;
char* restore_variant;
char* filesystem;
int delete_fs;
Expand Down
2 changes: 1 addition & 1 deletion src/dfu.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide
unsigned char* data = NULL;
uint32_t size = 0;

if (personalize_component(component, component_data, component_size, tss, &data, &size) < 0) {
if (personalize_component(client, component, component_data, component_size, tss, &data, &size) < 0) {
error("ERROR: Unable to get personalized component: %s\n", component);
free(component_data);
return -1;
Expand Down
60 changes: 49 additions & 11 deletions src/idevicerestore.c
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,29 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
} else {
free(nonce);
}
if (client->mode == MODE_NORMAL) {
plist_t ap_params = normal_get_lockdown_value(client, NULL, "ApParameters");
if (ap_params) {
if (!client->parameters) {
client->parameters = plist_new_dict();
}
plist_dict_merge(&client->parameters, ap_params);
plist_t p_sep_nonce = plist_dict_get_item(ap_params, "SepNonce");
uint64_t sep_nonce_size = 0;
const char* sep_nonce = plist_get_data_ptr(p_sep_nonce, &sep_nonce_size);
info("Getting SepNonce in normal mode... ");
int i = 0;
for (i = 0; i < sep_nonce_size; i++) {
info("%02x ", (unsigned char)sep_nonce[i]);
}
info("\n");
plist_free(ap_params);
}
plist_t req_nonce_slot = plist_access_path(build_identity, 2, "Info", "RequiresNonceSlot");
if (req_nonce_slot) {
plist_dict_set_item(client->parameters, "RequiresNonceSlot", plist_copy(req_nonce_slot));
}
}
}

if (client->flags & FLAG_QUIT) {
Expand Down Expand Up @@ -1263,8 +1286,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
plist_t recovery_variant = plist_access_path(build_identity, 2, "Info", "RecoveryVariant");
if (recovery_variant) {
const char* recovery_variant_str = plist_get_string_ptr(recovery_variant, NULL);
plist_t recovery_build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, recovery_variant_str, 1);
if (get_tss_response(client, recovery_build_identity, &client->tss_recoveryos_root_ticket) < 0) {
client->recovery_variant = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, recovery_variant_str, 1);
if (!client->recovery_variant) {
error("ERROR: Variant '%s' not found in BuildManifest\n", recovery_variant_str);
return -1;
}
if (get_tss_response(client, client->recovery_variant, &client->tss_recoveryos_root_ticket) < 0) {
error("ERROR: Unable to get SHSH blobs for this device (%s)\n", recovery_variant_str);
return -1;
}
Expand Down Expand Up @@ -1614,6 +1641,9 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client)
if (client->build_manifest) {
plist_free(client->build_manifest);
}
if (client->firmware_preflight_info) {
plist_free(client->firmware_preflight_info);
}
if (client->preflight_info) {
plist_free(client->preflight_info);
}
Expand Down Expand Up @@ -2276,17 +2306,21 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident

/* populate parameters */
plist_t parameters = plist_new_dict();
plist_dict_merge(&parameters, client->parameters);

plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid));
if (client->nonce) {
plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size));
}
unsigned char* sep_nonce = NULL;
unsigned int sep_nonce_size = 0;
get_sep_nonce(client, &sep_nonce, &sep_nonce_size);

if (sep_nonce) {
plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size));
free(sep_nonce);
if (!plist_dict_get_item(parameters, "SepNonce")) {
unsigned char* sep_nonce = NULL;
unsigned int sep_nonce_size = 0;
get_sep_nonce(client, &sep_nonce, &sep_nonce_size);
if (sep_nonce) {
plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size));
free(sep_nonce);
}
}

plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1));
Expand Down Expand Up @@ -2344,7 +2378,7 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident
if (client->mode == MODE_NORMAL) {
/* normal mode; request baseband ticket aswell */
plist_t pinfo = NULL;
normal_get_preflight_info(client, &pinfo);
normal_get_firmware_preflight_info(client, &pinfo);
if (pinfo) {
plist_dict_copy_data(parameters, pinfo, "BbNonce", "Nonce");
plist_dict_copy_uint(parameters, pinfo, "BbChipID", "ChipID");
Expand All @@ -2365,6 +2399,10 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident
tss_request_add_vinyl_tags(request, parameters, NULL);
}
}
client->firmware_preflight_info = pinfo;
pinfo = NULL;

normal_get_preflight_info(client, &pinfo);
client->preflight_info = pinfo;
}

Expand Down Expand Up @@ -2718,7 +2756,7 @@ int extract_component(ipsw_archive_t ipsw, const char* path, unsigned char** com
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)
int personalize_component(struct idevicerestore_client_t* client, 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;
Expand All @@ -2727,7 +2765,7 @@ int personalize_component(const char *component_name, const unsigned char* compo

if (tss_response && plist_dict_get_item(tss_response, "ApImg4Ticket")) {
/* stitch ApImg4Ticket into IMG4 file */
img4_stitch_component(component_name, component_data, component_size, tss_response, &stitched_component, &stitched_component_size);
img4_stitch_component(component_name, component_data, component_size, client->parameters, tss_response, &stitched_component, &stitched_component_size);
} else {
/* try to get blob for current component from tss response */
if (tss_response && tss_response_get_blob_by_entry(tss_response, component_name, &component_blob) < 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/idevicerestore.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ int build_identity_has_component(plist_t build_identity, const char* component);
int build_identity_get_component_path(plist_t build_identity, const char* component, char** path);
int ipsw_extract_filesystem(ipsw_archive_t ipsw, plist_t build_identity, char** filesystem);
int extract_component(ipsw_archive_t 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);
int personalize_component(struct idevicerestore_client_t* client, 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);
int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest);

const char* get_component_name(const char* filename);
Expand Down
193 changes: 131 additions & 62 deletions src/img4.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "common.h"
#include "img4.h"
#include "endianness.h"

#define ASN1_PRIVATE 0xc0
#define ASN1_PRIMITIVE_TAG 0x1f
Expand Down Expand Up @@ -395,7 +396,7 @@ static const char *_img4_get_component_tag(const char *compname)
return NULL;
}

int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size)
int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t parameters, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size)
{
unsigned char* magic_header = NULL;
unsigned int magic_header_size = 0;
Expand Down Expand Up @@ -459,14 +460,17 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo
snprintf(tbm_key, strlen(component_name)+5, "%s-TBM", component_name);
plist_t tbm_dict = plist_dict_get_item(tss_response, tbm_key);
free(tbm_key);
uint64_t ucon_size = 0;
const char* ucon_data = NULL;
uint64_t ucer_size = 0;
const char* ucer_data = NULL;
if (tbm_dict) {
plist_t dt = plist_dict_get_item(tbm_dict, "ucon");
if (!dt) {
error("ERROR: %s: Missing ucon node in %s-TBM dictionary\n", __func__, component_name);
return -1;
}
uint64_t ucon_size = 0;
const char* ucon_data = plist_get_data_ptr(dt, &ucon_size);
ucon_data = plist_get_data_ptr(dt, &ucon_size);
if (!ucon_data) {
error("ERROR: %s: Missing ucon data in %s-TBM dictionary\n", __func__, component_name);
return -1;
Expand All @@ -476,76 +480,141 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo
error("ERROR: %s: Missing ucer data node in %s-TBM dictionary\n", __func__, component_name);
return -1;
}
uint64_t ucer_size = 0;
const char* ucer_data = plist_get_data_ptr(dt, &ucer_size);
ucer_data = plist_get_data_ptr(dt, &ucer_size);
if (!ucer_data) {
error("ERROR: %s: Missing ucer data in %s-TBM dictionary\n", __func__, component_name);
return -1;
}
}

int nonce_slot_required = plist_dict_get_bool(parameters, "RequiresNonceSlot") && (!strcmp(component_name, "SEP") || !strcmp(component_name, "SepStage1") || !strcmp(component_name, "LLB"));

unsigned char *im4rset = (unsigned char*)malloc(16 + 8 + 8 + ucon_size + 16 + 8 + 8 + ucer_size + 16);
if (ucon_data || ucer_data || nonce_slot_required) {
size_t im4r_size = 16;
if (ucon_data) {
im4r_size += 8 + 8 + ucon_size + 16;
}
if (ucer_data) {
im4r_size += 8 + 8 + ucer_size + 16;
}
if (nonce_slot_required) {
im4r_size += 16;
}
unsigned char *im4rset = (unsigned char*)malloc(im4r_size);
unsigned char *p_im4rset = im4rset;
unsigned int im4rlen = 0;

// ----------- anid/snid -------
if (nonce_slot_required) {
const char* tag_name = NULL;
uint64_t tag_value = 0;
if (!strcmp(component_name, "SEP") || !strcmp(component_name, "SepStage1")) {
tag_name = "snid";
tag_value = 2;
if (plist_dict_get_item(parameters, "SepNonceSlotID")) {
tag_value = plist_dict_get_uint(parameters, "SepNonceSlotID");
}
} else {
tag_name = "anid";
tag_value = 0;
if (plist_dict_get_item(parameters, "ApNonceSlotID")) {
tag_value = plist_dict_get_uint(parameters, "ApNonceSlotID");
}
}
// write priv anid/snid element
asn1_write_priv_element(&p_im4rset, &im4rlen, __bswap_32(*(uint32_t*)tag_name));
// write anid/snid IA5STRING and anid/snid value
unsigned char inner_seq[16];
unsigned char *p_inner_seq = &inner_seq[0];
unsigned int inner_seq_hdr_len = 0;
asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)tag_name, -1);
asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_INTEGER, (void*)&tag_value, -1);

// write anid/snid sequence
unsigned char elem_seq[8];
unsigned char *p = &elem_seq[0];
unsigned int seq_hdr_len = 0;
asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len, &p, &seq_hdr_len);

// add size to priv anid/snid element
asn1_write_size(inner_seq_hdr_len + seq_hdr_len, &p_im4rset, &im4rlen);

// put it together
memcpy(p_im4rset, elem_seq, seq_hdr_len);
p_im4rset += seq_hdr_len;
im4rlen += seq_hdr_len;
memcpy(p_im4rset, inner_seq, inner_seq_hdr_len);
p_im4rset += inner_seq_hdr_len;
im4rlen += inner_seq_hdr_len;
}

// ----------- ucon ------------
// write priv ucon element
asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"nocu");

// write ucon IA5STRING and ucon data
unsigned char ucon_seq[16];
unsigned char *p_ucon_seq = &ucon_seq[0];
unsigned int ucon_seq_hdr_len = 0;
asn1_write_element(&p_ucon_seq, &ucon_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucon", -1);
asn1_write_element_header(ASN1_OCTET_STRING, ucon_size, &p_ucon_seq, &ucon_seq_hdr_len);

// write ucon sequence
unsigned char elem_seq[8];
unsigned char *p = &elem_seq[0];
unsigned int seq_hdr_len = 0;
asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucon_seq_hdr_len + ucon_size, &p, &seq_hdr_len);

// add size to priv ucon element
asn1_write_size(ucon_seq_hdr_len + ucon_size + seq_hdr_len, &p_im4rset, &im4rlen);

// put it together
memcpy(p_im4rset, elem_seq, seq_hdr_len);
p_im4rset += seq_hdr_len;
im4rlen += seq_hdr_len;
memcpy(p_im4rset, ucon_seq, ucon_seq_hdr_len);
p_im4rset += ucon_seq_hdr_len;
im4rlen += ucon_seq_hdr_len;
memcpy(p_im4rset, ucon_data, ucon_size);
p_im4rset += ucon_size;
im4rlen += ucon_size;
if (ucon_data) {
// write priv ucon element
asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"nocu");

// write ucon IA5STRING and ucon data header
unsigned char inner_seq[16];
unsigned char *p_inner_seq = &inner_seq[0];
unsigned int inner_seq_hdr_len = 0;
asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucon", -1);
asn1_write_element_header(ASN1_OCTET_STRING, ucon_size, &p_inner_seq, &inner_seq_hdr_len);

// write ucon sequence
unsigned char elem_seq[8];
unsigned char *p = &elem_seq[0];
unsigned int seq_hdr_len = 0;
asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len + ucon_size, &p, &seq_hdr_len);

// add size to priv ucon element
asn1_write_size(inner_seq_hdr_len + ucon_size + seq_hdr_len, &p_im4rset, &im4rlen);

// put it together
memcpy(p_im4rset, elem_seq, seq_hdr_len);
p_im4rset += seq_hdr_len;
im4rlen += seq_hdr_len;
memcpy(p_im4rset, inner_seq, inner_seq_hdr_len);
p_im4rset += inner_seq_hdr_len;
im4rlen += inner_seq_hdr_len;
// write ucon data
memcpy(p_im4rset, ucon_data, ucon_size);
p_im4rset += ucon_size;
im4rlen += ucon_size;
}

// ----------- ucer ------------
// write priv ucer element
asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"recu");

// write ucon IA5STRING and ucer data
unsigned char ucer_seq[16];
unsigned char *p_ucer_seq = &ucer_seq[0];
unsigned int ucer_seq_hdr_len = 0;
asn1_write_element(&p_ucer_seq, &ucer_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucer", -1);
asn1_write_element_header(ASN1_OCTET_STRING, ucer_size, &p_ucer_seq, &ucer_seq_hdr_len);

p = &elem_seq[0];
seq_hdr_len = 0;
asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, ucer_seq_hdr_len + ucer_size, &p, &seq_hdr_len);

// add size to priv ucer element
asn1_write_size(ucer_seq_hdr_len + ucer_size + seq_hdr_len, &p_im4rset, &im4rlen);

// put it together
memcpy(p_im4rset, elem_seq, seq_hdr_len);
p_im4rset += seq_hdr_len;
im4rlen += seq_hdr_len;
memcpy(p_im4rset, ucer_seq, ucer_seq_hdr_len);
p_im4rset += ucer_seq_hdr_len;
im4rlen += ucer_seq_hdr_len;
memcpy(p_im4rset, ucer_data, ucer_size);
p_im4rset += ucer_size;
im4rlen += ucer_size;
if (ucer_data) {
// write priv ucer element
asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"recu");

// write ucer IA5STRING and ucer data header
unsigned char inner_seq[16];
unsigned char *p_inner_seq = &inner_seq[0];
unsigned int inner_seq_hdr_len = 0;
asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucer", -1);
asn1_write_element_header(ASN1_OCTET_STRING, ucer_size, &p_inner_seq, &inner_seq_hdr_len);

// write ucer sequence
unsigned char elem_seq[8];
unsigned char *p = &elem_seq[0];
unsigned int seq_hdr_len = 0;
asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len + ucer_size, &p, &seq_hdr_len);

// add size to priv ucer element
asn1_write_size(inner_seq_hdr_len + ucer_size + seq_hdr_len, &p_im4rset, &im4rlen);

// put it together
memcpy(p_im4rset, elem_seq, seq_hdr_len);
p_im4rset += seq_hdr_len;
im4rlen += seq_hdr_len;
memcpy(p_im4rset, inner_seq, inner_seq_hdr_len);
p_im4rset += inner_seq_hdr_len;
im4rlen += inner_seq_hdr_len;
// write ucer data
memcpy(p_im4rset, ucer_data, ucer_size);
p_im4rset += ucer_size;
im4rlen += ucer_size;
}

// now construct IM4R

Expand Down
Loading

1 comment on commit 151c680

@hiepnh0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/usr/bin/ld: idevicerestore-restore.o: undefined reference to symbol 'ceil@@GLIBC_2.2.5'
//lib/x86_64-linux-gnu/libm.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

Please sign in to comment.