summaryrefslogtreecommitdiffstats
path: root/tools/ideviceimagemounter.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ideviceimagemounter.c')
-rw-r--r--tools/ideviceimagemounter.c873
1 files changed, 562 insertions, 311 deletions
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index 3d299f8..511583e 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -1,26 +1,30 @@
1/** 1/*
2 * ideviceimagemounter -- Mount developer/debug disk images on the iPhone/iPod 2 * ideviceimagemounter.c
3 * Mount developer/debug disk images on the device
3 * 4 *
4 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2010 Nikias Bassen <nikias@gmx.li>
5 * 6 *
6 * Licensed under the GNU General Public License Version 2 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
7 * 11 *
8 * This program is free software; you can redistribute it and/or modify 12 * This library is distributed in the hope that it will be useful,
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * GNU General Public License for more profile. 15 * Lesser General Public License for more details.
17 * 16 *
18 * You should have received a copy of the GNU General Public License 17 * You should have received a copy of the GNU Lesser General Public
19 * along with this program; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * USA
22 */ 20 */
23 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "ideviceimagemounter"
27
24#include <stdlib.h> 28#include <stdlib.h>
25#define _GNU_SOURCE 1 29#define _GNU_SOURCE 1
26#define __USE_GNU 1 30#define __USE_GNU 1
@@ -28,297 +32,311 @@
28#include <string.h> 32#include <string.h>
29#include <getopt.h> 33#include <getopt.h>
30#include <errno.h> 34#include <errno.h>
31#include <glib.h>
32#include <libgen.h> 35#include <libgen.h>
36#include <time.h>
37#include <sys/time.h>
38#include <inttypes.h>
39#ifndef WIN32
40#include <signal.h>
41#endif
33 42
34#include <libimobiledevice/libimobiledevice.h> 43#include <libimobiledevice/libimobiledevice.h>
35#include <libimobiledevice/lockdown.h> 44#include <libimobiledevice/lockdown.h>
36#include <libimobiledevice/afc.h> 45#include <libimobiledevice/afc.h>
37#include <libimobiledevice/notification_proxy.h> 46#include <libimobiledevice/notification_proxy.h>
38#include <libimobiledevice/mobile_image_mounter.h> 47#include <libimobiledevice/mobile_image_mounter.h>
39 48#include <libimobiledevice-glue/sha.h>
40static int indent_level = 0; 49#include <libimobiledevice-glue/utils.h>
50#include <asprintf.h>
51#include <plist/plist.h>
52#include <libtatsu/tss.h>
41 53
42static int list_mode = 0; 54static int list_mode = 0;
55static int use_network = 0;
43static int xml_mode = 0; 56static int xml_mode = 0;
44static char *uuid = NULL; 57static const char *udid = NULL;
45static char *imagetype = NULL; 58static const char *imagetype = NULL;
46 59
47static const char PKG_PATH[] = "PublicStaging"; 60static const char PKG_PATH[] = "PublicStaging";
48static const char PATH_PREFIX[] = "/private/var/mobile/Media"; 61static const char PATH_PREFIX[] = "/private/var/mobile/Media";
49 62
50static void print_usage(int argc, char **argv) 63typedef enum {
64 DISK_IMAGE_UPLOAD_TYPE_AFC,
65 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
66} disk_image_upload_type_t;
67
68enum cmd_mode {
69 CMD_NONE = 0,
70 CMD_MOUNT,
71 CMD_UNMOUNT,
72 CMD_LIST,
73 CMD_DEVMODESTATUS
74};
75
76int cmd = CMD_NONE;
77
78static void print_usage(int argc, char **argv, int is_error)
51{ 79{
52 char *name = NULL; 80 char *name = strrchr(argv[0], '/');
53 81 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
54 name = strrchr(argv[0], '/'); 82 fprintf(is_error ? stderr : stdout,
55 printf("Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n\n", (name ? name + 1: argv[0])); 83 "\n"
56 printf("Mounts the specified disk image on the device.\n\n"); 84 "Mount, list, or unmount a disk image on the device.\n"
57 printf(" -u, --uuid UUID\ttarget specific device by its 40-digit device UUID\n"); 85 "\n"
58 printf(" -l, --list\t\tList mount information\n"); 86 "COMMANDS:\n"
59 printf(" -t, --imagetype\tImage type to use, default is 'Developer'\n"); 87 " mount PATH Mount the developer disk image at PATH.\n"
60 printf(" -x, --xml\t\tUse XML output\n"); 88 " For iOS 17+, PATH is a directory containing a .dmg image,\n"
61 printf(" -d, --debug\t\tenable communication debugging\n"); 89 " a BuildManifest.plist, and a Firmware sub-directory;\n"
62 printf(" -h, --help\t\tprints usage information\n"); 90 " for older versions PATH is a .dmg filename with a"
63 printf("\n"); 91 " .dmg.signature in the same directory, or with another\n"
92 " parameter pointing to a file elsewhere.\n"
93 " list List mounted disk images.\n"
94 " unmount PATH Unmount the image mounted at PATH.\n"
95 " devmodestatus Query the developer mode status (iOS 16+)\n"
96 "\n"
97 "OPTIONS:\n"
98 " -u, --udid UDID target specific device by UDID\n"
99 " -n, --network connect to network device\n"
100 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
101 " -x, --xml Use XML output\n"
102 " -d, --debug enable communication debugging\n"
103 " -h, --help prints usage information\n"
104 " -v, --version prints version information\n"
105 "\n"
106 "Homepage: <" PACKAGE_URL ">\n"
107 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
108 );
64} 109}
65 110
66static void parse_opts(int argc, char **argv) 111static void parse_opts(int argc, char **argv)
67{ 112{
113 int debug_level = 0;
68 static struct option longopts[] = { 114 static struct option longopts[] = {
69 {"help", 0, NULL, 'h'}, 115 { "help", no_argument, NULL, 'h' },
70 {"uuid", 0, NULL, 'u'}, 116 { "udid", required_argument, NULL, 'u' },
71 {"list", 0, NULL, 'l'}, 117 { "network", no_argument, NULL, 'n' },
72 {"imagetype", 0, NULL, 't'}, 118 { "imagetype", required_argument, NULL, 't' },
73 {"xml", 0, NULL, 'x'}, 119 { "xml", no_argument, NULL, 'x' },
74 {"debug", 0, NULL, 'd'}, 120 { "debug", no_argument, NULL, 'd' },
75 {NULL, 0, NULL, 0} 121 { "version", no_argument, NULL, 'v' },
122 { NULL, 0, NULL, 0 }
76 }; 123 };
77 int c; 124 int c;
78 125
79 while (1) { 126 while (1) {
80 c = getopt_long(argc, argv, "hu:lt:xd", longopts, 127 c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
81 (int *) 0);
82 if (c == -1) { 128 if (c == -1) {
83 break; 129 break;
84 } 130 }
85 131
86 switch (c) { 132 switch (c) {
87 case 'h': 133 case 'h':
88 print_usage(argc, argv); 134 print_usage(argc, argv, 0);
89 exit(0); 135 exit(0);
90 case 'u': 136 case 'u':
91 if (strlen(optarg) != 40) { 137 if (!*optarg) {
92 printf("%s: invalid UUID specified (length != 40)\n", 138 fprintf(stderr, "ERROR: UDID must not be empty!\n");
93 argv[0]); 139 print_usage(argc, argv, 1);
94 print_usage(argc, argv);
95 exit(2); 140 exit(2);
96 } 141 }
97 uuid = strdup(optarg); 142 udid = optarg;
98 break; 143 break;
99 case 'l': 144 case 'n':
100 list_mode = 1; 145 use_network = 1;
101 break; 146 break;
102 case 't': 147 case 't':
103 imagetype = strdup(optarg); 148 imagetype = optarg;
104 break; 149 break;
105 case 'x': 150 case 'x':
106 xml_mode = 1; 151 xml_mode = 1;
107 break; 152 break;
108 case 'd': 153 case 'd':
109 idevice_set_debug_level(1); 154 debug_level++;
110 break; 155 break;
156 case 'v':
157 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
158 exit(0);
111 default: 159 default:
112 print_usage(argc, argv); 160 print_usage(argc, argv, 1);
113 exit(2); 161 exit(2);
114 } 162 }
115 } 163 }
164 idevice_set_debug_level(debug_level);
165 tss_set_debug_level(debug_level);
116} 166}
117 167
118static void plist_node_to_string(plist_t node); 168static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
119
120static void plist_array_to_string(plist_t node)
121{ 169{
122 /* iterate over items */ 170 return fread(buf, 1, size, (FILE*)userdata);
123 int i, count;
124 plist_t subnode = NULL;
125
126 count = plist_array_get_size(node);
127
128 for (i = 0; i < count; i++) {
129 subnode = plist_array_get_item(node, i);
130 printf("%*s", indent_level, "");
131 printf("%d: ", i);
132 plist_node_to_string(subnode);
133 }
134}
135
136static void plist_dict_to_string(plist_t node)
137{
138 /* iterate over key/value pairs */
139 plist_dict_iter it = NULL;
140
141 char* key = NULL;
142 plist_t subnode = NULL;
143 plist_dict_new_iter(node, &it);
144 plist_dict_next_item(node, it, &key, &subnode);
145 while (subnode)
146 {
147 printf("%*s", indent_level, "");
148 printf("%s", key);
149 if (plist_get_node_type(subnode) == PLIST_ARRAY)
150 printf("[%d]: ", plist_array_get_size(subnode));
151 else
152 printf(": ");
153 free(key);
154 key = NULL;
155 plist_node_to_string(subnode);
156 plist_dict_next_item(node, it, &key, &subnode);
157 }
158 free(it);
159}
160
161static void plist_node_to_string(plist_t node)
162{
163 char *s = NULL;
164 char *data = NULL;
165 double d;
166 uint8_t b;
167 uint64_t u = 0;
168 GTimeVal tv = { 0, 0 };
169
170 plist_type t;
171
172 if (!node)
173 return;
174
175 t = plist_get_node_type(node);
176
177 switch (t) {
178 case PLIST_BOOLEAN:
179 plist_get_bool_val(node, &b);
180 printf("%s\n", (b ? "true" : "false"));
181 break;
182
183 case PLIST_UINT:
184 plist_get_uint_val(node, &u);
185 printf("%llu\n", (long long)u);
186 break;
187
188 case PLIST_REAL:
189 plist_get_real_val(node, &d);
190 printf("%f\n", d);
191 break;
192
193 case PLIST_STRING:
194 plist_get_string_val(node, &s);
195 printf("%s\n", s);
196 free(s);
197 break;
198
199 case PLIST_KEY:
200 plist_get_key_val(node, &s);
201 printf("%s: ", s);
202 free(s);
203 break;
204
205 case PLIST_DATA:
206 plist_get_data_val(node, &data, &u);
207 uint64_t i;
208 for (i = 0; i < u; i++) {
209 printf("%02x", (unsigned char)data[i]);
210 }
211 free(data);
212 printf("\n");
213 g_free(s);
214 break;
215
216 case PLIST_DATE:
217 plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec);
218 s = g_time_val_to_iso8601(&tv);
219 printf("%s\n", s);
220 free(s);
221 break;
222
223 case PLIST_ARRAY:
224 printf("\n");
225 indent_level++;
226 plist_array_to_string(node);
227 indent_level--;
228 break;
229
230 case PLIST_DICT:
231 printf("\n");
232 indent_level++;
233 plist_dict_to_string(node);
234 indent_level--;
235 break;
236
237 default:
238 break;
239 }
240}
241
242static void print_xml(plist_t node)
243{
244 char *xml = NULL;
245 uint32_t len = 0;
246 plist_to_xml(node, &xml, &len);
247 if (xml)
248 puts(xml);
249} 171}
250 172
251int main(int argc, char **argv) 173int main(int argc, char **argv)
252{ 174{
253 idevice_t device = NULL; 175 idevice_t device = NULL;
254 lockdownd_client_t lckd = NULL; 176 lockdownd_client_t lckd = NULL;
177 lockdownd_error_t ldret = LOCKDOWN_E_UNKNOWN_ERROR;
255 mobile_image_mounter_client_t mim = NULL; 178 mobile_image_mounter_client_t mim = NULL;
256 afc_client_t afc = NULL; 179 afc_client_t afc = NULL;
257 uint16_t port = 0; 180 lockdownd_service_descriptor_t service = NULL;
258 int res = -1; 181 int res = -1;
259 char *image_path = NULL; 182 char *image_path = NULL;
183 size_t image_size = 0;
260 char *image_sig_path = NULL; 184 char *image_sig_path = NULL;
261 185
186#ifndef WIN32
187 signal(SIGPIPE, SIG_IGN);
188#endif
262 parse_opts(argc, argv); 189 parse_opts(argc, argv);
263 190
264 argc -= optind; 191 argc -= optind;
265 argv += optind; 192 argv += optind;
266 193
267 if (!list_mode) { 194 if (argc == 0) {
268 if (argc < 1) { 195 fprintf(stderr, "ERROR: Missing command.\n\n");
269 printf("ERROR: No IMAGE_FILE has been given!\n"); 196 print_usage(argc+optind, argv-optind, 1);
270 return -1; 197 return 2;
271 } 198 }
272 image_path = strdup(argv[0]); 199
273 if (argc >= 2) { 200 char* cmdstr = argv[0];
274 image_sig_path = strdup(argv[1]); 201
202 int optind2 = 0;
203 if (!strcmp(cmdstr, "mount")) {
204 cmd = CMD_MOUNT;
205 optind2++;
206 } else if (!strcmp(cmdstr, "list")) {
207 cmd = CMD_LIST;
208 optind2++;
209 } else if (!strcmp(cmdstr, "umount") || !strcmp(cmdstr, "unmount")) {
210 cmd = CMD_UNMOUNT;
211 optind2++;
212 } else if (!strcmp(cmdstr, "devmodestatus")) {
213 cmd = CMD_DEVMODESTATUS;
214 optind2++;
215 } else {
216 // assume mount command, unless -l / --list was specified
217 if (list_mode) {
218 cmd = CMD_LIST;
275 } else { 219 } else {
276 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { 220 cmd = CMD_MOUNT;
277 printf("Out of memory?!\n");
278 return -1;
279 }
280 } 221 }
281 } 222 }
282 223
283 if (IDEVICE_E_SUCCESS != idevice_new(&device, uuid)) { 224 argc -= optind2;
284 printf("No device found, is it plugged in?\n"); 225 argv += optind2;
285 return -1; 226 optind += optind2;
227
228 switch (cmd) {
229 case CMD_MOUNT:
230 if (argc < 1) {
231 fprintf(stderr, "ERROR: Missing IMAGE_FILE for mount command\n");
232 print_usage(argc+optind, argv-optind, 1);
233 return 2;
234 }
235 image_path = strdup(argv[0]);
236 if (argc >= 2) {
237 image_sig_path = strdup(argv[1]);
238 } else {
239 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
240 printf("Out of memory?!\n");
241 return 1;
242 }
243 }
244 break;
245 case CMD_UNMOUNT:
246 if (argc != 1) {
247 fprintf(stderr, "ERROR: Missing mount path (argc = %d)\n", argc);
248 print_usage(argc+optind, argv-optind, 1);
249 return 2;
250 }
251 break;
252 default:
253 break;
254 }
255
256 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
257 if (udid) {
258 printf("No device found with udid %s.\n", udid);
259 } else {
260 printf("No device found.\n");
261 }
262 return 1;
286 } 263 }
287 264
288 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) { 265 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
289 printf("ERROR: could not connect to lockdown. Exiting.\n"); 266 printf("ERROR: Could not connect to lockdown, error code %d.\n", ldret);
290 goto leave; 267 goto leave;
291 } 268 }
292 269
293 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &port); 270 plist_t pver = NULL;
271 char *product_version = NULL;
272 lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
273 if (pver && plist_get_node_type(pver) == PLIST_STRING) {
274 plist_get_string_val(pver, &product_version);
275 }
276 disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
277 int product_version_major = 0;
278 int product_version_minor = 0;
279 if (product_version) {
280 if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
281 if (product_version_major >= 7)
282 disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
283 }
284 }
294 285
295 if (port == 0) { 286 if (product_version_major >= 16) {
287 uint8_t dev_mode_status = 0;
288 plist_t val = NULL;
289 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
290 if (ldret == LOCKDOWN_E_SUCCESS) {
291 plist_get_bool_val(val, &dev_mode_status);
292 plist_free(val);
293 }
294 if (!dev_mode_status) {
295 printf("ERROR: You have to enable Developer Mode on the given device in order to allowing mounting a developer disk image.\n");
296 goto leave;
297 }
298 }
299
300 lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);
301
302 if (!service || service->port == 0) {
296 printf("ERROR: Could not start mobile_image_mounter service!\n"); 303 printf("ERROR: Could not start mobile_image_mounter service!\n");
297 goto leave; 304 goto leave;
298 } 305 }
299 306
300 if (mobile_image_mounter_new(device, port, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 307 if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
301 printf("ERROR: Could not connect to mobile_image_mounter!\n"); 308 printf("ERROR: Could not connect to mobile_image_mounter!\n");
302 goto leave; 309 goto leave;
303 } 310 }
311
312 if (service) {
313 lockdownd_service_descriptor_free(service);
314 service = NULL;
315 }
304 316
305 if (!list_mode) { 317 if (cmd == CMD_MOUNT) {
306 struct stat fst; 318 struct stat fst;
307 port = 0; 319 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
308 if ((lockdownd_start_service(lckd, "com.apple.afc", &port) != 320 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
309 LOCKDOWN_E_SUCCESS) || !port) { 321 LOCKDOWN_E_SUCCESS) || !service || !service->port) {
310 fprintf(stderr, "Could not start com.apple.afc!\n"); 322 fprintf(stderr, "Could not start com.apple.afc!\n");
311 goto leave; 323 goto leave;
312 } 324 }
313 if (afc_client_new(device, port, &afc) != AFC_E_SUCCESS) { 325 if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
314 fprintf(stderr, "Could not connect to AFC!\n"); 326 fprintf(stderr, "Could not connect to AFC!\n");
315 goto leave; 327 goto leave;
328 }
329 if (service) {
330 lockdownd_service_descriptor_free(service);
331 service = NULL;
332 }
316 } 333 }
317 if (stat(image_path, &fst) != 0) { 334 if (stat(image_path, &fst) != 0) {
318 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno)); 335 fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
319 goto leave; 336 goto leave;
320 } 337 }
321 if (stat(image_sig_path, &fst) != 0) { 338 image_size = fst.st_size;
339 if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) {
322 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno)); 340 fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
323 goto leave; 341 goto leave;
324 } 342 }
@@ -327,49 +345,240 @@ int main(int argc, char **argv)
327 lockdownd_client_free(lckd); 345 lockdownd_client_free(lckd);
328 lckd = NULL; 346 lckd = NULL;
329 347
330 mobile_image_mounter_error_t err; 348 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
331 plist_t result = NULL; 349 plist_t result = NULL;
332 350
333 if (list_mode) { 351 if (cmd == CMD_LIST) {
334 /* list mounts mode */ 352 /* list mounts mode */
335 if (!imagetype) { 353 if (!imagetype) {
336 imagetype = strdup("Developer"); 354 if (product_version_major < 17) {
355 imagetype = "Developer";
356 } else {
357 imagetype = "Personalized";
358 }
337 } 359 }
338 err = mobile_image_mounter_lookup_image(mim, imagetype, &result); 360 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
339 free(imagetype);
340 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 361 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
341 res = 0; 362 res = 0;
342 if (xml_mode) { 363 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
343 print_xml(result);
344 } else {
345 plist_dict_to_string(result);
346 }
347 } else { 364 } else {
348 printf("Error: lookup_image returned %d\n", err); 365 printf("Error: lookup_image returned %d\n", err);
349 } 366 }
350 } else { 367 } else if (cmd == CMD_MOUNT) {
351 char sig[8192]; 368 unsigned char *sig = NULL;
352 size_t sig_length = 0; 369 size_t sig_length = 0;
353 FILE *f = fopen(image_sig_path, "r"); 370 FILE *f;
354 if (!f) { 371 struct stat fst;
355 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); 372 plist_t mount_options = NULL;
356 goto leave;
357 }
358 sig_length = fread(sig, 1, sizeof(sig), f);
359 fclose(f);
360 if (sig_length == 0) {
361 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
362 goto leave;
363 }
364 373
365 f = fopen(image_path, "r"); 374 if (product_version_major < 17) {
366 if (!f) { 375 f = fopen(image_sig_path, "rb");
367 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); 376 if (!f) {
368 goto leave; 377 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
378 goto leave;
379 }
380 if (fstat(fileno(f), &fst) != 0) {
381 fprintf(stderr, "Error: fstat: %s\n", strerror(errno));
382 goto leave;
383 }
384 sig = malloc(fst.st_size);
385 sig_length = fread(sig, 1, fst.st_size, f);
386 fclose(f);
387 if (sig_length == 0) {
388 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
389 goto leave;
390 }
391
392 f = fopen(image_path, "rb");
393 if (!f) {
394 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
395 goto leave;
396 }
397 } else {
398 if (stat(image_path, &fst) != 0) {
399 fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
400 goto leave;
401 }
402 if (!S_ISDIR(fst.st_mode)) {
403 fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
404 goto leave;
405 }
406 char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
407 plist_t build_manifest = NULL;
408 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
409 free(build_manifest_path);
410 build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
411 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
412 char* image_path_new = string_build_path(image_path, "Restore", NULL);
413 free(image_path);
414 image_path = image_path_new;
415 }
416 }
417 if (!build_manifest) {
418 fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
419 goto leave;
420 }
421
422 plist_t identifiers = NULL;
423 mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
424 if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
425 fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
426 goto error_out;
427 }
428
429 unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
430 unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
431
432 plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
433 plist_array_iter iter;
434 plist_array_new_iter(build_identities, &iter);
435 plist_t item = NULL;
436 plist_t build_identity = NULL;
437 do {
438 plist_array_next_item(build_identities, iter, &item);
439 if (!item) {
440 break;
441 }
442 unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
443 unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
444 if (bi_chip_id == chip_id && bi_board_id == board_id) {
445 build_identity = item;
446 break;
447 }
448 } while (item);
449 plist_mem_free(iter);
450 if (!build_identity) {
451 fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
452 goto leave;
453 }
454 plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
455 if (!p_tc_path) {
456 fprintf(stderr, "Error: Could not determine path for trust cache!\n");
457 goto leave;
458 }
459 plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
460 if (!p_dmg_path) {
461 fprintf(stderr, "Error: Could not determine path for disk image!\n");
462 goto leave;
463 }
464 char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
465 unsigned char* trust_cache = NULL;
466 uint64_t trust_cache_size = 0;
467 if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
468 fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
469 goto leave;
470 }
471 mount_options = plist_new_dict();
472 plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
473 free(trust_cache);
474 char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
475 free(image_path);
476 image_path = dmg_path;
477 f = fopen(image_path, "rb");
478 if (!f) {
479 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
480 goto leave;
481 }
482
483 unsigned char buf[8192];
484 unsigned char sha384_digest[48];
485 sha384_context ctx;
486 sha384_init(&ctx);
487 fstat(fileno(f), &fst);
488 image_size = fst.st_size;
489 while (!feof(f)) {
490 ssize_t fr = fread(buf, 1, sizeof(buf), f);
491 if (fr <= 0) {
492 break;
493 }
494 sha384_update(&ctx, buf, fr);
495 }
496 rewind(f);
497 sha384_final(&ctx, sha384_digest);
498 unsigned char* manifest = NULL;
499 unsigned int manifest_size = 0;
500 /* check if the device already has a personalization manifest for this image */
501 if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
502 printf("Using existing personalization manifest from device.\n");
503 } else {
504 /* we need to re-connect in this case */
505 mobile_image_mounter_free(mim);
506 mim = NULL;
507 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
508 goto error_out;
509 }
510 printf("No personalization manifest, requesting from TSS...\n");
511 unsigned char* nonce = NULL;
512 unsigned int nonce_size = 0;
513
514 /* create new TSS request and fill parameters */
515 plist_t request = tss_request_new(NULL);
516 plist_t params = plist_new_dict();
517 tss_parameters_add_from_manifest(params, build_identity, 1);
518
519 /* copy all `Ap,*` items from identifiers */
520 plist_dict_iter di = NULL;
521 plist_dict_new_iter(identifiers, &di);
522 plist_t node = NULL;
523 do {
524 char* key = NULL;
525 plist_dict_next_item(identifiers, di, &key, &node);
526 if (node) {
527 if (!strncmp(key, "Ap,", 3)) {
528 plist_dict_set_item(request, key, plist_copy(node));
529 }
530 }
531 free(key);
532 } while (node);
533 plist_mem_free(di);
534
535 plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
536 plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
537 plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
538 plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
539
540 /* query nonce from image mounter service */
541 merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
542 if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
543 plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
544 } else {
545 fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
546 goto error_out;
547 }
548 mobile_image_mounter_free(mim);
549 mim = NULL;
550
551 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));
552 plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
553 tss_request_add_ap_tags(request, params, NULL);
554 tss_request_add_common_tags(request, params, NULL);
555 tss_request_add_ap_img4_tags(request, params);
556 plist_free(params);
557
558 /* request IM4M from TSS */
559 plist_t response = tss_request_send(request, NULL);
560 plist_free(request);
561
562 plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
563 if (!PLIST_IS_DATA(p_manifest)) {
564 fprintf(stderr, "Failed to get Img4Ticket\n");
565 goto error_out;
566 }
567
568 uint64_t m4m_len = 0;
569 plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
570 manifest_size = m4m_len;
571 plist_free(response);
572 printf("Done.\n");
573 }
574 sig = manifest;
575 sig_length = manifest_size;
576
577 imagetype = "Personalized";
369 } 578 }
370 579
371 char *targetname = NULL; 580 char *targetname = NULL;
372 if (asprintf(&targetname, "%s/%s", PKG_PATH, basename(image_path)) < 0) { 581 if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
373 fprintf(stderr, "Out of memory!?\n"); 582 fprintf(stderr, "Out of memory!?\n");
374 goto leave; 583 goto leave;
375 } 584 }
@@ -379,68 +588,91 @@ int main(int argc, char **argv)
379 goto leave; 588 goto leave;
380 } 589 }
381 590
382 printf("Copying '%s' --> '%s'\n", image_path, targetname); 591 if (!imagetype) {
383 592 imagetype = "Developer";
384 char **strs = NULL;
385 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
386 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
387 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
388 }
389 }
390 if (strs) {
391 int i = 0;
392 while (strs[i]) {
393 free(strs[i]);
394 i++;
395 }
396 free(strs);
397 } 593 }
398 594
399 uint64_t af = 0; 595 if (!mim) {
400 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) != 596 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
401 AFC_E_SUCCESS) || !af) { 597 goto error_out;
402 fclose(f); 598 }
403 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
404 goto leave;
405 } 599 }
406 600
407 char buf[8192]; 601 switch(disk_image_upload_type) {
408 size_t amount = 0; 602 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
409 do { 603 printf("Uploading %s\n", image_path);
410 amount = fread(buf, 1, sizeof(buf), f); 604 err = mobile_image_mounter_upload_image(mim, imagetype, image_size, sig, sig_length, mim_upload_cb, f);
411 if (amount > 0) { 605 break;
412 uint32_t written, total = 0; 606 case DISK_IMAGE_UPLOAD_TYPE_AFC:
413 while (total < amount) { 607 default:
414 written = 0; 608 printf("Uploading %s --> afc:///%s\n", image_path, targetname);
415 if (afc_file_write(afc, af, buf, amount, &written) != 609 char **strs = NULL;
416 AFC_E_SUCCESS) { 610 if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
417 fprintf(stderr, "AFC Write error!\n"); 611 if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
418 break; 612 fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
613 }
614 }
615 if (strs) {
616 int i = 0;
617 while (strs[i]) {
618 free(strs[i]);
619 i++;
419 } 620 }
420 total += written; 621 free(strs);
421 } 622 }
422 if (total != amount) { 623
423 fprintf(stderr, "Error: wrote only %d of %d\n", total, 624 uint64_t af = 0;
424 (unsigned int)amount); 625 if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
425 afc_file_close(afc, af); 626 AFC_E_SUCCESS) || !af) {
426 fclose(f); 627 fclose(f);
628 fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
427 goto leave; 629 goto leave;
428 } 630 }
429 } 631
632 char buf[8192];
633 size_t amount = 0;
634 do {
635 amount = fread(buf, 1, sizeof(buf), f);
636 if (amount > 0) {
637 uint32_t written, total = 0;
638 while (total < amount) {
639 written = 0;
640 if (afc_file_write(afc, af, buf + total, amount - total, &written) !=
641 AFC_E_SUCCESS) {
642 fprintf(stderr, "AFC Write error!\n");
643 break;
644 }
645 total += written;
646 }
647 if (total != amount) {
648 fprintf(stderr, "Error: wrote only %d of %d\n", total,
649 (unsigned int)amount);
650 afc_file_close(afc, af);
651 fclose(f);
652 goto leave;
653 }
654 }
655 }
656 while (amount > 0);
657
658 afc_file_close(afc, af);
659 break;
430 } 660 }
431 while (amount > 0);
432 661
433 afc_file_close(afc, af);
434 fclose(f); 662 fclose(f);
435 663
664 if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
665 if (err == MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED) {
666 printf("ERROR: Device is locked, can't mount. Unlock device and try again.\n");
667 } else {
668 printf("ERROR: Unknown error occurred, can't mount.\n");
669 }
670 goto error_out;
671 }
436 printf("done.\n"); 672 printf("done.\n");
437 673
438 printf("Mounting...\n"); 674 printf("Mounting...\n");
439 if (!imagetype) { 675 err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result);
440 imagetype = strdup("Developer");
441 }
442 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
443 free(imagetype);
444 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 676 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
445 if (result) { 677 if (result) {
446 plist_t node = plist_dict_get_item(result, "Status"); 678 plist_t node = plist_dict_get_item(result, "Status");
@@ -453,20 +685,12 @@ int main(int argc, char **argv)
453 res = 0; 685 res = 0;
454 } else { 686 } else {
455 printf("unexpected status value:\n"); 687 printf("unexpected status value:\n");
456 if (xml_mode) { 688 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
457 print_xml(result);
458 } else {
459 plist_dict_to_string(result);
460 }
461 } 689 }
462 free(status); 690 free(status);
463 } else { 691 } else {
464 printf("unexpected result:\n"); 692 printf("unexpected result:\n");
465 if (xml_mode) { 693 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
466 print_xml(result);
467 } else {
468 plist_dict_to_string(result);
469 }
470 } 694 }
471 } 695 }
472 node = plist_dict_get_item(result, "Error"); 696 node = plist_dict_get_item(result, "Error");
@@ -478,31 +702,58 @@ int main(int argc, char **argv)
478 free(error); 702 free(error);
479 } else { 703 } else {
480 printf("unexpected result:\n"); 704 printf("unexpected result:\n");
481 if (xml_mode) { 705 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
482 print_xml(result);
483 } else {
484 plist_dict_to_string(result);
485 }
486 } 706 }
487 707 node = plist_dict_get_item(result, "DetailedError");
488 } else { 708 if (node) {
489 if (xml_mode) { 709 printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
490 print_xml(result);
491 } else {
492 plist_dict_to_string(result);
493 } 710 }
711 } else {
712 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
494 } 713 }
495 } 714 }
496 } else { 715 } else {
497 printf("Error: mount_image returned %d\n", err); 716 printf("Error: mount_image returned %d\n", err);
498 717
499 } 718 }
719 } else if (cmd == CMD_UNMOUNT) {
720 err = mobile_image_mounter_unmount_image(mim, argv[0]);
721 switch (err) {
722 case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
723 printf("Success\n");
724 res = 0;
725 break;
726 case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
727 printf("Error: '%s' is not mounted\n", argv[0]);
728 res = 1;
729 break;
730 case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
731 printf("Error: 'unmount' is not supported on this device\n");
732 res = 1;
733 break;
734 case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
735 printf("Error: device is locked\n");
736 res = 1;
737 break;
738 default:
739 printf("Error: unmount returned %d\n", err);
740 break;
741 }
742 } else if (cmd == CMD_DEVMODESTATUS) {
743 err = mobile_image_mounter_query_developer_mode_status(mim, &result);
744 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
745 res = 0;
746 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
747 } else {
748 printf("Error: query_developer_mode_status returned %d\n", err);
749 }
500 } 750 }
501 751
502 if (result) { 752 if (result) {
503 plist_free(result); 753 plist_free(result);
504 } 754 }
505 755
756error_out:
506 /* perform hangup command */ 757 /* perform hangup command */
507 mobile_image_mounter_hangup(mim); 758 mobile_image_mounter_hangup(mim);
508 /* free client */ 759 /* free client */
@@ -518,7 +769,7 @@ leave:
518 idevice_free(device); 769 idevice_free(device);
519 770
520 if (image_path) 771 if (image_path)
521 free(image_path); 772 free(image_path);
522 if (image_sig_path) 773 if (image_sig_path)
523 free(image_sig_path); 774 free(image_sig_path);
524 775