diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile.am | 8 | ||||
-rw-r--r-- | tools/afcclient.c | 642 | ||||
-rw-r--r-- | tools/idevicebackup.c | 129 | ||||
-rw-r--r-- | tools/ideviceimagemounter.c | 399 |
4 files changed, 858 insertions, 320 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am index 4cac1fc..b0c2769 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -35,7 +35,7 @@ bin_PROGRAMS = \ afcclient idevicebtlogger_SOURCES = idevicebtlogger.c -iidevicebtlogger_CFLAGS = $(AM_CFLAGS) +idevicebtlogger_CFLAGS = $(AM_CFLAGS) idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS) idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la @@ -66,7 +66,7 @@ idevice_id_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la idevicebackup_SOURCES = idevicebackup.c idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) -idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(ssl_lib_LIBS) $(limd_glue_LIBS) +idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la idevicebackup2_SOURCES = idevicebackup2.c @@ -75,8 +75,8 @@ idevicebackup2_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la ideviceimagemounter_SOURCES = ideviceimagemounter.c -ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) -ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) +ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS) +ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(ssl_lib_LIBS) $(libtatsu_LIBS) ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la idevicescreenshot_SOURCES = idevicescreenshot.c diff --git a/tools/afcclient.c b/tools/afcclient.c index 9bcd77b..8f49831 100644 --- a/tools/afcclient.c +++ b/tools/afcclient.c @@ -36,6 +36,8 @@ #include <signal.h> #include <ctype.h> #include <unistd.h> +#include <dirent.h> +#include <time.h> #ifdef WIN32 #include <windows.h> @@ -70,6 +72,7 @@ #include <plist/plist.h> #include <libimobiledevice-glue/termcolors.h> +#include <libimobiledevice-glue/utils.h> #undef st_mtime #undef st_birthtime @@ -89,6 +92,26 @@ static idevice_subscription_context_t context = NULL; static char* curdir = NULL; static size_t curdir_len = 0; +static int file_exists(const char* path) +{ + struct stat tst; +#ifdef WIN32 + return (stat(path, &tst) == 0); +#else + return (lstat(path, &tst) == 0); +#endif +} + +static int is_directory(const char* path) +{ + struct stat tst; +#ifdef WIN32 + return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode); +#else + return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode); +#endif +} + static void print_usage(int argc, char **argv, int is_error) { char *name = strrchr(argv[0], '/'); @@ -171,9 +194,9 @@ static void handle_help(afc_client_t afc, int argc, char** argv) printf("ln [-s] FILE [LINK] - create a (symbolic) link to file named LINKNAME\n"); printf(" NOTE: This feature has been disabled in newer versions of iOS.\n"); printf("rm PATH - remove item at PATH\n"); - printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); - printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); - printf("\n"); + printf("get [-rf] PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); + printf("put [-rf] LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); + printf("\n"); } static const char* path_get_basename(const char* path) @@ -200,7 +223,7 @@ static int timeval_subtract(struct timeval *result, struct timeval *x, struct ti result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; + return x->tv_sec < y->tv_sec; } struct str_item { @@ -674,204 +697,499 @@ static void handle_remove(afc_client_t afc, int argc, char** argv) } } -static void handle_get(afc_client_t afc, int argc, char** argv) +static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size, uint8_t force_overwrite) { - if (argc < 1 || argc > 2) { - printf("Error: Invalid number of arguments\n"); - return; + uint64_t fh = 0; + afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; } - char *srcpath = NULL; - char* dstpath = NULL; - if (argc == 1) { - srcpath = get_absolute_path(argv[0]); - dstpath = strdup(path_get_basename(argv[0])); - } else { - srcpath = get_absolute_path(argv[0]); - dstpath = strdup(argv[1]); + if (file_exists(dstpath) && !force_overwrite) { + printf("Error: Failed to overwrite existing file without '-f' option: %s\n", dstpath); + return 0; + } + FILE *f = fopen(dstpath, "wb"); + if (!f) { + printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); + return 0; + } + struct timeval t1; + struct timeval t2; + struct timeval tdiff; + size_t bufsize = 0x100000; + char *buf = malloc(bufsize); + size_t total = 0; + int progress = 0; + int lastprog = 0; + if (file_size > 0x400000) { + progress = 1; + gettimeofday(&t1, NULL); + } + uint8_t succeed = 1; + while (err == AFC_E_SUCCESS) { + uint32_t bytes_read = 0; + size_t chunk = 0; + err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); + if (bytes_read == 0) { + break; + } + while (chunk < bytes_read) { + size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f); + if (wr == 0) { + if (progress) { + printf("\n"); + } + printf("Error: Failed to write to local file\n"); + succeed = 0; + break; + } + chunk += wr; + } + total += chunk; + if (progress) { + int prog = (int) ((double) total / (double) file_size * 100.0f); + if (prog > lastprog) { + gettimeofday(&t2, NULL); + timeval_subtract(&tdiff, &t2, &t1); + double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; + printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); + fflush(stdout); + lastprog = prog; + } + } } + if (progress) { + printf("\n"); + } + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + succeed = 0; + } + free(buf); + fclose(f); + afc_file_close(afc, fh); + return succeed; +} +static int __mkdir(const char* path) +{ +#ifdef WIN32 + return mkdir(path); +#else + return mkdir(path, 0755); +#endif +} + +static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get) +{ char **info = NULL; uint64_t file_size = 0; - afc_get_file_info(afc, srcpath, &info); + afc_error_t err = afc_get_file_info(afc, srcpath, &info); + if (err == AFC_E_OBJECT_NOT_FOUND) { + printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; + } + uint8_t is_dir = 0; if (info) { char **p = info; while (p && *p) { if (!strcmp(*p, "st_size")) { p++; - file_size = (uint64_t)strtoull(*p, NULL, 10); + file_size = (uint64_t) strtoull(*p, NULL, 10); + } + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); + } + p++; + } + afc_dictionary_free(info); + } + uint8_t succeed = 1; + if (is_dir) { + if (!recursive_get) { + printf("Error: Failed to get a directory without '-r' option: %s\n", srcpath); + return 0; + } + char **entries = NULL; + err = afc_read_directory(afc, srcpath, &entries); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err); + return 0; + } + char **p = entries; + size_t srcpath_len = strlen(srcpath); + uint8_t srcpath_is_root = strcmp(srcpath, "/") == 0; + // if directory exists, check force_overwrite flag + if (is_directory(dstpath)) { + if (!force_overwrite) { + printf("Error: Failed to write into existing directory without '-f': %s\n", dstpath); + return 0; + } + } else if (__mkdir(dstpath) != 0) { + printf("Error: Failed to create directory '%s': %s\n", dstpath, strerror(errno)); + afc_dictionary_free(entries); + return 0; + } + while (p && *p) { + if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) { + p++; + continue; + } + size_t len = srcpath_is_root ? strlen(*p) + 1 : srcpath_len + 1 + strlen(*p) + 1; + char *testpath = (char *) malloc(len); + if (srcpath_is_root) { + snprintf(testpath, len, "/%s", *p); + } else { + snprintf(testpath, len, "%s/%s", srcpath, *p); + } + uint8_t dst_is_root = strcmp(srcpath, "/") == 0; + size_t dst_len = dst_is_root ? strlen(*p) + 1 : strlen(dstpath) + 1 + strlen(*p) + 1; + char *newdst = (char *) malloc(dst_len); + if (dst_is_root) { + snprintf(newdst, dst_len, "/%s", *p); + } else { + snprintf(newdst, dst_len, "%s/%s", dstpath, *p); + } + if (!get_file(afc, testpath, newdst, force_overwrite, recursive_get)) { + succeed = 0; break; } + free(testpath); + free(newdst); p++; } + afc_dictionary_free(entries); + } else { + succeed = get_single_file(afc, srcpath, dstpath, file_size, force_overwrite); } - uint64_t fh = 0; - afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); - if (err != AFC_E_SUCCESS) { - free(srcpath); - free(dstpath); - printf("Error: Failed to open file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); + return succeed; +} + +static void handle_get(afc_client_t afc, int argc, char **argv) +{ + if (argc < 1) { + printf("Error: Invalid number of arguments\n"); return; } - FILE *f = fopen(dstpath, "wb"); - if (!f && errno == EISDIR) { - const char* basen = path_get_basename(argv[0]); - size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; - char* newdst = (char*)malloc(len); - snprintf(newdst, len, "%s/%s", dstpath, basen); - f = fopen(newdst, "wb"); + uint8_t force_overwrite = 0, recursive_get = 0; + char *srcpath = NULL; + char *dstpath = NULL; + int i = 0; + for ( ; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (!strcmp(argv[i], "-r")) { + recursive_get = 1; + } else if (!strcmp(argv[i], "-f")) { + force_overwrite = 1; + } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) { + recursive_get = 1; + force_overwrite = 1; + } else { + break; + } + } + if (argc - i == 1) { + char *tmp = strdup(argv[i]); + size_t src_len = strlen(tmp); + if (src_len > 1 && tmp[src_len - 1] == '/') { + tmp[src_len - 1] = '\0'; + } + srcpath = get_absolute_path(tmp); + dstpath = strdup(path_get_basename(tmp)); + free(tmp); + } else if (argc - i == 2) { + char *tmp = strdup(argv[i]); + size_t src_len = strlen(tmp); + if (src_len > 1 && tmp[src_len - 1] == '/') { + tmp[src_len - 1] = '\0'; + } + srcpath = get_absolute_path(tmp); + dstpath = strdup(argv[i + 1]); + size_t dst_len = strlen(dstpath); + if (dst_len > 1 && dstpath[dst_len - 1] == '/') { + dstpath[dst_len - 1] = '\0'; + } + free(tmp); + } else { + printf("Error: Invalid number of arguments\n"); + return; + } + + // target is a directory, put file under this target + if (is_directory(dstpath)) { + const char *basen = path_get_basename(argv[0]); + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? (strlen(basen) + 1) : (strlen(dstpath) + 1 + strlen(basen) + 1); + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", basen); + } else { + snprintf(newdst, len, "%s/%s", dstpath, basen); + } + get_file(afc, srcpath, newdst, force_overwrite, recursive_get); + free(srcpath); free(newdst); + free(dstpath); + } else { + // target is not a dir or does not exist, just try to create or rewrite it + get_file(afc, srcpath, dstpath, force_overwrite, recursive_get); + free(srcpath); + free(dstpath); } - if (f) { - struct timeval t1; - struct timeval t2; - struct timeval tdiff; - size_t bufsize = 0x100000; - char* buf = malloc(bufsize); - size_t total = 0; - int progress = 0; - int lastprog = 0; - if (file_size > 0x400000) { - progress = 1; - gettimeofday(&t1, NULL); - } - while (err == AFC_E_SUCCESS) { - uint32_t bytes_read = 0; - size_t chunk = 0; - err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); - if (bytes_read == 0) { - break; - } - while (chunk < bytes_read) { - size_t wr = fwrite(buf+chunk, 1, bytes_read-chunk, f); - if (wr == 0) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to write to local file\n"); - break; +} + +static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite) +{ + char **info = NULL; + afc_error_t ret = afc_get_file_info(afc, dstpath, &info); + // file exists, only overwrite with '-f' option was set + if (ret == AFC_E_SUCCESS && info) { + afc_dictionary_free(info); + if (!force_overwrite) { + printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath); + return 0; + } + } + FILE *f = fopen(srcpath, "rb"); + if (!f) { + printf("Error: Failed to open local file '%s': %s\n", srcpath, strerror(errno)); + return 0; + } + struct timeval t1; + struct timeval t2; + struct timeval tdiff; + struct stat fst; + int progress = 0; + size_t bufsize = 0x100000; + char *buf = malloc(bufsize); + + fstat(fileno(f), &fst); + if (fst.st_size >= 0x400000) { + progress = 1; + gettimeofday(&t1, NULL); + } + size_t total = 0; + int lastprog = 0; + uint64_t fh = 0; + afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); + uint8_t succeed = 1; + while (err == AFC_E_SUCCESS) { + uint32_t bytes_read = fread(buf, 1, bufsize, f); + if (bytes_read == 0) { + if (!feof(f)) { + if (progress) { + printf("\n"); } - chunk += wr; + printf("Error: Failed to read from local file\n"); + succeed = 0; } - total += chunk; - if (progress) { - int prog = (int)((double)total / (double)file_size * 100.0f); - if (prog > lastprog) { - gettimeofday(&t2, NULL); - timeval_subtract(&tdiff, &t2, &t1); - double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; - printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); - fflush(stdout); - lastprog = prog; + break; + } + uint32_t chunk = 0; + while (chunk < bytes_read) { + uint32_t bytes_written = 0; + err = afc_file_write(afc, fh, buf + chunk, bytes_read - chunk, &bytes_written); + if (err != AFC_E_SUCCESS) { + if (progress) { + printf("\n"); } + printf("Error: Failed to write to device file\n"); + succeed = 0; + break; } + chunk += bytes_written; } + total += chunk; if (progress) { - printf("\n"); + int prog = (int) ((double) total / (double) fst.st_size * 100.0f); + if (prog > lastprog) { + gettimeofday(&t2, NULL); + timeval_subtract(&tdiff, &t2, &t1); + double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; + printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); + fflush(stdout); + lastprog = prog; + } } - if (err != AFC_E_SUCCESS) { - printf("Error: Failed to read from file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); + } + free(buf); + afc_file_close(afc, fh); + fclose(f); + return succeed; +} + +static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_put) +{ + if (is_directory(srcpath)) { + if (!recursive_put) { + printf("Error: Failed to put directory without '-r' option: %s\n", srcpath); + return 0; + } + char **info = NULL; + afc_error_t err = afc_get_file_info(afc, dstpath, &info); + //create if target directory does not exist + afc_dictionary_free(info); + if (err == AFC_E_OBJECT_NOT_FOUND) { + err = afc_make_directory(afc, dstpath); + if (err != AFC_E_SUCCESS) { + printf("Error: Failed to create directory '%s': %s (%d)\n", dstpath, afc_strerror(err), err); + return 0; + } + } else if (!force_overwrite) { + printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath); + return 0; + } + afc_get_file_info(afc, dstpath, &info); + uint8_t is_dir = 0; + if (info) { + char **p = info; + while (p && *p) { + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); + break; + } + p++; + } + afc_dictionary_free(info); + } + if (!is_dir) { + printf("Error: Failed to create or access directory: '%s'\n", dstpath); + return 0; + } + + // walk dir recursively to put files + DIR *cur_dir = opendir(srcpath); + if (cur_dir) { + struct dirent *ep; + while ((ep = readdir(cur_dir))) { + if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0)) { + continue; + } + char *fpath = string_build_path(srcpath, ep->d_name, NULL); + if (fpath) { + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? strlen(ep->d_name) + 1 : strlen(dstpath) + 1 + strlen(ep->d_name) + 1; + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", ep->d_name); + } else { + snprintf(newdst, len, "%s/%s", dstpath, ep->d_name); + } + if (!put_file(afc, fpath, newdst, force_overwrite, recursive_put)) { + free(newdst); + free(fpath); + return 0; + } + free(newdst); + free(fpath); + } + } + closedir(cur_dir); + } else { + printf("Error: Failed to visit directory: '%s': %s\n", srcpath, strerror(errno)); + return 0; } - free(buf); - fclose(f); } else { - printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); + return put_single_file(afc, srcpath, dstpath, force_overwrite); } - afc_file_close(afc, fh); - free(srcpath); + return 1; } -static void handle_put(afc_client_t afc, int argc, char** argv) +static void handle_put(afc_client_t afc, int argc, char **argv) { - if (argc < 1 || argc > 2) { + if (argc < 1) { printf("Error: Invalid number of arguments\n"); return; } - + int i = 0; + uint8_t force_overwrite = 0, recursive_put = 0; + for ( ; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } else if (!strcmp(argv[i], "-r")) { + recursive_put = 1; + } else if (!strcmp(argv[i], "-f")) { + force_overwrite = 1; + } else if (!strcmp(argv[i], "-rf") || !strcmp(argv[i], "-fr")) { + recursive_put = 1; + force_overwrite = 1; + } else { + break; + } + } + if (i >= argc) { + printf("Error: Invalid number of arguments\n"); + return; + } + char *srcpath = strdup(argv[i]); + size_t src_len = strlen(srcpath); + if (src_len > 1 && srcpath[src_len - 1] == '/') { + srcpath[src_len - 1] = '\0'; + } char *dstpath = NULL; - if (argc == 1) { - dstpath = get_absolute_path(path_get_basename(argv[0])); + if (argc - i == 1) { + dstpath = get_absolute_path(path_get_basename(srcpath)); + } else if (argc - i == 2) { + char *tmp = strdup(argv[i + 1]); + size_t dst_len = strlen(tmp); + if (dst_len > 1 && tmp[dst_len - 1] == '/') { + tmp[dst_len - 1] = '\0'; + } + dstpath = get_absolute_path(tmp); + free(tmp); } else { - dstpath = get_absolute_path(argv[1]); + printf("Error: Invalid number of arguments\n"); + return; } - - uint64_t fh = 0; - FILE *f = fopen(argv[0], "rb"); - if (f) { - afc_error_t err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); - if (err == AFC_E_OBJECT_IS_DIR) { - const char* basen = path_get_basename(argv[0]); - size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; - char* newdst = (char*)malloc(len); - snprintf(newdst, len, "%s/%s", dstpath, basen); - free(dstpath); - dstpath = get_absolute_path(newdst); - free(newdst); - err = afc_file_open(afc, dstpath, AFC_FOPEN_RW, &fh); - } - if (err != AFC_E_SUCCESS) { - printf("Error: Failed to open file '%s' on device: %s (%d)\n", argv[1], afc_strerror(err), err); - } else { - struct timeval t1; - struct timeval t2; - struct timeval tdiff; - struct stat fst; - int progress = 0; - size_t bufsize = 0x100000; - char* buf = malloc(bufsize); - - fstat(fileno(f), &fst); - if (fst.st_size >= 0x400000) { - progress = 1; - gettimeofday(&t1, NULL); - } - size_t total = 0; - int lastprog = 0; - while (err == AFC_E_SUCCESS) { - uint32_t bytes_read = fread(buf, 1, bufsize, f); - if (bytes_read == 0) { - if (!feof(f)) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to read from local file\n"); - } + char **info = NULL; + afc_error_t err = afc_get_file_info(afc, dstpath, &info); + // target does not exist, put directly + if (err == AFC_E_OBJECT_NOT_FOUND) { + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + free(srcpath); + free(dstpath); + } else { + uint8_t is_dir = 0; + if (info) { + char **p = info; + while (p && *p) { + if (!strcmp(*p, "st_ifmt")) { + p++; + is_dir = !strcmp(*p, "S_IFDIR"); break; } - uint32_t chunk = 0; - while (chunk < bytes_read) { - uint32_t bytes_written = 0; - err = afc_file_write(afc, fh, buf+chunk, bytes_read-chunk, &bytes_written); - if (err != AFC_E_SUCCESS) { - if (progress) { - printf("\n"); - } - printf("Error: Failed to write to device file\n"); - break; - } - chunk += bytes_written; - } - total += chunk; - if (progress) { - int prog = (int)((double)total / (double)fst.st_size * 100.0f); - if (prog > lastprog) { - gettimeofday(&t2, NULL); - timeval_subtract(&tdiff, &t2, &t1); - double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; - printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); - fflush(stdout); - lastprog = prog; - } - } + p++; } - printf("\n"); - free(buf); - afc_file_close(afc, fh); + afc_dictionary_free(info); } - fclose(f); - } else { - printf("Error: Failed to open local file '%s': %s\n", argv[0], strerror(errno)); + // target is a directory, try to put under this directory + if (is_dir) { + const char *basen = path_get_basename(srcpath); + uint8_t dst_is_root = strcmp(dstpath, "/") == 0; + size_t len = dst_is_root ? strlen(basen) + 1 : strlen(dstpath) + 1 + strlen(basen) + 1; + char *newdst = (char *) malloc(len); + if (dst_is_root) { + snprintf(newdst, len, "/%s", basen); + } else { + snprintf(newdst, len, "%s/%s", dstpath, basen); + } + free(dstpath); + dstpath = get_absolute_path(newdst); + free(newdst); + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + } else { + //target is common file, rewrite it + put_file(afc, srcpath, dstpath, force_overwrite, recursive_put); + } + free(srcpath); + free(dstpath); } - free(dstpath); } static void handle_pwd(afc_client_t afc, int argc, char** argv) @@ -975,8 +1293,8 @@ static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline) } else if (*pos == '"' || *pos == '\'') { if (!qpos) { qpos = pos; - } else { - qpos = NULL; + } else { + qpos = NULL; } pos++; } else if (*pos == '\0' || (!qpos && isspace(*pos))) { @@ -1043,7 +1361,7 @@ static int process_args(afc_client_t afc, int argc, char** argv) handle_file_info(afc, argc-1, argv+1); } else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) { - handle_list(afc, argc-1, argv+1); + handle_list(afc, argc-1, argv+1); } else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) { handle_rename(afc, argc-1, argv+1); @@ -1113,7 +1431,7 @@ static void start_cmdline(afc_client_t afc) break; } } - } + } } static void device_event_cb(const idevice_event_t* event, void* userdata) diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c index 5694c12..c0537b8 100644 --- a/tools/idevicebackup.c +++ b/tools/idevicebackup.c @@ -32,24 +32,6 @@ #include <stdlib.h> #include <signal.h> #include <getopt.h> -#if defined(HAVE_OPENSSL) -#include <openssl/sha.h> -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -#include <openssl/evp.h> -#endif -#elif defined(HAVE_GNUTLS) -#include <gcrypt.h> -#elif defined(HAVE_MBEDTLS) -#include <mbedtls/sha1.h> -#if MBEDTLS_VERSION_NUMBER < 0x03000000 -#define mbedtls_sha1 mbedtls_sha1_ret -#define mbedtls_sha1_starts mbedtls_sha1_starts_ret -#define mbedtls_sha1_update mbedtls_sha1_update_ret -#define mbedtls_sha1_finish mbedtls_sha1_finish_ret -#endif -#else -#error No supported crypto library enabled -#endif #include <unistd.h> #include <ctype.h> #include <time.h> @@ -59,6 +41,7 @@ #include <libimobiledevice/mobilebackup.h> #include <libimobiledevice/notification_proxy.h> #include <libimobiledevice/afc.h> +#include <libimobiledevice-glue/sha.h> #include <libimobiledevice-glue/utils.h> #include <plist/plist.h> @@ -91,17 +74,6 @@ enum device_link_file_status_t { DEVICE_LINK_FILE_STATUS_LAST_HUNK }; -static void sha1_of_data(const char *input, uint32_t size, unsigned char *hash_out) -{ -#if defined(HAVE_OPENSSL) - SHA1((const unsigned char*)input, size, hash_out); -#elif defined(HAVE_GNUTLS) - gcry_md_hash_buffer(GCRY_MD_SHA1, hash_out, input, size); -#elif defined(HAVE_MBEDTLS) - mbedtls_sha1((unsigned char*)input, size, hash_out); -#endif -} - static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, int hash_len) { int i; @@ -113,104 +85,49 @@ static int compare_hash(const unsigned char *hash1, const unsigned char *hash2, return 1; } -static void _sha1_update(void* context, const char* data, size_t len) -{ -#if defined(HAVE_OPENSSL) -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - EVP_DigestUpdate(context, data, len); -#else - SHA1_Update(context, data, len); -#endif -#elif defined(HAVE_GNUTLS) - gcry_md_write(context, data, len); -#elif defined(HAVE_MBEDTLS) - mbedtls_sha1_update(context, (const unsigned char*)data, len); -#endif -} - static void compute_datahash(const char *path, const char *destpath, uint8_t greylist, const char *domain, const char *appid, const char *version, unsigned char *hash_out) { -#if defined(HAVE_OPENSSL) -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - EVP_MD_CTX* sha1 = EVP_MD_CTX_new(); - EVP_DigestInit(sha1, EVP_sha1()); - void* psha1 = sha1; -#else - SHA_CTX sha1; - SHA1_Init(&sha1); - void* psha1 = &sha1; -#endif -#elif defined(HAVE_GNUTLS) - gcry_md_hd_t hd = NULL; - gcry_md_open(&hd, GCRY_MD_SHA1, 0); - if (!hd) { - printf("ERROR: Could not initialize libgcrypt/SHA1\n"); - return; - } - gcry_md_reset(hd); - void* psha1 = hd; -#elif defined(HAVE_MBEDTLS) - mbedtls_sha1_context sha1; - mbedtls_sha1_init(&sha1); - mbedtls_sha1_starts(&sha1); - void* psha1 = &sha1; -#endif + sha1_context sha1; + sha1_init(&sha1); FILE *f = fopen(path, "rb"); if (f) { unsigned char buf[16384]; size_t len; while ((len = fread(buf, 1, 16384, f)) > 0) { - _sha1_update(psha1, (const char*)buf, len); + sha1_update(&sha1, buf, len); } fclose(f); - _sha1_update(psha1, destpath, strlen(destpath)); - _sha1_update(psha1, ";", 1); + sha1_update(&sha1, destpath, strlen(destpath)); + sha1_update(&sha1, ";", 1); if (greylist == 1) { - _sha1_update(psha1, "true", 4); + sha1_update(&sha1, "true", 4); } else { - _sha1_update(psha1, "false", 5); + sha1_update(&sha1, "false", 5); } - _sha1_update(psha1, ";", 1); + sha1_update(&sha1, ";", 1); if (domain) { - _sha1_update(psha1, domain, strlen(domain)); + sha1_update(&sha1, domain, strlen(domain)); } else { - _sha1_update(psha1, "(null)", 6); + sha1_update(&sha1, "(null)", 6); } - _sha1_update(psha1, ";", 1); + sha1_update(&sha1, ";", 1); if (appid) { - _sha1_update(psha1, appid, strlen(appid)); + sha1_update(&sha1, appid, strlen(appid)); } else { - _sha1_update(psha1, "(null)", 6); + sha1_update(&sha1, "(null)", 6); } - _sha1_update(psha1, ";", 1); + sha1_update(&sha1, ";", 1); if (version) { - _sha1_update(psha1, version, strlen(version)); + sha1_update(&sha1, version, strlen(version)); } else { - _sha1_update(psha1, "(null)", 6); + sha1_update(&sha1, "(null)", 6); } -#if defined(HAVE_OPENSSL) -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - EVP_DigestFinal(sha1, hash_out, NULL); - EVP_MD_CTX_destroy(sha1); -#else - SHA1_Final(hash_out, &sha1); -#endif -#elif defined(HAVE_GNUTLS) - unsigned char *newhash = gcry_md_read(hd, GCRY_MD_SHA1); - memcpy(hash_out, newhash, 20); -#elif defined(HAVE_MBEDTLS) - mbedtls_sha1_finish(&sha1, hash_out); -#endif + sha1_final(&sha1, hash_out); } -#if defined(HAVE_GNUTLS) - gcry_md_close(hd); -#elif defined(HAVE_MBEDTLS) - mbedtls_sha1_free(&sha1); -#endif } static void print_hash(const unsigned char *hash, int len) @@ -547,7 +464,7 @@ static int mobilebackup_check_file_integrity(const char *backup_directory, const unsigned char fnhash[20]; char fnamehash[41]; char *p = fnamehash; - sha1_of_data(fnstr, strlen(fnstr), fnhash); + sha1((const unsigned char*)fnstr, strlen(fnstr), fnhash); free(fnstr); int i; for ( i = 0; i < 20; i++, p += 2 ) { @@ -1285,14 +1202,14 @@ files_out: } printf("Verifying backup integrity, please wait.\n"); - char *bin = NULL; + unsigned char *bin = NULL; uint64_t binsize = 0; node = plist_dict_get_item(manifest_plist, "Data"); if (!node || (plist_get_node_type(node) != PLIST_DATA)) { printf("Could not read Data key from Manifest.plist!\n"); break; } - plist_get_data_val(node, &bin, &binsize); + plist_get_data_val(node, (char**)&bin, &binsize); plist_t backup_data = NULL; if (bin) { char *auth_ver = NULL; @@ -1309,7 +1226,7 @@ files_out: if (auth_sig && (auth_sig_len == 20)) { /* calculate the sha1, then compare */ unsigned char data_sha1[20]; - sha1_of_data(bin, binsize, data_sha1); + sha1(bin, binsize, data_sha1); if (compare_hash(auth_sig, data_sha1, 20)) { printf("AuthSignature is valid\n"); } else { @@ -1322,7 +1239,7 @@ files_out: } else if (auth_ver) { printf("Unknown AuthVersion '%s', cannot verify AuthSignature\n", auth_ver); } - plist_from_bin(bin, (uint32_t)binsize, &backup_data); + plist_from_bin((char*)bin, (uint32_t)binsize, &backup_data); free(bin); } if (!backup_data) { diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c index f551b6c..511583e 100644 --- a/tools/ideviceimagemounter.c +++ b/tools/ideviceimagemounter.c @@ -45,8 +45,11 @@ #include <libimobiledevice/afc.h> #include <libimobiledevice/notification_proxy.h> #include <libimobiledevice/mobile_image_mounter.h> +#include <libimobiledevice-glue/sha.h> +#include <libimobiledevice-glue/utils.h> #include <asprintf.h> #include <plist/plist.h> +#include <libtatsu/tss.h> static int list_mode = 0; static int use_network = 0; @@ -62,18 +65,38 @@ typedef enum { DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE } disk_image_upload_type_t; +enum cmd_mode { + CMD_NONE = 0, + CMD_MOUNT, + CMD_UNMOUNT, + CMD_LIST, + CMD_DEVMODESTATUS +}; + +int cmd = CMD_NONE; + static void print_usage(int argc, char **argv, int is_error) { char *name = strrchr(argv[0], '/'); - fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n", (name ? name + 1: argv[0])); + fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0])); fprintf(is_error ? stderr : stdout, "\n" - "Mounts the specified disk image on the device.\n" + "Mount, list, or unmount a disk image on the device.\n" + "\n" + "COMMANDS:\n" + " mount PATH Mount the developer disk image at PATH.\n" + " For iOS 17+, PATH is a directory containing a .dmg image,\n" + " a BuildManifest.plist, and a Firmware sub-directory;\n" + " for older versions PATH is a .dmg filename with a" + " .dmg.signature in the same directory, or with another\n" + " parameter pointing to a file elsewhere.\n" + " list List mounted disk images.\n" + " unmount PATH Unmount the image mounted at PATH.\n" + " devmodestatus Query the developer mode status (iOS 16+)\n" "\n" "OPTIONS:\n" " -u, --udid UDID target specific device by UDID\n" " -n, --network connect to network device\n" - " -l, --list List mount information\n" " -t, --imagetype TYPE Image type to use, default is 'Developer'\n" " -x, --xml Use XML output\n" " -d, --debug enable communication debugging\n" @@ -87,11 +110,11 @@ static void print_usage(int argc, char **argv, int is_error) static void parse_opts(int argc, char **argv) { + int debug_level = 0; static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "udid", required_argument, NULL, 'u' }, { "network", no_argument, NULL, 'n' }, - { "list", no_argument, NULL, 'l' }, { "imagetype", required_argument, NULL, 't' }, { "xml", no_argument, NULL, 'x' }, { "debug", no_argument, NULL, 'd' }, @@ -101,7 +124,7 @@ static void parse_opts(int argc, char **argv) int c; while (1) { - c = getopt_long(argc, argv, "hu:lt:xdnv", longopts, NULL); + c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL); if (c == -1) { break; } @@ -121,9 +144,6 @@ static void parse_opts(int argc, char **argv) case 'n': use_network = 1; break; - case 'l': - list_mode = 1; - break; case 't': imagetype = optarg; break; @@ -131,7 +151,7 @@ static void parse_opts(int argc, char **argv) xml_mode = 1; break; case 'd': - idevice_set_debug_level(1); + debug_level++; break; case 'v': printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); @@ -141,6 +161,8 @@ static void parse_opts(int argc, char **argv) exit(2); } } + idevice_set_debug_level(debug_level); + tss_set_debug_level(debug_level); } static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata) @@ -169,29 +191,75 @@ int main(int argc, char **argv) argc -= optind; argv += optind; - if (!list_mode) { - if (argc < 1) { - printf("ERROR: No IMAGE_FILE has been given!\n"); - return -1; - } - image_path = strdup(argv[0]); - if (argc >= 2) { - image_sig_path = strdup(argv[1]); + if (argc == 0) { + fprintf(stderr, "ERROR: Missing command.\n\n"); + print_usage(argc+optind, argv-optind, 1); + return 2; + } + + char* cmdstr = argv[0]; + + int optind2 = 0; + if (!strcmp(cmdstr, "mount")) { + cmd = CMD_MOUNT; + optind2++; + } else if (!strcmp(cmdstr, "list")) { + cmd = CMD_LIST; + optind2++; + } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) { + cmd = CMD_UNMOUNT; + optind2++; + } else if (!strcmp(cmdstr, "devmodestatus")) { + cmd = CMD_DEVMODESTATUS; + optind2++; + } else { + // assume mount command, unless -l / --list was specified + if (list_mode) { + cmd = CMD_LIST; } else { - if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { - printf("Out of memory?!\n"); - return -1; - } + cmd = CMD_MOUNT; } } + argc -= optind2; + argv += optind2; + optind += optind2; + + switch (cmd) { + case CMD_MOUNT: + if (argc < 1) { + fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n"); + print_usage(argc+optind, argv-optind, 1); + return 2; + } + image_path = strdup(argv[0]); + if (argc >= 2) { + image_sig_path = strdup(argv[1]); + } else { + if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { + printf("Out of memory?!\n"); + return 1; + } + } + break; + case CMD_UNMOUNT: + if (argc != 1) { + fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc); + print_usage(argc+optind, argv-optind, 1); + return 2; + } + break; + default: + break; + } + if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) { if (udid) { printf("No device found with udid %s.\n", udid); } else { printf("No device found.\n"); } - return -1; + return 1; } if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) { @@ -215,7 +283,7 @@ int main(int argc, char **argv) } } - if (product_version_major == 16) { + if (product_version_major >= 16) { uint8_t dev_mode_status = 0; plist_t val = NULL; ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val); @@ -246,7 +314,7 @@ int main(int argc, char **argv) service = NULL; } - if (!list_mode) { + if (cmd == CMD_MOUNT) { struct stat fst; if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) { if ((lockdownd_start_service(lckd, "com.apple.afc", &service) != @@ -268,7 +336,7 @@ int main(int argc, char **argv) goto leave; } image_size = fst.st_size; - if (stat(image_sig_path, &fst) != 0) { + if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) { fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); goto leave; } @@ -280,10 +348,14 @@ int main(int argc, char **argv) mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; plist_t result = NULL; - if (list_mode) { + if (cmd == CMD_LIST) { /* list mounts mode */ if (!imagetype) { - imagetype = "Developer"; + if (product_version_major < 17) { + imagetype = "Developer"; + } else { + imagetype = "Personalized"; + } } err = mobile_image_mounter_lookup_image(mim, imagetype, &result); if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { @@ -292,25 +364,217 @@ int main(int argc, char **argv) } else { printf("Error: lookup_image returned %d\n", err); } - } else { - char sig[8192]; + } else if (cmd == CMD_MOUNT) { + unsigned char *sig = NULL; size_t sig_length = 0; - FILE *f = fopen(image_sig_path, "rb"); - if (!f) { - fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); - goto leave; - } - sig_length = fread(sig, 1, sizeof(sig), f); - fclose(f); - if (sig_length == 0) { - fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path); - goto leave; - } + FILE *f; + struct stat fst; + plist_t mount_options = NULL; - f = fopen(image_path, "rb"); - if (!f) { - fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); - goto leave; + if (product_version_major < 17) { + f = fopen(image_sig_path, "rb"); + if (!f) { + fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); + goto leave; + } + if (fstat(fileno(f), &fst) != 0) { + fprintf(stderr, "Error: fstat: %s\n", strerror(errno)); + goto leave; + } + sig = malloc(fst.st_size); + sig_length = fread(sig, 1, fst.st_size, f); + fclose(f); + if (sig_length == 0) { + fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path); + goto leave; + } + + f = fopen(image_path, "rb"); + if (!f) { + fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); + goto leave; + } + } else { + if (stat(image_path, &fst) != 0) { + fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno)); + goto leave; + } + if (!S_ISDIR(fst.st_mode)) { + fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n"); + goto leave; + } + char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL); + plist_t build_manifest = NULL; + if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) { + free(build_manifest_path); + build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL); + if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) { + char* image_path_new = string_build_path(image_path, "Restore", NULL); + free(image_path); + image_path = image_path_new; + } + } + if (!build_manifest) { + fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n"); + goto leave; + } + + plist_t identifiers = NULL; + mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers); + if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr); + goto error_out; + } + + unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId"); + unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID"); + + plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities"); + plist_array_iter iter; + plist_array_new_iter(build_identities, &iter); + plist_t item = NULL; + plist_t build_identity = NULL; + do { + plist_array_next_item(build_identities, iter, &item); + if (!item) { + break; + } + unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID"); + unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID"); + if (bi_chip_id == chip_id && bi_board_id == board_id) { + build_identity = item; + break; + } + } while (item); + plist_mem_free(iter); + if (!build_identity) { + fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n"); + goto leave; + } + plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path"); + if (!p_tc_path) { + fprintf(stderr, "Error: Could not determine path for trust cache!\n"); + goto leave; + } + plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path"); + if (!p_dmg_path) { + fprintf(stderr, "Error: Could not determine path for disk image!\n"); + goto leave; + } + char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL); + unsigned char* trust_cache = NULL; + uint64_t trust_cache_size = 0; + if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) { + fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path); + goto leave; + } + mount_options = plist_new_dict(); + plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size)); + free(trust_cache); + char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL); + free(image_path); + image_path = dmg_path; + f = fopen(image_path, "rb"); + if (!f) { + fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); + goto leave; + } + + unsigned char buf[8192]; + unsigned char sha384_digest[48]; + sha384_context ctx; + sha384_init(&ctx); + fstat(fileno(f), &fst); + image_size = fst.st_size; + while (!feof(f)) { + ssize_t fr = fread(buf, 1, sizeof(buf), f); + if (fr <= 0) { + break; + } + sha384_update(&ctx, buf, fr); + } + rewind(f); + sha384_final(&ctx, sha384_digest); + unsigned char* manifest = NULL; + unsigned int manifest_size = 0; + /* check if the device already has a personalization manifest for this image */ + if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + printf("Using existing personalization manifest from device.\n"); + } else { + /* we need to re-connect in this case */ + mobile_image_mounter_free(mim); + mim = NULL; + if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + goto error_out; + } + printf("No personalization manifest, requesting from TSS...\n"); + unsigned char* nonce = NULL; + unsigned int nonce_size = 0; + + /* create new TSS request and fill parameters */ + plist_t request = tss_request_new(NULL); + plist_t params = plist_new_dict(); + tss_parameters_add_from_manifest(params, build_identity, 1); + + /* copy all `Ap,*` items from identifiers */ + plist_dict_iter di = NULL; + plist_dict_new_iter(identifiers, &di); + plist_t node = NULL; + do { + char* key = NULL; + plist_dict_next_item(identifiers, di, &key, &node); + if (node) { + if (!strncmp(key, "Ap,", 3)) { + plist_dict_set_item(request, key, plist_copy(node)); + } + } + free(key); + } while (node); + plist_mem_free(di); + + plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID"); + plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1)); + plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1)); + plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1)); + + /* query nonce from image mounter service */ + merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size); + if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size)); + } else { + fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr); + goto error_out; + } + mobile_image_mounter_free(mim); + mim = NULL; + + plist_dict_set_item(params, "ApSepNonce", plist_new_data("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20)); + plist_dict_set_item(params, "UID_MODE", plist_new_bool(0)); + tss_request_add_ap_tags(request, params, NULL); + tss_request_add_common_tags(request, params, NULL); + tss_request_add_ap_img4_tags(request, params); + plist_free(params); + + /* request IM4M from TSS */ + plist_t response = tss_request_send(request, NULL); + plist_free(request); + + plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket"); + if (!PLIST_IS_DATA(p_manifest)) { + fprintf(stderr, "Failed to get Img4Ticket\n"); + goto error_out; + } + + uint64_t m4m_len = 0; + plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len); + manifest_size = m4m_len; + plist_free(response); + printf("Done.\n"); + } + sig = manifest; + sig_length = manifest_size; + + imagetype = "Personalized"; } char *targetname = NULL; @@ -324,11 +588,16 @@ int main(int argc, char **argv) goto leave; } - if (!imagetype) { imagetype = "Developer"; } + if (!mim) { + if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + goto error_out; + } + } + switch(disk_image_upload_type) { case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE: printf("Uploading %s\n", image_path); @@ -403,7 +672,7 @@ int main(int argc, char **argv) printf("done.\n"); printf("Mounting...\n"); - err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result); + err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result); if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { if (result) { plist_t node = plist_dict_get_item(result, "Status"); @@ -435,7 +704,10 @@ int main(int argc, char **argv) printf("unexpected result:\n"); plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); } - + node = plist_dict_get_item(result, "DetailedError"); + if (node) { + printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL)); + } } else { plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); } @@ -444,6 +716,37 @@ int main(int argc, char **argv) printf("Error: mount_image returned %d\n", err); } + } else if (cmd == CMD_UNMOUNT) { + err = mobile_image_mounter_unmount_image(mim, argv[0]); + switch (err) { + case MOBILE_IMAGE_MOUNTER_E_SUCCESS: + printf("Success\n"); + res = 0; + break; + case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED: + printf("Error: '%s' is not mounted\n", argv[0]); + res = 1; + break; + case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED: + printf("Error: 'unmount' is not supported on this device\n"); + res = 1; + break; + case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED: + printf("Error: device is locked\n"); + res = 1; + break; + default: + printf("Error: unmount returned %d\n", err); + break; + } + } else if (cmd == CMD_DEVMODESTATUS) { + err = mobile_image_mounter_query_developer_mode_status(mim, &result); + if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { + res = 0; + plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); + } else { + printf("Error: query_developer_mode_status returned %d\n", err); + } } if (result) { @@ -466,7 +769,7 @@ leave: idevice_free(device); if (image_path) - free(image_path); + free(image_path); if (image_sig_path) free(image_sig_path); |