summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2024-06-27 11:20:59 +0200
committerGravatar Nikias Bassen2024-06-27 11:20:59 +0200
commit68df374762b95ab40ca5242da66e3474360669b5 (patch)
treea75acdd2a57df58346f02e75577c7dad00b52b83
parented0d66d0341562731bb19928dfe48155509fa7a7 (diff)
downloadlibimobiledevice-68df374762b95ab40ca5242da66e3474360669b5.tar.gz
libimobiledevice-68df374762b95ab40ca5242da66e3474360669b5.tar.bz2
Add support for iOS 17+ Personalized Developer Disk image mounting
-rw-r--r--configure.ac2
-rw-r--r--cython/mobile_image_mounter.pxi43
-rw-r--r--docs/ideviceimagemounter.134
-rw-r--r--include/libimobiledevice/mobile_image_mounter.h111
-rw-r--r--src/mobile_image_mounter.c274
-rw-r--r--tools/Makefile.am6
-rw-r--r--tools/ideviceimagemounter.c396
7 files changed, 792 insertions, 74 deletions
diff --git a/configure.ac b/configure.ac
index f1b9c76..c67e896 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,6 +28,7 @@ dnl Minimum package versions
28LIBUSBMUXD_VERSION=2.0.2 28LIBUSBMUXD_VERSION=2.0.2
29LIBPLIST_VERSION=2.3.0 29LIBPLIST_VERSION=2.3.0
30LIMD_GLUE_VERSION=1.3.0 30LIMD_GLUE_VERSION=1.3.0
31LIBTATSU_VERSION=1.0.3
31 32
32AC_SUBST(LIBUSBMUXD_VERSION) 33AC_SUBST(LIBUSBMUXD_VERSION)
33AC_SUBST(LIBPLIST_VERSION) 34AC_SUBST(LIBPLIST_VERSION)
@@ -43,6 +44,7 @@ LT_INIT
43PKG_CHECK_MODULES(libusbmuxd, libusbmuxd-2.0 >= $LIBUSBMUXD_VERSION) 44PKG_CHECK_MODULES(libusbmuxd, libusbmuxd-2.0 >= $LIBUSBMUXD_VERSION)
44PKG_CHECK_MODULES(libplist, libplist-2.0 >= $LIBPLIST_VERSION) 45PKG_CHECK_MODULES(libplist, libplist-2.0 >= $LIBPLIST_VERSION)
45PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION) 46PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION)
47PKG_CHECK_MODULES(libtatsu, libtatsu-1.0 >= $LIBTATSU_VERSION)
46AC_ARG_WITH([readline], 48AC_ARG_WITH([readline],
47 [AS_HELP_STRING([--without-readline], 49 [AS_HELP_STRING([--without-readline],
48 [build without support for libreadline (default is yes)])], 50 [build without support for libreadline (default is yes)])],
diff --git a/cython/mobile_image_mounter.pxi b/cython/mobile_image_mounter.pxi
index a23a59b..d9d40d5 100644
--- a/cython/mobile_image_mounter.pxi
+++ b/cython/mobile_image_mounter.pxi
@@ -13,7 +13,9 @@ cdef extern from "libimobiledevice/mobile_image_mounter.h":
13 mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t descriptor, mobile_image_mounter_client_t *client) 13 mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t descriptor, mobile_image_mounter_client_t *client)
14 mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) 14 mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)
15 mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, char *image_type, plist.plist_t *result) 15 mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, char *image_type, plist.plist_t *result)
16 mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, char *image_path, char *image_signature, uint16_t signature_length, char *image_type, plist.plist_t *result) 16 mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, char *image_path, const unsigned char *signature, unsigned int signature_length, char *image_type, plist.plist_t options, plist.plist_t *result)
17 mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, char *image_path, const unsigned char *signature, unsigned int signature_length, char *image_type, plist.plist_t *result)
18 mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path);
17 mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) 19 mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
18 20
19cdef class MobileImageMounterError(BaseError): 21cdef class MobileImageMounterError(BaseError):
@@ -57,11 +59,39 @@ cdef class MobileImageMounterClient(PropertyListService):
57 if c_node != NULL: 59 if c_node != NULL:
58 plist.plist_free(c_node) 60 plist.plist_free(c_node)
59 61
60 cpdef plist.Node mount_image(self, bytes image_path, bytes image_signature, bytes image_type): 62 cpdef plist.Node mount_image_with_options(self, bytes image_path, bytes signature, bytes image_type, object options):
61 cdef: 63 cdef:
64 plist.Node n_options
65 plist.plist_t c_options
66 plist.plist_t c_result = NULL
67 bint free_options = False
62 plist.plist_t c_node = NULL 68 plist.plist_t c_node = NULL
63 mobile_image_mounter_error_t err 69 mobile_image_mounter_error_t err
64 err = mobile_image_mounter_mount_image(self._c_client, image_path, image_signature, len(image_signature), 70 if isinstance(options, plist.Dict):
71 n_options = options
72 c_options = n_options._c_node
73 elif isinstance(options, dict):
74 c_options = plist.native_to_plist_t(options)
75 free_options = True
76 else:
77 raise InstallationProxyError(INSTPROXY_E_INVALID_ARG)
78 err = mobile_image_mounter_mount_image_with_options(self._c_client, image_path, signature, len(signature),
79 image_type, c_options, &c_node)
80 if free_options:
81 plist.plist_free(c_options)
82 try:
83 self.handle_error(err)
84
85 return plist.plist_t_to_node(c_node)
86 except Exception, e:
87 if c_node != NULL:
88 plist.plist_free(c_node)
89
90 cpdef plist.Node mount_image(self, bytes image_path, bytes signature, bytes image_type):
91 cdef:
92 plist.plist_t c_node = NULL
93 mobile_image_mounter_error_t err
94 err = mobile_image_mounter_mount_image(self._c_client, image_path, signature, len(signature),
65 image_type, &c_node) 95 image_type, &c_node)
66 96
67 try: 97 try:
@@ -72,6 +102,13 @@ cdef class MobileImageMounterClient(PropertyListService):
72 if c_node != NULL: 102 if c_node != NULL:
73 plist.plist_free(c_node) 103 plist.plist_free(c_node)
74 104
105 cpdef unmount_image(self, bytes mount_path):
106 cdef:
107 mobile_image_mounter_error_t err
108 err = mobile_image_mounter_unmount_image(self._c_client, mount_path)
109
110 self.handle_error(err)
111
75 cpdef hangup(self): 112 cpdef hangup(self):
76 cdef mobile_image_mounter_error_t err 113 cdef mobile_image_mounter_error_t err
77 err = mobile_image_mounter_hangup(self._c_client) 114 err = mobile_image_mounter_hangup(self._c_client)
diff --git a/docs/ideviceimagemounter.1 b/docs/ideviceimagemounter.1
index 832850a..1fe7e45 100644
--- a/docs/ideviceimagemounter.1
+++ b/docs/ideviceimagemounter.1
@@ -1,13 +1,32 @@
1.TH "ideviceimagemounter" 1 1.TH "ideviceimagemounter" 1
2.SH NAME 2.SH NAME
3ideviceimagemounter \- Mount disk images on the device. 3ideviceimagemounter \- Mount, list, or unmount a disk image on the device.
4.SH SYNOPSIS 4.SH SYNOPSIS
5.B ideviceimagemounter 5.B ideviceimagemounter
6[OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE 6[OPTIONS] COMMAND [COMMAND OPTIONS]
7 7
8.SH DESCRIPTION 8.SH DESCRIPTION
9 9
10Mounts the specified disk image on the device. 10Mount, list, or unmount a disk image on the device.
11
12.SH COMMANDS
13.TP
14.B mount PATH
15Mount the developer disk image at PATH.
16For iOS 17+, PATH is a directory containing a .dmg image, a BuildManifest.plist,
17and a Firmware sub-directory.
18
19For older versions PATH is a .dmg filename with a .dmg.signature file in the same directory, or with
20another parameter pointing to a file elsewhere.
21.TP
22.B list
23List mounted disk images.
24.TP
25.B unmount PATH
26Unmount the image mounted at PATH.
27.TP
28.B devmodestatus
29Query the developer mode status (iOS 16+)
11 30
12.SH OPTIONS 31.SH OPTIONS
13.TP 32.TP
@@ -20,9 +39,6 @@ connect to network device.
20.B \-d, \-\-debug 39.B \-d, \-\-debug
21enable communication debugging. 40enable communication debugging.
22.TP 41.TP
23.B \-l, \-\-list
24list mount information
25.TP
26.B \-t, \-\-imagetype NAME 42.B \-t, \-\-imagetype NAME
27the image type to use, default is 'Developer' 43the image type to use, default is 'Developer'
28.TP 44.TP
@@ -34,12 +50,6 @@ prints usage information
34.TP 50.TP
35.B \-v, \-\-version 51.B \-v, \-\-version
36prints version information. 52prints version information.
37.TP
38.B IMAGE_FILE
39the image filename to mount
40.TP
41.B IMAGE_SIGNATURE_FILE
42corresponding signature file for image filename
43 53
44.SH AUTHOR 54.SH AUTHOR
45Nikias Bassen 55Nikias Bassen
diff --git a/include/libimobiledevice/mobile_image_mounter.h b/include/libimobiledevice/mobile_image_mounter.h
index d4fc3f4..76bb61a 100644
--- a/include/libimobiledevice/mobile_image_mounter.h
+++ b/include/libimobiledevice/mobile_image_mounter.h
@@ -42,6 +42,7 @@ typedef enum {
42 MOBILE_IMAGE_MOUNTER_E_CONN_FAILED = -3, 42 MOBILE_IMAGE_MOUNTER_E_CONN_FAILED = -3,
43 MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED = -4, 43 MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED = -4,
44 MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED = -5, 44 MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED = -5,
45 MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED = -6,
45 MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR = -256 46 MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR = -256
46} mobile_image_mounter_error_t; 47} mobile_image_mounter_error_t;
47 48
@@ -127,7 +128,7 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_lookup_im
127 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on succes, or a 128 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on succes, or a
128 * MOBILE_IMAGE_MOUNTER_E_* error code otherwise. 129 * MOBILE_IMAGE_MOUNTER_E_* error code otherwise.
129 */ 130 */
130LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const char *signature, uint16_t signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata); 131LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const unsigned char *signature, unsigned int signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata);
131 132
132/** 133/**
133 * Mounts an image on the device. 134 * Mounts an image on the device.
@@ -138,19 +139,50 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_im
138 * @param signature Pointer to a buffer holding the images' signature 139 * @param signature Pointer to a buffer holding the images' signature
139 * @param signature_size Length of the signature image_signature points to 140 * @param signature_size Length of the signature image_signature points to
140 * @param image_type Type of image to mount 141 * @param image_type Type of image to mount
142 * @param options A dictionary containing additional key/value pairs to add
141 * @param result Pointer to a plist that will receive the result of the 143 * @param result Pointer to a plist that will receive the result of the
142 * operation. 144 * operation.
143 * 145 *
144 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the 146 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
145 * operation has failed. Check the resulting plist for further information. 147 * operation has failed. Check the resulting plist for further information.
146 * Note that there is no unmounting function. The mount persists until the
147 * device is rebooted.
148 * 148 *
149 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 149 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
150 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are 150 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are
151 * invalid, or another error code otherwise. 151 * invalid, or another error code otherwise.
152 */ 152 */
153LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *signature, uint16_t signature_size, const char *image_type, plist_t *result); 153LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t options, plist_t *result);
154
155/**
156 * Mounts an image on the device.
157 *
158 * @param client The connected mobile_image_mounter client.
159 * @param image_path The absolute path of the image to mount. The image must
160 * be present before calling this function.
161 * @param signature Pointer to a buffer holding the images' signature
162 * @param signature_size Length of the signature image_signature points to
163 * @param image_type Type of image to mount
164 * @param result Pointer to a plist that will receive the result of the
165 * operation.
166 *
167 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
168 * operation has failed. Check the resulting plist for further information.
169 *
170 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
171 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are
172 * invalid, or another error code otherwise.
173 */
174LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t *result);
175
176/**
177 * Unmount a mounted image at given path on the device.
178 *
179 * @param client The connected mobile_image_mounter client.
180 * @param mount_path The mount path of the mounted image on the device.
181 *
182 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
183 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
184 */
185LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path);
154 186
155/** 187/**
156 * Hangs up the connection to the mobile_image_mounter service. 188 * Hangs up the connection to the mobile_image_mounter service.
@@ -165,6 +197,77 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_ima
165 */ 197 */
166LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client); 198LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client);
167 199
200/**
201 * Query the developer mode status of the given device.
202 *
203 * @param client The connected mobile_image_mounter client.
204 * @param result A pointer to a plist_t that will be set to the resulting developer mode status dictionary.
205 *
206 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
207 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
208 */
209LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result);
210
211/**
212 * Query a personalization nonce for the given image type, used for personalized disk images (iOS 17+).
213 * This nonce is supposed to be added to the TSS request for the corresponding image.
214 *
215 * @param client The connected mobile_image_mounter client.
216 * @param image_type The image_type to get the personalization nonce for, usually `DeveloperDiskImage`.
217 * @param nonce Pointer that will be set to an allocated buffer with the nonce value.
218 * @param nonce_size Pointer to an unsigned int that will receive the size of the nonce value.
219 *
220 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
221 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
222 */
223LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_query_nonce(mobile_image_mounter_client_t client, const char* image_type, unsigned char** nonce, unsigned int* nonce_size);
224
225/**
226 * Query personalization identitifers for the given image_type.
227 *
228 * @param client The connected mobile_image_mounter client.
229 * @param image_type The image_type to get the personalization identifiers for. Can be NULL.
230 * @param result A pointer to a plist_t that will be set to the resulting identifier dictionary.
231 *
232 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
233 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
234 */
235LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result);
236
237/**
238 *
239 * @param client The connected mobile_image_mounter client.
240 * @param image_type The image_type to get the personalization identifiers for. Can be NULL.
241 * @param signature The signature of the corresponding personalized image.
242 * @param signature_size The size of signature.
243 * @param manifest Pointer that will be set to an allocated buffer with the manifest data.
244 * @param manifest_size Pointer to an unsigned int that will be set to the size of the manifest data.
245 *
246 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
247 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
248 */
249LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_query_personalization_manifest(mobile_image_mounter_client_t client, const char* image_type, const unsigned char* signature, unsigned int signature_size, unsigned char** manifest, unsigned int* manifest_size);
250
251/**
252 * Roll the personalization nonce.
253 *
254 * @param client The connected mobile_image_mounter client.
255 *
256 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
257 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
258 */
259LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client);
260
261/**
262 * Roll the Cryptex nonce.
263 *
264 * @param client The connected mobile_image_mounter client.
265 *
266 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
267 * or a MOBILE_IMAGE_MOUNTER_E_* error code on error.
268 */
269LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client);
270
168#ifdef __cplusplus 271#ifdef __cplusplus
169} 272}
170#endif 273#endif
diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c
index 5df8e86..6df50c4 100644
--- a/src/mobile_image_mounter.c
+++ b/src/mobile_image_mounter.c
@@ -181,7 +181,7 @@ static mobile_image_mounter_error_t process_result(plist_t result, const char *e
181 return res; 181 return res;
182} 182}
183 183
184mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const char *signature, uint16_t signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata) 184mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const unsigned char *signature, unsigned int signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata)
185{ 185{
186 if (!client || !image_type || (image_size == 0) || !upload_cb) { 186 if (!client || !image_type || (image_size == 0) || !upload_cb) {
187 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; 187 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
@@ -192,7 +192,7 @@ mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_moun
192 plist_t dict = plist_new_dict(); 192 plist_t dict = plist_new_dict();
193 plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes")); 193 plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes"));
194 if (signature && signature_size != 0) 194 if (signature && signature_size != 0)
195 plist_dict_set_item(dict, "ImageSignature", plist_new_data(signature, signature_size)); 195 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
196 plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size)); 196 plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size));
197 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); 197 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
198 198
@@ -241,6 +241,7 @@ mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_moun
241 free(buf); 241 free(buf);
242 if (tx < image_size) { 242 if (tx < image_size) {
243 debug_info("Error: failed to upload image"); 243 debug_info("Error: failed to upload image");
244 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
244 goto leave_unlock; 245 goto leave_unlock;
245 } 246 }
246 debug_info("image uploaded"); 247 debug_info("image uploaded");
@@ -260,7 +261,7 @@ leave_unlock:
260 261
261} 262}
262 263
263mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *signature, uint16_t signature_size, const char *image_type, plist_t *result) 264mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t options, plist_t *result)
264{ 265{
265 if (!client || !image_path || !image_type || !result) { 266 if (!client || !image_path || !image_type || !result) {
266 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; 267 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
@@ -271,8 +272,11 @@ mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mount
271 plist_dict_set_item(dict, "Command", plist_new_string("MountImage")); 272 plist_dict_set_item(dict, "Command", plist_new_string("MountImage"));
272 plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path)); 273 plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path));
273 if (signature && signature_size != 0) 274 if (signature && signature_size != 0)
274 plist_dict_set_item(dict, "ImageSignature", plist_new_data(signature, signature_size)); 275 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
275 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); 276 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
277 if (PLIST_IS_DICT(options)) {
278 plist_dict_merge(&dict, options);
279 }
276 280
277 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 281 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
278 plist_free(dict); 282 plist_free(dict);
@@ -292,6 +296,56 @@ leave_unlock:
292 return res; 296 return res;
293} 297}
294 298
299mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t *result)
300{
301 return mobile_image_mounter_mount_image_with_options(client, image_path, signature, signature_size, image_type, NULL, result);
302}
303
304mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path)
305{
306 if (!client || !mount_path) {
307 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
308 }
309 mobile_image_mounter_lock(client);
310
311 plist_t dict = plist_new_dict();
312 plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage"));
313 plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path));
314 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
315 plist_free(dict);
316
317 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
318 debug_info("%s: Error sending XML plist to device!", __func__);
319 goto leave_unlock;
320 }
321
322 plist_t result = NULL;
323 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
324 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
325 debug_info("%s: Error receiving response from device!", __func__);
326 } else {
327 plist_t p_error = plist_dict_get_item(result, "Error");
328 if (p_error) {
329 plist_t p_detailed = plist_dict_get_item(result, "DetailedError");
330 const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : "";
331 const char* errstr = plist_get_string_ptr(p_error, NULL);
332 if (errstr && !strcmp(errstr, "UnknownCommand")) {
333 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
334 } else if (errstr && !strcmp(errstr, "DeviceLocked")) {
335 res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED;
336 } else if (strstr(detailederr, "no matching entry")) {
337 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
338 } else {
339 res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
340 }
341 }
342 }
343
344leave_unlock:
345 mobile_image_mounter_unlock(client);
346 return res;
347}
348
295mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) 349mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
296{ 350{
297 if (!client) { 351 if (!client) {
@@ -324,3 +378,215 @@ leave_unlock:
324 mobile_image_mounter_unlock(client); 378 mobile_image_mounter_unlock(client);
325 return res; 379 return res;
326} 380}
381
382mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result)
383{
384 if (!client || !result) {
385 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
386 }
387 mobile_image_mounter_lock(client);
388
389 plist_t dict = plist_new_dict();
390 plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus"));
391 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
392 plist_free(dict);
393
394 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
395 debug_info("%s: Error sending XML plist to device!", __func__);
396 goto leave_unlock;
397 }
398
399 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
400 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
401 debug_info("%s: Error receiving response from device!", __func__);
402 }
403
404leave_unlock:
405 mobile_image_mounter_unlock(client);
406 return res;
407}
408
409mobile_image_mounter_error_t mobile_image_mounter_query_nonce(mobile_image_mounter_client_t client, const char* image_type, unsigned char** nonce, unsigned int* nonce_size)
410{
411 if (!client || !nonce || !nonce_size) {
412 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
413 }
414 mobile_image_mounter_lock(client);
415
416 plist_t dict = plist_new_dict();
417 plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce"));
418 if (image_type) {
419 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
420 }
421 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
422 plist_free(dict);
423
424 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
425 debug_info("%s: Error sending XML plist to device!", __func__);
426 goto leave_unlock;
427 }
428
429 plist_t result = NULL;
430 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
431 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
432 debug_info("%s: Error receiving response from device!", __func__);
433 } else {
434 plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce");
435 if (!p_nonce) {
436 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
437 } else {
438 uint64_t nonce_size_ = 0;
439 plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_);
440 if (*nonce) {
441 *nonce_size = (unsigned int)nonce_size_;
442 } else {
443 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
444 }
445 }
446 }
447 plist_free(result);
448
449leave_unlock:
450 mobile_image_mounter_unlock(client);
451 return res;
452}
453
454mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result)
455{
456 if (!client || !result) {
457 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
458 }
459 mobile_image_mounter_lock(client);
460
461 plist_t dict = plist_new_dict();
462 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers"));
463 if (image_type) {
464 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
465 }
466 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
467 plist_free(dict);
468
469 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
470 debug_info("%s: Error sending XML plist to device!", __func__);
471 goto leave_unlock;
472 }
473
474 plist_t _result = NULL;
475 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result));
476 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
477 debug_info("%s: Error receiving response from device!", __func__);
478 }
479 *result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers"));
480 if (!*result) {
481 debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__);
482 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
483 }
484
485leave_unlock:
486 mobile_image_mounter_unlock(client);
487 return res;
488}
489
490mobile_image_mounter_error_t mobile_image_mounter_query_personalization_manifest(mobile_image_mounter_client_t client, const char* image_type, const unsigned char* signature, unsigned int signature_size, unsigned char** manifest, unsigned int* manifest_size)
491{
492 if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) {
493 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
494 }
495 mobile_image_mounter_lock(client);
496
497 plist_t dict = plist_new_dict();
498 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest"));
499 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
500 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
501 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
502
503 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
504 plist_free(dict);
505
506 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
507 debug_info("%s: Error sending XML plist to device!", __func__);
508 goto leave_unlock;
509 }
510
511 plist_t result = NULL;
512 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
513 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
514 debug_info("%s: Error receiving response from device!", __func__);
515 } else {
516 plist_t p_manifest = plist_dict_get_item(result, "ImageSignature");
517 if (!p_manifest) {
518 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
519 } else {
520 uint64_t manifest_size_ = 0;
521 plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_);
522 if (*manifest) {
523 *manifest_size = (unsigned int)manifest_size_;
524 } else {
525 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
526 }
527 }
528 }
529 plist_free(result);
530
531leave_unlock:
532 mobile_image_mounter_unlock(client);
533 return res;
534}
535
536mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client)
537{
538 if (!client) {
539 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
540 }
541 mobile_image_mounter_lock(client);
542
543 plist_t dict = plist_new_dict();
544 plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce"));
545 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
546 plist_free(dict);
547
548 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
549 debug_info("%s: Error sending XML plist to device!", __func__);
550 goto leave_unlock;
551 }
552
553 plist_t result = NULL;
554 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
555 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
556 debug_info("%s: Error receiving response from device!", __func__);
557 }
558 plist_free(result);
559
560leave_unlock:
561 mobile_image_mounter_unlock(client);
562 return res;
563}
564
565mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client)
566{
567 if (!client) {
568 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
569 }
570 mobile_image_mounter_lock(client);
571
572 plist_t dict = plist_new_dict();
573 plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce"));
574 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
575 plist_free(dict);
576
577 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
578 debug_info("%s: Error sending XML plist to device!", __func__);
579 goto leave_unlock;
580 }
581
582 plist_t result = NULL;
583 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
584 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
585 debug_info("%s: Error receiving response from device!", __func__);
586 }
587 plist_free(result);
588
589leave_unlock:
590 mobile_image_mounter_unlock(client);
591 return res;
592}
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 4cac1fc..7c9060b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -66,7 +66,7 @@ idevice_id_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
66 66
67idevicebackup_SOURCES = idevicebackup.c 67idevicebackup_SOURCES = idevicebackup.c
68idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) 68idevicebackup_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
69idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(ssl_lib_LIBS) $(limd_glue_LIBS) 69idevicebackup_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
70idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la 70idevicebackup_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
71 71
72idevicebackup2_SOURCES = idevicebackup2.c 72idevicebackup2_SOURCES = idevicebackup2.c
@@ -75,8 +75,8 @@ idevicebackup2_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
75idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la 75idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
76 76
77ideviceimagemounter_SOURCES = ideviceimagemounter.c 77ideviceimagemounter_SOURCES = ideviceimagemounter.c
78ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) 78ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS)
79ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) 79ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(ssl_lib_LIBS) $(libtatsu_LIBS)
80ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la 80ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
81 81
82idevicescreenshot_SOURCES = idevicescreenshot.c 82idevicescreenshot_SOURCES = idevicescreenshot.c
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index f551b6c..52b0666 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -45,8 +45,11 @@
45#include <libimobiledevice/afc.h> 45#include <libimobiledevice/afc.h>
46#include <libimobiledevice/notification_proxy.h> 46#include <libimobiledevice/notification_proxy.h>
47#include <libimobiledevice/mobile_image_mounter.h> 47#include <libimobiledevice/mobile_image_mounter.h>
48#include <libimobiledevice-glue/sha.h>
49#include <libimobiledevice-glue/utils.h>
48#include <asprintf.h> 50#include <asprintf.h>
49#include <plist/plist.h> 51#include <plist/plist.h>
52#include <libtatsu/tss.h>
50 53
51static int list_mode = 0; 54static int list_mode = 0;
52static int use_network = 0; 55static int use_network = 0;
@@ -62,18 +65,38 @@ typedef enum {
62 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE 65 DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE
63} disk_image_upload_type_t; 66} disk_image_upload_type_t;
64 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
65static void print_usage(int argc, char **argv, int is_error) 78static void print_usage(int argc, char **argv, int is_error)
66{ 79{
67 char *name = strrchr(argv[0], '/'); 80 char *name = strrchr(argv[0], '/');
68 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] IMAGE_FILE IMAGE_SIGNATURE_FILE\n", (name ? name + 1: argv[0])); 81 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND [COMMAND OPTIONS...]\n", (name ? name + 1: argv[0]));
69 fprintf(is_error ? stderr : stdout, 82 fprintf(is_error ? stderr : stdout,
70 "\n" 83 "\n"
71 "Mounts the specified disk image on the device.\n" 84 "Mount, list, or unmount a disk image on the device.\n"
85 "\n"
86 "COMMANDS:\n"
87 " mount PATH Mount the developer disk image at PATH.\n"
88 " For iOS 17+, PATH is a directory containing a .dmg image,\n"
89 " a BuildManifest.plist, and a Firmware sub-directory;\n"
90 " for older versions PATH is a .dmg filename with a"
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"
72 "\n" 96 "\n"
73 "OPTIONS:\n" 97 "OPTIONS:\n"
74 " -u, --udid UDID target specific device by UDID\n" 98 " -u, --udid UDID target specific device by UDID\n"
75 " -n, --network connect to network device\n" 99 " -n, --network connect to network device\n"
76 " -l, --list List mount information\n"
77 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n" 100 " -t, --imagetype TYPE Image type to use, default is 'Developer'\n"
78 " -x, --xml Use XML output\n" 101 " -x, --xml Use XML output\n"
79 " -d, --debug enable communication debugging\n" 102 " -d, --debug enable communication debugging\n"
@@ -87,11 +110,11 @@ static void print_usage(int argc, char **argv, int is_error)
87 110
88static void parse_opts(int argc, char **argv) 111static void parse_opts(int argc, char **argv)
89{ 112{
113 int debug_level = 0;
90 static struct option longopts[] = { 114 static struct option longopts[] = {
91 { "help", no_argument, NULL, 'h' }, 115 { "help", no_argument, NULL, 'h' },
92 { "udid", required_argument, NULL, 'u' }, 116 { "udid", required_argument, NULL, 'u' },
93 { "network", no_argument, NULL, 'n' }, 117 { "network", no_argument, NULL, 'n' },
94 { "list", no_argument, NULL, 'l' },
95 { "imagetype", required_argument, NULL, 't' }, 118 { "imagetype", required_argument, NULL, 't' },
96 { "xml", no_argument, NULL, 'x' }, 119 { "xml", no_argument, NULL, 'x' },
97 { "debug", no_argument, NULL, 'd' }, 120 { "debug", no_argument, NULL, 'd' },
@@ -101,7 +124,7 @@ static void parse_opts(int argc, char **argv)
101 int c; 124 int c;
102 125
103 while (1) { 126 while (1) {
104 c = getopt_long(argc, argv, "hu:lt:xdnv", longopts, NULL); 127 c = getopt_long(argc, argv, "hu:t:xdnv", longopts, NULL);
105 if (c == -1) { 128 if (c == -1) {
106 break; 129 break;
107 } 130 }
@@ -121,9 +144,6 @@ static void parse_opts(int argc, char **argv)
121 case 'n': 144 case 'n':
122 use_network = 1; 145 use_network = 1;
123 break; 146 break;
124 case 'l':
125 list_mode = 1;
126 break;
127 case 't': 147 case 't':
128 imagetype = optarg; 148 imagetype = optarg;
129 break; 149 break;
@@ -131,7 +151,7 @@ static void parse_opts(int argc, char **argv)
131 xml_mode = 1; 151 xml_mode = 1;
132 break; 152 break;
133 case 'd': 153 case 'd':
134 idevice_set_debug_level(1); 154 debug_level++;
135 break; 155 break;
136 case 'v': 156 case 'v':
137 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); 157 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
@@ -141,6 +161,8 @@ static void parse_opts(int argc, char **argv)
141 exit(2); 161 exit(2);
142 } 162 }
143 } 163 }
164 idevice_set_debug_level(debug_level);
165 tss_set_debug_level(debug_level);
144} 166}
145 167
146static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata) 168static ssize_t mim_upload_cb(void* buf, size_t size, void* userdata)
@@ -169,29 +191,75 @@ int main(int argc, char **argv)
169 argc -= optind; 191 argc -= optind;
170 argv += optind; 192 argv += optind;
171 193
172 if (!list_mode) { 194 if (argc == 0) {
173 if (argc < 1) { 195 fprintf(stderr, "ERROR: Missing command.\n\n");
174 printf("ERROR: No IMAGE_FILE has been given!\n"); 196 print_usage(argc+optind, argv-optind, 1);
175 return -1; 197 return 2;
176 } 198 }
177 image_path = strdup(argv[0]); 199
178 if (argc >= 2) { 200 char* cmdstr = argv[0];
179 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;
180 } else { 219 } else {
181 if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) { 220 cmd = CMD_MOUNT;
182 printf("Out of memory?!\n");
183 return -1;
184 }
185 } 221 }
186 } 222 }
187 223
224 argc -= optind2;
225 argv += optind2;
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
188 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) { 256 if (IDEVICE_E_SUCCESS != idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX)) {
189 if (udid) { 257 if (udid) {
190 printf("No device found with udid %s.\n", udid); 258 printf("No device found with udid %s.\n", udid);
191 } else { 259 } else {
192 printf("No device found.\n"); 260 printf("No device found.\n");
193 } 261 }
194 return -1; 262 return 1;
195 } 263 }
196 264
197 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) { 265 if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake(device, &lckd, TOOL_NAME))) {
@@ -215,7 +283,7 @@ int main(int argc, char **argv)
215 } 283 }
216 } 284 }
217 285
218 if (product_version_major == 16) { 286 if (product_version_major >= 16) {
219 uint8_t dev_mode_status = 0; 287 uint8_t dev_mode_status = 0;
220 plist_t val = NULL; 288 plist_t val = NULL;
221 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val); 289 ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
@@ -246,7 +314,7 @@ int main(int argc, char **argv)
246 service = NULL; 314 service = NULL;
247 } 315 }
248 316
249 if (!list_mode) { 317 if (cmd == CMD_MOUNT) {
250 struct stat fst; 318 struct stat fst;
251 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) { 319 if (disk_image_upload_type == DISK_IMAGE_UPLOAD_TYPE_AFC) {
252 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) != 320 if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
@@ -268,7 +336,7 @@ int main(int argc, char **argv)
268 goto leave; 336 goto leave;
269 } 337 }
270 image_size = fst.st_size; 338 image_size = fst.st_size;
271 if (stat(image_sig_path, &fst) != 0) { 339 if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) {
272 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));
273 goto leave; 341 goto leave;
274 } 342 }
@@ -280,10 +348,14 @@ int main(int argc, char **argv)
280 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; 348 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
281 plist_t result = NULL; 349 plist_t result = NULL;
282 350
283 if (list_mode) { 351 if (cmd == CMD_LIST) {
284 /* list mounts mode */ 352 /* list mounts mode */
285 if (!imagetype) { 353 if (!imagetype) {
286 imagetype = "Developer"; 354 if (product_version_major < 17) {
355 imagetype = "Developer";
356 } else {
357 imagetype = "Personalized";
358 }
287 } 359 }
288 err = mobile_image_mounter_lookup_image(mim, imagetype, &result); 360 err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
289 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 361 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
@@ -292,25 +364,214 @@ int main(int argc, char **argv)
292 } else { 364 } else {
293 printf("Error: lookup_image returned %d\n", err); 365 printf("Error: lookup_image returned %d\n", err);
294 } 366 }
295 } else { 367 } else if (cmd == CMD_MOUNT) {
296 char sig[8192]; 368 unsigned char *sig = NULL;
297 size_t sig_length = 0; 369 size_t sig_length = 0;
298 FILE *f = fopen(image_sig_path, "rb"); 370 FILE *f;
299 if (!f) { 371 struct stat fst;
300 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno)); 372 plist_t mount_options = NULL;
301 goto leave;
302 }
303 sig_length = fread(sig, 1, sizeof(sig), f);
304 fclose(f);
305 if (sig_length == 0) {
306 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
307 goto leave;
308 }
309 373
310 f = fopen(image_path, "rb"); 374 if (product_version_major < 17) {
311 if (!f) { 375 f = fopen(image_sig_path, "rb");
312 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno)); 376 if (!f) {
313 goto leave; 377 fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
378 goto leave;
379 }
380 fstat(fileno(f), &fst);
381 sig = malloc(sig_length);
382 sig_length = fread(sig, 1, fst.st_size, f);
383 fclose(f);
384 if (sig_length == 0) {
385 fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
386 goto leave;
387 }
388
389 f = fopen(image_path, "rb");
390 if (!f) {
391 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
392 goto leave;
393 }
394 } else {
395 if (stat(image_path, &fst) != 0) {
396 fprintf(stderr, "Error: stat: '%s': %s\n", image_path, strerror(errno));
397 goto leave;
398 }
399 if (!S_ISDIR(fst.st_mode)) {
400 fprintf(stderr, "Error: Personalized Disk Image mount expects a directory as image path.\n");
401 goto leave;
402 }
403 char* build_manifest_path = string_build_path(image_path, "BuildManifest.plist", NULL);
404 plist_t build_manifest = NULL;
405 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) != 0) {
406 free(build_manifest_path);
407 build_manifest_path = string_build_path(image_path, "Restore", "BuildManifest.plist", NULL);
408 if (plist_read_from_file(build_manifest_path, &build_manifest, NULL) == 0) {
409 char* image_path_new = string_build_path(image_path, "Restore", NULL);
410 free(image_path);
411 image_path = image_path_new;
412 }
413 }
414 if (!build_manifest) {
415 fprintf(stderr, "Error: Could not locate BuildManifest.plist inside given disk image path!\n");
416 goto leave;
417 }
418
419 plist_t identifiers = NULL;
420 mobile_image_mounter_error_t merr = mobile_image_mounter_query_personalization_identifiers(mim, NULL, &identifiers);
421 if (merr != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
422 fprintf(stderr, "Failed to query personalization identifiers: %d\n", merr);
423 goto error_out;
424 }
425
426 unsigned int board_id = plist_dict_get_uint(identifiers, "BoardId");
427 unsigned int chip_id = plist_dict_get_uint(identifiers, "ChipID");
428
429 plist_t build_identities = plist_dict_get_item(build_manifest, "BuildIdentities");
430 plist_array_iter iter;
431 plist_array_new_iter(build_identities, &iter);
432 plist_t item = NULL;
433 plist_t build_identity = NULL;
434 do {
435 plist_array_next_item(build_identities, iter, &item);
436 if (!item) {
437 break;
438 }
439 unsigned int bi_board_id = (unsigned int)plist_dict_get_uint(item, "ApBoardID");
440 unsigned int bi_chip_id = (unsigned int)plist_dict_get_uint(item, "ApChipID");
441 if (bi_chip_id == chip_id && bi_board_id == board_id) {
442 build_identity = item;
443 break;
444 }
445 } while (item);
446 plist_mem_free(iter);
447 if (!build_identity) {
448 fprintf(stderr, "Error: The given disk image is not compatible with the current device.\n");
449 goto leave;
450 }
451 plist_t p_tc_path = plist_access_path(build_identity, 4, "Manifest", "LoadableTrustCache", "Info", "Path");
452 if (!p_tc_path) {
453 fprintf(stderr, "Error: Could not determine path for trust cache!\n");
454 goto leave;
455 }
456 plist_t p_dmg_path = plist_access_path(build_identity, 4, "Manifest", "PersonalizedDMG", "Info", "Path");
457 if (!p_dmg_path) {
458 fprintf(stderr, "Error: Could not determine path for disk image!\n");
459 goto leave;
460 }
461 char *tc_path = string_build_path(image_path, plist_get_string_ptr(p_tc_path, NULL), NULL);
462 unsigned char* trust_cache = NULL;
463 uint64_t trust_cache_size = 0;
464 if (!buffer_read_from_filename(tc_path, (char**)&trust_cache, &trust_cache_size)) {
465 fprintf(stderr, "Error: Trust cache does not exist at '%s'!\n", tc_path);
466 goto leave;
467 }
468 mount_options = plist_new_dict();
469 plist_dict_set_item(mount_options, "ImageTrustCache", plist_new_data((char*)trust_cache, trust_cache_size));
470 free(trust_cache);
471 char *dmg_path = string_build_path(image_path, plist_get_string_ptr(p_dmg_path, NULL), NULL);
472 free(image_path);
473 image_path = dmg_path;
474 f = fopen(image_path, "rb");
475 if (!f) {
476 fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
477 goto leave;
478 }
479
480 unsigned char buf[8192];
481 unsigned char sha384_digest[48];
482 sha384_context ctx;
483 sha384_init(&ctx);
484 fstat(fileno(f), &fst);
485 image_size = fst.st_size;
486 while (!feof(f)) {
487 ssize_t fr = fread(buf, 1, sizeof(buf), f);
488 if (fr <= 0) {
489 break;
490 }
491 sha384_update(&ctx, buf, fr);
492 }
493 rewind(f);
494 sha384_final(&ctx, sha384_digest);
495 unsigned char* manifest = NULL;
496 unsigned int manifest_size = 0;
497 /* check if the device already has a personalization manifest for this image */
498 if (mobile_image_mounter_query_personalization_manifest(mim, "DeveloperDiskImage", sha384_digest, sizeof(sha384_digest), &manifest, &manifest_size) == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
499 printf("Using existing personalization manifest from device.\n");
500 } else {
501 /* we need to re-connect in this case */
502 mobile_image_mounter_free(mim);
503 mim = NULL;
504 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
505 goto error_out;
506 }
507 printf("No personalization manifest, requesting from TSS...\n");
508 unsigned char* nonce = NULL;
509 unsigned int nonce_size = 0;
510
511 /* create new TSS request and fill parameters */
512 plist_t request = tss_request_new(NULL);
513 plist_t params = plist_new_dict();
514 tss_parameters_add_from_manifest(params, build_identity, 1);
515
516 /* copy all `Ap,*` items from identifiers */
517 plist_dict_iter di = NULL;
518 plist_dict_new_iter(identifiers, &di);
519 plist_t node = NULL;
520 do {
521 char* key = NULL;
522 plist_dict_next_item(identifiers, di, &key, &node);
523 if (node) {
524 if (!strncmp(key, "Ap,", 3)) {
525 plist_dict_set_item(request, key, plist_copy(node));
526 }
527 }
528 free(key);
529 } while (node);
530 plist_mem_free(di);
531
532 plist_dict_copy_uint(params, identifiers, "ApECID", "UniqueChipID");
533 plist_dict_set_item(params, "ApProductionMode", plist_new_bool(1));
534 plist_dict_set_item(params, "ApSecurityMode", plist_new_bool(1));
535 plist_dict_set_item(params, "ApSupportsImg4", plist_new_bool(1));
536
537 /* query nonce from image mounter service */
538 merr = mobile_image_mounter_query_nonce(mim, "DeveloperDiskImage", &nonce, &nonce_size);
539 if (merr == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
540 plist_dict_set_item(params, "ApNonce", plist_new_data((char*)nonce, nonce_size));
541 } else {
542 fprintf(stderr, "ERROR: Failed to query nonce for developer disk image: %d\n", merr);
543 goto error_out;
544 }
545 mobile_image_mounter_free(mim);
546 mim = NULL;
547
548 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));
549 plist_dict_set_item(params, "UID_MODE", plist_new_bool(0));
550 tss_request_add_ap_tags(request, params, NULL);
551 tss_request_add_common_tags(request, params, NULL);
552 tss_request_add_ap_img4_tags(request, params);
553 plist_free(params);
554
555 /* request IM4M from TSS */
556 plist_t response = tss_request_send(request, NULL);
557 plist_free(request);
558
559 plist_t p_manifest = plist_dict_get_item(response, "ApImg4Ticket");
560 if (!PLIST_IS_DATA(p_manifest)) {
561 fprintf(stderr, "Failed to get Img4Ticket\n");
562 goto error_out;
563 }
564
565 uint64_t m4m_len = 0;
566 plist_get_data_val(p_manifest, (char**)&manifest, &m4m_len);
567 manifest_size = m4m_len;
568 plist_free(response);
569 printf("Done.\n");
570 }
571 sig = manifest;
572 sig_length = manifest_size;
573
574 imagetype = "Personalized";
314 } 575 }
315 576
316 char *targetname = NULL; 577 char *targetname = NULL;
@@ -324,11 +585,16 @@ int main(int argc, char **argv)
324 goto leave; 585 goto leave;
325 } 586 }
326 587
327
328 if (!imagetype) { 588 if (!imagetype) {
329 imagetype = "Developer"; 589 imagetype = "Developer";
330 } 590 }
331 591
592 if (!mim) {
593 if (mobile_image_mounter_start_service(device, &mim, TOOL_NAME) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
594 goto error_out;
595 }
596 }
597
332 switch(disk_image_upload_type) { 598 switch(disk_image_upload_type) {
333 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE: 599 case DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE:
334 printf("Uploading %s\n", image_path); 600 printf("Uploading %s\n", image_path);
@@ -403,7 +669,7 @@ int main(int argc, char **argv)
403 printf("done.\n"); 669 printf("done.\n");
404 670
405 printf("Mounting...\n"); 671 printf("Mounting...\n");
406 err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result); 672 err = mobile_image_mounter_mount_image_with_options(mim, mountname, sig, sig_length, imagetype, mount_options, &result);
407 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) { 673 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
408 if (result) { 674 if (result) {
409 plist_t node = plist_dict_get_item(result, "Status"); 675 plist_t node = plist_dict_get_item(result, "Status");
@@ -435,7 +701,10 @@ int main(int argc, char **argv)
435 printf("unexpected result:\n"); 701 printf("unexpected result:\n");
436 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); 702 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
437 } 703 }
438 704 node = plist_dict_get_item(result, "DetailedError");
705 if (node) {
706 printf("DetailedError: %s\n", plist_get_string_ptr(node, NULL));
707 }
439 } else { 708 } else {
440 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0); 709 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
441 } 710 }
@@ -444,6 +713,37 @@ int main(int argc, char **argv)
444 printf("Error: mount_image returned %d\n", err); 713 printf("Error: mount_image returned %d\n", err);
445 714
446 } 715 }
716 } else if (cmd == CMD_UNMOUNT) {
717 err = mobile_image_mounter_unmount_image(mim, argv[0]);
718 switch (err) {
719 case MOBILE_IMAGE_MOUNTER_E_SUCCESS:
720 printf("Success\n");
721 res = 0;
722 break;
723 case MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED:
724 printf("Error: '%s' is not mounted\n", argv[0]);
725 res = 1;
726 break;
727 case MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED:
728 printf("Error: 'unmount' is not supported on this device\n");
729 res = 1;
730 break;
731 case MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED:
732 printf("Error: device is locked\n");
733 res = 1;
734 break;
735 default:
736 printf("Error: unmount returned %d\n", err);
737 break;
738 }
739 } else if (cmd == CMD_DEVMODESTATUS) {
740 err = mobile_image_mounter_query_developer_mode_status(mim, &result);
741 if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
742 res = 0;
743 plist_write_to_stream(result, stdout, (xml_mode) ? PLIST_FORMAT_XML : PLIST_FORMAT_LIMD, 0);
744 } else {
745 printf("Error: query_developer_mode_status returned %d\n", err);
746 }
447 } 747 }
448 748
449 if (result) { 749 if (result) {
@@ -466,7 +766,7 @@ leave:
466 idevice_free(device); 766 idevice_free(device);
467 767
468 if (image_path) 768 if (image_path)
469 free(image_path); 769 free(image_path);
470 if (image_sig_path) 770 if (image_sig_path)
471 free(image_sig_path); 771 free(image_sig_path);
472 772