LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_response, plist_t *info) { if (!client || !info) return MOBILEACTIVATION_E_INVALID_ARG; plist_t result = NULL; plist_t data = plist_data_from_plist(handshake_response); mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateTunnel1ActivationInfoRequest", data, &result); plist_free(data); if (ret == MOBILEACTIVATION_E_SUCCESS) { plist_t node = plist_dict_get_item(result, "Value"); if (!node) { debug_info("ERROR: CreateTunnel1ActivationInfoRequest command returned success but has no value in reply"); ret = MOBILEACTIVATION_E_UNKNOWN_ERROR; } else { *info = plist_copy(node); } } plist_free(result); result = NULL; return ret; }
/** * Internal function used by lockdownd_do_start_service to create the * StartService request's plist. * * @param client The lockdownd client * @param identifier The identifier of the service to start * @param send_escrow_bag Should we send the device's escrow bag with the request * @param request The request's plist on success, NULL on failure * * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure * to read the escrow bag from the device's record (when used). */ static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request) { plist_t dict = plist_new_dict(); /* create the basic request params */ plist_dict_add_label(dict, client->label); plist_dict_set_item(dict, "Request", plist_new_string("StartService")); plist_dict_set_item(dict, "Service", plist_new_string(identifier)); /* if needed - get the escrow bag for the device and send it with the request */ if (send_escrow_bag) { /* get the pairing record */ plist_t pair_record = NULL; userpref_read_pair_record(client->udid, &pair_record); if (!pair_record) { debug_info("ERROR: failed to read pair record for device: %s", client->udid); plist_free(dict); return LOCKDOWN_E_INVALID_CONF; } /* try to read the escrow bag from the record */ plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY); if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) { debug_info("ERROR: Failed to retrieve the escrow bag from the device's record"); plist_free(dict); plist_free(pair_record); return LOCKDOWN_E_INVALID_CONF; } debug_info("Adding escrow bag to StartService for %s", identifier); plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag)); plist_free(pair_record); } *request = dict; return LOCKDOWN_E_SUCCESS; }
/** * Sends a DLMessageStatusResponse to the device. * * @param client The MobileBackup client to use. * @param status_code The status code to send. * @param status1 A status message to send. Can be NULL if not required. * @param status2 An additional status plist to attach to the response. * Can be NULL if not required. * * @return MOBILEBACKUP2_E_SUCCESS on success, MOBILEBACKUP2_E_INVALID_ARG * if client is invalid, or another MOBILEBACKUP2_E_* otherwise. */ mobilebackup2_error_t mobilebackup2_send_status_response(mobilebackup2_client_t client, int status_code, const char *status1, plist_t status2) { if (!client || !client->parent) return MOBILEBACKUP2_E_INVALID_ARG; plist_t array = plist_new_array(); plist_array_append_item(array, plist_new_string("DLMessageStatusResponse")); plist_array_append_item(array, plist_new_uint(status_code)); if (status1) { plist_array_append_item(array, plist_new_string(status1)); } else { plist_array_append_item(array, plist_new_string("___EmptyParameterString___")); } if (status2) { plist_array_append_item(array, plist_copy(status2)); } else { plist_array_append_item(array, plist_new_string("___EmptyParameterString___")); } mobilebackup2_error_t err = mobilebackup2_error(device_link_service_send(client->parent, array)); plist_free(array); return err; }
LIBIMOBILEDEVICE_API void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list) { plist_t node = NULL; if (status && plist_get_node_type(status) == PLIST_DICT) { /* command specific logic: parse browsed list */ if (list != NULL) { node = plist_dict_get_item(status, "CurrentList"); if (node) { *current_amount = plist_array_get_size(node); *list = plist_copy(node); } } if (total != NULL) { node = plist_dict_get_item(status, "Total"); if (node) { plist_get_uint_val(node, total); } } if (current_amount != NULL) { node = plist_dict_get_item(status, "CurrentAmount"); if (node) { plist_get_uint_val(node, current_amount); } } if (current_index != NULL) { node = plist_dict_get_item(status, "CurrentIndex"); if (node) { plist_get_uint_val(node, current_index); } } } }
LIBIMOBILEDEVICE_API instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result) { if (!capabilities || (plist_get_node_type(capabilities) != PLIST_ARRAY && plist_get_node_type(capabilities) != PLIST_DICT)) return INSTPROXY_E_INVALID_ARG; plist_t lookup_result = NULL; instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; plist_t command = plist_new_dict(); plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch")); if (client_options) plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); if (capabilities) { int i = 0; plist_t capabilities_array = plist_new_array(); while (capabilities[i]) { plist_array_append_item(capabilities_array, plist_new_string(capabilities[i])); i++; } plist_dict_set_item(command, "Capabilities", capabilities_array); } res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result); if (res == INSTPROXY_E_SUCCESS) { *result = lookup_result; } else { plist_free(lookup_result); } plist_free(command); return res; }
/** * Installs the given provisioning profile. Only works with valid profiles. * * @param client The connected misagent to use for installation * @param profile The valid provisioning profile to install. This has to be * passed as a PLIST_DATA, otherwise the function will fail. * * @return MISAGENT_E_SUCCESS on success, MISAGENT_E_INVALID_ARG when * client is invalid, or an MISAGENT_E_* error code otherwise. */ misagent_error_t misagent_install(misagent_client_t client, plist_t profile) { if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA)) return MISAGENT_E_INVALID_ARG; client->last_error = MISAGENT_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); plist_dict_insert_item(dict, "MessageType", plist_new_string("Install")); plist_dict_insert_item(dict, "Profile", plist_copy(profile)); plist_dict_insert_item(dict, "ProfileType", plist_new_string("Provisioning")); misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); plist_free(dict); dict = NULL; if (res != MISAGENT_E_SUCCESS) { debug_info("could not send plist, error %d", res); return res; } res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); if (res != MISAGENT_E_SUCCESS) { debug_info("could not receive response, error %d", res); return res; } if (!dict) { debug_info("could not get response plist"); return MISAGENT_E_UNKNOWN_ERROR; } res = misagent_check_result(dict, &client->last_error); plist_free(dict); return res; }
int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity) { plist_t node = NULL; char* string = NULL; /* UniqueBuildID */ node = plist_dict_get_item(build_identity, "UniqueBuildID"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find UniqueBuildID node\n"); return -1; } plist_dict_set_item(parameters, "UniqueBuildID", plist_copy(node)); node = NULL; /* ApChipID */ int chip_id = 0; node = plist_dict_get_item(build_identity, "ApChipID"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApChipID node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &chip_id); plist_dict_set_item(parameters, "ApChipID", plist_new_uint(chip_id)); free(string); string = NULL; node = NULL; /* ApBoardID */ int board_id = 0; node = plist_dict_get_item(build_identity, "ApBoardID"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApBoardID node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &board_id); plist_dict_set_item(parameters, "ApBoardID", plist_new_uint(board_id)); free(string); string = NULL; node = NULL; /* ApSecurityDomain */ int security_domain = 0; node = plist_dict_get_item(build_identity, "ApSecurityDomain"); if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to find ApSecurityDomain node\n"); return -1; } plist_get_string_val(node, &string); sscanf(string, "%x", &security_domain); plist_dict_set_item(parameters, "ApSecurityDomain", plist_new_uint(security_domain)); free(string); string = NULL; node = NULL; /* BbChipID */ int bb_chip_id = 0; char* bb_chip_id_string = NULL; node = plist_dict_get_item(build_identity, "BbChipID"); if (node && plist_get_node_type(node) == PLIST_STRING) { plist_get_string_val(node, &bb_chip_id_string); sscanf(bb_chip_id_string, "%x", &bb_chip_id); plist_dict_set_item(parameters, "BbChipID", plist_new_uint(bb_chip_id)); } else { error("WARNING: Unable to find BbChipID node\n"); } node = NULL; /* BbProvisioningManifestKeyHash */ node = plist_dict_get_item(build_identity, "BbProvisioningManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbProvisioningManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbProvisioningManifestKeyHash node\n"); } node = NULL; /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ node = plist_dict_get_item(build_identity, "BbActivationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbActivationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbActivationManifestKeyHash node\n"); } node = NULL; node = plist_dict_get_item(build_identity, "BbCalibrationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbCalibrationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbCalibrationManifestKeyHash node\n"); } node = NULL; /* BbFactoryActivationManifestKeyHash */ node = plist_dict_get_item(build_identity, "BbFactoryActivationManifestKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbFactoryActivationManifestKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbFactoryActivationManifestKeyHash node\n"); } node = NULL; /* BbFDRSecurityKeyHash */ node = plist_dict_get_item(build_identity, "BbFDRSecurityKeyHash"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbFDRSecurityKeyHash", plist_copy(node)); } else { debug("NOTE: Unable to find BbFDRSecurityKeyHash node\n"); } node = NULL; /* BbSkeyId - Used by XMM 6180/GSM */ node = plist_dict_get_item(build_identity, "BbSkeyId"); if (node && plist_get_node_type(node) == PLIST_DATA) { plist_dict_set_item(parameters, "BbSkeyId", plist_copy(node)); } else { error("WARNING: Unable to find BbSkeyId node\n"); } node = NULL; /* add build identity manifest dictionary */ node = plist_dict_get_item(build_identity, "Manifest"); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unable to find Manifest node\n"); return -1; } plist_dict_set_item(parameters, "Manifest", plist_copy(node)); return 0; }
int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = 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 SE,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); /* add SE,ChipID */ node = plist_dict_get_item(parameters, "SE,ChipID"); if (!node) { error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ChipID", plist_copy(node)); node = NULL; /* add SE,ID */ node = plist_dict_get_item(parameters, "SE,ID"); if (!node) { error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,ID", plist_copy(node)); node = NULL; /* add SE,Nonce */ node = plist_dict_get_item(parameters, "SE,Nonce"); if (!node) { error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,Nonce", plist_copy(node)); node = NULL; /* add SE,RootKeyIdentifier */ node = plist_dict_get_item(parameters, "SE,RootKeyIdentifier"); if (!node) { error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__); return -1; } plist_dict_set_item(request, "SE,RootKeyIdentifier", plist_copy(node)); node = NULL; /* 'IsDev' determines whether we have Production or Development */ const char *removing_cmac_key = "DevelopmentCMAC"; node = plist_dict_get_item(parameters, "SE,IsDev"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t is_dev = 0; plist_get_bool_val(node, &is_dev); removing_cmac_key = (is_dev) ? "ProductionCMAC" : "DevelopmentCMAC"; } /* add SE,* components from build manifest to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { key = NULL; plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { free(key); error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } if (strncmp(key, "SE,", 3)) { free(key); continue; } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove Info node */ plist_dict_remove_item(tss_entry, "Info"); /* remove 'DevelopmentCMAC' (or 'ProductionCMAC') node */ if (plist_dict_get_item(tss_entry, removing_cmac_key)) { plist_dict_remove_item(tss_entry, removing_cmac_key); } /* add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides) { plist_t node = NULL; /* BbChipID */ node = plist_dict_get_item(parameters, "BbChipID"); if (node) { plist_dict_set_item(request, "BbChipID", plist_copy(node)); } node = NULL; /* BbProvisioningManifestKeyHash */ node = plist_dict_get_item(parameters, "BbProvisioningManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbProvisioningManifestKeyHash", plist_copy(node)); } node = NULL; /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ node = plist_dict_get_item(parameters, "BbActivationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbActivationManifestKeyHash", plist_copy(node)); } node = NULL; node = plist_dict_get_item(parameters, "BbCalibrationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbCalibrationManifestKeyHash", plist_copy(node)); } node = NULL; /* BbFactoryActivationManifestKeyHash */ node = plist_dict_get_item(parameters, "BbFactoryActivationManifestKeyHash"); if (node) { plist_dict_set_item(request, "BbFactoryActivationManifestKeyHash", plist_copy(node)); } node = NULL; /* BbFDRSecurityKeyHash */ node = plist_dict_get_item(parameters, "BbFDRSecurityKeyHash"); if (node) { plist_dict_set_item(request, "BbFDRSecurityKeyHash", plist_copy(node)); } node = NULL; /* BbSkeyId - Used by XMM 6180/GSM */ node = plist_dict_get_item(parameters, "BbSkeyId"); if (node) { plist_dict_set_item(request, "BbSkeyId", plist_copy(node)); } node = NULL; /* BbNonce */ node = plist_dict_get_item(parameters, "BbNonce"); if (node) { plist_dict_set_item(request, "BbNonce", plist_copy(node)); } node = NULL; /* @BBTicket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); /* BbGoldCertId */ node = plist_dict_get_item(parameters, "BbGoldCertId"); if (!node || plist_get_node_type(node) != PLIST_UINT) { error("ERROR: Unable to find required BbGoldCertId in parameters\n"); return -1; } node = plist_copy(node); uint64_t val; plist_get_uint_val(node, &val); plist_set_uint_val(node, (int32_t)val); plist_dict_set_item(request, "BbGoldCertId", node); node = NULL; /* BbSNUM */ node = plist_dict_get_item(parameters, "BbSNUM"); if (!node || plist_get_node_type(node) != PLIST_DATA) { error("ERROR: Unable to find required BbSNUM in parameters\n"); return -1; } plist_dict_set_item(request, "BbSNUM", plist_copy(node)); node = NULL; /* BasebandFirmware */ node = plist_access_path(parameters, 2, "Manifest", "BasebandFirmware"); if (!node || plist_get_node_type(node) != PLIST_DICT) { error("ERROR: Unable to get BasebandFirmware node\n"); return -1; } plist_t bbfwdict = plist_copy(node); node = NULL; if (plist_dict_get_item(bbfwdict, "Info")) { plist_dict_remove_item(bbfwdict, "Info"); } plist_dict_set_item(request, "BasebandFirmware", bbfwdict); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
static instproxy_error_t instproxy_client_get_object_by_key_from_info_directionary_for_bundle_identifier(instproxy_client_t client, const char* appid, const char* key, plist_t* node) { if (!client || !appid || !key) return INSTPROXY_E_INVALID_ARG; plist_t apps = NULL; // create client options for any application types plist_t client_opts = instproxy_client_options_new(); instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL); // only return attributes we need plist_t return_attributes = plist_new_array(); plist_array_append_item(return_attributes, plist_new_string("CFBundleIdentifier")); plist_array_append_item(return_attributes, plist_new_string("CFBundleExecutable")); plist_array_append_item(return_attributes, plist_new_string(key)); instproxy_client_options_add(client_opts, "ReturnAttributes", return_attributes, NULL); plist_free(return_attributes); return_attributes = NULL; // query device for list of apps instproxy_error_t ierr = instproxy_browse(client, client_opts, &apps); instproxy_client_options_free(client_opts); if (ierr != INSTPROXY_E_SUCCESS) { return ierr; } plist_t app_found = NULL; uint32_t i; for (i = 0; i < plist_array_get_size(apps); i++) { char *appid_str = NULL; plist_t app_info = plist_array_get_item(apps, i); plist_t idp = plist_dict_get_item(app_info, "CFBundleIdentifier"); if (idp) { plist_get_string_val(idp, &appid_str); } if (appid_str && strcmp(appid, appid_str) == 0) { app_found = app_info; } free(appid_str); if (app_found) { break; } } if (!app_found) { if (apps) plist_free(apps); *node = NULL; return INSTPROXY_E_OP_FAILED; } plist_t object = plist_dict_get_item(app_found, key); if (object) { *node = plist_copy(object); } else { debug_info("key %s not found", key); return INSTPROXY_E_OP_FAILED; } plist_free(apps); return INSTPROXY_E_SUCCESS; }
Uid& Uid::operator=(PList::Uid& i) { plist_free(_node); _node = plist_copy(i.GetPlist()); return *this; }
/** * Function used internally by lockdownd_pair() and lockdownd_validate_pair() * * @param client The lockdown client to pair with. * @param pair_record The pair record to use for pairing. If NULL is passed, then * the pair records from the current machine are used. New records will be * generated automatically when pairing is done for the first time. * @param verb This is either "Pair", "ValidatePair" or "Unpair". * * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id */ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) { if (!client) return LOCKDOWN_E_INVALID_ARG; lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t dict = NULL; plist_t pair_record_plist = NULL; plist_t wifi_node = NULL; int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ if (pair_record && pair_record->system_buid && pair_record->host_id) { /* valid pair_record passed? */ if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { return LOCKDOWN_E_PLIST_ERROR; } /* use passed pair_record */ pair_record_plist = lockdownd_pair_record_to_plist(pair_record); pairing_mode = 1; } else { /* generate a new pair record if pairing */ if (!strcmp("Pair", verb)) { ret = pair_record_generate(client, &pair_record_plist); if (ret != LOCKDOWN_E_SUCCESS) { if (pair_record_plist) plist_free(pair_record_plist); return ret; } /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */ lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node); } else { /* use existing pair record */ if (userpref_has_pair_record(client->udid)) { userpref_read_pair_record(client->udid, &pair_record_plist); if (!pair_record_plist) { return LOCKDOWN_E_INVALID_CONF; } } else { return LOCKDOWN_E_INVALID_HOST_ID; } } } plist_t request_pair_record = plist_copy(pair_record_plist); /* remove stuff that is private */ plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); /* setup pair request plist */ dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict, "PairRecord", request_pair_record); plist_dict_set_item(dict, "Request", plist_new_string(verb)); plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION)); plist_t options = plist_new_dict(); plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1)); plist_dict_set_item(dict, "PairingOptions", options); /* send to device */ ret = lockdownd_send(client, dict); plist_free(dict); dict = NULL; if (ret != LOCKDOWN_E_SUCCESS) { plist_free(pair_record_plist); if (wifi_node) plist_free(wifi_node); return ret; } /* Now get device's answer */ ret = lockdownd_receive(client, &dict); if (ret != LOCKDOWN_E_SUCCESS) { plist_free(pair_record_plist); if (wifi_node) plist_free(wifi_node); return ret; } if (strcmp(verb, "Unpair") == 0) { /* workaround for Unpair giving back ValidatePair, * seems to be a bug in the device's fw */ if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { ret = LOCKDOWN_E_PAIRING_FAILED; } } else { if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { ret = LOCKDOWN_E_PAIRING_FAILED; } } /* if pairing succeeded */ if (ret == LOCKDOWN_E_SUCCESS) { debug_info("%s success", verb); if (!pairing_mode) { debug_info("internal pairing mode"); if (!strcmp("Unpair", verb)) { /* remove public key from config */ userpref_delete_pair_record(client->udid); } else { if (!strcmp("Pair", verb)) { /* add returned escrow bag if available */ plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY); if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) { debug_info("Saving EscrowBag from response in pair record"); plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node)); plist_free(extra_node); extra_node = NULL; } /* save previously retrieved wifi mac address in pair record */ if (wifi_node) { debug_info("Saving WiFiAddress from device in pair record"); plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node)); plist_free(wifi_node); wifi_node = NULL; } userpref_save_pair_record(client->udid, pair_record_plist); } } } else { debug_info("external pairing mode"); } } else { debug_info("%s failure", verb); plist_t error_node = NULL; /* verify error condition */ error_node = plist_dict_get_item(dict, "Error"); if (error_node) { char *value = NULL; plist_get_string_val(error_node, &value); if (value) { /* the first pairing fails if the device is password protected */ if (!strcmp(value, "PasswordProtected")) { ret = LOCKDOWN_E_PASSWORD_PROTECTED; } else if (!strcmp(value, "InvalidHostID")) { ret = LOCKDOWN_E_INVALID_HOST_ID; } else if (!strcmp(value, "UserDeniedPairing")) { ret = LOCKDOWN_E_USER_DENIED_PAIRING; } else if (!strcmp(value, "PairingDialogResponsePending")) { ret = LOCKDOWN_E_PAIRING_DIALOG_PENDING; } free(value); } plist_free(error_node); error_node = NULL; } } if (pair_record_plist) { plist_free(pair_record_plist); pair_record_plist = NULL; } if (wifi_node) { plist_free(wifi_node); wifi_node = NULL; } plist_free(dict); dict = NULL; return ret; }
Key& Key::operator=(PList::Key& k) { plist_free(_node); _node = plist_copy(k.GetPlist()); return *this; }
/** * Receives any remapped identifiers reported after the device merged submitted changes. * * @param client The mobilesync client * @param mapping A pointer to an array plist containing a dict of identifier remappings * * @retval MOBILESYNC_E_SUCCESS on success * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid * form * @retval MOBILESYNC_E_WRONG_DIRECTION if the current sync direction does * not permit this call * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the * session */ mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; } if (client->direction == MOBILESYNC_SYNC_DIR_DEVICE_TO_COMPUTER) { return MOBILESYNC_E_WRONG_DIRECTION; } plist_t msg = NULL; plist_t response_type_node = NULL; char *response_type = NULL; mobilesync_error_t err = mobilesync_receive(client, &msg); if (err != MOBILESYNC_E_SUCCESS) { goto out; } response_type_node = plist_array_get_item(msg, 0); if (!response_type_node) { err = MOBILESYNC_E_PLIST_ERROR; goto out; } plist_get_string_val(response_type_node, &response_type); if (!response_type) { err = MOBILESYNC_E_PLIST_ERROR; goto out; } if (!strcmp(response_type, "SDMessageCancelSession")) { char *reason = NULL; err = MOBILESYNC_E_CANCELLED; plist_get_string_val(plist_array_get_item(msg, 2), &reason); debug_info("Device cancelled: %s", reason); free(reason); goto out; } if (strcmp(response_type, "SDMessageRemapRecordIdentifiers") != 0) { err = MOBILESYNC_E_PLIST_ERROR; goto out; } if (mapping != NULL) { plist_t map = plist_array_get_item(msg, 2); if (plist_get_node_type(map) == PLIST_DICT) { *mapping = plist_copy(map); } else { *mapping = NULL; } } err = MOBILESYNC_E_SUCCESS; out: if (response_type) { free(response_type); response_type = NULL; } if (msg) { plist_free(msg); msg = NULL; } return err; }
/** * Receives changed entitites of the currently set data class from the device * * @param client The mobilesync client * @param entities A pointer to store the changed entity records as a PLIST_DICT * @param is_last_record A pointer to store a flag indicating if this submission is the last one * @param actions A pointer to additional flags the device is sending or NULL to ignore * * @retval MOBILESYNC_E_SUCCESS on success * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the * session */ mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; } plist_t msg = NULL; plist_t response_type_node = NULL; plist_t actions_node = NULL; char *response_type = NULL; uint8_t has_more_changes = 0; mobilesync_error_t err = mobilesync_receive(client, &msg); if (err != MOBILESYNC_E_SUCCESS) { goto out; } response_type_node = plist_array_get_item(msg, 0); if (!response_type_node) { err = MOBILESYNC_E_PLIST_ERROR; goto out; } plist_get_string_val(response_type_node, &response_type); if (!response_type) { err = MOBILESYNC_E_PLIST_ERROR; goto out; } if (!strcmp(response_type, "SDMessageCancelSession")) { char *reason = NULL; err = MOBILESYNC_E_CANCELLED; plist_get_string_val(plist_array_get_item(msg, 2), &reason); debug_info("Device cancelled: %s", reason); free(reason); goto out; } if (entities != NULL) { *entities = plist_copy(plist_array_get_item(msg, 2)); } if (is_last_record != NULL) { plist_get_bool_val(plist_array_get_item(msg, 3), &has_more_changes); *is_last_record = (has_more_changes > 0 ? 0 : 1); } if (actions != NULL) { actions_node = plist_array_get_item(msg, 4); if (plist_get_node_type(actions) == PLIST_DICT) *actions = plist_copy(actions_node); else *actions = NULL; } out: if (response_type) { free(response_type); response_type = NULL; } if (msg) { plist_free(msg); msg = NULL; } return err; }
int idevicerestore_start(struct idevicerestore_client_t* client) { int tss_enabled = 0; int result = 0; if (!client) { return -1; } if ((client->flags & FLAG_LATEST) && (client->flags & FLAG_CUSTOM)) { error("ERROR: FLAG_LATEST cannot be used with FLAG_CUSTOM.\n"); return -1; } if (!client->ipsw && !(client->flags & FLAG_PWN) && !(client->flags & FLAG_LATEST)) { error("ERROR: no ipsw file given\n"); return -1; } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); irecv_set_debug_level(1); idevicerestore_debug = 1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0); // update version data (from cache, or apple if too old) load_version_data(client); // check which mode the device is currently in so we know where to start if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) { error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.1); info("Found device in %s mode\n", client->mode->string); if (client->mode->index == MODE_WTF) { unsigned int cpid = 0; if (dfu_client_new(client) != 0) { error("ERROR: Could not open device in WTF mode\n"); return -1; } if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { error("ERROR: Could not get CPID for WTF mode device\n"); dfu_client_free(client); return -1; } char wtfname[256]; sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); unsigned char* wtftmp = NULL; unsigned int wtfsize = 0; // Prefer to get WTF file from the restore IPSW ipsw_extract_to_memory(client->ipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { // Download WTF IPSW char* s_wtfurl = NULL; plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { plist_get_string_val(wtfurl, &s_wtfurl); } if (!s_wtfurl) { info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); } // make a local file name char* fnpart = strrchr(s_wtfurl, '/'); if (!fnpart) { fnpart = "x12220000_5_Recovery.ipsw"; } else { fnpart++; } struct stat fst; char wtfipsw[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &fst) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(wtfipsw, client->cache_dir); strcat(wtfipsw, "/"); strcat(wtfipsw, fnpart); } else { strcpy(wtfipsw, fnpart); } if (stat(wtfipsw, &fst) != 0) { download_to_file(s_wtfurl, wtfipsw, 0); } ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } } if (wtftmp) { if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) { error("ERROR: Could not send WTF...\n"); } } dfu_client_free(client); sleep(1); free(wtftmp); client->mode = &idevicerestore_modes[MODE_DFU]; } // discover the device type if (check_product_type(client) == NULL || client->device == NULL) { error("ERROR: Unable to discover device type\n"); return -1; } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.2); info("Identified device as %s\n", client->device->product_type); if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->flags & FLAG_PWN) { recovery_client_free(client); info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n...\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("Device should be in pwned DFU state now.\n"); return 0; } if (client->flags & FLAG_LATEST) { char* ipsw = NULL; int res = ipsw_download_latest_fw(client->version_data, client->device->product_type, "cache", &ipsw); if (res != 0) { if (ipsw) { free(ipsw); } return res; } else { client->ipsw = ipsw; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); if (client->flags & FLAG_NOACTION) { return 0; } if (client->mode->index == MODE_RESTORE) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -2; } // we need to refresh the current mode again check_mode(client); info("Found device in %s mode\n", client->mode->string); } // verify if ipsw file exists if (access(client->ipsw, F_OK) < 0) { error("ERROR: Firmware file %s does not exist.\n", client->ipsw); return -1; } // extract buildmanifest plist_t buildmanifest = NULL; if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(client->ipsw, &buildmanifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(client->ipsw, &buildmanifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); return -1; } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(buildmanifest, client->device->product_type) < 0) { error("ERROR: Could not make sure this firmware is suitable for the current device. Refusing to continue.\n"); return -1; } /* print iOS information from the manifest */ build_manifest_get_version_information(buildmanifest, client); info("Product Version: %s\n", client->version); info("Product Build: %s Major: %d\n", client->build, client->build_major); if (client->flags & FLAG_CUSTOM) { /* prevent signing custom firmware */ tss_enabled = 0; info("Custom firmware requested. Disabled TSS request.\n"); } // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); { plist_t node; plist_t comp; plist_t inf; plist_t manifest; char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; strcpy(lcmodel, client->device->hardware_model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); x++; } sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); // get all_flash file manifest char *files[16]; char *fmanifest = NULL; uint32_t msize = 0; if (ipsw_extract_to_memory(client->ipsw, tmpstr, (unsigned char**)&fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); return -1; } char *tok = strtok(fmanifest, "\r\n"); int fc = 0; while (tok) { files[fc++] = strdup(tok); if (fc >= 16) { break; } tok = strtok(NULL, "\r\n"); } free(fmanifest); manifest = plist_new_dict(); for (x = 0; x < fc; x++) { inf = plist_new_dict(); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/"); strcat(tmpstr, files[x]); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); const char* compname = get_component_name(files[x]); if (compname) { plist_dict_insert_item(manifest, compname, comp); if (!strncmp(files[x], "DeviceTree", 10)) { plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp)); } } else { error("WARNING: unhandled component %s\n", files[x]); plist_free(comp); } free(files[x]); files[x] = NULL; } // add iBSS sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "iBSS", comp); // add iBEC sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "iBEC", comp); // add kernel cache plist_t kdict = NULL; node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { char tt[4]; strncpy(tt, lcmodel, 3); tt[3] = 0; kdict = plist_dict_get_item(node, tt); } else { // Populated in older iOS IPSWs kdict = plist_dict_get_item(buildmanifest, "RestoreKernelCaches"); } if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) { plist_t kc = plist_dict_get_item(kdict, "Release"); if (kc && (plist_get_node_type(kc) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(kc)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "KernelCache", comp); plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp)); } } // add ramdisk node = plist_dict_get_item(buildmanifest, "RestoreRamDisks"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update"); // if no "Update" ram disk entry is found try "User" ram disk instead if (!rd && !(client->flags & FLAG_ERASE)) { rd = plist_dict_get_item(node, "User"); // also, set the ERASE flag since we actually change the restore variant client->flags |= FLAG_ERASE; } if (rd && (plist_get_node_type(rd) == PLIST_STRING)) { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(rd)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "RestoreRamDisk", comp); } } // add OS filesystem node = plist_dict_get_item(buildmanifest, "SystemRestoreImages"); if (!node) { error("ERROR: missing SystemRestoreImages in Restore.plist\n"); } plist_t os = plist_dict_get_item(node, "User"); if (!os) { error("ERROR: missing filesystem in Restore.plist\n"); } else { inf = plist_new_dict(); plist_dict_insert_item(inf, "Path", plist_copy(os)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", inf); plist_dict_insert_item(manifest, "OS", comp); } // add info inf = plist_new_dict(); plist_dict_insert_item(inf, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); plist_dict_insert_item(inf, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); plist_dict_insert_item(build_identity, "Info", inf); // finally add manifest plist_dict_insert_item(build_identity, "Manifest", manifest); } } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity(buildmanifest, 0); if (build_identity == NULL) { error("ERROR: Unable to find any build identities\n"); plist_free(buildmanifest); return -1; } } else { // loop through all build identities in the build manifest // and list the valid ones int i = 0; int valid_builds = 0; int build_count = build_manifest_get_identity_count(buildmanifest); for (i = 0; i < build_count; i++) { build_identity = build_manifest_get_build_identity(buildmanifest, i); valid_builds++; } } /* print information about current build identity */ build_identity_print_information(build_identity); idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.0); /* retrieve shsh blobs if required */ if (tss_enabled) { debug("Getting device's ECID for TSS request\n"); /* fetch the device's ECID for the TSS request */ if (get_ecid(client, &client->ecid) < 0) { error("ERROR: Unable to find device ECID\n"); return -1; } info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid); if (client->build_major > 8) { unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { /* the first nonce request with older firmware releases can fail and it's OK */ info("NOTE: Unable to get nonce from device\n"); } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } } if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } } if (client->flags & FLAG_SHSHONLY) { if (!tss_enabled) { info("This device does not require a TSS record"); return 0; } if (!client->tss) { error("ERROR: could not fetch TSS record"); plist_free(buildmanifest); return -1; } else { char *bin = NULL; uint32_t blen = 0; plist_to_bin(client->tss, &bin, &blen); if (bin) { char zfn[1024]; if (client->cache_dir) { strcpy(zfn, client->cache_dir); strcat(zfn, "/shsh"); } else { strcpy(zfn, "shsh"); } mkdir_with_parents(zfn, 0755); sprintf(zfn+strlen(zfn), "/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product_type, client->version); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); gzwrite(zf, bin, blen); gzclose(zf); info("SHSH saved to '%s'\n", zfn); } else { info("SHSH '%s' already present.\n", zfn); } free(bin); } else { error("ERROR: could not get TSS record data\n"); } plist_free(client->tss); plist_free(buildmanifest); return 0; } } /* verify if we have tss records if required */ if ((tss_enabled) && (client->tss == NULL)) { error("ERROR: Unable to proceed without a TSS record.\n"); plist_free(buildmanifest); return -1; } if ((tss_enabled) && client->tss) { /* fix empty dicts */ fixup_tss(client->tss); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.1); // if the device is in normal mode, place device into recovery mode if (client->mode->index == MODE_NORMAL) { info("Entering recovery mode...\n"); if (normal_enter_recovery(client) < 0) { error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -5; } } // Get filesystem name from build identity char* fsname = NULL; if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { error("ERROR: Unable get path for filesystem component\n"); return -1; } // check if we already have an extracted filesystem int delete_fs = 0; char* filesystem = NULL; struct stat st; memset(&st, '\0', sizeof(struct stat)); char tmpf[1024]; if (client->cache_dir) { if (stat(client->cache_dir, &st) < 0) { mkdir_with_parents(client->cache_dir, 0755); } strcpy(tmpf, client->cache_dir); strcat(tmpf, "/"); char *ipswtmp = strdup(client->ipsw); strcat(tmpf, basename(ipswtmp)); free(ipswtmp); } else { strcpy(tmpf, client->ipsw); } char* p = strrchr((const char*)tmpf, '.'); if (p) { *p = '\0'; } if (stat(tmpf, &st) < 0) { __mkdir(tmpf, 0755); } strcat(tmpf, "/"); strcat(tmpf, fsname); memset(&st, '\0', sizeof(struct stat)); if (stat(tmpf, &st) == 0) { off_t fssize = 0; ipsw_get_file_size(client->ipsw, fsname, &fssize); if ((fssize > 0) && (st.st_size == fssize)) { info("Using cached filesystem from '%s'\n", tmpf); filesystem = strdup(tmpf); } } if (!filesystem) { char extfn[1024]; strcpy(extfn, tmpf); strcat(extfn, ".extract"); char lockfn[1024]; strcpy(lockfn, tmpf); strcat(lockfn, ".lock"); lock_info_t li; lock_file(lockfn, &li); FILE* extf = NULL; if (access(extfn, F_OK) != 0) { extf = fopen(extfn, "w"); } unlock_file(&li); if (!extf) { // use temp filename filesystem = tempnam(NULL, "ipsw_"); if (!filesystem) { error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname); filesystem = strdup(fsname); } delete_fs = 1; } else { // use <fsname>.extract as filename filesystem = strdup(extfn); fclose(extf); } remove(lockfn); // Extract filesystem from IPSW info("Extracting filesystem from IPSW\n"); if (ipsw_extract_to_file(client->ipsw, fsname, filesystem) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } if (strstr(filesystem, ".extract")) { // rename <fsname>.extract to <fsname> rename(filesystem, tmpf); free(filesystem); filesystem = strdup(tmpf); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); // if the device is in DFU mode, place device into recovery mode if (client->mode->index == MODE_DFU) { recovery_client_free(client); if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { if (delete_fs && filesystem) unlink(filesystem); return -1; } info("exploiting with limera1n\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); if (delete_fs && filesystem) unlink(filesystem); return -1; } dfu_client_free(client); info("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode from %s mode\n", client->mode->string); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } } if (client->mode->index == MODE_DFU) { 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"); } } /* now we load the iBEC */ if (recovery_send_ibec(client, build_identity) < 0) { error("ERROR: Unable to send iBEC\n"); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); /* this must be long enough to allow the device to run the iBEC */ /* FIXME: Probably better to detect if the device is back then */ sleep(7); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->build_major > 8) { // we need another tss request with nonce. unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); if (delete_fs && filesystem) unlink(filesystem); return -2; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } fixup_tss(client->tss); } } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); // now finally do the magic to put the device into restore mode if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { error("ERROR: could not retrieve device serial number. Can't continue.\n"); if (delete_fs && filesystem) unlink(filesystem); return -1; } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); if (delete_fs && filesystem) unlink(filesystem); return -2; } recovery_client_free(client); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.9); // device is finally in restore mode, let's do this if (client->mode->index == MODE_RESTORE) { info("About to restore device... \n"); result = restore_device(client, build_identity, filesystem); if (result < 0) { error("ERROR: Unable to restore device\n"); if (delete_fs && filesystem) unlink(filesystem); return result; } } info("Cleaning up...\n"); if (delete_fs && filesystem) unlink(filesystem); /* special handling of AppleTVs */ if (strncmp(client->device->product_type, "AppleTV", 7) == 0) { if (recovery_client_new(client) == 0) { if (recovery_set_autoboot(client, 1) == 0) { recovery_send_reset(client); } else { error("Setting auto-boot failed?!\n"); } } else { error("Could not connect to device in recovery mode.\n"); } } info("DONE\n"); if (result == 0) { idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); } return result; }
LIBIMOBILEDEVICE_API restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) { if (!client) return RESTORE_E_INVALID_ARG; restored_error_t ret = RESTORE_E_SUCCESS; idevice_error_t idev_ret; static struct lockdownd_service_descriptor service = { .port = 0xf27e, .ssl_enabled = 0 }; property_list_service_client_t plistclient = NULL; if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("could not connect to restored (device %s)", device->udid); return RESTORE_E_MUX_ERROR; } restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private)); client_loc->parent = plistclient; client_loc->udid = NULL; client_loc->label = NULL; client_loc->info = NULL; if (label != NULL) client_loc->label = strdup(label); idev_ret = idevice_get_udid(device, &client_loc->udid); if (IDEVICE_E_SUCCESS != idev_ret) { debug_info("failed to get device udid."); ret = RESTORE_E_DEVICE_ERROR; } debug_info("device udid: %s", client_loc->udid); if (RESTORE_E_SUCCESS == ret) { *client = client_loc; } else { restored_client_free(client_loc); } return ret; } LIBIMOBILEDEVICE_API restored_error_t restored_goodbye(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict,"Request", plist_new_string("Goodbye")); debug_info("called"); ret = restored_send(client, dict); plist_free(dict); dict = NULL; ret = restored_receive(client, &dict); if (!dict) { debug_info("did not get goodbye response back"); return RESTORE_E_PLIST_ERROR; } if (restored_check_result(dict) == RESULT_SUCCESS) { debug_info("success"); ret = RESTORE_E_SUCCESS; } plist_free(dict); dict = NULL; return ret; } LIBIMOBILEDEVICE_API restored_error_t restored_start_restore(restored_client_t client, plist_t options, uint64_t version) { if (!client) return RESTORE_E_INVALID_ARG; plist_t dict = NULL; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict,"Request", plist_new_string("StartRestore")); if (options) { plist_dict_set_item(dict, "RestoreOptions", plist_copy(options)); } plist_dict_set_item(dict,"RestoreProtocolVersion", plist_new_uint(version)); /* send to device */ ret = restored_send(client, dict); plist_free(dict); dict = NULL; return ret; } LIBIMOBILEDEVICE_API restored_error_t restored_reboot(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; plist_t dict = NULL; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict,"Request", plist_new_string("Reboot")); /* send to device */ ret = restored_send(client, dict); plist_free(dict); dict = NULL; if (RESTORE_E_SUCCESS != ret) return ret; ret = restored_receive(client, &dict); if (RESTORE_E_SUCCESS != ret) return ret; if (!dict) return RESTORE_E_PLIST_ERROR; plist_free(dict); dict = NULL; return ret; }
/** * Creates a new restored client for the device. * * @param device The device to create a restored client for * @param client The pointer to the location of the new restored_client * @param label The label to use for communication. Usually the program name. * * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL */ restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) { if (!client) return RESTORE_E_INVALID_ARG; restored_error_t ret = RESTORE_E_SUCCESS; static struct lockdownd_service_descriptor service = { .port = 0xf27e, .ssl_enabled = 0 }; property_list_service_client_t plistclient = NULL; if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("could not connect to restored (device %s)", device->udid); return RESTORE_E_MUX_ERROR; } restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private)); client_loc->parent = plistclient; client_loc->udid = NULL; client_loc->label = NULL; client_loc->info = NULL; if (label != NULL) client_loc->label = strdup(label); ret = idevice_get_udid(device, &client_loc->udid); if (RESTORE_E_SUCCESS != ret) { debug_info("failed to get device udid."); } debug_info("device udid: %s", client_loc->udid); if (RESTORE_E_SUCCESS == ret) { *client = client_loc; } else { restored_client_free(client_loc); } return ret; } /** * Sends the Goodbye request to restored signaling the end of communication. * * @param client The restore client * * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, * RESTORE_E_PLIST_ERROR if the device did not acknowledge the request */ restored_error_t restored_goodbye(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); debug_info("called"); ret = restored_send(client, dict); plist_free(dict); dict = NULL; ret = restored_receive(client, &dict); if (!dict) { debug_info("did not get goodbye response back"); return RESTORE_E_PLIST_ERROR; } if (restored_check_result(dict) == RESULT_SUCCESS) { debug_info("success"); ret = RESTORE_E_SUCCESS; } plist_free(dict); dict = NULL; return ret; } /** * Requests to start a restore and retrieve it's port on success. * * @param client The restored client * @param options PLIST_DICT with options for the restore process or NULL * @param version the restore protocol version, see restored_query_type() * * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter * is NULL, RESTORE_E_START_RESTORE_FAILED if the request fails */ restored_error_t restored_start_restore(restored_client_t client, plist_t options, uint64_t version) { if (!client) return RESTORE_E_INVALID_ARG; plist_t dict = NULL; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_insert_item(dict,"Request", plist_new_string("StartRestore")); if (options) { plist_dict_insert_item(dict, "RestoreOptions", plist_copy(options)); } plist_dict_insert_item(dict,"RestoreProtocolVersion", plist_new_uint(version)); /* send to device */ ret = restored_send(client, dict); plist_free(dict); dict = NULL; return ret; } /** * Requests device to reboot. * * @param client The restored client * * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter * is NULL */ restored_error_t restored_reboot(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; plist_t dict = NULL; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_insert_item(dict,"Request", plist_new_string("Reboot")); /* send to device */ ret = restored_send(client, dict); plist_free(dict); dict = NULL; if (RESTORE_E_SUCCESS != ret) return ret; ret = restored_receive(client, &dict); if (RESTORE_E_SUCCESS != ret) return ret; if (!dict) return RESTORE_E_PLIST_ERROR; plist_free(dict); dict = NULL; return ret; }
int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) { plist_t node = 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 Savage,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Savage,Ticket", plist_new_bool(1)); /* add Savage,UID */ node = plist_dict_get_item(parameters, "Savage,UID"); if (!node) { error("ERROR: %s: Unable to find required Savage,UID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,UID", plist_copy(node)); node = NULL; /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); if (!node) { error("ERROR: Unable to get SEP digest from manifest\n"); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); /* add Savage,PatchEpoch */ node = plist_dict_get_item(parameters, "Savage,PatchEpoch"); if (!node) { error("ERROR: %s: Unable to find required Savage,PatchEpoch in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,PatchEpoch", plist_copy(node)); node = NULL; /* add Savage,ChipID */ node = plist_dict_get_item(parameters, "Savage,ChipID"); if (!node) { error("ERROR: %s: Unable to find required Savage,ChipID in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ChipID", plist_copy(node)); node = NULL; /* add Savage,AllowOfflineBoot */ node = plist_dict_get_item(parameters, "Savage,AllowOfflineBoot"); if (!node) { error("ERROR: %s: Unable to find required Savage,AllowOfflineBoot in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,AllowOfflineBoot", plist_copy(node)); node = NULL; /* add Savage,ReadFWKey */ node = plist_dict_get_item(parameters, "Savage,ReadFWKey"); if (!node) { error("ERROR: %s: Unable to find required Savage,ReadFWKey in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ReadFWKey", plist_copy(node)); node = NULL; /* add Savage,ProductionMode */ node = plist_dict_get_item(parameters, "Savage,ProductionMode"); if (!node) { error("ERROR: %s: Unable to find required Savage,ProductionMode in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ProductionMode", plist_copy(node)); const char *comp_name = NULL; uint8_t isprod = 0; plist_get_bool_val(node, &isprod); node = NULL; /* get the right component name */ comp_name = (isprod) ? "Savage,B0-Prod-Patch" : "Savage,B0-Dev-Patch"; node = plist_dict_get_item(parameters, "Savage,Revision"); if (node && (plist_get_node_type(node) == PLIST_DATA)) { unsigned char *savage_rev = NULL; uint64_t savage_rev_len = 0; plist_get_data_val(node, (char**)&savage_rev, &savage_rev_len); if (savage_rev_len > 0) { if (((savage_rev[0] | 0x10) & 0xF0) == 0x30) { comp_name = (isprod) ? "Savage,B2-Prod-Patch" : "Savage,B2-Dev-Patch"; } else if ((savage_rev[0] & 0xF0) == 0xA0) { comp_name = (isprod) ? "Savage,BA-Prod-Patch" : "Savage,BA-Dev-Patch"; } } free(savage_rev); } /* add Savage,B?-*-Patch */ node = plist_dict_get_item(manifest_node, comp_name); if (!node) { error("ERROR: Unable to get %s entry from manifest\n", comp_name); return -1; } dict = plist_copy(node); plist_dict_remove_item(dict, "Info"); plist_dict_set_item(request, comp_name, dict); if (component_name) { *component_name = strdup(comp_name); } /* add Savage,Nonce */ node = plist_dict_get_item(parameters, "Savage,Nonce"); if (!node) { error("ERROR: %s: Unable to find required Savage,Nonce in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,Nonce", plist_copy(node)); node = NULL; /* add Savage,ReadECKey */ node = plist_dict_get_item(parameters, "Savage,ReadECKey"); if (!node) { error("ERROR: %s: Unable to find required Savage,ReadECKey in parameters\n", __func__); return -1; } plist_dict_set_item(request, "Savage,ReadECKey", plist_copy(node)); node = NULL; /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
Real& Real::operator=(PList::Real& d) { plist_free(_node); _node = plist_copy(d.GetPlist()); return *this; }
/** * List installed applications. This function runs synchronously. * * @param client The connected installation_proxy client * @param client_options The client options to use, as PLIST_DICT, or NULL. * Valid client options include: * "ApplicationType" -> "User" * "ApplicationType" -> "System" * @param result Pointer that will be set to a plist that will hold an array * of PLIST_DICT holding information about the applications found. * * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if * an error occured. */ instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result) { if (!client || !client->parent || !result) return INSTPROXY_E_INVALID_ARG; instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; instproxy_lock(client); res = instproxy_send_command(client, "Browse", client_options, NULL, NULL); if (res != INSTPROXY_E_SUCCESS) { debug_info("could not send plist"); goto leave_unlock; } int browsing = 0; plist_t apps_array = plist_new_array(); plist_t dict = NULL; do { browsing = 0; dict = NULL; res = instproxy_error(property_list_service_receive_plist(client->parent, &dict)); if (res != INSTPROXY_E_SUCCESS) { break; } if (dict) { uint64_t i; uint64_t current_amount = 0; char *status = NULL; plist_t camount = plist_dict_get_item(dict, "CurrentAmount"); plist_t pstatus = plist_dict_get_item(dict, "Status"); if (camount) { plist_get_uint_val(camount, ¤t_amount); } if (current_amount > 0) { plist_t current_list = plist_dict_get_item(dict, "CurrentList"); for (i = 0; current_list && (i < current_amount); i++) { plist_t item = plist_array_get_item(current_list, i); plist_array_append_item(apps_array, plist_copy(item)); } } if (pstatus) { plist_get_string_val(pstatus, &status); } if (status) { if (!strcmp(status, "BrowsingApplications")) { browsing = 1; } else if (!strcmp(status, "Complete")) { debug_info("Browsing applications completed"); res = INSTPROXY_E_SUCCESS; } free(status); } plist_free(dict); } } while (browsing); if (res == INSTPROXY_E_SUCCESS) { *result = apps_array; } else { plist_free(apps_array); } leave_unlock: instproxy_unlock(client); return res; }
int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) { plist_t node = 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 Savage,Ticket */ plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); plist_dict_set_item(request, "@Yonkers,Ticket", plist_new_bool(1)); /* add SEP */ node = plist_access_path(manifest_node, 2, "SEP", "Digest"); if (!node) { error("ERROR: Unable to get SEP digest from manifest\n"); return -1; } plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Digest", plist_copy(node)); plist_dict_set_item(request, "SEP", dict); { static const char *keys[] = {"Yonkers,AllowOfflineBoot", "Yonkers,BoardID", "Yonkers,ChipID", "Yonkers,ECID", "Yonkers,Nonce", "Yonkers,PatchEpoch", "Yonkers,ProductionMode", "Yonkers,ReadECKey", "Yonkers,ReadFWKey", }; int i; for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); ++i) { node = plist_dict_get_item(parameters, keys[i]); if (!node) { error("ERROR: %s: Unable to find required %s in parameters\n", __func__, keys[i]); } plist_dict_set_item(request, keys[i], plist_copy(node)); node = NULL; } } char *comp_name = NULL; plist_t comp_node = NULL; uint8_t isprod = 1; uint64_t fabrevision = (uint64_t)-1; node = plist_dict_get_item(parameters, "Yonkers,ProductionMode"); if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { plist_get_bool_val(node, &isprod); } node = plist_dict_get_item(parameters, "Yonkers,FabRevision"); if (node && (plist_get_node_type(node) == PLIST_UINT)) { plist_get_uint_val(node, &fabrevision); } 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, "Yonkers,", 8) == 0) { int target_node = 1; plist_t sub_node; if ((sub_node = plist_dict_get_item(node, "EPRO")) != NULL && plist_get_node_type(sub_node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(sub_node, &b); target_node &= ((isprod) ? b : !b); } if ((sub_node = plist_dict_get_item(node, "FabRevision")) != NULL && plist_get_node_type(sub_node) == PLIST_UINT) { uint64_t v = 0; plist_get_uint_val(sub_node, &v); target_node &= (v == fabrevision); } if (target_node) { comp_node = node; break; } } free(comp_name); } free(iter); if (comp_name == NULL) { error("ERROR: No Yonkers node for %s/%lu\n", (isprod) ? "Production" : "Development", (unsigned long)fabrevision); return -1; } /* add Yonkers,SysTopPatch* */ if (comp_node != NULL) { plist_t comp_dict = plist_copy(comp_node); plist_dict_remove_item(comp_dict, "Info"); plist_dict_set_item(request, comp_name, comp_dict); } if (component_name) { *component_name = comp_name; } else { free(comp_name); } /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
plist_t tss_create_request(plist_t build_identity, uint64_t ecid, unsigned char* nonce, int nonce_size) { uint64_t unique_build_size = 0; char* unique_build_data = NULL; plist_t unique_build_node = plist_dict_get_item(build_identity, "UniqueBuildID"); if (!unique_build_node || plist_get_node_type(unique_build_node) != PLIST_DATA) { error("ERROR: Unable to find UniqueBuildID node\n"); return NULL; } plist_get_data_val(unique_build_node, &unique_build_data, &unique_build_size); int chip_id = 0; char* chip_id_string = NULL; plist_t chip_id_node = plist_dict_get_item(build_identity, "ApChipID"); if (!chip_id_node || plist_get_node_type(chip_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApChipID node\n"); return NULL; } plist_get_string_val(chip_id_node, &chip_id_string); sscanf(chip_id_string, "%x", &chip_id); int board_id = 0; char* board_id_string = NULL; plist_t board_id_node = plist_dict_get_item(build_identity, "ApBoardID"); if (!board_id_node || plist_get_node_type(board_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApBoardID node\n"); return NULL; } plist_get_string_val(board_id_node, &board_id_string); sscanf(board_id_string, "%x", &board_id); int security_domain = 0; char* security_domain_string = NULL; plist_t security_domain_node = plist_dict_get_item(build_identity, "ApSecurityDomain"); if (!security_domain_node || plist_get_node_type(security_domain_node) != PLIST_STRING) { error("ERROR: Unable to find ApSecurityDomain node\n"); return NULL; } plist_get_string_val(security_domain_node, &security_domain_string); sscanf(security_domain_string, "%x", &security_domain); char ecid_string[ECID_STRSIZE]; memset(ecid_string, '\0', ECID_STRSIZE); if (ecid == 0) { error("ERROR: Unable to get ECID\n"); return NULL; } snprintf(ecid_string, ECID_STRSIZE, FMT_qu, (long long unsigned int)ecid); // Add build information to TSS request plist_t tss_request = plist_new_dict(); plist_dict_insert_item(tss_request, "@APTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@BBTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@HostIpAddress", plist_new_string("192.168.0.1")); plist_dict_insert_item(tss_request, "@HostPlatformInfo", plist_new_string("mac")); plist_dict_insert_item(tss_request, "@Locality", plist_new_string("en_US")); char* guid = generate_guid(); if (guid) { plist_dict_insert_item(tss_request, "@UUID", plist_new_string(guid)); free(guid); } plist_dict_insert_item(tss_request, "@VersionInfo", plist_new_string("libauthinstall-107.3")); plist_dict_insert_item(tss_request, "ApBoardID", plist_new_uint(board_id)); plist_dict_insert_item(tss_request, "ApChipID", plist_new_uint(chip_id)); plist_dict_insert_item(tss_request, "ApECID", plist_new_string(ecid_string)); if (nonce && (nonce_size > 0)) { plist_dict_insert_item(tss_request, "ApNonce", plist_new_data(nonce, nonce_size)); } plist_dict_insert_item(tss_request, "ApProductionMode", plist_new_bool(1)); plist_dict_insert_item(tss_request, "ApSecurityDomain", plist_new_uint(security_domain)); plist_dict_insert_item(tss_request, "UniqueBuildID", plist_new_data(unique_build_data, unique_build_size)); free(unique_build_data); // Add all firmware files to TSS request plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); plist_free(tss_request); return NULL; } char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); free(tss_request); return NULL; } if (strcmp(key, "BasebandFirmware") == 0) { free(key); continue; } plist_t tss_entry = plist_copy(manifest_entry); plist_dict_insert_item(tss_request, key, tss_entry); free(key); } if (idevicerestore_debug) { debug_plist(tss_request); } return tss_request; }
int activate_fetch_record(lockdownd_client_t client, plist_t* record) { int size = 0; char* request = NULL; struct curl_httppost* post = NULL; struct curl_httppost* last = NULL; activate_response* response = NULL; char* imei = NULL; char* imsi = NULL; char* iccid = NULL; char* serial_number = NULL; char* activation_info = NULL; plist_t imei_node = NULL; plist_t imsi_node = NULL; plist_t iccid_node = NULL; plist_t serial_number_node = NULL; plist_t activation_info_node = NULL; char* device_class = NULL; plist_t device_class_node = NULL; lockdownd_get_value(client, NULL, "DeviceClass", &device_class_node); if (!device_class_node || plist_get_node_type(device_class_node) != PLIST_STRING) { fprintf(stderr, "Unable to get DeviceClass from lockdownd\n"); return -1; } plist_get_string_val(device_class_node, &device_class); plist_free(device_class_node); if (!strcmp(device_class, "iPhone")) { lockdownd_get_value(client, NULL, "IntegratedCircuitCardIdentity", &iccid_node); if (!iccid_node || plist_get_node_type(iccid_node) != PLIST_STRING) { fprintf(stderr, "Unable to get ICCID from lockdownd\n"); return -1; } plist_get_string_val(iccid_node, &iccid); plist_free(iccid_node); lockdownd_get_value(client, NULL, "InternationalMobileEquipmentIdentity", &imei_node); if (!imei_node || plist_get_node_type(imei_node) != PLIST_STRING) { fprintf(stderr, "Unable to get IMEI from lockdownd\n"); return -1; } plist_get_string_val(imei_node, &imei); plist_free(imei_node); lockdownd_get_value(client, NULL, "InternationalMobileSubscriberIdentity", &imsi_node); if (!imsi_node || plist_get_node_type(imsi_node) != PLIST_STRING) { fprintf(stderr, "Unable to get IMSI from lockdownd\n"); return -1; } plist_get_string_val(imsi_node, &imsi); plist_free(imsi_node); } lockdownd_get_value(client, NULL, "SerialNumber", &serial_number_node); if (!serial_number_node || plist_get_node_type(serial_number_node) != PLIST_STRING) { fprintf(stderr, "Unable to get SerialNumber from lockdownd\n"); return -1; } plist_get_string_val(serial_number_node, &serial_number); plist_free(serial_number_node); lockdownd_get_value(client, NULL, "ActivationInfo", &activation_info_node); int type = plist_get_node_type(activation_info_node); if (!activation_info_node || plist_get_node_type(activation_info_node) != PLIST_DICT) { fprintf(stderr, "Unable to get ActivationInfo from lockdownd\n"); return -1; } //plist_get_string_val(activation_info_node, &activation_info); uint32_t activation_info_size = 0; char* activation_info_data = NULL; plist_to_xml(activation_info_node, &activation_info_data, &activation_info_size); plist_free(activation_info_node); printf("%s\n\n", activation_info_data); char* activation_info_start = strstr(activation_info_data, "<dict>"); if (activation_info_start == NULL) { fprintf(stderr, "Unable to locate beginning of ActivationInfo\n"); return -1; } char* activation_info_stop = strstr(activation_info_data, "</dict>"); if (activation_info_stop == NULL) { fprintf(stderr, "Unable to locate end of ActivationInfo\n"); return -1; } activation_info_stop += strlen("</dict>"); activation_info_size = activation_info_stop - activation_info_start; activation_info = malloc(activation_info_size + 1); memset(activation_info, '\0', activation_info_size + 1); memcpy(activation_info, activation_info_start, activation_info_size); free(activation_info_data); curl_global_init(CURL_GLOBAL_ALL); CURL* handle = curl_easy_init(); if (handle == NULL) { fprintf(stderr, "Unable to initialize libcurl\n"); curl_global_cleanup(); return -1; } curl_formadd(&post, &last, CURLFORM_COPYNAME, "machineName", CURLFORM_COPYCONTENTS, "linux", CURLFORM_END); curl_formadd(&post, &last, CURLFORM_COPYNAME, "InStoreActivation", CURLFORM_COPYCONTENTS, "false", CURLFORM_END); if (imei != NULL) { curl_formadd(&post, &last, CURLFORM_COPYNAME, "IMEI", CURLFORM_COPYCONTENTS, imei, CURLFORM_END); free(imei); } if (imsi != NULL) { curl_formadd(&post, &last, CURLFORM_COPYNAME, "IMSI", CURLFORM_COPYCONTENTS, imsi, CURLFORM_END); free(imsi); } if (iccid != NULL) { curl_formadd(&post, &last, CURLFORM_COPYNAME, "ICCID", CURLFORM_COPYCONTENTS, iccid, CURLFORM_END); free(iccid); } if (serial_number != NULL) { curl_formadd(&post, &last, CURLFORM_COPYNAME, "AppleSerialNumber", CURLFORM_COPYCONTENTS, serial_number, CURLFORM_END); free(serial_number); } if (activation_info != NULL) { curl_formadd(&post, &last, CURLFORM_COPYNAME, "activation-info", CURLFORM_COPYCONTENTS, activation_info, CURLFORM_END); free(activation_info); } struct curl_slist* header = NULL; header = curl_slist_append(header, "X-Apple-Tz: -14400"); header = curl_slist_append(header, "X-Apple-Store-Front: 143441-1"); response = malloc(sizeof(activate_response)); if (response == NULL) { fprintf(stderr, "Unable to allocate sufficent memory\n"); return -1; } response->length = 0; response->content = malloc(1); curl_easy_setopt(handle, CURLOPT_HTTPPOST, post); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &activate_write_callback); curl_easy_setopt(handle, CURLOPT_USERAGENT, "iTunes/9.1 (Macintosh; U; Intel Mac OS X 10.5.6)"); curl_easy_setopt(handle, CURLOPT_URL, "https://albert.apple.com/WebObjects/ALUnbrick.woa/wa/deviceActivation"); curl_easy_perform(handle); curl_slist_free_all(header); curl_easy_cleanup(handle); curl_global_cleanup(); uint32_t ticket_size = response->length; char* ticket_data = response->content; char* ticket_start = strstr(ticket_data, "<plist"); if (ticket_start == NULL) { fprintf(stderr, "Unable to locate beginning of ActivationInfo\n"); return -1; } char* ticket_stop = strstr(ticket_data, "</plist>"); if (ticket_stop == NULL) { fprintf(stderr, "Unable to locate end of ActivationInfo\n"); return -1; } ticket_stop += strlen("</plist>"); ticket_size = ticket_stop - ticket_start; char* ticket = malloc(ticket_size + 1); memset(ticket, '\0', ticket_size + 1); memcpy(ticket, ticket_start, ticket_size); //free(ticket_data); printf("%s\n\n", ticket); plist_t ticket_dict = NULL; plist_from_xml(ticket, ticket_size, &ticket_dict); if (ticket_dict == NULL) { printf("Unable to convert activation ticket into plist\n"); return -1; } plist_t iphone_activation_node = plist_dict_get_item(ticket_dict, "iphone-activation"); if (!iphone_activation_node) { iphone_activation_node = plist_dict_get_item(ticket_dict, "device-activation"); if (!iphone_activation_node) { printf("Unable to find device activation node\n"); return -1; } } plist_t activation_record = plist_dict_get_item(iphone_activation_node, "activation-record"); if (!activation_record) { printf("Unable to find activation record node"); return -1; } *record = plist_copy(activation_record); //free(response->content); //free(response); //free(request); return 0; }
int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) { /* loop over components from build manifest */ plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); return -1; } /* add components to request */ char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); return -1; } /* do not populate BaseBandFirmware, only in basebaseband request */ if ((strcmp(key, "BasebandFirmware") == 0)) { free(key); continue; } /* FIXME: only used with diagnostics firmware */ if (strcmp(key, "Diags") == 0) { free(key); continue; } /* copy this entry */ plist_t tss_entry = plist_copy(manifest_entry); /* remove obsolete Info node */ plist_dict_remove_item(tss_entry, "Info"); /* 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", key); tss_entry_apply_restore_request_rules(tss_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", key); plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); } } /* finally add entry to request */ plist_dict_set_item(request, key, tss_entry); free(key); } free(iter); /* apply overrides */ if (overrides) { plist_dict_merge(&request, overrides); } return 0; }
int main(int argc, char* argv[]) { int opt = 0; int optindex = 0; char* ipsw = NULL; char* uuid = NULL; int tss_enabled = 0; int shsh_only = 0; char* shsh_dir = NULL; use_apple_server=1; // create an instance of our context struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t)); if (client == NULL) { error("ERROR: Out of memory\n"); return -1; } memset(client, '\0', sizeof(struct idevicerestore_client_t)); while ((opt = getopt_long(argc, argv, "dhcesxtpi:u:", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv); return 0; case 'd': client->flags |= FLAG_DEBUG; idevicerestore_debug = 1; break; case 'e': client->flags |= FLAG_ERASE; break; case 'c': client->flags |= FLAG_CUSTOM; break; case 's': use_apple_server=0; break; case 'x': client->flags |= FLAG_EXCLUDE; break; case 'i': if (optarg) { char* tail = NULL; client->ecid = strtoull(optarg, &tail, 16); if (tail && (tail[0] != '\0')) { client->ecid = 0; } if (client->ecid == 0) { error("ERROR: Could not parse ECID from '%s'\n", optarg); return -1; } } break; case 'u': uuid = optarg; break; case 't': shsh_only = 1; break; case 'p': client->flags |= FLAG_PWN; break; default: usage(argc, argv); return -1; } } if (((argc-optind) == 1) || (client->flags & FLAG_PWN)) { argc -= optind; argv += optind; ipsw = argv[0]; } else { usage(argc, argv); return -1; } if (client->flags & FLAG_DEBUG) { idevice_set_debug_level(1); irecv_set_debug_level(1); } client->uuid = uuid; client->ipsw = ipsw; // update version data (from cache, or apple if too old) load_version_data(client); // check which mode the device is currently in so we know where to start if (check_mode(client) < 0 || client->mode->index == MODE_UNKNOWN) { error("ERROR: Unable to discover device mode. Please make sure a device is attached.\n"); return -1; } info("Found device in %s mode\n", client->mode->string); if (client->mode->index == MODE_WTF) { int cpid = 0; if (dfu_client_new(client) != 0) { error("ERROR: Could not open device in WTF mode\n"); return -1; } if ((dfu_get_cpid(client, &cpid) < 0) || (cpid == 0)) { error("ERROR: Could not get CPID for WTF mode device\n"); dfu_client_free(client); return -1; } char* s_wtfurl = NULL; plist_t wtfurl = plist_access_path(client->version_data, 7, "MobileDeviceSoftwareVersionsByVersion", "5", "RecoverySoftwareVersions", "WTF", "304218112", "5", "FirmwareURL"); if (wtfurl && (plist_get_node_type(wtfurl) == PLIST_STRING)) { plist_get_string_val(wtfurl, &s_wtfurl); } if (!s_wtfurl) { info("Using hardcoded x12220000_5_Recovery.ipsw URL\n"); s_wtfurl = strdup("http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-6618.20090617.Xse7Y/x12220000_5_Recovery.ipsw"); } // make a local file name char* fnpart = strrchr(s_wtfurl, '/'); if (!fnpart) { fnpart = "x12220000_5_Recovery.ipsw"; } else { fnpart++; } struct stat fst; char wtfipsw[256]; sprintf(wtfipsw, "cache/%s", fnpart); if (stat(wtfipsw, &fst) != 0) { __mkdir("cache", 0755); download_to_file(s_wtfurl, wtfipsw); } char wtfname[256]; sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); char* wtftmp = NULL; uint32_t wtfsize = 0; ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } else { if (dfu_send_buffer(client, wtftmp, wtfsize) != 0) { error("ERROR: Could not send WTF...\n"); } } dfu_client_free(client); sleep(1); free(wtftmp); client->mode = &idevicerestore_modes[MODE_DFU]; } // discover the device type if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) { error("ERROR: Unable to discover device type\n"); return -1; } info("Identified device as %s\n", client->device->product); if ((client->flags & FLAG_PWN) && (client->mode->index != MODE_DFU)) { error("ERROR: you need to put your device into DFU mode to pwn it.\n"); return -1; } if (client->flags & FLAG_PWN) { recovery_client_free(client); info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n...\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("Device should be in pwned DFU state now.\n"); return 0; } if (client->mode->index == MODE_RESTORE) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -1; } } // extract buildmanifest plist_t buildmanifest = NULL; if (client->flags & FLAG_CUSTOM) { info("Extracting Restore.plist from IPSW\n"); if (ipsw_extract_restore_plist(ipsw, &buildmanifest) < 0) { error("ERROR: Unable to extract Restore.plist from %s\n", ipsw); return -1; } } else { info("Extracting BuildManifest from IPSW\n"); if (ipsw_extract_build_manifest(ipsw, &buildmanifest, &tss_enabled) < 0) { error("ERROR: Unable to extract BuildManifest from %s\n", ipsw); return -1; } } /* check if device type is supported by the given build manifest */ if (build_manifest_check_compatibility(buildmanifest, client->device->product) < 0) { error("ERROR: could not make sure this firmware is suitable for the current device. refusing to continue.\n"); return -1; } /* print iOS information from the manifest */ build_manifest_get_version_information(buildmanifest, &client->version, &client->build); info("Product Version: %s\n", client->version); info("Product Build: %s\n", client->build); if (client->flags & FLAG_CUSTOM) { /* prevent signing custom firmware */ tss_enabled = 0; info("Custom firmware requested. Disabled TSS request.\n"); } // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; if (client->flags & FLAG_CUSTOM) { build_identity = plist_new_dict(); { plist_t node; plist_t comp; plist_t info; plist_t manifest; info = plist_new_dict(); plist_dict_insert_item(info, "RestoreBehavior", plist_new_string((client->flags & FLAG_ERASE) ? "Erase" : "Update")); plist_dict_insert_item(info, "Variant", plist_new_string((client->flags & FLAG_ERASE) ? "Customer Erase Install (IPSW)" : "Customer Upgrade Install (IPSW)")); plist_dict_insert_item(build_identity, "Info", info); manifest = plist_new_dict(); char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; strcpy(lcmodel, client->device->model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); x++; } sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); // get all_flash file manifest char *files[16]; char *fmanifest = NULL; uint32_t msize = 0; if (ipsw_extract_to_memory(ipsw, tmpstr, &fmanifest, &msize) < 0) { error("ERROR: could not extract %s from IPSW\n", tmpstr); return -1; } char *tok = strtok(fmanifest, "\r\n"); int fc = 0; while (tok) { files[fc++] = strdup(tok); if (fc >= 16) { break; } tok = strtok(NULL, "\r\n"); } free(fmanifest); for (x = 0; x < fc; x++) { info = plist_new_dict(); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/"); strcat(tmpstr, files[x]); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); const char* compname = get_component_name(files[x]); if (compname) { plist_dict_insert_item(manifest, compname, comp); if (!strncmp(files[x], "DeviceTree", 10)) { plist_dict_insert_item(manifest, "RestoreDeviceTree", plist_copy(comp)); } } else { error("WARNING: unhandled component %s\n", files[x]); plist_free(comp); } free(files[x]); files[x] = NULL; } // add iBSS sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "iBSS", comp); // add iBEC sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "iBEC", comp); // add kernel cache node = plist_dict_get_item(buildmanifest, "KernelCachesByTarget"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { char tt[4]; strncpy(tt, lcmodel, 3); tt[3] = 0; plist_t kdict = plist_dict_get_item(node, tt); if (kdict && (plist_get_node_type(kdict) == PLIST_DICT)) { plist_t kc = plist_dict_get_item(kdict, "Release"); if (kc && (plist_get_node_type(kc) == PLIST_STRING)) { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(kc)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "KernelCache", comp); plist_dict_insert_item(manifest, "RestoreKernelCache", plist_copy(comp)); } } } // add ramdisk node = plist_dict_get_item(buildmanifest, "RestoreRamDisks"); if (node && (plist_get_node_type(node) == PLIST_DICT)) { plist_t rd = plist_dict_get_item(node, (client->flags & FLAG_ERASE) ? "User" : "Update"); if (rd && (plist_get_node_type(rd) == PLIST_STRING)) { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(rd)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "RestoreRamDisk", comp); } } // add OS filesystem node = plist_dict_get_item(buildmanifest, "SystemRestoreImages"); if (!node) { error("ERROR: missing SystemRestoreImages in Restore.plist\n"); } plist_t os = plist_dict_get_item(node, "User"); if (!os) { error("ERROR: missing filesystem in Restore.plist\n"); } else { info = plist_new_dict(); plist_dict_insert_item(info, "Path", plist_copy(os)); comp = plist_new_dict(); plist_dict_insert_item(comp, "Info", info); plist_dict_insert_item(manifest, "OS", comp); } // finally add manifest plist_dict_insert_item(build_identity, "Manifest", manifest); } } else if (client->flags & FLAG_ERASE) { build_identity = build_manifest_get_build_identity(buildmanifest, 0); if (build_identity == NULL) { error("ERROR: Unable to find any build identities\n"); plist_free(buildmanifest); return -1; } } else { // loop through all build identities in the build manifest // and list the valid ones int i = 0; int valid_builds = 0; int build_count = build_manifest_get_identity_count(buildmanifest); for (i = 0; i < build_count; i++) { build_identity = build_manifest_get_build_identity(buildmanifest, i); valid_builds++; } } /* print information about current build identity */ build_identity_print_information(build_identity); /* retrieve shsh blobs if required */ if (tss_enabled) { debug("Getting device's ECID for TSS request\n"); /* fetch the device's ECID for the TSS request */ if (get_ecid(client, &client->ecid) < 0) { error("ERROR: Unable to find device ECID\n"); return -1; } info("Found ECID " FMT_qu "\n", (long long unsigned int)client->ecid); if (get_shsh_blobs(client, client->ecid, NULL, 0, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } } if (shsh_only) { if (!tss_enabled) { info("This device does not require a TSS record"); return 0; } if (!client->tss) { error("ERROR: could not fetch TSS record"); plist_free(buildmanifest); return -1; } else { char *bin = NULL; uint32_t blen = 0; plist_to_bin(client->tss, &bin, &blen); if (bin) { char zfn[512]; sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product, client->version); __mkdir("shsh", 0755); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); gzwrite(zf, bin, blen); gzclose(zf); info("SHSH saved to '%s'\n", zfn); } else { info("SHSH '%s' already present.\n", zfn); } free(bin); } else { error("ERROR: could not get TSS record data\n"); } plist_free(client->tss); plist_free(buildmanifest); return 0; } } /* verify if we have tss records if required */ if ((tss_enabled) && (client->tss == NULL)) { error("ERROR: Unable to proceed without a TSS record.\n"); plist_free(buildmanifest); return -1; } if ((tss_enabled) && client->tss) { /* fix empty dicts */ fixup_tss(client->tss); } // Extract filesystem from IPSW and return its name char* filesystem = NULL; if (ipsw_extract_filesystem(client->ipsw, build_identity, &filesystem) < 0) { error("ERROR: Unable to extract filesystem from IPSW\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } // if the device is in normal mode, place device into recovery mode if (client->mode->index == MODE_NORMAL) { info("Entering recovery mode...\n"); if (normal_enter_recovery(client) < 0) { error("ERROR: Unable to place device into recovery mode\n"); if (client->tss) plist_free(client->tss); plist_free(buildmanifest); return -1; } } // if the device is in DFU mode, place device into recovery mode if (client->mode->index == MODE_DFU) { recovery_client_free(client); if (client->flags & FLAG_CUSTOM) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { return -1; } info("exploiting with limera1n\n"); // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); return -1; } dfu_client_free(client); info("exploited\n"); } if (dfu_enter_recovery(client, build_identity) < 0) { error("ERROR: Unable to place device into recovery mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); return -1; } } if (client->mode->index == MODE_DFU) { client->mode = &idevicerestore_modes[MODE_RECOVERY]; } else { /* now we load the iBEC */ if (recovery_send_ibec(client, build_identity) < 0) { error("ERROR: Unable to send iBEC\n"); return -1; } recovery_client_free(client); /* this must be long enough to allow the device to run the iBEC */ /* FIXME: Probably better to detect if the device is back then */ sleep(7); } if (client->build[0] > '8') { // we need another tss request with nonce. unsigned char* nonce = NULL; int nonce_size = 0; int nonce_changed = 0; if (get_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); return -1; } if (!client->nonce || (nonce_size != client->nonce_size) || (memcmp(nonce, client->nonce, nonce_size) != 0)) { nonce_changed = 1; if (client->nonce) { free(client->nonce); } client->nonce = nonce; client->nonce_size = nonce_size; } else { free(nonce); } info("Nonce: "); int i; for (i = 0; i < client->nonce_size; i++) { info("%02x ", client->nonce[i]); } info("\n"); if (nonce_changed && !(client->flags & FLAG_CUSTOM)) { // Welcome iOS5. We have to re-request the TSS with our nonce. plist_free(client->tss); if (get_shsh_blobs(client, client->ecid, client->nonce, client->nonce_size, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); return -1; } fixup_tss(client->tss); } } // now finally do the magic to put the device into restore mode if (client->mode->index == MODE_RECOVERY) { if (client->srnm == NULL) { error("ERROR: could not retrieve device serial number. Can't continue.\n"); return -1; } if (recovery_enter_restore(client, build_identity) < 0) { error("ERROR: Unable to place device into restore mode\n"); plist_free(buildmanifest); if (client->tss) plist_free(client->tss); return -1; } } // device is finally in restore mode, let's do this if (client->mode->index == MODE_RESTORE) { info("About to restore device... \n"); if (restore_device(client, build_identity, filesystem) < 0) { error("ERROR: Unable to restore device\n"); return -1; } } info("Cleaning up...\n"); if (filesystem) unlink(filesystem); info("DONE\n"); return 0; }