summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/Makefile.am1
-rw-r--r--docs/idevicedevmodectl.158
-rw-r--r--tools/Makefile.am6
-rw-r--r--tools/idevicedevmodectl.c451
4 files changed, 516 insertions, 0 deletions
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 9cdf82e..4a4c56f 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -16,6 +16,7 @@ man_MANS = \
16 idevicecrashreport.1 \ 16 idevicecrashreport.1 \
17 idevicename.1 \ 17 idevicename.1 \
18 idevicedebug.1 \ 18 idevicedebug.1 \
19 idevicedevmodectl.1 \
19 idevicenotificationproxy.1 \ 20 idevicenotificationproxy.1 \
20 idevicesetlocation.1 21 idevicesetlocation.1
21 22
diff --git a/docs/idevicedevmodectl.1 b/docs/idevicedevmodectl.1
new file mode 100644
index 0000000..5edaa80
--- /dev/null
+++ b/docs/idevicedevmodectl.1
@@ -0,0 +1,58 @@
1.TH "idevicedevmodectl" 1
2.SH NAME
3idevicedevmodectl \- Enable Developer Mode on iOS 16+ devices or print the current status.
4.SH SYNOPSIS
5.B idevicedevmodectl
6COMMAND
7[OPTIONS]
8
9.SH DESCRIPTION
10
11Enable Developer Mode on iOS 16+ devices or print the current status.
12
13.SH NOTE
14Passcode-protected devices will NOT allow enabling of Developer Mode from the command line. It has to be enabled on the device itself under Settings -> Privacy & Security -> Developer Mode.
15The \f[B]enable\f[] command will try to enable it, and tell you if that's the case.
16If the menu is not shown, you may use the \f[B]reveal\f[] command to reveal it.
17
18.SH COMMANDS
19.TP
20.B list
21Prints the Developer Mode status of all connected devices, or for a specific one if \f[B]\-\-udid\f[] is given.
22.TP
23.B enable
24Enable Developer Mode (device will reboot), and confirm it after device booted up again.
25.TP
26.B arm
27Arm the Developer Mode (device will reboot)
28.TP
29.B confirm
30Confirm enabling of Developer Mode
31.TP
32.B reveal
33Reveal the Developer Mode menu on the device under Settings -> Privacy & Security
34
35.SH OPTIONS
36.TP
37.B \-u, \-\-udid UDID
38target specific device by UDID
39.TP
40.B \-n, \-\-network
41connect to network device
42.TP
43.B \-d, \-\-debug
44enable communication debugging
45.TP
46.B \-h, \-\-help
47print usage information
48.TP
49.B \-v, \-\-version
50print version information
51
52.SH AUTHORS
53Nikias Bassen
54
55.SH ON THE WEB
56https://libimobiledevice.org
57
58https://github.com/libimobiledevice/libimobiledevice
diff --git a/tools/Makefile.am b/tools/Makefile.am
index e8ef3ab..47e05b2 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -27,6 +27,7 @@ bin_PROGRAMS = \
27 idevicedebugserverproxy \ 27 idevicedebugserverproxy \
28 idevicediagnostics \ 28 idevicediagnostics \
29 idevicedebug \ 29 idevicedebug \
30 idevicedevmodectl \
30 idevicenotificationproxy \ 31 idevicenotificationproxy \
31 idevicecrashreport \ 32 idevicecrashreport \
32 idevicesetlocation 33 idevicesetlocation
@@ -111,6 +112,11 @@ idevicedebug_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
111idevicedebug_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) 112idevicedebug_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
112idevicedebug_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la 113idevicedebug_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
113 114
115idevicedevmodectl_SOURCES = idevicedevmodectl.c
116idevicedevmodectl_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
117idevicedevmodectl_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
118idevicedevmodectl_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la
119
114idevicenotificationproxy_SOURCES = idevicenotificationproxy.c 120idevicenotificationproxy_SOURCES = idevicenotificationproxy.c
115idevicenotificationproxy_CFLAGS = $(AM_CFLAGS) 121idevicenotificationproxy_CFLAGS = $(AM_CFLAGS)
116idevicenotificationproxy_LDFLAGS = $(AM_LDFLAGS) 122idevicenotificationproxy_LDFLAGS = $(AM_LDFLAGS)
diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c
new file mode 100644
index 0000000..739bc13
--- /dev/null
+++ b/tools/idevicedevmodectl.c
@@ -0,0 +1,451 @@
1/*
2 * idevicedevmodectl.c
3 * List or enable Developer Mode on iOS 16+ devices
4 *
5 * Copyright (c) 2022 Nikias Bassen, All Rights Reserved.
6 *
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.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define TOOL_NAME "idevicedevmodectl"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <getopt.h>
32#include <sys/stat.h>
33#include <errno.h>
34#ifndef WIN32
35#include <signal.h>
36#endif
37
38#ifdef WIN32
39#include <windows.h>
40#define __usleep(x) Sleep(x/1000)
41#else
42#include <arpa/inet.h>
43#define __usleep(x) usleep(x)
44#endif
45
46#include <libimobiledevice/libimobiledevice.h>
47#include <libimobiledevice/lockdown.h>
48#include <libimobiledevice/property_list_service.h>
49#include <libimobiledevice-glue/utils.h>
50
51#define AMFI_LOCKDOWN_SERVICE_NAME "com.apple.amfi.lockdown"
52
53static char* udid = NULL;
54static int use_network = 0;
55
56static void print_usage(int argc, char **argv, int is_error)
57{
58 char *name = strrchr(argv[0], '/');
59 fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] COMMAND\n", (name ? name + 1: argv[0]));
60 fprintf(is_error ? stderr : stdout,
61 "\n"
62 "Enable Developer Mode on iOS 16+ devices or print the current status.\n"
63 "\n"
64 "Where COMMAND is one of:\n"
65 " list Print the Developer Mode status of all connected devices\n"
66 " or for a specific one if --udid is given.\n"
67 " enable Enable Developer Mode (device will reboot),\n"
68 " and confirm it after device booted up again.\n"
69 "\n"
70 " arm Arm the Developer Mode (device will reboot)\n"
71 " confirm Confirm enabling of Developer Mode\n"
72 " reveal Reveal the Developer Mode menu on the device\n"
73 "\n"
74 "The following OPTIONS are accepted:\n"
75 " -u, --udid UDID target specific device by UDID\n"
76 " -n, --network connect to network device\n"
77 " -d, --debug enable communication debugging\n"
78 " -h, --help print usage information\n"
79 " -v, --version print version information\n"
80 "\n"
81 "Homepage: <" PACKAGE_URL ">\n"
82 "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
83 );
84}
85
86enum {
87 OP_LIST,
88 OP_ENABLE,
89 OP_ARM,
90 OP_CONFIRM,
91 OP_REVEAL,
92 NUM_OPS
93};
94#define DEV_MODE_REVEAL 0
95#define DEV_MODE_ARM 1
96#define DEV_MODE_ENABLE 2
97
98static int get_developer_mode_status(const char* device_udid, int _use_network)
99{
100 idevice_error_t ret;
101 idevice_t device = NULL;
102 lockdownd_client_t lockdown = NULL;
103 lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
104 plist_t val = NULL;
105
106 ret = idevice_new_with_options(&device, device_udid, (_use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
107 if (ret != IDEVICE_E_SUCCESS) {
108 return -1;
109 }
110
111 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
112 idevice_free(device);
113 return -1;
114 }
115
116 lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
117 if (lerr != LOCKDOWN_E_SUCCESS) {
118 fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
119 lockdownd_client_free(lockdown);
120 idevice_free(device);
121 return -2;
122 }
123
124 uint8_t dev_mode_status = 0;
125 plist_get_bool_val(val, &dev_mode_status);
126 plist_free(val);
127
128 lockdownd_client_free(lockdown);
129 idevice_free(device);
130
131 return dev_mode_status;
132}
133
134static int amfi_service_send_msg(property_list_service_client_t amfi, plist_t msg)
135{
136 int res;
137 property_list_service_error_t perr;
138
139 perr = property_list_service_send_xml_plist(amfi, plist_copy(msg));
140 if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
141 fprintf(stderr, "Could not send request to device: %d\n", perr);
142 res = 2;
143 } else {
144 plist_t reply = NULL;
145 perr = property_list_service_receive_plist(amfi, &reply);
146 if (perr == PROPERTY_LIST_SERVICE_E_SUCCESS) {
147 uint8_t success = 0;
148 plist_t val = plist_dict_get_item(reply, "Error");
149 if (val) {
150 const char* err = plist_get_string_ptr(val, NULL);
151 fprintf(stderr, "Request failed: %s\n", err);
152 if (strstr(err, "passcode")) {
153 res = 2;
154 } else {
155 res = 1;
156 }
157 } else {
158 val = plist_dict_get_item(reply, "success");
159 if (val) {
160 plist_get_bool_val(val, &success);
161 }
162 if (success) {
163 res = 0;
164 } else {
165 res = 1;
166 }
167 }
168 } else {
169 fprintf(stderr, "Could not receive reply from device: %d\n", perr);
170 res = 2;
171 }
172 plist_free(reply);
173 }
174 return res;
175}
176
177static int amfi_send_action(idevice_t device, unsigned int action)
178{
179 lockdownd_client_t lockdown = NULL;
180 lockdownd_service_descriptor_t service = NULL;
181 lockdownd_error_t lerr;
182
183 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
184 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
185 return 1;
186 }
187
188 lerr = lockdownd_start_service(lockdown, AMFI_LOCKDOWN_SERVICE_NAME, &service);
189 if (lerr != LOCKDOWN_E_SUCCESS) {
190 fprintf(stderr, "Could not start service %s: %s\nPlease note that this feature is only available on iOS 16+.\n", AMFI_LOCKDOWN_SERVICE_NAME, lockdownd_strerror(lerr));
191 lockdownd_client_free(lockdown);
192 return 1;
193 }
194 lockdownd_client_free(lockdown);
195 lockdown = NULL;
196
197 property_list_service_client_t amfi = NULL;
198 if (property_list_service_client_new(device, service, &amfi) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
199 fprintf(stderr, "Could not connect to %s on device\n", AMFI_LOCKDOWN_SERVICE_NAME);
200 if (service)
201 lockdownd_service_descriptor_free(service);
202 idevice_free(device);
203 return 1;
204 }
205 lockdownd_service_descriptor_free(service);
206
207 plist_t dict = plist_new_dict();
208 plist_dict_set_item(dict, "action", plist_new_uint(action));
209
210 int result = amfi_service_send_msg(amfi, dict);
211 plist_free(dict);
212
213 property_list_service_client_free(amfi);
214 amfi = NULL;
215
216 return result;
217}
218
219static int device_connected = 0;
220
221static void device_event_cb(const idevice_event_t* event, void* userdata)
222{
223 if (use_network && event->conn_type != CONNECTION_NETWORK) {
224 return;
225 }
226 if (!use_network && event->conn_type != CONNECTION_USBMUXD) {
227 return;
228 }
229 if (event->event == IDEVICE_DEVICE_ADD) {
230 if (!udid) {
231 udid = strdup(event->udid);
232 }
233 if (strcmp(udid, event->udid) == 0) {
234 device_connected = 1;
235 }
236 } else if (event->event == IDEVICE_DEVICE_REMOVE) {
237 if (strcmp(udid, event->udid) == 0) {
238 device_connected = 0;
239 }
240 }
241}
242
243
244#define WAIT_INTERVAL 200000
245#define WAIT_MAX(x) (x * (1000000 / WAIT_INTERVAL))
246#define WAIT_FOR(cond, timeout) { int __repeat = WAIT_MAX(timeout); while (!(cond) && __repeat-- > 0) { __usleep(WAIT_INTERVAL); } }
247
248int main(int argc, char *argv[])
249{
250 idevice_t device = NULL;
251 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
252 lockdownd_client_t lockdown = NULL;
253 lockdownd_error_t lerr = LOCKDOWN_E_UNKNOWN_ERROR;
254 int res = 0;
255 int i;
256 int op = -1;
257 plist_t val = NULL;
258
259 int c = 0;
260 const struct option longopts[] = {
261 { "debug", no_argument, NULL, 'd' },
262 { "help", no_argument, NULL, 'h' },
263 { "udid", required_argument, NULL, 'u' },
264 { "network", no_argument, NULL, 'n' },
265 { "version", no_argument, NULL, 'v' },
266 { NULL, 0, NULL, 0}
267 };
268
269#ifndef WIN32
270 signal(SIGPIPE, SIG_IGN);
271#endif
272 /* parse cmdline args */
273 while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
274 switch (c) {
275 case 'd':
276 idevice_set_debug_level(1);
277 break;
278 case 'u':
279 if (!*optarg) {
280 fprintf(stderr, "ERROR: UDID argument must not be empty!\n");
281 print_usage(argc, argv, 1);
282 return 2;
283 }
284 udid = optarg;
285 break;
286 case 'n':
287 use_network = 1;
288 break;
289 case 'h':
290 print_usage(argc, argv, 0);
291 return 0;
292 case 'v':
293 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
294 return 0;
295 default:
296 print_usage(argc, argv, 1);
297 return 2;
298 }
299 }
300 argc -= optind;
301 argv += optind;
302
303 if (!argv[0]) {
304 fprintf(stderr, "ERROR: Missing command.\n");
305 print_usage(argc+optind, argv-optind, 1);
306 return 2;
307 }
308
309 i = 0;
310 if (!strcmp(argv[i], "list")) {
311 op = OP_LIST;
312 }
313 else if (!strcmp(argv[i], "enable")) {
314 op = OP_ENABLE;
315 }
316 else if (!strcmp(argv[i], "arm")) {
317 op = OP_ARM;
318 }
319 else if (!strcmp(argv[i], "confirm")) {
320 op = OP_CONFIRM;
321 }
322 else if (!strcmp(argv[i], "reveal")) {
323 op = OP_REVEAL;
324 }
325
326 if ((op == -1) || (op >= NUM_OPS)) {
327 fprintf(stderr, "ERROR: Unsupported command '%s'\n", argv[i]);
328 print_usage(argc+optind, argv-optind, 1);
329 return 2;
330 }
331
332 if (op == OP_LIST) {
333 idevice_info_t *dev_list = NULL;
334
335 if (idevice_get_device_list_extended(&dev_list, &i) < 0) {
336 fprintf(stderr, "ERROR: Unable to retrieve device list!\n");
337 return -1;
338 }
339 if (i > 0) {
340 printf("%-40s %s\n", "Device", "DeveloperMode");
341 }
342 for (i = 0; dev_list[i] != NULL; i++) {
343 if (dev_list[i]->conn_type == CONNECTION_USBMUXD && use_network) continue;
344 if (dev_list[i]->conn_type == CONNECTION_NETWORK && !use_network) continue;
345 if (udid && (strcmp(dev_list[i]->udid, udid) != 0)) continue;
346 int mode = get_developer_mode_status(dev_list[i]->udid, use_network);
347 const char *mode_str = "N/A";
348 if (mode == 1) {
349 mode_str = "enabled";
350 } else if (mode == 0) {
351 mode_str = "disabled";
352 }
353 printf("%-40s %s\n", dev_list[i]->udid, mode_str);
354 }
355 idevice_device_list_extended_free(dev_list);
356
357 return 0;
358 }
359
360 idevice_subscription_context_t context = NULL;
361 idevice_events_subscribe(&context, device_event_cb, NULL);
362
363 WAIT_FOR(device_connected, 10);
364
365 ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
366 if (ret != IDEVICE_E_SUCCESS) {
367 if (udid) {
368 printf("No device found with udid %s.\n", udid);
369 } else {
370 printf("No device found.\n");
371 }
372 return 1;
373 }
374
375 if (!udid) {
376 idevice_get_udid(device, &udid);
377 }
378
379 if (LOCKDOWN_E_SUCCESS != (lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME))) {
380 fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", lerr);
381 idevice_free(device);
382 return 1;
383 }
384
385 lerr = lockdownd_get_value(lockdown, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
386 lockdownd_client_free(lockdown);
387 lockdown = NULL;
388 if (lerr != LOCKDOWN_E_SUCCESS) {
389 fprintf(stderr, "ERROR: Could not get DeveloperModeStatus: %s\nPlease note that this feature is only available on iOS 16+.\n", lockdownd_strerror(lerr));
390 idevice_free(device);
391 return 1;
392 }
393
394 uint8_t dev_mode_status = 0;
395 plist_get_bool_val(val, &dev_mode_status);
396
397 if ((op == OP_ENABLE || op == OP_ARM) && dev_mode_status) {
398 if (dev_mode_status) {
399 printf("DeveloperMode is already enabled.\n");
400 return 0;
401 }
402 res = 0;
403 } else {
404 if (op == OP_ENABLE || op == OP_ARM) {
405 res = amfi_send_action(device, DEV_MODE_ARM);
406 if (res == 0) {
407 if (op == OP_ARM) {
408 printf("%s: Developer Mode armed, device will reboot now.\n", udid);
409 } else {
410 printf("%s: Developer Mode armed, waiting for reboot...\n", udid);
411
412 // waiting for device to disconnect...
413 WAIT_FOR(!device_connected, 20);
414
415 // waiting for device to reconnect...
416 WAIT_FOR(device_connected, 60);
417
418 res = amfi_send_action(device, DEV_MODE_ENABLE);
419 if (res == 0) {
420 printf("%s: Developer Mode successfully enabled.\n", udid);
421 } else {
422 printf("%s: Failed to enable developer mode (%d)\n", udid, res);
423 }
424 }
425 } else if (res == 2) {
426 amfi_send_action(device, DEV_MODE_REVEAL);
427 printf("%s: Developer Mode could not be enabled because the device has a passcode set. You have to enable it on the device itself under Settings -> Privacy & Security -> Developer Mode.\n", udid);
428 } else {
429 printf("%s: Failed to arm Developer Mode (%d)\n", udid, res);
430 }
431 } else if (op == OP_CONFIRM) {
432 res = amfi_send_action(device, DEV_MODE_ENABLE);
433 if (res == 0) {
434 printf("%s: Developer Mode successfully enabled.\n", udid);
435 } else {
436 printf("%s: Failed to enable Developer Mode (%d)\n", udid, res);
437 }
438 } else if (op == OP_REVEAL) {
439 res = amfi_send_action(device, DEV_MODE_REVEAL);
440 if (res == 0) {
441 printf("%s: Developer Mode menu revealed successfully.\n", udid);
442 } else {
443 printf("%s: Failed to reveal Developer Mode menu (%d)\n", udid, res);
444 }
445 }
446 }
447
448 idevice_free(device);
449
450 return res;
451}