diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile.am | 13 | ||||
-rw-r--r-- | tools/irecovery.c | 707 |
2 files changed, 720 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..ebb085c --- /dev/null +++ b/tools/Makefile.am | |||
@@ -0,0 +1,13 @@ | |||
1 | if BUILD_TOOLS | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/include | ||
3 | |||
4 | AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS) | ||
5 | AM_LDFLAGS = $(libusb_LIBS) -lreadline | ||
6 | |||
7 | bin_PROGRAMS = irecovery | ||
8 | |||
9 | irecovery_SOURCES = irecovery.c | ||
10 | irecovery_CFLAGS = $(AM_CFLAGS) | ||
11 | irecovery_LDFLAGS = $(AM_LDFLAGS) | ||
12 | irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la | ||
13 | endif | ||
diff --git a/tools/irecovery.c b/tools/irecovery.c new file mode 100644 index 0000000..61d053a --- /dev/null +++ b/tools/irecovery.c | |||
@@ -0,0 +1,707 @@ | |||
1 | /* | ||
2 | * irecovery.c | ||
3 | * Software frontend for iBoot/iBSS communication with iOS devices | ||
4 | * | ||
5 | * Copyright (c) 2012-2023 Nikias Bassen <nikias@gmx.li> | ||
6 | * Copyright (c) 2012-2015 Martin Szulecki <martin.szulecki@libimobiledevice.org> | ||
7 | * Copyright (c) 2010-2011 Chronic-Dev Team | ||
8 | * Copyright (c) 2010-2011 Joshua Hill | ||
9 | * Copyright (c) 2008-2011 Nicolas Haunold | ||
10 | * | ||
11 | * All rights reserved. This program and the accompanying materials | ||
12 | * are made available under the terms of the GNU Lesser General Public License | ||
13 | * (LGPL) version 2.1 which accompanies this distribution, and is available at | ||
14 | * http://www.gnu.org/licenses/lgpl-2.1.html | ||
15 | * | ||
16 | * This library is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * Lesser General Public License for more details. | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include "config.h" | ||
24 | #endif | ||
25 | |||
26 | #define TOOL_NAME "irecovery" | ||
27 | |||
28 | #include <stdio.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <unistd.h> | ||
31 | #include <string.h> | ||
32 | #include <getopt.h> | ||
33 | #include <inttypes.h> | ||
34 | #include <libirecovery.h> | ||
35 | #include <readline/readline.h> | ||
36 | #include <readline/history.h> | ||
37 | |||
38 | #ifdef WIN32 | ||
39 | #include <windows.h> | ||
40 | #ifndef sleep | ||
41 | #define sleep(n) Sleep(1000 * n) | ||
42 | #endif | ||
43 | #endif | ||
44 | |||
45 | #define FILE_HISTORY_PATH ".irecovery" | ||
46 | #define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__) | ||
47 | |||
48 | enum { | ||
49 | kNoAction, | ||
50 | kResetDevice, | ||
51 | kStartShell, | ||
52 | kSendCommand, | ||
53 | kSendFile, | ||
54 | kSendExploit, | ||
55 | kSendScript, | ||
56 | kShowMode, | ||
57 | kRebootToNormalMode, | ||
58 | kQueryInfo, | ||
59 | kListDevices | ||
60 | }; | ||
61 | |||
62 | static unsigned int quit = 0; | ||
63 | static unsigned int verbose = 0; | ||
64 | |||
65 | void print_progress_bar(double progress); | ||
66 | int received_cb(irecv_client_t client, const irecv_event_t* event); | ||
67 | int progress_cb(irecv_client_t client, const irecv_event_t* event); | ||
68 | int precommand_cb(irecv_client_t client, const irecv_event_t* event); | ||
69 | int postcommand_cb(irecv_client_t client, const irecv_event_t* event); | ||
70 | |||
71 | static void shell_usage() | ||
72 | { | ||
73 | printf("Usage:\n"); | ||
74 | printf(" /upload FILE\t\tsend FILE to device\n"); | ||
75 | printf(" /limera1n [FILE]\trun limera1n exploit and send optional payload from FILE\n"); | ||
76 | printf(" /deviceinfo\t\tprint device information (ECID, IMEI, etc.)\n"); | ||
77 | printf(" /help\t\t\tshow this help\n"); | ||
78 | printf(" /exit\t\t\texit interactive shell\n"); | ||
79 | } | ||
80 | |||
81 | static const char* mode_to_str(int mode) | ||
82 | { | ||
83 | switch (mode) { | ||
84 | case IRECV_K_RECOVERY_MODE_1: | ||
85 | case IRECV_K_RECOVERY_MODE_2: | ||
86 | case IRECV_K_RECOVERY_MODE_3: | ||
87 | case IRECV_K_RECOVERY_MODE_4: | ||
88 | return "Recovery"; | ||
89 | break; | ||
90 | case IRECV_K_DFU_MODE: | ||
91 | return "DFU"; | ||
92 | break; | ||
93 | case IRECV_K_PORT_DFU_MODE: | ||
94 | return "Port DFU"; | ||
95 | break; | ||
96 | case IRECV_K_WTF_MODE: | ||
97 | return "WTF"; | ||
98 | break; | ||
99 | default: | ||
100 | return "Unknown"; | ||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) | ||
106 | { | ||
107 | FILE *f; | ||
108 | uint64_t size; | ||
109 | |||
110 | *length = 0; | ||
111 | |||
112 | f = fopen(filename, "rb"); | ||
113 | if (!f) { | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | fseek(f, 0, SEEK_END); | ||
118 | size = ftell(f); | ||
119 | rewind(f); | ||
120 | |||
121 | if (size == 0) { | ||
122 | fclose(f); | ||
123 | return; | ||
124 | } | ||
125 | |||
126 | *buffer = (char*)malloc(sizeof(char)*(size+1)); | ||
127 | fread(*buffer, sizeof(char), size, f); | ||
128 | fclose(f); | ||
129 | |||
130 | *length = size; | ||
131 | } | ||
132 | |||
133 | static void print_hex(unsigned char *buf, size_t len) | ||
134 | { | ||
135 | size_t i; | ||
136 | for (i = 0; i < len; i++) { | ||
137 | printf("%02x", buf[i]); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void print_device_info(irecv_client_t client) | ||
142 | { | ||
143 | int ret, mode; | ||
144 | irecv_device_t device = NULL; | ||
145 | const struct irecv_device_info *devinfo = irecv_get_device_info(client); | ||
146 | if (devinfo) { | ||
147 | printf("CPID: 0x%04x\n", devinfo->cpid); | ||
148 | printf("CPRV: 0x%02x\n", devinfo->cprv); | ||
149 | printf("BDID: 0x%02x\n", devinfo->bdid); | ||
150 | printf("ECID: 0x%016" PRIx64 "\n", devinfo->ecid); | ||
151 | printf("CPFM: 0x%02x\n", devinfo->cpfm); | ||
152 | printf("SCEP: 0x%02x\n", devinfo->scep); | ||
153 | printf("IBFL: 0x%02x\n", devinfo->ibfl); | ||
154 | printf("SRTG: %s\n", (devinfo->srtg) ? devinfo->srtg : "N/A"); | ||
155 | printf("SRNM: %s\n", (devinfo->srnm) ? devinfo->srnm : "N/A"); | ||
156 | printf("IMEI: %s\n", (devinfo->imei) ? devinfo->imei : "N/A"); | ||
157 | printf("NONC: "); | ||
158 | if (devinfo->ap_nonce) { | ||
159 | print_hex(devinfo->ap_nonce, devinfo->ap_nonce_size); | ||
160 | } else { | ||
161 | printf("N/A"); | ||
162 | } | ||
163 | printf("\n"); | ||
164 | printf("SNON: "); | ||
165 | if (devinfo->sep_nonce) { | ||
166 | print_hex(devinfo->sep_nonce, devinfo->sep_nonce_size); | ||
167 | } else { | ||
168 | printf("N/A"); | ||
169 | } | ||
170 | printf("\n"); | ||
171 | char* p = strstr(devinfo->serial_string, "PWND:["); | ||
172 | if (p) { | ||
173 | p+=6; | ||
174 | char* pend = strchr(p, ']'); | ||
175 | if (pend) { | ||
176 | printf("PWND: %.*s\n", (int)(pend-p), p); | ||
177 | } | ||
178 | } | ||
179 | } else { | ||
180 | printf("Could not get device info?!\n"); | ||
181 | } | ||
182 | |||
183 | ret = irecv_get_mode(client, &mode); | ||
184 | if (ret == IRECV_E_SUCCESS) { | ||
185 | switch (devinfo->pid) { | ||
186 | case 0x1881: | ||
187 | printf("MODE: DFU via Debug USB (KIS)\n"); | ||
188 | break; | ||
189 | default: | ||
190 | printf("MODE: %s\n", mode_to_str(mode)); | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | irecv_devices_get_device_by_client(client, &device); | ||
196 | if (device) { | ||
197 | printf("PRODUCT: %s\n", device->product_type); | ||
198 | printf("MODEL: %s\n", device->hardware_model); | ||
199 | printf("NAME: %s\n", device->display_name); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | static void print_devices() | ||
204 | { | ||
205 | struct irecv_device *devices = irecv_devices_get_all(); | ||
206 | struct irecv_device *device = NULL; | ||
207 | int i = 0; | ||
208 | |||
209 | for (i = 0; devices[i].product_type != NULL; i++) { | ||
210 | device = &devices[i]; | ||
211 | |||
212 | printf("%s %s 0x%02x 0x%04x %s\n", device->product_type, device->hardware_model, device->board_id, device->chip_id, device->display_name); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static int _is_breq_command(const char* cmd) | ||
217 | { | ||
218 | return ( | ||
219 | !strcmp(cmd, "go") | ||
220 | || !strcmp(cmd, "bootx") | ||
221 | || !strcmp(cmd, "reboot") | ||
222 | || !strcmp(cmd, "memboot") | ||
223 | ); | ||
224 | } | ||
225 | |||
226 | static void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) | ||
227 | { | ||
228 | char* cmd = strdup((char*)command); | ||
229 | char* action = strtok(cmd, " "); | ||
230 | |||
231 | if (!strcmp(cmd, "/exit")) { | ||
232 | quit = 1; | ||
233 | } else if (!strcmp(cmd, "/help")) { | ||
234 | shell_usage(); | ||
235 | } else if (!strcmp(cmd, "/upload")) { | ||
236 | char* filename = strtok(NULL, " "); | ||
237 | debug("Uploading file %s\n", filename); | ||
238 | if (filename != NULL) { | ||
239 | irecv_send_file(client, filename, 0); | ||
240 | } | ||
241 | } else if (!strcmp(cmd, "/deviceinfo")) { | ||
242 | print_device_info(client); | ||
243 | } else if (!strcmp(cmd, "/limera1n")) { | ||
244 | char* filename = strtok(NULL, " "); | ||
245 | debug("Sending limera1n payload %s\n", filename); | ||
246 | if (filename != NULL) { | ||
247 | irecv_send_file(client, filename, 0); | ||
248 | } | ||
249 | irecv_trigger_limera1n_exploit(client); | ||
250 | } else if (!strcmp(cmd, "/execute")) { | ||
251 | char* filename = strtok(NULL, " "); | ||
252 | debug("Executing script %s\n", filename); | ||
253 | if (filename != NULL) { | ||
254 | char* buffer = NULL; | ||
255 | uint64_t buffer_length = 0; | ||
256 | buffer_read_from_filename(filename, &buffer, &buffer_length); | ||
257 | if (buffer) { | ||
258 | buffer[buffer_length] = '\0'; | ||
259 | irecv_execute_script(client, buffer); | ||
260 | free(buffer); | ||
261 | } else { | ||
262 | printf("Could not read file '%s'\n", filename); | ||
263 | } | ||
264 | } | ||
265 | } else { | ||
266 | printf("Unsupported command %s. Use /help to get a list of available commands.\n", cmd); | ||
267 | } | ||
268 | |||
269 | free(action); | ||
270 | } | ||
271 | |||
272 | static void load_command_history() | ||
273 | { | ||
274 | read_history(FILE_HISTORY_PATH); | ||
275 | } | ||
276 | |||
277 | static void append_command_to_history(char* cmd) | ||
278 | { | ||
279 | add_history(cmd); | ||
280 | write_history(FILE_HISTORY_PATH); | ||
281 | } | ||
282 | |||
283 | static void init_shell(irecv_client_t client) | ||
284 | { | ||
285 | irecv_error_t error = 0; | ||
286 | load_command_history(); | ||
287 | irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); | ||
288 | irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL); | ||
289 | irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL); | ||
290 | irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL); | ||
291 | while (!quit) { | ||
292 | error = irecv_receive(client); | ||
293 | if (error != IRECV_E_SUCCESS) { | ||
294 | debug("%s\n", irecv_strerror(error)); | ||
295 | break; | ||
296 | } | ||
297 | |||
298 | char* cmd = readline("> "); | ||
299 | if (cmd && *cmd) { | ||
300 | if (_is_breq_command(cmd)) { | ||
301 | error = irecv_send_command_breq(client, cmd, 1); | ||
302 | } else { | ||
303 | error = irecv_send_command(client, cmd); | ||
304 | } | ||
305 | if (error != IRECV_E_SUCCESS) { | ||
306 | quit = 1; | ||
307 | } | ||
308 | |||
309 | append_command_to_history(cmd); | ||
310 | free(cmd); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | int received_cb(irecv_client_t client, const irecv_event_t* event) | ||
316 | { | ||
317 | if (event->type == IRECV_RECEIVED) { | ||
318 | int i = 0; | ||
319 | int size = event->size; | ||
320 | const char* data = event->data; | ||
321 | for (i = 0; i < size; i++) { | ||
322 | printf("%c", data[i]); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | int precommand_cb(irecv_client_t client, const irecv_event_t* event) | ||
330 | { | ||
331 | if (event->type == IRECV_PRECOMMAND) { | ||
332 | if (event->data[0] == '/') { | ||
333 | parse_command(client, (unsigned char*)event->data, event->size); | ||
334 | return -1; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | int postcommand_cb(irecv_client_t client, const irecv_event_t* event) | ||
342 | { | ||
343 | char* value = NULL; | ||
344 | char* action = NULL; | ||
345 | char* command = NULL; | ||
346 | char* argument = NULL; | ||
347 | irecv_error_t error = IRECV_E_SUCCESS; | ||
348 | |||
349 | if (event->type == IRECV_POSTCOMMAND) { | ||
350 | command = strdup(event->data); | ||
351 | action = strtok(command, " "); | ||
352 | if (!strcmp(action, "getenv")) { | ||
353 | argument = strtok(NULL, " "); | ||
354 | error = irecv_getenv(client, argument, &value); | ||
355 | if (error != IRECV_E_SUCCESS) { | ||
356 | debug("%s\n", irecv_strerror(error)); | ||
357 | free(command); | ||
358 | return error; | ||
359 | } | ||
360 | printf("%s\n", value); | ||
361 | free(value); | ||
362 | } | ||
363 | |||
364 | if (!strcmp(action, "reboot")) { | ||
365 | quit = 1; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | free(command); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | int progress_cb(irecv_client_t client, const irecv_event_t* event) | ||
375 | { | ||
376 | if (event->type == IRECV_PROGRESS) { | ||
377 | print_progress_bar(event->progress); | ||
378 | } | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | void print_progress_bar(double progress) | ||
384 | { | ||
385 | int i = 0; | ||
386 | |||
387 | if (progress < 0) { | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | if (progress > 100) { | ||
392 | progress = 100; | ||
393 | } | ||
394 | |||
395 | printf("\r["); | ||
396 | |||
397 | for (i = 0; i < 50; i++) { | ||
398 | if (i < progress / 2) { | ||
399 | printf("="); | ||
400 | } else { | ||
401 | printf(" "); | ||
402 | } | ||
403 | } | ||
404 | |||
405 | printf("] %3.1f%%", progress); | ||
406 | |||
407 | fflush(stdout); | ||
408 | |||
409 | if (progress == 100) { | ||
410 | printf("\n"); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | static void print_usage(int argc, char **argv) | ||
415 | { | ||
416 | char *name = NULL; | ||
417 | name = strrchr(argv[0], '/'); | ||
418 | printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0])); | ||
419 | printf("\n"); | ||
420 | printf("Interact with an iOS device in DFU or recovery mode.\n"); | ||
421 | printf("\n"); | ||
422 | printf("OPTIONS:\n"); | ||
423 | printf(" -i, --ecid ECID\tconnect to specific device by its ECID\n"); | ||
424 | printf(" -c, --command CMD\trun CMD on device\n"); | ||
425 | printf(" -m, --mode\t\tprint current device mode\n"); | ||
426 | printf(" -f, --file FILE\tsend file to device\n"); | ||
427 | printf(" -k, --payload FILE\tsend limera1n usb exploit payload from FILE\n"); | ||
428 | printf(" -r, --reset\t\treset client\n"); | ||
429 | printf(" -n, --normal\t\treboot device into normal mode (exit recovery loop)\n"); | ||
430 | printf(" -e, --script FILE\texecutes recovery script from FILE\n"); | ||
431 | printf(" -s, --shell\t\tstart an interactive shell\n"); | ||
432 | printf(" -q, --query\t\tquery device info\n"); | ||
433 | printf(" -a, --devices\t\tlist information for all known devices\n"); | ||
434 | printf(" -v, --verbose\t\tenable verbose output, repeat for higher verbosity\n"); | ||
435 | printf(" -h, --help\t\tprints this usage information\n"); | ||
436 | printf(" -V, --version\t\tprints version information\n"); | ||
437 | printf("\n"); | ||
438 | printf("Homepage: <" PACKAGE_URL ">\n"); | ||
439 | printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n"); | ||
440 | } | ||
441 | |||
442 | int main(int argc, char* argv[]) | ||
443 | { | ||
444 | static struct option longopts[] = { | ||
445 | { "ecid", required_argument, NULL, 'i' }, | ||
446 | { "command", required_argument, NULL, 'c' }, | ||
447 | { "mode", no_argument, NULL, 'm' }, | ||
448 | { "file", required_argument, NULL, 'f' }, | ||
449 | { "payload", required_argument, NULL, 'k' }, | ||
450 | { "reset", no_argument, NULL, 'r' }, | ||
451 | { "normal", no_argument, NULL, 'n' }, | ||
452 | { "script", required_argument, NULL, 'e' }, | ||
453 | { "shell", no_argument, NULL, 's' }, | ||
454 | { "query", no_argument, NULL, 'q' }, | ||
455 | { "devices", no_argument, NULL, 'a' }, | ||
456 | { "verbose", no_argument, NULL, 'v' }, | ||
457 | { "help", no_argument, NULL, 'h' }, | ||
458 | { "version", no_argument, NULL, 'V' }, | ||
459 | { NULL, 0, NULL, 0 } | ||
460 | }; | ||
461 | int i = 0; | ||
462 | int opt = 0; | ||
463 | int action = kNoAction; | ||
464 | uint64_t ecid = 0; | ||
465 | int mode = -1; | ||
466 | char* argument = NULL; | ||
467 | irecv_error_t error = 0; | ||
468 | |||
469 | char* buffer = NULL; | ||
470 | uint64_t buffer_length = 0; | ||
471 | |||
472 | if (argc == 1) { | ||
473 | print_usage(argc, argv); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | while ((opt = getopt_long(argc, argv, "i:vVhrsmnc:f:e:k:qa", longopts, NULL)) > 0) { | ||
478 | switch (opt) { | ||
479 | case 'i': | ||
480 | if (optarg) { | ||
481 | char* tail = NULL; | ||
482 | ecid = strtoull(optarg, &tail, 0); | ||
483 | if (tail && (tail[0] != '\0')) { | ||
484 | ecid = 0; | ||
485 | } | ||
486 | if (ecid == 0) { | ||
487 | fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg); | ||
488 | return -1; | ||
489 | } | ||
490 | } | ||
491 | break; | ||
492 | |||
493 | case 'v': | ||
494 | verbose += 1; | ||
495 | break; | ||
496 | |||
497 | case 'h': | ||
498 | print_usage(argc, argv); | ||
499 | return 0; | ||
500 | |||
501 | case 'm': | ||
502 | action = kShowMode; | ||
503 | break; | ||
504 | |||
505 | case 'n': | ||
506 | action = kRebootToNormalMode; | ||
507 | break; | ||
508 | |||
509 | case 'r': | ||
510 | action = kResetDevice; | ||
511 | break; | ||
512 | |||
513 | case 's': | ||
514 | action = kStartShell; | ||
515 | break; | ||
516 | |||
517 | case 'f': | ||
518 | action = kSendFile; | ||
519 | argument = optarg; | ||
520 | break; | ||
521 | |||
522 | case 'c': | ||
523 | action = kSendCommand; | ||
524 | argument = optarg; | ||
525 | break; | ||
526 | |||
527 | case 'k': | ||
528 | action = kSendExploit; | ||
529 | argument = optarg; | ||
530 | break; | ||
531 | |||
532 | case 'e': | ||
533 | action = kSendScript; | ||
534 | argument = optarg; | ||
535 | break; | ||
536 | |||
537 | case 'q': | ||
538 | action = kQueryInfo; | ||
539 | break; | ||
540 | |||
541 | case 'a': | ||
542 | action = kListDevices; | ||
543 | print_devices(); | ||
544 | return 0; | ||
545 | |||
546 | case 'V': | ||
547 | printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); | ||
548 | return 0; | ||
549 | |||
550 | default: | ||
551 | fprintf(stderr, "Unknown argument\n"); | ||
552 | return -1; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | if (action == kNoAction) { | ||
557 | fprintf(stderr, "ERROR: Missing action option\n"); | ||
558 | print_usage(argc, argv); | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | if (verbose) | ||
563 | irecv_set_debug_level(verbose); | ||
564 | |||
565 | irecv_client_t client = NULL; | ||
566 | for (i = 0; i <= 5; i++) { | ||
567 | debug("Attempting to connect... \n"); | ||
568 | |||
569 | irecv_error_t err = irecv_open_with_ecid(&client, ecid); | ||
570 | if (err == IRECV_E_UNSUPPORTED) { | ||
571 | fprintf(stderr, "ERROR: %s\n", irecv_strerror(err)); | ||
572 | return -1; | ||
573 | } | ||
574 | else if (err != IRECV_E_SUCCESS) | ||
575 | sleep(1); | ||
576 | else | ||
577 | break; | ||
578 | |||
579 | if (i == 5) { | ||
580 | fprintf(stderr, "ERROR: %s\n", irecv_strerror(err)); | ||
581 | return -1; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | irecv_device_t device = NULL; | ||
586 | irecv_devices_get_device_by_client(client, &device); | ||
587 | if (device) | ||
588 | debug("Connected to %s, model %s, cpid 0x%04x, bdid 0x%02x\n", device->product_type, device->hardware_model, device->chip_id, device->board_id); | ||
589 | |||
590 | const struct irecv_device_info *devinfo = irecv_get_device_info(client); | ||
591 | |||
592 | switch (action) { | ||
593 | case kResetDevice: | ||
594 | irecv_reset(client); | ||
595 | break; | ||
596 | |||
597 | case kSendFile: | ||
598 | irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); | ||
599 | error = irecv_send_file(client, argument, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); | ||
600 | debug("%s\n", irecv_strerror(error)); | ||
601 | break; | ||
602 | |||
603 | case kSendCommand: | ||
604 | if (devinfo->pid == 0x1881) { | ||
605 | printf("Shell is not available in Debug USB (KIS) mode.\n"); | ||
606 | break; | ||
607 | } | ||
608 | if (_is_breq_command(argument)) { | ||
609 | error = irecv_send_command_breq(client, argument, 1); | ||
610 | } else { | ||
611 | error = irecv_send_command(client, argument); | ||
612 | } | ||
613 | debug("%s\n", irecv_strerror(error)); | ||
614 | break; | ||
615 | |||
616 | case kSendExploit: | ||
617 | if (devinfo->pid == 0x1881) { | ||
618 | printf("Shell is not available in Debug USB (KIS) mode.\n"); | ||
619 | break; | ||
620 | } | ||
621 | if (argument != NULL) { | ||
622 | irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL); | ||
623 | error = irecv_send_file(client, argument, 0); | ||
624 | if (error != IRECV_E_SUCCESS) { | ||
625 | debug("%s\n", irecv_strerror(error)); | ||
626 | break; | ||
627 | } | ||
628 | } | ||
629 | error = irecv_trigger_limera1n_exploit(client); | ||
630 | debug("%s\n", irecv_strerror(error)); | ||
631 | break; | ||
632 | |||
633 | case kStartShell: | ||
634 | if (devinfo->pid == 0x1881) { | ||
635 | printf("This feature is not supported in Debug USB (KIS) mode.\n"); | ||
636 | break; | ||
637 | } | ||
638 | init_shell(client); | ||
639 | break; | ||
640 | |||
641 | case kSendScript: | ||
642 | if (devinfo->pid == 0x1881) { | ||
643 | printf("This feature is not supported in Debug USB (KIS) mode.\n"); | ||
644 | break; | ||
645 | } | ||
646 | buffer_read_from_filename(argument, &buffer, &buffer_length); | ||
647 | if (buffer) { | ||
648 | buffer[buffer_length] = '\0'; | ||
649 | |||
650 | error = irecv_execute_script(client, buffer); | ||
651 | if (error != IRECV_E_SUCCESS) { | ||
652 | debug("%s\n", irecv_strerror(error)); | ||
653 | } | ||
654 | |||
655 | free(buffer); | ||
656 | } else { | ||
657 | fprintf(stderr, "Could not read file '%s'\n", argument); | ||
658 | } | ||
659 | break; | ||
660 | |||
661 | case kShowMode: { | ||
662 | irecv_get_mode(client, &mode); | ||
663 | printf("%s Mode", mode_to_str(mode)); | ||
664 | if (devinfo->pid == 0x1881) { | ||
665 | printf(" via Debug USB (KIS)"); | ||
666 | } | ||
667 | printf("\n"); | ||
668 | break; | ||
669 | } | ||
670 | case kRebootToNormalMode: | ||
671 | if (devinfo->pid == 0x1881) { | ||
672 | printf("This feature is not supported in Debug USB (KIS) mode.\n"); | ||
673 | break; | ||
674 | } | ||
675 | error = irecv_setenv(client, "auto-boot", "true"); | ||
676 | if (error != IRECV_E_SUCCESS) { | ||
677 | debug("%s\n", irecv_strerror(error)); | ||
678 | break; | ||
679 | } | ||
680 | |||
681 | error = irecv_saveenv(client); | ||
682 | if (error != IRECV_E_SUCCESS) { | ||
683 | debug("%s\n", irecv_strerror(error)); | ||
684 | break; | ||
685 | } | ||
686 | |||
687 | error = irecv_reboot(client); | ||
688 | if (error != IRECV_E_SUCCESS) { | ||
689 | debug("%s\n", irecv_strerror(error)); | ||
690 | } else { | ||
691 | debug("%s\n", irecv_strerror(error)); | ||
692 | } | ||
693 | break; | ||
694 | |||
695 | case kQueryInfo: | ||
696 | print_device_info(client); | ||
697 | break; | ||
698 | |||
699 | default: | ||
700 | fprintf(stderr, "Unknown action\n"); | ||
701 | break; | ||
702 | } | ||
703 | |||
704 | irecv_close(client); | ||
705 | |||
706 | return 0; | ||
707 | } | ||