summaryrefslogtreecommitdiffstats
path: root/src/idevicerestore.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/idevicerestore.c')
-rw-r--r--src/idevicerestore.c394
1 files changed, 385 insertions, 9 deletions
diff --git a/src/idevicerestore.c b/src/idevicerestore.c
index 7e9a03d..d6551a4 100644
--- a/src/idevicerestore.c
+++ b/src/idevicerestore.c
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <plist/plist.h>
#include <libirecovery.h>
+#include <libimobiledevice/restore.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/libimobiledevice.h>
@@ -34,10 +35,14 @@
#include "idevicerestore.h"
#define UNKNOWN_MODE 0
-#define RECOVERY_MODE 1
-#define NORMAL_MODE 2
+#define NORMAL_MODE 1
+#define RECOVERY_MODE 2
+#define RESTORE_MODE 3
+
+#define ASR_PORT 12345
int idevicerestore_debug = 0;
+static int idevicerestore_mode = 0;
void usage(int argc, char* argv[]);
int write_file(const char* filename, char* data, int size);
@@ -47,10 +52,10 @@ int send_devicetree(char* ipsw, plist_t tss);
int send_ramdisk(char* ipsw, plist_t tss);
int send_kernelcache(char* ipsw, plist_t tss);
int get_tss_data(plist_t tss, const char* entry, char** path, char** blob);
+void device_callback(const idevice_event_t* event, void *user_data);
int main(int argc, char* argv[]) {
int opt = 0;
- int mode = 0;
char* ipsw = NULL;
char* uuid = NULL;
uint64_t ecid = 0;
@@ -104,14 +109,14 @@ int main(int argc, char* argv[]) {
return -1;
}
info("Found device in recovery mode\n");
- mode = RECOVERY_MODE;
+ idevicerestore_mode = RECOVERY_MODE;
} else {
info("Found device in normal mode\n");
- mode = NORMAL_MODE;
+ idevicerestore_mode = NORMAL_MODE;
}
- if (mode == NORMAL_MODE) {
+ if (idevicerestore_mode == NORMAL_MODE) {
lockdown_error = lockdownd_client_new_with_handshake(device, &lockdown, "idevicerestore");
if (lockdown_error != LOCKDOWN_E_SUCCESS) {
error("ERROR: Unable to connect to lockdownd\n");
@@ -140,7 +145,8 @@ int main(int argc, char* argv[]) {
idevice_free(device);
lockdown = NULL;
device = NULL;
- } else if (mode == RECOVERY_MODE) {
+
+ } else if (idevicerestore_mode == RECOVERY_MODE) {
recovery_error = irecv_get_ecid(recovery, &ecid);
if (recovery_error != IRECV_E_SUCCESS) {
error("ERROR: Unable to get device ECID\n");
@@ -185,10 +191,34 @@ int main(int argc, char* argv[]) {
plist_free(tss_request);
return -1;
}
- plist_free(tss_request);
info("Got TSS response\n");
- if (mode == NORMAL_MODE) {
+ // Get name of filesystem DMG in IPSW
+ char* filesystem = NULL;
+ plist_t filesystem_node = plist_dict_get_item(tss_request, "OS");
+ if(!filesystem_node || plist_get_node_type(filesystem_node) != PLIST_DICT) {
+ error("ERROR: Unable to find OS filesystem\n");
+ plist_free(tss_request);
+ return -1;
+ }
+
+ plist_t filesystem_info_node = plist_dict_get_item(filesystem_node, "Info");
+ if(!filesystem_info_node || plist_get_node_type(filesystem_info_node) != PLIST_DICT) {
+ error("ERROR: Unable to find filesystem info node\n");
+ plist_free(tss_request);
+ return -1;
+ }
+
+ plist_t filesystem_info_path_node = plist_dict_get_item(filesystem_info_node, "Path");
+ if(!filesystem_info_path_node || plist_get_node_type(filesystem_info_path_node) != PLIST_STRING) {
+ error("ERROR: Unable to find filesystem info path node\n");
+ plist_free(tss_request);
+ return -1;
+ }
+ plist_get_string_val(filesystem_info_path_node, &filesystem);
+ plist_free(tss_request);
+
+ if (idevicerestore_mode == NORMAL_MODE) {
// Place the device in recovery mode
info("Entering recovery mode...\n");
device_error = idevice_new(&device, uuid);
@@ -256,10 +286,107 @@ int main(int argc, char* argv[]) {
return -1;
}
+ idevice_event_subscribe(&device_callback, NULL);
+ info("Waiting for device to enter restore mode\n");
+ while(idevicerestore_mode != RESTORE_MODE) sleep(1);
+ device_error = idevice_new(&device, uuid);
+ if (device_error != IDEVICE_E_SUCCESS) {
+ error("ERROR: Unable to open device\n");
+ plist_free(tss_response);
+ return -1;
+ }
+
+ restored_client_t restore = NULL;
+ restored_error_t restore_error = restored_client_new(device, &restore, "idevicerestore");
+ if(restore_error != RESTORE_E_SUCCESS) {
+ error("ERROR: Unable to start restored client\n");
+ plist_free(tss_response);
+ idevice_free(device);
+ return -1;
+ }
+
+ char* type = NULL;
+ uint64_t version = 0;
+ if (restored_query_type(restore, &type, &version) != RESTORE_E_SUCCESS) {
+ printf("ERROR: Device is not in restore mode. QueryType returned \"%s\"\n", type);
+ plist_free(tss_response);
+ restored_client_free(restore);
+ idevice_free(device);
+ return -1;
+ }
+ info("Device has successfully entered restore mode\n");
+
+ /* start restored service and retrieve port */
+ int quit_flag = 0;
+ char* kernelcache = NULL;
+ printf("Restore protocol version is %llu.\n", version);
+ restore_error = restored_start_restore(restore);
+ if (restore_error == RESTORE_E_SUCCESS) {
+ while (!quit_flag) {
+ plist_t message = NULL;
+ restore_error = restored_receive(restore, &message);
+ plist_t msgtype_node = plist_dict_get_item(message, "MsgType");
+ if (msgtype_node && PLIST_STRING == plist_get_node_type(msgtype_node)) {
+ char *msgtype = NULL;
+ plist_get_string_val(msgtype_node, &msgtype);
+ if(!strcmp(msgtype, "ProgressMsg")) {
+ restore_error = progress_msg(restore, message);
+
+ }
+ else if(!strcmp(msgtype, "DataRequestMsg")) {
+ //restore_error = data_request_msg(device, restore, message, filesystem);
+ plist_t datatype_node = plist_dict_get_item(message, "DataType");
+ if (datatype_node && PLIST_STRING == plist_get_node_type(datatype_node)) {
+ char *datatype = NULL;
+ plist_get_string_val(datatype_node, &datatype);
+ if(!strcmp(datatype, "SystemImageData")) {
+ send_system_data(device, restore, filesystem);
+ }
+ else if(!strcmp(datatype, "KernelCache")) {
+ send_kernel_data(device, restore, kernelcache);
+ }
+ else if(!strcmp(datatype, "NORData")) {
+ send_nor_data(device, restore);
+ }
+ else {
+ // Unknown DataType!!
+ error("Unknown DataType\n");
+ return -1;
+ }
+ }
+
+ }
+ else if(!strcmp(msgtype, "StatusMsg")) {
+ restore_error = status_msg(restore, message);
+
+ }
+ else {
+ printf("Received unknown message type: %s\n", msgtype);
+ }
+ }
+
+ if (RESTORE_E_SUCCESS != restore_error) {
+ printf("Invalid return status %d\n", restore_error);
+ }
+
+ plist_free(message);
+ }
+ } else {
+ printf("ERROR: Could not start restore. %d\n", restore_error);
+ }
+
+ restored_client_free(restore);
+ idevice_free(device);
plist_free(tss_response);
return 0;
}
+void device_callback(const idevice_event_t* event, void *user_data) {
+ if(event->event == IDEVICE_DEVICE_ADD) {
+ idevicerestore_mode = RESTORE_MODE;
+ }
+}
+
void usage(int argc, char* argv[]) {
char *name = strrchr(argv[0], '/');
printf("Usage: %s [OPTIONS]\n", (name ? name + 1 : argv[0]));
@@ -274,6 +401,255 @@ void usage(int argc, char* argv[]) {
exit(1);
}
+int progress_msg(restored_client_t client, plist_t msg) {
+ info("Got progress message\n");
+ return 0;
+}
+
+int data_request_msg(idevice_t device, restored_client_t client, plist_t msg, const char *filesystem, const char *kernel) {
+ plist_t datatype_node = plist_dict_get_item(msg, "DataType");
+ if (datatype_node && PLIST_STRING == plist_get_node_type(datatype_node)) {
+ char *datatype = NULL;
+ plist_get_string_val(datatype_node, &datatype);
+ if(!strcmp(datatype, "SystemImageData")) {
+ send_system_data(device, client, filesystem);
+ }
+ else if(!strcmp(datatype, "KernelCache")) {
+ send_kernel_data(device, client, kernel);
+ }
+ else if(!strcmp(datatype, "NORData")) {
+ send_nor_data(device, client);
+ }
+ else {
+ // Unknown DataType!!
+ error("Unknown DataType\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int status_msg(restored_client_t client, plist_t msg) {
+ info("Got status message\n");
+ return 0;
+}
+
+int send_system_data(idevice_t device, restored_client_t client, const char *filesystem) {
+ int i = 0;
+ char buffer[0x1000];
+ uint32_t recv_bytes = 0;
+ memset(buffer, '\0', 0x1000);
+ idevice_connection_t connection = NULL;
+ idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
+
+ for(i = 0; i < 5; i++) {
+ ret = idevice_connect(device, ASR_PORT, &connection);
+ if(ret == IDEVICE_E_SUCCESS)
+ break;
+
+ else
+ sleep(1);
+ }
+
+ if(ret != IDEVICE_E_SUCCESS)
+ return ret;
+
+ memset(buffer, '\0', 0x1000);
+ ret = idevice_connection_receive(connection, buffer, 0x1000, &recv_bytes);
+ if(ret != IDEVICE_E_SUCCESS) {
+ idevice_disconnect(connection);
+ return ret;
+ }
+ printf("Received %d bytes\n", recv_bytes);
+ printf("%s", buffer);
+
+ FILE* fd = fopen(filesystem, "rb");
+ if(fd == NULL) {
+ idevice_disconnect(connection);
+ return ret;
+ }
+
+ fseek(fd, 0, SEEK_END);
+ uint64_t len = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+
+ printf("Connected to ASR\n");
+ plist_t dict = plist_new_dict();
+ plist_dict_insert_item(dict, "FEC Slice Stride", plist_new_uint(40));
+ plist_dict_insert_item(dict, "Packet Payload Size", plist_new_uint(1450));
+ plist_dict_insert_item(dict, "Packets Per FEC", plist_new_uint(25));
+
+ plist_t payload = plist_new_dict();
+ plist_dict_insert_item(payload, "Port", plist_new_uint(1));
+ plist_dict_insert_item(payload, "Size", plist_new_uint(len));
+ plist_dict_insert_item(dict, "Payload", payload);
+
+ plist_dict_insert_item(dict, "Stream ID", plist_new_uint(1));
+ plist_dict_insert_item(dict, "Version", plist_new_uint(1));
+
+ char* xml = NULL;
+ unsigned int dict_size = 0;
+ unsigned int sent_bytes = 0;
+ plist_to_xml(dict, &xml, &dict_size);
+
+ ret = idevice_connection_send(connection, xml, dict_size, &sent_bytes);
+ if(ret != IDEVICE_E_SUCCESS) {
+ idevice_disconnect(connection);
+ return ret;
+ }
+
+ printf("Sent %d bytes\n", sent_bytes);
+ printf("%s", xml);
+ plist_free(dict);
+ free(xml);
+
+ char* command = NULL;
+ do {
+ memset(buffer, '\0', 0x1000);
+ ret = idevice_connection_receive(connection, buffer, 0x1000, &recv_bytes);
+ if(ret != IDEVICE_E_SUCCESS) {
+ idevice_disconnect(connection);
+ return ret;
+ }
+ info("Received %d bytes\n", recv_bytes);
+ info("%s", buffer);
+
+ plist_t request = NULL;
+ plist_from_xml(buffer, recv_bytes, &request);
+ plist_t command_node = plist_dict_get_item(request, "Command");
+ if (command_node && PLIST_STRING == plist_get_node_type(command_node)) {
+ plist_get_string_val(command_node, &command);
+ if(!strcmp(command, "OOBData")) {
+ plist_t oob_length_node = plist_dict_get_item(request, "OOB Length");
+ if (!oob_length_node || PLIST_UINT != plist_get_node_type(oob_length_node)) {
+ printf("Error fetching OOB Length\n");
+ idevice_disconnect(connection);
+ return IDEVICE_E_UNKNOWN_ERROR;
+ }
+ uint64_t oob_length = 0;
+ plist_get_uint_val(oob_length_node, &oob_length);
+
+ plist_t oob_offset_node = plist_dict_get_item(request, "OOB Offset");
+ if (!oob_offset_node || PLIST_UINT != plist_get_node_type(oob_offset_node)) {
+ error("Error fetching OOB Offset\n");
+ idevice_disconnect(connection);
+ return IDEVICE_E_UNKNOWN_ERROR;
+ }
+ uint64_t oob_offset = 0;
+ plist_get_uint_val(oob_offset_node, &oob_offset);
+
+ char* oob_data = (char*) malloc(oob_length);
+ if(oob_data == NULL) {
+ error("Out of memory\n");
+ idevice_disconnect(connection);
+ return IDEVICE_E_UNKNOWN_ERROR;
+ }
+
+ fseek(fd, oob_offset, SEEK_SET);
+ if(fread(oob_data, 1, oob_length, fd) != oob_length) {
+ error("Unable to read filesystem offset\n");
+ idevice_disconnect(connection);
+ free(oob_data);
+ return ret;
+ }
+
+ ret = idevice_connection_send(connection, oob_data, oob_length, &sent_bytes);
+ if(sent_bytes != oob_length || ret != IDEVICE_E_SUCCESS) {
+ printf("Unable to send %d bytes to asr\n", sent_bytes);
+ idevice_disconnect(connection);
+ free(oob_data);
+ return ret;
+ }
+ plist_free(request);
+ free(oob_data);
+ }
+ }
+
+ } while(strcmp(command, "Payload"));
+
+ fseek(fd, 0, SEEK_SET);
+ char data[1450];
+ for(i = len; i > 0; i -= 1450) {
+ int size = 1450;
+ if(i < 1450) {
+ size = i;
+ }
+
+ if(fread(data, 1, size, fd) != (unsigned int)size) {
+ fclose(fd);
+ idevice_disconnect(connection);
+ printf("Error reading filesystem\n");
+ return IDEVICE_E_UNKNOWN_ERROR;
+ }
+
+ ret = idevice_connection_send(connection, data, size, &sent_bytes);
+ if(ret != IDEVICE_E_SUCCESS) {
+ fclose(fd);
+ }
+
+ if(i % (1450*1000) == 0) {
+ printf(".");
+ }
+ }
+
+ printf("Done sending filesystem\n");
+ fclose(fd);
+ ret = idevice_disconnect(connection);
+ return ret;
+}
+
+int send_kernel_data(idevice_t device, restored_client_t client, const char *kernel) {
+ printf("Sending kernelcache\n");
+ FILE* fd = fopen(kernel, "rb");
+ if(fd == NULL) {
+ info("Unable to open kernelcache");
+ return -1;
+ }
+
+ fseek(fd, 0, SEEK_END);
+ uint64_t len = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+
+ char* kernel_data = (char*) malloc(len);
+ if(kernel_data == NULL) {
+ error("Unable to allocate memory for kernel data");
+ fclose(fd);
+ return -1;
+ }
+
+ if(fread(kernel_data, 1, len, fd) != len) {
+ error("Unable to read kernel data\n");
+ free(kernel_data);
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+
+ plist_t kernelcache_node = plist_new_data(kernel_data, len);
+
+ plist_t dict = plist_new_dict();
+ plist_dict_insert_item(dict, "KernelCacheFile", kernelcache_node);
+
+ restored_error_t ret = restored_send(client, dict);
+ if(ret != RESTORE_E_SUCCESS) {
+ error("Unable to send kernelcache data\n");
+ free(kernel_data);
+ plist_free(dict);
+ return -1;
+ }
+
+ info("Done sending kernelcache\n");
+ free(kernel_data);
+ plist_free(dict);
+ return 0;
+}
+
+
+int send_nor_data(idevice_t device, restored_client_t client) {
+ info("Not implemented\n");
+ return 0;
+}
+
int write_file(const char* filename, char* data, int size) {
debug("Writing data to %s\n", filename);
FILE* file = fopen(filename, "wb");