libimobiledevice / idevicerestore

Restore/upgrade firmware of iOS devices
https://libimobiledevice.org
GNU Lesser General Public License v3.0
1.35k stars 396 forks source link

iPad Air 5th gen, idevicerestore fails, Got unknown updater name 'AppleTypeCRetimer'. #473

Closed skortela closed 2 years ago

skortela commented 2 years ago

Erasing of iPad Air 5th gen fails.

[08:57:38.872][22828] : Updating Veridian (66)
[08:57:38.873][22828] : Checkpoint 4294972177 complete with code 0
[08:57:38.873][22828] : Unknown operation (79)
[08:57:38.875][22828] : Checkpoint 4886 complete with code 0
[08:57:38.876][22828] : Unknown operation (82)
[08:57:38.885][22828] : ERROR: restore_send_firmware_updater_data: Got unknown updater name 'AppleTypeCRetimer'.
[08:57:38.885][22828] : ERROR: Unable to send FirmwareUpdater data
[08:57:38.885][22828] : ERROR: Unable to successfully restore device

idevicerestore.log idevicerestore_debug.log

pekn commented 2 years ago

I tested above patch with iPad Air 5th Gen.. It still fails, here is last lines from log:

... Sending IsFUDFirmware for iBootData... Checkpoint 4874 complete with code 0 Updating gas gauge software (47) Checkpoint 4294972161 complete with code 0 Updating gas gauge softwTSS server returned: STATUS=94&MESSAGE=This device isn't eligible for the requested build. ERROR: TSS request failed (status=94, message=This device isn't eligible for the requested build.) ERROR: Unable to fetch Timer1 ticket ERROR: restore_send_firmware_updater_data: Couldn't get AppleTypeCRetimer firmware data ERROR: Unable to send FirmwareUpdater data ERROR: Unable to successfully restore device

pekn commented 2 years ago

now it fails with different message:

... Sending Timer1 TSS request... Request URL set to https://gs.apple.com/TSS/controller?action=2 TSS server returned: STATUS=168&MESSAGE=An internal error occurred. ERROR: tss_send_request: Unhandled status code 168

nikias commented 2 years ago

Yes I figured it out. Wait a little for me to make a new patch!

pekn commented 2 years ago

Tested above patch. Still same error:

... Sending Timer1,Ticket TSS request... Request URL set to https://gs.apple.com/TSS/controller?action=2 TSS server returned: STATUS=168&MESSAGE=An internal error occurred. ERROR: tss_send_request: Unhandled status code 168

nikias commented 2 years ago

Did you run with debug output? I want to see the actual request that gets sent.

pekn commented 2 years ago

Tried to run with debug output but output wasn't very good (Timer TSS request was not shown completely).. Anyway, added code to dump Timer TSS request to a file. Here's link to file: https://www.dropbox.com/s/5nbr3fpj2n09og5/tssreq?dl=0

pekn commented 2 years ago

Here is output of 'idevicerestore -e -l &> output.txt' (Doesn't look quite OK though, not sure why) https://www.dropbox.com/s/04sdudr5l9lfiah/output.txt?dl=0

nikias commented 2 years ago

Ah OK I see what is missing, need to check the code again...

nikias commented 2 years ago

Final revised patch:

diff --git a/src/restore.c b/src/restore.c
index ef907ba..48eca78 100644
--- a/src/restore.c
+++ b/src/restore.c
@@ -2683,6 +2683,204 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct
    return response;
 }

+static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info)
+{
+   char comp_name[64];
+   char *comp_path = NULL;
+   plist_t comp_node = NULL;
+   unsigned char* component_data = NULL;
+   unsigned int component_size = 0;
+   ftab_t ftab = NULL;
+   ftab_t rftab = NULL;
+   uint32_t ftag = 0;
+   plist_t parameters = NULL;
+   plist_t request = NULL;
+   plist_t response = NULL;
+   plist_t node = NULL;
+   const char* ticket_name = NULL;
+   uint32_t tag = 0;
+   int ret;
+
+   /* create Timer request */
+   request = tss_request_new(NULL);
+   if (request == NULL) {
+       error("ERROR: Unable to create Timer TSS request\n");
+       return NULL;
+   }
+
+   parameters = plist_new_dict();
+
+   /* add manifest for current build_identity to parameters */
+   tss_parameters_add_from_manifest(parameters, build_identity);
+
+   plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1));
+   if (client->image4supported) {
+       plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1));
+       plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(1));
+   } else {
+       plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0));
+   }
+
+   /* add Timer,* tags from info dictionary to parameters */
+   plist_t info_array = plist_dict_get_item(p_info, "InfoArray");
+   if (!info_array) {
+       error("ERROR: Could not find InfoArray in info dictionary\n");
+       plist_free(parameters);
+       return NULL;
+   } else {
+       plist_t info_dict = plist_array_get_item(info_array, 0);
+       plist_t hwid = plist_dict_get_item(info_dict, "HardwareID");
+       uint64_t u64val;
+       uint8_t bval;
+       tag = (uint32_t)_plist_dict_get_uint(info_dict, "TagNumber");
+       char key[64];
+
+       plist_dict_set_item(parameters, "TagNumber", plist_new_uint(tag));
+       plist_t node = plist_dict_get_item(info_dict, "TicketName");
+       if (node) {
+           ticket_name = plist_get_string_ptr(node, NULL);
+           plist_dict_set_item(parameters, "TicketName", plist_copy(node));
+       }
+
+       sprintf(key, "Timer,ChipID,%u", tag);
+       u64val = _plist_dict_get_uint(hwid, "ChipID");
+       plist_dict_set_item(parameters, key, plist_new_uint(u64val));
+
+       sprintf(key, "Timer,BoardID,%u", tag);
+       u64val = _plist_dict_get_uint(hwid, "BoardID");
+       plist_dict_set_item(parameters, key, plist_new_uint(u64val));
+
+       sprintf(key, "Timer,ECID,%u", tag);
+       u64val = _plist_dict_get_uint(hwid, "ECID");
+       plist_dict_set_item(parameters, key, plist_new_uint(u64val));
+
+       plist_t p_nonce = plist_dict_get_item(hwid, "Nonce");
+       if (p_nonce) {
+           sprintf(key, "Timer,Nonce,%u", tag);
+           plist_dict_set_item(parameters, key, plist_copy(p_nonce));
+       }
+
+       sprintf(key, "Timer,SecurityMode,%u", tag);
+       bval = _plist_dict_get_bool(hwid, "SecurityMode");
+       plist_dict_set_item(parameters, key, plist_new_bool(bval));
+
+       sprintf(key, "Timer,SecurityDomain,%u", tag);
+       u64val = _plist_dict_get_uint(hwid, "SecurityDomain");
+       plist_dict_set_item(parameters, key, plist_new_uint(u64val));
+
+       sprintf(key, "Timer,ProductionMode,%u", tag);
+       u64val = _plist_dict_get_uint(hwid, "ProductionStatus");
+       plist_dict_set_item(parameters, key, plist_new_uint(u64val));
+   }
+   plist_t ap_info = plist_dict_get_item(p_info, "APInfo");
+   if (!ap_info) {
+       error("ERROR: Could not find APInfo in info dictionary\n");
+       plist_free(parameters);
+       return NULL;
+   } else {
+       plist_dict_merge(parameters, ap_info);
+   }
+
+   /* add required tags for Timer TSS request */
+   tss_request_add_timer_tags(request, parameters, NULL);
+
+   plist_free(parameters);
+
+   info("Sending %s TSS request...\n", ticket_name);
+   response = tss_request_send(request, client->tss_url);
+   plist_free(request);
+   if (response == NULL) {
+       error("ERROR: Unable to fetch %s\n", ticket_name);
+       return NULL;
+   }
+
+   if (plist_dict_get_item(response, ticket_name)) {
+       info("Received %s\n", ticket_name);
+   } else {
+       error("ERROR: No '%s' in TSS response, this might not work\n", ticket_name);
+   }
+
+   sprintf(comp_name, "Timer,RTKitOS,%u", tag);
+   if (build_identity_has_component(build_identity, comp_name)) {
+       if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) {
+           error("ERROR: Unable to get path for '%s' component\n", comp_name);
+           return NULL;
+       }
+       ret = extract_component(client->ipsw, comp_path, &component_data, &component_size);
+       free(comp_path);
+       comp_path = NULL;
+       if (ret < 0) {
+           error("ERROR: Unable to extract '%s' component\n", comp_name);
+           return NULL;
+       }
+       if (ftab_parse(component_data, component_size, &ftab, &ftag) != 0) {
+           free(component_data);
+           error("ERROR: Failed to parse '%s' component data.\n", comp_name);
+           return NULL;
+       }
+       free(component_data);
+       component_data = NULL;
+       component_size = 0;
+       if (ftag != 'rkos') {
+           error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos');
+       }
+   } else {
+       info("NOTE: Build identity does not have a '%s' component.\n", comp_name);
+   }
+
+   sprintf(comp_name, "Timer,RestoreRTKitOS,%u", tag);
+   if (build_identity_has_component(build_identity, comp_name)) {
+       if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) {
+           ftab_free(ftab);
+           error("ERROR: Unable to get path for '%s' component\n", comp_name);
+           return NULL;
+       }
+       ret = extract_component(client->ipsw, comp_path, &component_data, &component_size);
+       free(comp_path);
+       comp_path = NULL;
+       if (ret < 0) {
+           ftab_free(ftab);
+           error("ERROR: Unable to extract '%s' component\n", comp_name);
+           return NULL;
+       }
+
+       ftag = 0;
+       if (ftab_parse(component_data, component_size, &rftab, &ftag) != 0) {
+           free(component_data);
+           ftab_free(ftab);
+           error("ERROR: Failed to parse '%s' component data.\n", comp_name);
+           return NULL;
+       }
+       free(component_data);
+       component_data = NULL;
+       component_size = 0;
+       if (ftag != 'rkos') {
+           error("WARNING: Unexpected tag 0x%08x, expected 0x%08x; continuing anyway.\n", ftag, 'rkos');
+       }
+
+       if (ftab_get_entry_ptr(rftab, 'rrko', &component_data, &component_size) == 0) {
+           ftab_add_entry(ftab, 'rrko', component_data, component_size);
+       } else {
+           error("ERROR: Could not find 'rrko' entry in ftab. This will probably break things.\n");
+       }
+       ftab_free(rftab);
+       component_data = NULL;
+       component_size = 0;
+   } else {
+       info("NOTE: Build identity does not have a '%s' component.\n", comp_name);
+   }
+
+   ftab_write(ftab, &component_data, &component_size);
+   ftab_free(ftab);
+
+   plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size));
+   free(component_data);
+   component_data = NULL;
+   component_size = 0;
+
+   return response;
+}
+
 static int restore_send_firmware_updater_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message)
 {
    plist_t arguments;
@@ -2776,6 +2974,12 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct
            error("ERROR: %s: Couldn't get AppleTCON firmware data\n", __func__);
            goto error_out;
        }
+   } else if (strcmp(s_updater_name, "AppleTypeCRetimer") == 0) {
+       fwdict = restore_get_timer_firmware_data(restore, client, build_identity, p_info);
+       if (fwdict == NULL) {
+           error("ERROR: %s: Couldn't get AppleTypeCRetimer firmware data\n", __func__);
+           goto error_out;
+       }
    } else {
        error("ERROR: %s: Got unknown updater name '%s'.\n", __func__, s_updater_name);
        goto error_out;
diff --git a/src/tss.c b/src/tss.c
index 084ad10..9c3ad05 100644
--- a/src/tss.c
+++ b/src/tss.c
@@ -490,6 +490,48 @@ int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity)
    }
    node = NULL;

+   /* add Timer,BoardID,1 */
+   node = plist_dict_get_item(build_identity, "Timer,BoardID,1");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,BoardID,1", plist_copy(node));
+   }
+   node = NULL;
+
+   /* add Timer,BoardID,2 */
+   node = plist_dict_get_item(build_identity, "Timer,BoardID,2");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,BoardID,2", plist_copy(node));
+   }
+   node = NULL;
+
+   /* add Timer,ChipID,1 */
+   node = plist_dict_get_item(build_identity, "Timer,ChipID,1");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,ChipID,1", plist_copy(node));
+   }
+   node = NULL;
+
+   /* add Timer,ChipID,2 */
+   node = plist_dict_get_item(build_identity, "Timer,ChipID,2");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,ChipID,2", plist_copy(node));
+   }
+   node = NULL;
+
+   /* add Timer,SecurityDomain,1 */
+   node = plist_dict_get_item(build_identity, "Timer,SecurityDomain,1");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,SecurityDomain,1", plist_copy(node));
+   }
+   node = NULL;
+
+   /* add Timer,SecurityDomain,2 */
+   node = plist_dict_get_item(build_identity, "Timer,SecurityDomain,2");
+   if (node) {
+       plist_dict_set_item(parameters, "Timer,SecurityDomain,2", plist_copy(node));
+   }
+   node = NULL;
+
    /* add build identity manifest dictionary */
    node = plist_dict_get_item(build_identity, "Manifest");
    if (!node || plist_get_node_type(node) != PLIST_DICT) {
@@ -1810,6 +1852,112 @@ int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overr
    return 0;
 }

+int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides)
+{
+   plist_t node = NULL;
+   uint64_t u64val = 0;
+   uint8_t bval = 0;
+   uint32_t tag = 0;
+   char *ticket_name = NULL;
+
+   plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
+   if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
+       error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
+       return -1;
+   }
+
+   /* add tags indicating we want to get the Timer ticket */
+   plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
+
+   node = plist_dict_get_item(parameters, "TicketName");
+   if (!node) {
+       error("ERROR: %s: Missing TicketName\n", __func__);
+       return -1;
+   }
+   char key[64];
+   sprintf(key, "@%s", plist_get_string_ptr(node, NULL));
+
+   plist_dict_set_item(request, key, plist_new_bool(1));
+
+   tag = (uint32_t)_plist_dict_get_uint(parameters, "TagNumber");
+
+   sprintf(key, "Timer,BoardID,%u", tag);
+   u64val = _plist_dict_get_uint(parameters, key);
+   plist_dict_set_item(request, key, plist_new_uint(u64val));
+
+   sprintf(key, "Timer,ChipID,%u", tag);
+   u64val = _plist_dict_get_uint(parameters, key);
+   plist_dict_set_item(request, key, plist_new_uint(u64val));
+
+   sprintf(key, "Timer,SecurityDomain,%u", tag);
+   u64val = _plist_dict_get_uint(parameters, key);
+   plist_dict_set_item(request, key, plist_new_uint(u64val));
+
+   sprintf(key, "Timer,SecurityMode,%u", tag);
+   bval = _plist_dict_get_bool(parameters, key);
+   plist_dict_set_item(request, key, plist_new_bool(bval));
+
+   sprintf(key, "Timer,ProductionMode,%u", tag);
+   bval = _plist_dict_get_bool(parameters, key);
+   plist_dict_set_item(request, key, plist_new_bool(bval));
+
+   sprintf(key, "Timer,ECID,%u", tag);
+   u64val = _plist_dict_get_uint(parameters, key);
+   plist_dict_set_item(request, key, plist_new_uint(u64val));
+
+   sprintf(key, "Timer,Nonce,%u", tag);
+   plist_t p_nonce = plist_dict_get_item(parameters, key);
+   plist_dict_set_item(request, key, plist_copy(p_nonce));
+
+   char *comp_name = NULL;
+   plist_dict_iter iter = NULL;
+   plist_dict_new_iter(manifest_node, &iter);
+   while (iter) {
+       node = NULL;
+       comp_name = NULL;
+       plist_dict_next_item(manifest_node, iter, &comp_name, &node);
+       if (comp_name == NULL) {
+           node = NULL;
+           break;
+       }
+       if (!strncmp(comp_name, "Timer,", 6)) {
+           plist_t manifest_entry = plist_copy(node);
+
+           /* handle RestoreRequestRules */
+           plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
+           if (rules) {
+               debug("DEBUG: Applying restore request rules for entry %s\n", comp_name);
+               tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules);
+           }
+
+           /* Make sure we have a Digest key for Trusted items even if empty */
+           plist_t node = plist_dict_get_item(manifest_entry, "Trusted");
+           if (node && plist_get_node_type(node) == PLIST_BOOLEAN) {
+               uint8_t trusted;
+               plist_get_bool_val(node, &trusted);
+               if (trusted && !plist_access_path(manifest_entry, 1, "Digest")) {
+                   debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name);
+                   plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0));
+               }
+           }
+
+           plist_dict_remove_item(manifest_entry, "Info");
+
+           /* finally add entry to request */
+           plist_dict_set_item(request, comp_name, manifest_entry);
+       }
+       free(comp_name);
+   }
+   free(iter);
+
+   /* apply overrides */
+   if (overrides) {
+       plist_dict_merge(&request, overrides);
+   }
+
+   return 0;
+}
+
 static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response) {
    size_t total = size * nmemb;
    if (total != 0) {
diff --git a/src/tss.h b/src/tss.h
index 3590aed..8464f11 100644
--- a/src/tss.h
+++ b/src/tss.h
@@ -48,6 +48,7 @@ int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t over
 int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides);
 int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides);
 int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overrides);
+int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides);

 int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters);
 int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters);
pekn commented 2 years ago

Now it works, output: https://www.dropbox.com/s/uouznum277zmipl/output2.txt?dl=0

nikias commented 2 years ago

Great! Thanks for confirming. Will push the changes right away!

nikias commented 2 years ago

Note: I deleted the old patches to not have people play with incomplete patches.

nikias commented 2 years ago

See 7eaa1fa.

skortela commented 2 years ago

Thanks @nikias and @pekn. However this is still failing on my tests.

[10:04:40.378][19188] : ERROR: Unable to get (null)-FirmwarePreflightInfo from lockdownd
[10:04:40.378][19188] : ERROR: Unable to get FirmwarePreflightInfo

[10:04:40.379][19188] : Sending TSS request attempt 1... [10:04:41.053][19188] : TSS server returned: STATUS=8&MESSAGE=An internal error occurred.
[10:04:41.053][19188] : ERROR: TSS request failed (status=8, message=An internal error occurred.)
[10:04:41.053][19188] : ERROR: Unable to send TSS request
[10:04:41.054][19188] : ERROR: Unable to get SHSH blobs for this device

idevicerestore_2.log

nikias commented 2 years ago

Yes that's on me, I broke the TSS in my "cleanup commit". Should work again after my fix https://github.com/libimobiledevice/idevicerestore/commit/ca44e4dab1ff5ebffa54859d7a6142f0ecbecc8e

nikias commented 2 years ago

However I would love to find out how to fix the FirmwarePreflightInfo thing, let me check the code...

nikias commented 2 years ago

@skortela can you hop on Telegram so we can look into it? Join our group https://t.me/libimobiledevice and then you can find me. Seems like

ideviceinfo -k FirmwarePreflightInfo

Doesn't return any data.

skortela commented 2 years ago

Works now. Thanks.