From 6c6faeae37e449f928e4b338a8e97c978201df07 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Wed, 1 Sep 2021 15:58:11 +0200 Subject: Add new --ipsw-info command line switch to show information about build identities etc. --- docs/idevicerestore.1 | 3 + src/idevicerestore.c | 20 ++++- src/ipsw.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ipsw.h | 2 + 4 files changed, 247 insertions(+), 2 deletions(-) diff --git a/docs/idevicerestore.1 b/docs/idevicerestore.1 index 4a244b5..8e0a884 100644 --- a/docs/idevicerestore.1 +++ b/docs/idevicerestore.1 @@ -47,6 +47,9 @@ put device in pwned DFU mode and exit (limera1n devices only). do not perform any restore action. If combined with -l option the on demand IPSW download is performed before exiting. .TP +.B \-\-ipsw\-info +Print information about the IPSW at PATH and exit. +.TP .B \-C, \-\-cache\-path DIR use specified directory for caching extracted or other reused files. .TP diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 9805f65..d20de69 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -85,6 +85,7 @@ static struct option longopts[] = { { "ticket", required_argument, NULL, 'T' }, { "no-restore", no_argument, NULL, 'z' }, { "version", no_argument, NULL, 'v' }, + { "ipsw-info", no_argument, NULL, 'I' }, { NULL, 0, NULL, 0 } }; @@ -120,6 +121,7 @@ static void usage(int argc, char* argv[], int err) " -n, --no-action Do not perform any restore action. If combined with -l\n" \ " option the on-demand ipsw download is performed before\n" \ " exiting.\n" \ + " --ipsw-info Print information about the IPSW at PATH and exit.\n" \ " -h, --help Prints this usage information\n" \ " -C, --cache-path DIR Use specified directory for caching extracted or other\n" \ " reused files.\n" \ @@ -146,8 +148,8 @@ static void usage(int argc, char* argv[], int err) const uint8_t lpol_file[22] = { 0x30, 0x14, 0x16, 0x04, 0x49, 0x4d, 0x34, 0x50, - 0x16, 0x04, 0x6c, 0x70, 0x6f, 0x6c, 0x16, 0x03, - 0x31, 0x2e, 0x30, 0x04, 0x01, 0x00 + 0x16, 0x04, 0x6c, 0x70, 0x6f, 0x6c, 0x16, 0x03, + 0x31, 0x2e, 0x30, 0x04, 0x01, 0x00 }; const uint32_t lpol_file_length = 22; @@ -1568,6 +1570,7 @@ int main(int argc, char* argv[]) { int opt = 0; int optindex = 0; char* ipsw = NULL; + int ipsw_info = 0; int result = 0; struct idevicerestore_client_t* client = idevicerestore_client_new(); @@ -1704,12 +1707,25 @@ int main(int argc, char* argv[]) { break; } + case 'I': + ipsw_info = 1; + break; + default: usage(argc, argv, 1); return EXIT_FAILURE; } } + if (ipsw_info) { + if (argc-optind != 1) { + error("ERROR: --ipsw-info requires an IPSW path.\n"); + usage(argc, argv, 1); + return EXIT_FAILURE; + } + return (ipsw_print_info(*(argv + optind)) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + } + if (((argc-optind) == 1) || (client->flags & FLAG_PWN) || (client->flags & FLAG_LATEST)) { argc -= optind; argv += optind; diff --git a/src/ipsw.c b/src/ipsw.c index e7bd639..f3b7f57 100644 --- a/src/ipsw.c +++ b/src/ipsw.c @@ -42,6 +42,8 @@ #define SHA1_Final SHA1Final #endif +#include + #include "ipsw.h" #include "locking.h" #include "download.h" @@ -76,6 +78,228 @@ static char* build_path(const char* path, const char* file) return fullpath; } +int ipsw_print_info(const char* path) +{ + struct stat fst; + + if (stat(path, &fst) != 0) { + error("ERROR: '%s': %s\n", path, strerror(errno)); + return -1; + } + + char thepath[PATH_MAX]; + + if (S_ISDIR(fst.st_mode)) { + sprintf(thepath, "%s/BuildManifest.plist", path); + if (stat(thepath, &fst) != 0) { + error("ERROR: '%s': %s\n", thepath, strerror(errno)); + return -1; + } + } else { + sprintf(thepath, "%s", path); + } + + FILE* f = fopen(thepath, "r"); + if (!f) { + error("ERROR: Can't open '%s': %s\n", thepath, strerror(errno)); + return -1; + } + uint32_t magic; + if (fread(&magic, 1, 4, f) != 4) { + fclose(f); + fprintf(stderr, "Failed to read from '%s'\n", path); + return -1; + } + fclose(f); + + char* plist_buf = NULL; + uint32_t plist_len = 0; + + if (memcmp(&magic, "PK\x03\x04", 4) == 0) { + unsigned int rlen = 0; + if (ipsw_extract_to_memory(thepath, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); + return -1; + } + plist_len = (uint32_t)rlen; + } else { + size_t rlen = 0; + if (read_file(thepath, (void**)&plist_buf, &rlen) < 0) { + error("ERROR: Failed to read BuildManifest.plist!\n"); + return -1; + } + plist_len = (uint32_t)rlen; + } + + plist_t manifest = NULL; + plist_from_memory(plist_buf, plist_len, &manifest); + free(plist_buf); + + plist_t val; + + char* prod_ver = NULL; + char* build_ver = NULL; + + val = plist_dict_get_item(manifest, "ProductVersion"); + if (val) { + plist_get_string_val(val, &prod_ver); + } + + val = plist_dict_get_item(manifest, "ProductBuildVersion"); + if (val) { + plist_get_string_val(val, &build_ver); + } + + cprintf(COLOR_WHITE "Product Version: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET COLOR_WHITE " Build: " COLOR_BRIGHT_YELLOW "%s" COLOR_RESET "\n", prod_ver, build_ver); + free(prod_ver); + free(build_ver); + cprintf(COLOR_WHITE "Supported Product Types:" COLOR_RESET); + val = plist_dict_get_item(manifest, "SupportedProductTypes"); + if (val) { + plist_array_iter iter = NULL; + plist_array_new_iter(val, &iter); + if (iter) { + plist_t item = NULL; + do { + plist_array_next_item(val, iter, &item); + if (item) { + char* item_str = NULL; + plist_get_string_val(item, &item_str); + cprintf(" " COLOR_BRIGHT_CYAN "%s" COLOR_RESET, item_str); + free(item_str); + } + } while (item); + free(iter); + } + } + cprintf("\n"); + + cprintf(COLOR_WHITE "Build Identities:" COLOR_RESET "\n"); + + plist_t build_ids_grouped = plist_new_dict(); + + plist_t build_ids = plist_dict_get_item(manifest, "BuildIdentities"); + plist_array_iter build_id_iter = NULL; + plist_array_new_iter(build_ids, &build_id_iter); + if (build_id_iter) { + plist_t build_identity = NULL; + do { + plist_array_next_item(build_ids, build_id_iter, &build_identity); + if (!build_identity) { + break; + } + plist_t node; + char* variant_str = NULL; + + node = plist_access_path(build_identity, 2, "Info", "Variant"); + plist_get_string_val(node, &variant_str); + + plist_t entries = NULL; + plist_t group = plist_dict_get_item(build_ids_grouped, variant_str); + if (!group) { + group = plist_new_dict(); + node = plist_access_path(build_identity, 2, "Info", "RestoreBehavior"); + plist_dict_set_item(group, "RestoreBehavior", plist_copy(node)); + entries = plist_new_array(); + plist_dict_set_item(group, "Entries", entries); + plist_dict_set_item(build_ids_grouped, variant_str, group); + } else { + entries = plist_dict_get_item(group, "Entries"); + } + free(variant_str); + plist_array_append_item(entries, plist_copy(build_identity)); + } while (build_identity); + free(build_id_iter); + } + + plist_dict_iter build_ids_group_iter = NULL; + plist_dict_new_iter(build_ids_grouped, &build_ids_group_iter); + if (build_ids_group_iter) { + plist_t group = NULL; + int group_no = 0; + do { + group = NULL; + char* key = NULL; + plist_dict_next_item(build_ids_grouped, build_ids_group_iter, &key, &group); + if (!key) { + break; + } + plist_t node; + char* rbehavior = NULL; + + group_no++; + node = plist_dict_get_item(group, "RestoreBehavior"); + plist_get_string_val(node, &rbehavior); + cprintf(" " COLOR_WHITE "[%d] Variant: " COLOR_BRIGHT_CYAN "%s" COLOR_WHITE " Behavior: " COLOR_BRIGHT_CYAN "%s" COLOR_RESET "\n", group_no, key, rbehavior); + free(key); + free(rbehavior); + + build_ids = plist_dict_get_item(group, "Entries"); + if (!build_ids) { + continue; + } + build_id_iter = NULL; + plist_array_new_iter(build_ids, &build_id_iter); + if (!build_id_iter) { + continue; + } + plist_t build_id; + do { + build_id = NULL; + plist_array_next_item(build_ids, build_id_iter, &build_id); + if (!build_id) { + break; + } + uint64_t chip_id = 0; + uint64_t board_id = 0; + char* hwmodel = NULL; + + node = plist_dict_get_item(build_id, "ApChipID"); + if (PLIST_IS_STRING(node)) { + char* strval = NULL; + plist_get_string_val(node, &strval); + if (strval) { + chip_id = strtoull(strval, NULL, 0); + free(strval); + } + } else { + plist_get_uint_val(node, &chip_id); + } + + node = plist_dict_get_item(build_id, "ApBoardID"); + if (PLIST_IS_STRING(node)) { + char* strval = NULL; + plist_get_string_val(node, &strval); + if (strval) { + board_id = strtoull(strval, NULL, 0); + free(strval); + } + } else { + plist_get_uint_val(node, &board_id); + } + + node = plist_access_path(build_id, 2, "Info", "DeviceClass"); + plist_get_string_val(node, &hwmodel); + + irecv_device_t irecvdev = NULL; + if (irecv_devices_get_device_by_hardware_model(hwmodel, &irecvdev) == 0) { + cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%-8s" COLOR_RESET " " COLOR_MAGENTA "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel, irecvdev->display_name); + } else { + cprintf(" ChipID: " COLOR_GREEN "%04x" COLOR_RESET " BoardID: " COLOR_GREEN "%02x" COLOR_RESET " Model: " COLOR_YELLOW "%s" COLOR_RESET "\n", (int)chip_id, (int)board_id, hwmodel); + } + free(hwmodel); + } while (build_id); + free(build_id_iter); + } while (group); + free(build_ids_group_iter); + } + plist_free(build_ids_grouped); + + plist_free(manifest); + + return 0; +} + ipsw_archive* ipsw_open(const char* ipsw) { int err = 0; diff --git a/src/ipsw.h b/src/ipsw.h index a664f2b..5c75699 100644 --- a/src/ipsw.h +++ b/src/ipsw.h @@ -31,6 +31,8 @@ extern "C" { #include #include +int ipsw_print_info(const char* ipsw); + int ipsw_is_directory(const char* ipsw); int ipsw_file_exists(const char* ipsw, const char* infile); int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size); -- cgit v1.1-32-gdbae