summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--src/Makefile.am1
-rw-r--r--src/ace3.c244
-rw-r--r--src/ace3.h17
-rw-r--r--src/common.c1
-rw-r--r--src/common.h2
-rw-r--r--src/dfu.c83
-rw-r--r--src/dfu.h4
-rw-r--r--src/idevicerestore.c157
9 files changed, 511 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 572e280..1cd5844 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,11 +15,11 @@ if test -z $PACKAGE_VERSION; then
fi
# Minimum package versions
-LIBIRECOVERY_VERSION=1.0.1
+LIBIRECOVERY_VERSION=1.2.0
LIBIMOBILEDEVICE_VERSION=1.3.0
LIBUSBMUXD_VERSION=2.0.2
LIBPLIST_VERSION=2.3.0
-LIMD_GLUE_VERSION=1.0.0
+LIMD_GLUE_VERSION=1.2.0
LIBZIP_VERSION=1.0
LIBCURL_VERSION=7.0
OPENSSL_VERSION=0.9.8
diff --git a/src/Makefile.am b/src/Makefile.am
index 019424b..722487a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,6 +44,7 @@ idevicerestore_SOURCES = \
restore.c restore.h \
asr.c asr.h \
fdr.c fdr.h \
+ ace3.c ace3.h \
limera1n_payload.h \
limera1n.c limera1n.h \
download.c download.h \
diff --git a/src/ace3.c b/src/ace3.c
new file mode 100644
index 0000000..b96e6b4
--- /dev/null
+++ b/src/ace3.c
@@ -0,0 +1,244 @@
+/*
+ * ace3.c
+ * Functions to handle Ace3/uarp firmware format
+ *
+ * Copyright (c) 2024 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <libimobiledevice-glue/nskeyedarchive.h>
+
+#include "common.h"
+#include "ace3.h"
+#include "endianness.h"
+
+static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt)
+{
+ uint32_t result;
+ unsigned int i;
+ unsigned int j;
+
+ if ( !buffer )
+ return 0xFFFFFFFF;
+ result = salt;
+ for (i = 0; i < bufsize; ++i) {
+ for (j = 0; j != 8; ++j) {
+ unsigned int tmp0 = 2 * result;
+ unsigned int tmp1 = *(unsigned char*)(buffer + i);
+ unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1);
+ result = (tmp2 + 2 * result) ^ 0x4C11DB6;
+ if (!tmp2)
+ result = tmp0;
+ }
+ }
+ return result;
+}
+
+int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size)
+{
+ struct ace3bin_header {
+ uint32_t magic; // 0xACE00003
+ uint32_t unk4; // 0x00203400
+ uint32_t unk8; // 0x00002800
+ uint32_t header_size; // 0x00000040
+ uint32_t data1_size;
+ uint32_t data2_size;
+ uint32_t im4m_offset;
+ uint32_t im4m_dl_size;
+ uint32_t content_size;
+ uint32_t crc;
+ uint64_t fill1; // 0xFFFFFFFFFFFFFFFF
+ uint64_t fill2; // 0xFFFFFFFFFFFFFFFF
+ uint64_t fill3; // 0xFFFFFFFFFFFFFFFF
+ };
+
+ struct uarp_header {
+ uint32_t unk_00; // BE 0x00000002
+ uint32_t header_size; // BE usually 0x0000002C
+ uint32_t plist_offset; // BE
+ uint32_t unk_0c; // 0
+ uint32_t unk_10; // 0
+ uint32_t unk_14; // 0
+ uint32_t unk_18; // 0
+ uint32_t c_offset; // BE
+ uint32_t unk_20; // 0
+ uint32_t toc_offset; // BE usually 0x0000002c
+ uint32_t toc_size; // BE
+ };
+ struct uarp_toc_entry {
+ uint32_t this_size; // BE usually 0x28
+ uint32_t fourcc; // 'PT01' or similar
+ uint32_t index; // BE starting with 0, increment+1 for each entry
+ uint32_t unk_0c; // BE usually not zero
+ uint32_t unk_10; // BE usually 0
+ uint32_t unk_14; // BE usually 0
+ uint32_t unk_18; // BE other offset, not sure
+ uint32_t unk_1c; // BE usually 0
+ uint32_t offset; // BE
+ uint32_t size; //
+ };
+
+ plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket");
+ uint64_t im4m_size = 0;
+ const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size);
+
+ struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw;
+ uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size);
+ uint32_t plist_offset = be32toh(uarp_hdr->plist_offset);
+ uint32_t plist_size = uarp_size - plist_offset;
+ nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size);
+ if (!ka) {
+ return -1;
+ }
+ plist_t uarp_dict = nskeyedarchive_to_plist(ka);
+ nskeyedarchive_free(ka);
+
+ // find the corresponding entries for given BoardID+PREV
+
+ char* payload_4cc = NULL;
+ char* data_payload_4ccs = NULL;
+
+ plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads");
+ if (PLIST_IS_ARRAY(sb_payloads)) {
+ plist_array_iter iter = NULL;
+ plist_array_new_iter(sb_payloads, &iter);
+ plist_t payload = NULL;
+ do {
+ plist_array_next_item(sb_payloads, iter, &payload);
+ if (!payload) {
+ break;
+ }
+ plist_t meta = plist_dict_get_item(payload, "Payload MetaData");
+ if (!PLIST_IS_DICT(meta)) {
+ continue;
+ }
+ plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix");
+ if (!PLIST_IS_STRING(prefix)) {
+ continue;
+ }
+ if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) {
+ continue;
+ }
+ plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)");
+ if (!PLIST_IS_INT(p_boardid)) {
+ continue;
+ }
+ uint64_t boardid = 0;
+ plist_get_uint_val(p_boardid, &boardid);
+ if (boardid == bdid) {
+ plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC");
+ plist_get_string_val(p4cc, &payload_4cc);
+ plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data");
+ if (PLIST_IS_ARRAY(matching)) {
+ plist_array_iter iter2 = NULL;
+ plist_array_new_iter(matching, &iter2);
+ plist_t match = NULL;
+ do {
+ plist_array_next_item(matching, iter2, &match);
+ if (!PLIST_IS_DICT(match)) {
+ break;
+ }
+ uint64_t minrev = 0;
+ plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum");
+ plist_get_uint_val(p_min, &minrev);
+ uint64_t maxrev = 0;
+ plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum");
+ plist_get_uint_val(p_max, &maxrev);
+ if (prev >= minrev && prev <= maxrev) {
+ plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags");
+ plist_get_string_val(tags, &data_payload_4ccs);
+ break;
+ }
+ } while (match);
+ plist_mem_free(iter2);
+ }
+ break;
+ }
+ } while (payload);
+ plist_mem_free(iter);
+ }
+ if (!payload_4cc) {
+ printf("Failed to get payload 4cc\n");
+ return -1;
+ }
+ if (!data_payload_4ccs) {
+ printf("Failed to get data payload 4ccs\n");
+ return -1;
+ }
+
+ // now find the blobs in UARP data
+ uint32_t dl_offset = 0;
+ uint32_t dl_size = 0;
+ uint32_t data1_offset = 0;
+ uint32_t data1_size = 0;
+ uint32_t data2_offset = 0;
+ uint32_t data2_size = 0;
+ uint32_t toc_offset = be32toh(uarp_hdr->toc_offset);
+ uint32_t toc_size = be32toh(uarp_hdr->toc_size);
+ const unsigned char* p = uarp_fw + uarp_hdr_size;
+ while (p < uarp_fw + toc_size) {
+ struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p;
+ uint32_t te_size = be32toh(entry->this_size);
+ if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) {
+ dl_offset = be32toh(entry->offset);
+ dl_size = be32toh(entry->size);
+ } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) {
+ data1_offset = be32toh(entry->offset);
+ data1_size = be32toh(entry->size);
+ } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) {
+ data2_offset = be32toh(entry->offset);
+ data2_size = be32toh(entry->size);
+ }
+ p += te_size;
+ }
+
+ uint32_t content_size = data1_size + data2_size + im4m_size + dl_size;
+
+ *bin_out = (unsigned char*)malloc(0x40 + content_size);
+ struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out);
+ hdr->magic = htole32(0xACE00003);
+ hdr->unk4 = htole32(0x00203400);
+ hdr->unk8 = htole32(0x00002800);
+ hdr->header_size = htole32(0x40);
+ hdr->data1_size = htole32(data1_size);
+ hdr->data2_size = htole32(data2_size);;
+ hdr->im4m_offset = htole32(0x40 + data1_size + data2_size);
+ hdr->im4m_dl_size = htole32(im4m_size + dl_size);
+ hdr->content_size = htole32(content_size);
+ hdr->crc = 0;
+ hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL;
+ hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL;
+ hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL;
+
+ // write data1 payload
+ memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size);
+ // write data2 payload
+ memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size);
+ // write IM4M
+ memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size);
+ // write dl payload
+ memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size);
+
+ // calculate CRC and update header
+ hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF));
+
+ *bin_size = 0x40 + content_size;
+
+ return 0;
+}
diff --git a/src/ace3.h b/src/ace3.h
new file mode 100644
index 0000000..2ef65a7
--- /dev/null
+++ b/src/ace3.h
@@ -0,0 +1,17 @@
+#ifndef IDEVICERESTORE_ACE3_H
+#define IDEVICERESTORE_ACE3_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <plist/plist.h>
+
+int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/common.c b/src/common.c
index 9a73994..499509d 100644
--- a/src/common.c
+++ b/src/common.c
@@ -63,6 +63,7 @@ struct idevicerestore_mode_t idevicerestore_modes[] = {
{ 3, "Recovery" },
{ 4, "Restore" },
{ 5, "Normal" },
+ { 6, "Port DFU" },
};
int idevicerestore_debug = 0;
diff --git a/src/common.h b/src/common.h
index 493b1df..9b3c1e3 100644
--- a/src/common.h
+++ b/src/common.h
@@ -47,6 +47,7 @@ extern "C" {
#define _MODE_RECOVERY 3
#define _MODE_RESTORE 4
#define _MODE_NORMAL 5
+#define _MODE_PORTDFU 6
#define MODE_UNKNOWN &idevicerestore_modes[_MODE_UNKNOWN]
#define MODE_WTF &idevicerestore_modes[_MODE_WTF]
@@ -54,6 +55,7 @@ extern "C" {
#define MODE_RECOVERY &idevicerestore_modes[_MODE_RECOVERY]
#define MODE_RESTORE &idevicerestore_modes[_MODE_RESTORE]
#define MODE_NORMAL &idevicerestore_modes[_MODE_NORMAL]
+#define MODE_PORTDFU &idevicerestore_modes[_MODE_PORTDFU]
#define FLAG_QUIT 1
diff --git a/src/dfu.c b/src/dfu.c
index 8602bc0..62a3dc0 100644
--- a/src/dfu.c
+++ b/src/dfu.c
@@ -118,6 +118,21 @@ int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffe
return 0;
}
+int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options)
+{
+ irecv_error_t err = 0;
+
+ info("Sending data (%d bytes)...\n", size);
+
+ err = irecv_send_buffer(client->dfu->client, buffer, size, irecv_options);
+ if (err != IRECV_E_SUCCESS) {
+ error("ERROR: Unable to send data: %s\n", irecv_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component)
{
char* path = NULL;
@@ -204,6 +219,24 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide
return 0;
}
+int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid)
+{
+ if(client->dfu == NULL) {
+ if (dfu_client_new(client) < 0) {
+ return -1;
+ }
+ }
+
+ const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client);
+ if (!device_info) {
+ return -1;
+ }
+
+ *bdid = device_info->bdid;
+
+ return 0;
+}
+
int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid)
{
if(client->dfu == NULL) {
@@ -222,6 +255,27 @@ int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid)
return 0;
}
+int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev)
+{
+ if(client->dfu == NULL) {
+ if (dfu_client_new(client) < 0) {
+ return -1;
+ }
+ }
+
+ const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client);
+ if (!device_info) {
+ return -1;
+ }
+ char* ptr = strstr(device_info->serial_string, "PREV:");
+ if (ptr) {
+ sscanf(ptr, "PREV:%x", prev);
+ return 0;
+ }
+ return -1;
+}
+
+
int dfu_is_image4_supported(struct idevicerestore_client_t* client)
{
if(client->dfu == NULL) {
@@ -238,6 +292,35 @@ int dfu_is_image4_supported(struct idevicerestore_client_t* client)
return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE);
}
+int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size)
+{
+ if(client->dfu == NULL) {
+ if (dfu_client_new(client) < 0) {
+ return -1;
+ }
+ }
+
+ const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client);
+ if (!device_info) {
+ return -1;
+ }
+
+ if (device_info->ap_nonce && device_info->ap_nonce_size > 0) {
+ *nonce = (unsigned char*)malloc(device_info->ap_nonce_size);
+ if (!*nonce) {
+ return -1;
+ }
+ *nonce_size = device_info->ap_nonce_size;
+ // The nonce is backwards, so we have to swap the bytes
+ unsigned int i = 0;
+ for (i = 0; i < *nonce_size; i++) {
+ (*nonce)[(*nonce_size)-1-i] = device_info->ap_nonce[i];
+ }
+ }
+
+ return 0;
+}
+
int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size)
{
if(client->dfu == NULL) {
diff --git a/src/dfu.h b/src/dfu.h
index f6174ab..4590dbd 100644
--- a/src/dfu.h
+++ b/src/dfu.h
@@ -41,9 +41,13 @@ int dfu_client_new(struct idevicerestore_client_t* client);
void dfu_client_free(struct idevicerestore_client_t* client);
irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client);
int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size);
+int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options);
int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component);
+int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid);
int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid);
+int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev);
int dfu_is_image4_supported(struct idevicerestore_client_t* client);
+int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size);
int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size);
int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size);
int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity);
diff --git a/src/idevicerestore.c b/src/idevicerestore.c
index e71dea4..ad02f7f 100644
--- a/src/idevicerestore.c
+++ b/src/idevicerestore.c
@@ -47,6 +47,7 @@
#include <libimobiledevice-glue/utils.h>
+#include "ace3.h"
#include "dfu.h"
#include "tss.h"
#include "img3.h"
@@ -298,6 +299,9 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata)
case IRECV_K_DFU_MODE:
client->mode = MODE_DFU;
break;
+ case IRECV_K_PORT_DFU_MODE:
+ client->mode = MODE_PORTDFU;
+ break;
case IRECV_K_RECOVERY_MODE_1:
case IRECV_K_RECOVERY_MODE_2:
case IRECV_K_RECOVERY_MODE_3:
@@ -316,6 +320,12 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata)
mutex_lock(&client->device_event_mutex);
client->mode = MODE_UNKNOWN;
debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A");
+ if (event->mode == IRECV_K_PORT_DFU_MODE) {
+ // We have to reset the ECID here if a port DFU device disconnects,
+ // because when the device reconnects in a different mode, it will
+ // have the actual device ECID and wouldn't get detected.
+ client->ecid = 0;
+ }
cond_signal(&client->device_event_cond);
mutex_unlock(&client->device_event_mutex);
}
@@ -683,6 +693,152 @@ int idevicerestore_start(struct idevicerestore_client_t* client)
}
}
+ if (client->mode == MODE_PORTDFU) {
+ unsigned int pdfu_bdid = 0;
+ unsigned int pdfu_cpid = 0;
+ unsigned int prev = 0;
+
+ if (dfu_get_bdid(client, &pdfu_bdid) < 0) {
+ error("ERROR: Failed to get bdid for Port DFU device!\n");
+ return -1;
+ }
+ if (dfu_get_cpid(client, &pdfu_cpid) < 0) {
+ error("ERROR: Failed to get cpid for Port DFU device!\n");
+ return -1;
+ }
+ if (dfu_get_prev(client, &prev) < 0) {
+ error("ERROR: Failed to get PREV for Port DFU device!\n");
+ return -1;
+ }
+
+ unsigned char* pdfu_nonce = NULL;
+ unsigned int pdfu_nsize = 0;
+ if (dfu_get_portdfu_nonce(client, &pdfu_nonce, &pdfu_nsize) < 0) {
+ error("ERROR: Failed to get nonce for Port DFU device!\n");
+ return -1;
+ }
+
+ plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0);
+ if (!build_identity) {
+ error("ERORR: Failed to get build identity\n");
+ return -1;
+ }
+
+ unsigned int b_pdfu_cpid = (unsigned int)_plist_dict_get_uint(build_identity, "USBPortController1,ChipID");
+ if (b_pdfu_cpid != pdfu_cpid) {
+ error("ERROR: cpid 0x%02x doesn't match USBPortController1,ChipID in build identity (0x%02x)\n", pdfu_cpid, b_pdfu_cpid);
+ return -1;
+ }
+ unsigned int b_pdfu_bdid = (unsigned int)_plist_dict_get_uint(build_identity, "USBPortController1,BoardID");
+ if (b_pdfu_bdid != pdfu_bdid) {
+ error("ERROR: bdid 0x%x doesn't match USBPortController1,BoardID in build identity (0x%x)\n", pdfu_bdid, b_pdfu_bdid);
+ return -1;
+ }
+
+ plist_t parameters = plist_new_dict();
+ plist_dict_set_item(parameters, "@USBPortController1,Ticket", plist_new_bool(1));
+ plist_dict_set_item(parameters, "USBPortController1,ECID", plist_new_int(client->ecid));
+ _plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL);
+ _plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL);
+ _plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL);
+ plist_dict_set_item(parameters, "USBPortController1,SecurityMode", plist_new_bool(1));
+ plist_dict_set_item(parameters, "USBPortController1,ProductionMode", plist_new_bool(1));
+ plist_t usbf = plist_access_path(build_identity, 2, "Manifest", "USBPortController1,USBFirmware");
+ if (!usbf) {
+ plist_free(parameters);
+ error("ERROR: Unable to find USBPortController1,USBFirmware in build identity\n");
+ return -1;
+ }
+ plist_t p_fwpath = plist_access_path(usbf, 2, "Info", "Path");
+ if (!p_fwpath) {
+ plist_free(parameters);
+ error("ERROR: Unable to find path of USBPortController1,USBFirmware component\n");
+ return -1;
+ }
+ const char* fwpath = plist_get_string_ptr(p_fwpath, NULL);
+ if (!fwpath) {
+ plist_free(parameters);
+ error("ERROR: Unable to get path of USBPortController1,USBFirmware component\n");
+ return -1;
+ }
+ unsigned char* uarp_buf = NULL;
+ unsigned int uarp_size = 0;
+ if (ipsw_extract_to_memory(client->ipsw, fwpath, &uarp_buf, &uarp_size) < 0) {
+ plist_free(parameters);
+ error("ERROR: Unable to extract '%s' from IPSW\n", fwpath);
+ return -1;
+ }
+ usbf = plist_copy(usbf);
+ plist_dict_remove_item(usbf, "Info");
+ plist_dict_set_item(parameters, "USBPortController1,USBFirmware", usbf);
+ plist_dict_set_item(parameters, "USBPortController1,Nonce", plist_new_data((const char*)pdfu_nonce, pdfu_nsize));
+
+ plist_t request = tss_request_new(NULL);
+ if (request == NULL) {
+ plist_free(parameters);
+ error("ERROR: Unable to create TSS request\n");
+ return -1;
+ }
+ plist_dict_merge(&request, parameters);
+ plist_free(parameters);
+
+ // send request and grab response
+ plist_t response = tss_request_send(request, client->tss_url);
+ plist_free(request);
+ if (response == NULL) {
+ error("ERROR: Unable to send TSS request\n");
+ return -1;
+ }
+ info("Received USBPortController1,Ticket\n");
+
+ info("Creating Ace3Binary\n");
+ unsigned char* ace3bin = NULL;
+ size_t ace3bin_size = 0;
+ if (ace3_create_binary(uarp_buf, uarp_size, pdfu_bdid, prev, response, &ace3bin, &ace3bin_size) < 0) {
+ error("ERROR: Could not create Ace3Binary\n");
+ return -1;
+ }
+ plist_free(response);
+ free(uarp_buf);
+
+ if (idevicerestore_keep_pers) {
+ write_file("Ace3Binary", (const char*)ace3bin, ace3bin_size);
+ }
+
+ if (dfu_send_buffer_with_options(client, ace3bin, ace3bin_size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH | IRECV_SEND_OPT_DFU_SMALL_PKT) < 0) {
+ error("ERROR: Could not send Ace3Buffer to device\n");
+ return -1;
+ }
+
+ debug("Waiting for device to disconnect...\n");
+ cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000);
+ if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) {
+ mutex_unlock(&client->device_event_mutex);
+
+ if (!(client->flags & FLAG_QUIT)) {
+ error("ERROR: Device did not disconnect. Port DFU failed.\n");
+ }
+ return -2;
+ }
+ debug("Waiting for device to reconnect in DFU mode...\n");
+ cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000);
+ if (client->mode != MODE_DFU || (client->flags & FLAG_QUIT)) {
+ mutex_unlock(&client->device_event_mutex);
+ if (!(client->flags & FLAG_QUIT)) {
+ error("ERROR: Device did not reconnect in DFU mode. Port DFU failed.\n");
+ }
+ return -2;
+ }
+ mutex_unlock(&client->device_event_mutex);
+
+ if (client->flags & FLAG_NOACTION) {
+ info("Port DFU restore successful.\n");
+ return 0;
+ } else {
+ info("Port DFU restore successful. Continuing.\n");
+ }
+ }
+
idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8);
/* check if device type is supported by the given build manifest */
@@ -1773,6 +1929,7 @@ irecv_device_t get_irecv_device(struct idevicerestore_client_t *client)
return normal_get_irecv_device(client);
case _MODE_DFU:
+ case _MODE_PORTDFU:
case _MODE_RECOVERY:
return dfu_get_irecv_device(client);