From da393b9c399e0c5541311e07f68c4f2c337d50b7 Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Fri, 27 Sep 2013 20:26:50 +0200 Subject: Require libirecovery >= 0.2.0 and port code to it's new API --- src/common.h | 2 +- src/dfu.c | 49 +++++++++++++++++++++++++++++++++---------------- src/dfu.h | 2 +- src/idevicerestore.c | 44 ++++++++++++++++++-------------------------- src/idevicerestore.h | 2 +- src/limera1n.c | 32 ++++++++++++++++++++++---------- src/normal.c | 36 +++++++++++++++++++++--------------- src/normal.h | 2 +- src/recovery.c | 13 ++++++++----- src/restore.c | 26 ++++++++++++++------------ src/restore.h | 2 +- 11 files changed, 121 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/common.h b/src/common.h index e626e2c..af3d1f8 100644 --- a/src/common.h +++ b/src/common.h @@ -78,7 +78,7 @@ struct idevicerestore_client_t { struct normal_client_t* normal; struct restore_client_t* restore; struct recovery_client_t* recovery; - struct irecv_device* device; + irecv_device_t device; struct idevicerestore_entry_t** entries; struct idevicerestore_mode_t* mode; char* version; diff --git a/src/dfu.c b/src/dfu.c index 29d409d..796bd47 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -54,7 +54,7 @@ int dfu_client_new(struct idevicerestore_client_t* client) { } for (i = 1; i <= attempts; i++) { - dfu_error = irecv_open(&dfu, client->ecid); + dfu_error = irecv_open_with_ecid(&dfu, client->ecid); if (dfu_error == IRECV_E_SUCCESS) { break; } @@ -89,45 +89,47 @@ void dfu_client_free(struct idevicerestore_client_t* client) { int dfu_check_mode(struct idevicerestore_client_t* client, int* mode) { irecv_client_t dfu = NULL; irecv_error_t dfu_error = IRECV_E_SUCCESS; + int probe_mode = -1; irecv_init(); - dfu_error = irecv_open(&dfu, client->ecid); - + dfu_error = irecv_open_with_ecid(&dfu, client->ecid); if (dfu_error != IRECV_E_SUCCESS) { return -1; } - if ((dfu->mode != kDfuMode) && (dfu->mode != kWTFMode)) { + irecv_get_mode(dfu, &probe_mode); + + if ((probe_mode != IRECV_K_DFU_MODE) && (probe_mode != IRECV_K_WTF_MODE)) { irecv_close(dfu); return -1; } - *mode = (dfu->mode == kWTFMode) ? MODE_WTF : MODE_DFU; + *mode = (probe_mode == IRECV_K_WTF_MODE) ? MODE_WTF : MODE_DFU; irecv_close(dfu); return 0; } -int dfu_check_device(struct idevicerestore_client_t* client) { +const char* dfu_check_product_type(struct idevicerestore_client_t* client) { irecv_client_t dfu = NULL; irecv_error_t dfu_error = IRECV_E_SUCCESS; irecv_device_t device = NULL; irecv_init(); - dfu_error = irecv_open(&dfu, client->ecid); + dfu_error = irecv_open_with_ecid(&dfu, client->ecid); if (dfu_error != IRECV_E_SUCCESS) { - return -1; + return NULL; } - dfu_error = irecv_get_device(dfu, &device); + dfu_error = irecv_devices_get_device_by_client(dfu, &device); if (dfu_error != IRECV_E_SUCCESS) { - return -1; + return NULL; } irecv_close(dfu); - return device->index; + return device->product_type; } int dfu_send_buffer(struct idevicerestore_client_t* client, char* buffer, uint32_t size) @@ -254,13 +256,16 @@ int dfu_get_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity) { irecv_error_t dfu_error = IRECV_E_SUCCESS; + int mode = 0; if (dfu_client_new(client) < 0) { error("ERROR: Unable to connect to DFU device\n"); return -1; } - if (client->dfu->client->mode != kDfuMode) { + irecv_get_mode(client->dfu->client, &mode); + + if (mode != IRECV_K_DFU_MODE) { info("NOTE: device is not in DFU mode, assuming recovery mode.\n"); client->mode = &idevicerestore_modes[MODE_RECOVERY]; return 0; @@ -273,7 +278,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide return -1; } - irecv_control_transfer(client->dfu->client, 0x21, 1, 0, 0, 0, 0, 5000); + irecv_usb_control_transfer(client->dfu->client, 0x21, 1, 0, 0, 0, 0, 5000); dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { @@ -330,7 +335,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide fixup_tss(client->tss); } - if (irecv_set_configuration(client->dfu->client, 1) < 0) { + if (irecv_usb_set_configuration(client->dfu->client, 1) < 0) { error("ERROR: set configuration failed\n"); } @@ -342,7 +347,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide return -1; } - irecv_control_transfer(client->dfu->client, 0x21, 1, 0, 0, 0, 0, 5000); + irecv_usb_control_transfer(client->dfu->client, 0x21, 1, 0, 0, 0, 0, 5000); dfu_error = irecv_reset(client->dfu->client); if (dfu_error != IRECV_E_SUCCESS) { @@ -358,7 +363,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide sleep(7); // Reconnect to device, but this time make sure we're not still in DFU mode - if (recovery_client_new(client) < 0 || client->recovery->client->mode == kDfuMode) { + if (recovery_client_new(client) < 0) { error("ERROR: Unable to connect to recovery device\n"); if (client->recovery->client) { irecv_close(client->recovery->client); @@ -366,6 +371,18 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide } return -1; } + + irecv_get_mode(client->recovery->client, &mode); + + if (mode == IRECV_K_DFU_MODE) { + error("ERROR: Unable to connect to recovery device\n"); + if (client->recovery->client) { + irecv_close(client->recovery->client); + client->recovery->client = NULL; + } + return -1; + } + return 0; } diff --git a/src/dfu.h b/src/dfu.h index 9a1e3e3..3f5ea1c 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -40,7 +40,7 @@ struct dfu_client_t { int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); int dfu_check_mode(struct idevicerestore_client_t* client, int* mode); -int dfu_check_device(struct idevicerestore_client_t* client); +const char* dfu_check_product_type(struct idevicerestore_client_t* client); int dfu_send_buffer(struct idevicerestore_client_t* client, char* buffer, uint32_t size); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); diff --git a/src/idevicerestore.c b/src/idevicerestore.c index b2336c7..45f78ba 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -260,12 +260,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } // discover the device type - if (check_device(client) < 0 || client->device->index == DEVICE_UNKNOWN) { + 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); + 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"); @@ -294,7 +294,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (client->flags & FLAG_LATEST) { char* ipsw = NULL; - int res = ipsw_download_latest_fw(client->version_data, client->device->product, "cache", &ipsw); + int res = ipsw_download_latest_fw(client->version_data, client->device->product_type, "cache", &ipsw); if (res != 0) { if (ipsw) { free(ipsw); @@ -345,7 +345,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) 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) < 0) { + 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; } @@ -383,7 +383,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) char tmpstr[256]; char p_all_flash[128]; char lcmodel[8]; - strcpy(lcmodel, client->device->model); + strcpy(lcmodel, client->device->hardware_model); int x = 0; while (lcmodel[x]) { lcmodel[x] = tolower(lcmodel[x]); @@ -587,7 +587,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) 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, client->version); + 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"); @@ -1120,40 +1120,32 @@ int check_mode(struct idevicerestore_client_t* client) { return mode; } -int check_device(struct idevicerestore_client_t* client) { - int device = DEVICE_UNKNOWN; +const char* check_product_type(struct idevicerestore_client_t* client) { + const char* product_type = NULL; + irecv_device_t res = NULL; switch (client->mode->index) { case MODE_RESTORE: - device = restore_check_device(client); - if (device < 0) { - device = DEVICE_UNKNOWN; - } + product_type = restore_check_product_type(client); break; case MODE_NORMAL: - device = normal_check_device(client); - if (device < 0) { - device = DEVICE_UNKNOWN; - } + product_type = normal_check_product_type(client); break; case MODE_DFU: case MODE_RECOVERY: - device = dfu_check_device(client); - if (device < 0) { - device = DEVICE_UNKNOWN; - } + product_type = dfu_check_product_type(client); break; default: - device = DEVICE_UNKNOWN; break; } - if (device != DEVICE_UNKNOWN) - client->device = &irecv_devices[device]; + if (product_type != NULL) { + irecv_devices_get_device_by_product_type(product_type, &client->device); + } - return device; + return product_type; } int get_bdid(struct idevicerestore_client_t* client, uint32_t* bdid) { @@ -1308,9 +1300,9 @@ int get_shsh_blobs(struct idevicerestore_client_t* client, uint64_t ecid, unsign char zfn[1024]; if (client->version) { if (client->cache_dir) { - sprintf(zfn, "%s/shsh/" FMT_qu "-%s-%s.shsh", client->cache_dir, (long long int)client->ecid, client->device->product, client->version); + sprintf(zfn, "%s/shsh/" FMT_qu "-%s-%s.shsh", client->cache_dir, (long long int)client->ecid, client->device->product_type, client->version); } else { - sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product, client->version); + sprintf(zfn, "shsh/" FMT_qu "-%s-%s.shsh", (long long int)client->ecid, client->device->product_type, client->version); } struct stat fst; if (stat(zfn, &fst) == 0) { diff --git a/src/idevicerestore.h b/src/idevicerestore.h index 1b960cf..65bb001 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -73,7 +73,7 @@ const char* idevicerestore_get_error(); void usage(int argc, char* argv[]); int check_mode(struct idevicerestore_client_t* client); -int check_device(struct idevicerestore_client_t* client); +const char* check_product_type(struct idevicerestore_client_t* client); int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int get_bdid(struct idevicerestore_client_t* client, uint32_t* bdid); int get_cpid(struct idevicerestore_client_t* client, uint32_t* cpid); diff --git a/src/limera1n.c b/src/limera1n.c index 265052c..6ceb65a 100644 --- a/src/limera1n.c +++ b/src/limera1n.c @@ -42,15 +42,24 @@ int limera1n_exploit(struct irecv_device *device, irecv_client_t *pclient) unsigned int shellcode_address = 0; unsigned int shellcode_length = 0; - if (device->chip_id == irecv_devices[DEVICE_IPHONE4].chip_id) { + irecv_device_t iphone4 = NULL; + irecv_device_t iphone3gs = NULL; + irecv_device_t ipod3g = NULL; + int mode = 0; + + irecv_devices_get_device_by_product_type("iPhone3,1", &iphone4); + irecv_devices_get_device_by_product_type("iPhone2,1", &iphone3gs); + irecv_devices_get_device_by_product_type("iPod3,1", &ipod3g); + + if (device->chip_id == iphone4->chip_id) { max_size = 0x2C000; stack_address = 0x8403BF9C; shellcode_address = 0x8402B001; - } else if (device->chip_id == irecv_devices[DEVICE_IPHONE3GS].chip_id) { + } else if (device->chip_id == iphone3gs->chip_id) { max_size = 0x24000; stack_address = 0x84033FA4; shellcode_address = 0x84023001; - } else if (device->chip_id == irecv_devices[DEVICE_IPOD3G].chip_id) { + } else if (device->chip_id == ipod3g->chip_id) { max_size = 0x24000; stack_address = 0x84033F98; shellcode_address = 0x84023001; @@ -82,23 +91,23 @@ int limera1n_exploit(struct irecv_device *device, irecv_client_t *pclient) } debug("Sending chunk headers\n"); - irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); + irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); memset(buf, 0xCC, 0x800); for(i = 0; i < (max_size - (0x800 * 3)); i += 0x800) { - irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); + irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 1000); } debug("Sending exploit payload\n"); - irecv_control_transfer(client, 0x21, 1, 0, 0, shellcode, 0x800, 1000); + irecv_usb_control_transfer(client, 0x21, 1, 0, 0, shellcode, 0x800, 1000); debug("Sending fake data\n"); memset(buf, 0xBB, 0x800); - irecv_control_transfer(client, 0xA1, 1, 0, 0, buf, 0x800, 1000); - irecv_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 10); + irecv_usb_control_transfer(client, 0xA1, 1, 0, 0, buf, 0x800, 1000); + irecv_usb_control_transfer(client, 0x21, 1, 0, 0, buf, 0x800, 10); //debug("Executing exploit\n"); - irecv_control_transfer(client, 0x21, 2, 0, 0, buf, 0, 1000); + irecv_usb_control_transfer(client, 0x21, 2, 0, 0, buf, 0, 1000); irecv_reset(client); irecv_finish_transfer(client); @@ -110,7 +119,10 @@ int limera1n_exploit(struct irecv_device *device, irecv_client_t *pclient) error("Unable to reconnect\n"); return -1; } - if ((*pclient)->mode != kDfuMode) { + + irecv_get_mode((*pclient), &mode); + + if (mode != IRECV_K_DFU_MODE) { error("Device reconnected in non-DFU mode\n"); return -1; } diff --git a/src/normal.c b/src/normal.c index 5a47658..92fff4c 100644 --- a/src/normal.c +++ b/src/normal.c @@ -210,10 +210,11 @@ int normal_open_with_timeout(struct idevicerestore_client_t* client) { return 0; } -int normal_check_device(struct idevicerestore_client_t* client) { +const char* normal_check_product_type(struct idevicerestore_client_t* client) { int i = 0; idevice_t device = NULL; char* product_type = NULL; + irecv_device_t irecv_device = NULL; plist_t product_type_node = NULL; lockdownd_client_t lockdown = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; @@ -221,7 +222,7 @@ int normal_check_device(struct idevicerestore_client_t* client) { normal_idevice_new(client, &device); if (!device) { - return -1; + return product_type; } lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore"); @@ -238,7 +239,7 @@ int normal_check_device(struct idevicerestore_client_t* client) { } if (lockdown_error != LOCKDOWN_E_SUCCESS) { idevice_free(device); - return -1; + return product_type; } plist_t pval = NULL; @@ -247,11 +248,9 @@ int normal_check_device(struct idevicerestore_client_t* client) { char* strval = NULL; plist_get_string_val(pval, &strval); if (strval) { - for (i = 0; irecv_devices[i].model != NULL; i++) { - if (!strcasecmp(strval, irecv_devices[i].model)) { - product_type = (char*)irecv_devices[i].product; - break; - } + irecv_devices_get_device_by_hardware_model(strval, &irecv_device); + if (irecv_device) { + product_type = strdup(irecv_device->product_type); } free(strval); } @@ -265,7 +264,7 @@ int normal_check_device(struct idevicerestore_client_t* client) { if (lockdown_error != LOCKDOWN_E_SUCCESS) { lockdownd_client_free(lockdown); idevice_free(device); - return -1; + return product_type; } } @@ -274,23 +273,30 @@ int normal_check_device(struct idevicerestore_client_t* client) { lockdown = NULL; device = NULL; + if (irecv_device) { + if (product_type) + free(product_type); + + return irecv_device->product_type; + } + if (product_type_node != NULL) { if (!product_type_node || plist_get_node_type(product_type_node) != PLIST_STRING) { if (product_type_node) plist_free(product_type_node); - return -1; + return product_type; } plist_get_string_val(product_type_node, &product_type); plist_free(product_type_node); - } - for (i = 0; irecv_devices[i].product != NULL; i++) { - if (!strcasecmp(product_type, irecv_devices[i].product)) { - break; + irecv_devices_get_device_by_product_type(product_type, &irecv_device); + if (irecv_device && irecv_device->product_type) { + free(product_type); + return irecv_device->product_type; } } - return irecv_devices[i].index; + return product_type; } int normal_enter_recovery(struct idevicerestore_client_t* client) { diff --git a/src/normal.h b/src/normal.h index 44cf854..e210128 100644 --- a/src/normal.h +++ b/src/normal.h @@ -41,7 +41,7 @@ struct normal_client_t { int normal_check_mode(struct idevicerestore_client_t* client); -int normal_check_device(struct idevicerestore_client_t* client); +const char* normal_check_product_type(struct idevicerestore_client_t* client); int normal_client_new(struct idevicerestore_client_t* client); void normal_client_free(struct idevicerestore_client_t* client); int normal_open_with_timeout(struct idevicerestore_client_t* client); diff --git a/src/recovery.c b/src/recovery.c index a1001dd..5e3dc91 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -70,7 +70,7 @@ int recovery_client_new(struct idevicerestore_client_t* client) { } for (i = 1; i <= attempts; i++) { - recovery_error = irecv_open(&recovery, client->ecid); + recovery_error = irecv_open_with_ecid(&recovery, client->ecid); if (recovery_error == IRECV_E_SUCCESS) { break; } @@ -102,15 +102,18 @@ int recovery_client_new(struct idevicerestore_client_t* client) { int recovery_check_mode(struct idevicerestore_client_t* client) { irecv_client_t recovery = NULL; irecv_error_t recovery_error = IRECV_E_SUCCESS; + int mode = 0; irecv_init(); - recovery_error=irecv_open(&recovery, client->ecid); + recovery_error=irecv_open_with_ecid(&recovery, client->ecid); if (recovery_error != IRECV_E_SUCCESS) { return -1; } - if ((recovery->mode == kDfuMode) || (recovery->mode == kWTFMode)) { + irecv_get_mode(recovery, &mode); + + if ((mode == IRECV_K_DFU_MODE) || (mode == IRECV_K_WTF_MODE)) { irecv_close(recovery); return -1; } @@ -319,7 +322,7 @@ int recovery_send_ibec(struct idevicerestore_client_t* client, plist_t build_ide error("ERROR: Unable to execute %s\n", component); return -1; } - irecv_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); + irecv_usb_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); return 0; } @@ -422,7 +425,7 @@ int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t bu return -1; } - irecv_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); + irecv_usb_control_transfer(client->recovery->client, 0x21, 1, 0, 0, 0, 0, 5000); if (client->restore_boot_args) { char setba[256]; diff --git a/src/restore.c b/src/restore.c index bd4c39e..7e613e6 100644 --- a/src/restore.c +++ b/src/restore.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "idevicerestore.h" #include "asr.h" @@ -192,7 +193,7 @@ int restore_check_mode(struct idevicerestore_client_t* client) { return 0; } -int restore_check_device(struct idevicerestore_client_t* client) { +const char* restore_check_product_type(struct idevicerestore_client_t* client) { int i = 0; char* model = NULL; plist_t node = NULL; @@ -200,22 +201,24 @@ int restore_check_device(struct idevicerestore_client_t* client) { restored_client_t restore = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; restored_error_t restore_error = RESTORE_E_SUCCESS; + char* product_type = NULL; + irecv_device_t irecv_device = NULL; restore_idevice_new(client, &device); if (!device) { - return -1; + return product_type; } restore_error = restored_client_new(device, &restore, "idevicerestore"); if (restore_error != RESTORE_E_SUCCESS) { idevice_free(device); - return -1; + return product_type; } if (restored_query_type(restore, NULL, NULL) != RESTORE_E_SUCCESS) { restored_client_free(restore); idevice_free(device); - return -1; + return product_type; } if (client->srnm == NULL) { @@ -224,7 +227,7 @@ int restore_check_device(struct idevicerestore_client_t* client) { error("ERROR: Unable to get SerialNumber from restored\n"); restored_client_free(restore); idevice_free(device); - return -1; + return product_type; } plist_get_string_val(node, &client->srnm); @@ -237,7 +240,7 @@ int restore_check_device(struct idevicerestore_client_t* client) { error("ERROR: Unable to get HardwareModel from restored\n"); restored_client_free(restore); idevice_free(device); - return -1; + return product_type; } restored_client_free(restore); @@ -249,17 +252,16 @@ int restore_check_device(struct idevicerestore_client_t* client) { error("ERROR: Unable to get HardwareModel information\n"); if (node) plist_free(node); - return -1; + return product_type; } plist_get_string_val(node, &model); - for (i = 0; irecv_devices[i].model != NULL; i++) { - if (!strcasecmp(model, irecv_devices[i].model)) { - break; - } + irecv_devices_get_device_by_hardware_model(model, &irecv_device); + if (irecv_device && irecv_device->product_type) { + return irecv_device->product_type; } - return irecv_devices[i].index; + return product_type; } void restore_device_callback(const idevice_event_t* event, void* userdata) { diff --git a/src/restore.h b/src/restore.h index d37e3ce..7b3be7f 100644 --- a/src/restore.h +++ b/src/restore.h @@ -44,7 +44,7 @@ struct restore_client_t { }; int restore_check_mode(struct idevicerestore_client_t* client); -int restore_check_device(struct idevicerestore_client_t* client); +const char* restore_check_product_type(struct idevicerestore_client_t* client); int restore_client_new(struct idevicerestore_client_t* client); void restore_client_free(struct idevicerestore_client_t* client); int restore_reboot(struct idevicerestore_client_t* client); -- cgit v1.1-32-gdbae