summaryrefslogtreecommitdiffstats
path: root/src/debugserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/debugserver.c')
-rw-r--r--src/debugserver.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/src/debugserver.c b/src/debugserver.c
new file mode 100644
index 0000000..74ade8a
--- /dev/null
+++ b/src/debugserver.c
@@ -0,0 +1,657 @@
1/*
2 * debugserver.c
3 * com.apple.debugserver service implementation.
4 *
5 * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2014-2015 Martin Szulecki All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26#include <string.h>
27#include <stdlib.h>
28#define _GNU_SOURCE 1
29#define __USE_GNU 1
30#include <stdio.h>
31
32#include <libimobiledevice-glue/utils.h>
33
34#include "debugserver.h"
35#include "lockdown.h"
36#include "common/debug.h"
37#include "asprintf.h"
38
39/**
40 * Convert a service_error_t value to a debugserver_error_t value.
41 * Used internally to get correct error codes.
42 *
43 * @param err An service_error_t error code
44 *
45 * @return A matching debugserver_error_t error code,
46 * DEBUGSERVER_E_UNKNOWN_ERROR otherwise.
47 */
48static debugserver_error_t debugserver_error(service_error_t err)
49{
50 switch (err) {
51 case SERVICE_E_SUCCESS:
52 return DEBUGSERVER_E_SUCCESS;
53 case SERVICE_E_INVALID_ARG:
54 return DEBUGSERVER_E_INVALID_ARG;
55 case SERVICE_E_MUX_ERROR:
56 return DEBUGSERVER_E_MUX_ERROR;
57 case SERVICE_E_SSL_ERROR:
58 return DEBUGSERVER_E_SSL_ERROR;
59 case SERVICE_E_TIMEOUT:
60 return DEBUGSERVER_E_TIMEOUT;
61 default:
62 break;
63 }
64 return DEBUGSERVER_E_UNKNOWN_ERROR;
65}
66
67debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client)
68{
69 *client = NULL;
70
71 if (!device || !service || service->port == 0 || !client || *client) {
72 debug_info("Incorrect parameter passed to debugserver_client_new.");
73 return DEBUGSERVER_E_INVALID_ARG;
74 }
75
76 debug_info("Creating debugserver_client, port = %d.", service->port);
77
78 service_client_t parent = NULL;
79 debugserver_error_t ret = debugserver_error(service_client_new(device, service, &parent));
80 if (ret != DEBUGSERVER_E_SUCCESS) {
81 debug_info("Creating base service client failed. Error: %i", ret);
82 return ret;
83 }
84
85 if (service->identifier && (strcmp(service->identifier, DEBUGSERVER_SECURE_SERVICE_NAME) != 0)) {
86 service_disable_bypass_ssl(parent, 1);
87 }
88
89 debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private));
90 client_loc->parent = parent;
91 client_loc->noack_mode = 0;
92 client_loc->cancel_receive = NULL;
93 client_loc->receive_loop_timeout = 1000;
94
95 *client = client_loc;
96
97 debug_info("debugserver_client successfully created.");
98 return DEBUGSERVER_E_SUCCESS;
99}
100
101debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label)
102{
103 debugserver_error_t err = DEBUGSERVER_E_UNKNOWN_ERROR;
104 service_client_factory_start_service(device, DEBUGSERVER_SECURE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err);
105 if (err != DEBUGSERVER_E_SUCCESS) {
106 err = DEBUGSERVER_E_UNKNOWN_ERROR;
107 service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err);
108 }
109 return err;
110}
111
112debugserver_error_t debugserver_client_free(debugserver_client_t client)
113{
114 if (!client)
115 return DEBUGSERVER_E_INVALID_ARG;
116
117 debugserver_error_t err = debugserver_error(service_client_free(client->parent));
118 client->parent = NULL;
119 free(client);
120
121 return err;
122}
123
124debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent)
125{
126 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
127 int bytes = 0;
128
129 if (!client || !data || (size == 0)) {
130 return DEBUGSERVER_E_INVALID_ARG;
131 }
132
133 debug_info("sending %d bytes", size);
134 res = debugserver_error(service_send(client->parent, data, size, (uint32_t*)&bytes));
135 if (bytes <= 0) {
136 debug_info("ERROR: sending to device failed.");
137 }
138 if (sent) {
139 *sent = (uint32_t)bytes;
140 }
141
142 return res;
143}
144
145debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
146{
147 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
148 int bytes = 0;
149
150 if (!client || !data || (size == 0)) {
151 return DEBUGSERVER_E_INVALID_ARG;
152 }
153
154 res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
155 if (bytes <= 0 && res != DEBUGSERVER_E_TIMEOUT) {
156 debug_info("Could not read data, error %d", res);
157 }
158 if (received) {
159 *received = (uint32_t)bytes;
160 }
161
162 return (bytes > 0) ? DEBUGSERVER_E_SUCCESS : res;
163}
164
165debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received)
166{
167 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
168 do {
169 /* Is this allowed to return DEBUGSERVER_E_TIMEOUT and also set data and received? */
170 res = debugserver_client_receive_with_timeout(client, data, size, received, client->receive_loop_timeout);
171 } while (res == DEBUGSERVER_E_TIMEOUT && client->cancel_receive != NULL && !client->cancel_receive());
172 return res;
173}
174
175debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command)
176{
177 int i;
178 debugserver_command_t tmp = (debugserver_command_t) malloc(sizeof(struct debugserver_command_private));
179
180 /* copy name */
181 tmp->name = strdup(name);
182
183 /* copy arguments */
184 tmp->argc = argc;
185 tmp->argv = NULL;
186 if (argc > 0) {
187 tmp->argv = malloc(sizeof(char*) * (argc + 2));
188 for (i = 0; i < argc; i++) {
189 tmp->argv[i] = strdup(argv[i]);
190 }
191 tmp->argv[i+1] = NULL;
192 }
193
194 /* return */
195 *command = tmp;
196
197 return DEBUGSERVER_E_SUCCESS;
198}
199
200debugserver_error_t debugserver_command_free(debugserver_command_t command)
201{
202 int i;
203 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
204
205 if (!command)
206 return DEBUGSERVER_E_INVALID_ARG;
207
208 if (command) {
209 if (command->name)
210 free(command->name);
211 if (command->argv && command->argc) {
212 for (i = 0; i < command->argc; i++) {
213 free(command->argv[i]);
214 }
215 free(command->argv);
216 }
217 free(command);
218 res = DEBUGSERVER_E_SUCCESS;
219 }
220
221 return res;
222}
223
224static int debugserver_hex2int(char c)
225{
226 if (c >= '0' && c <= '9')
227 return c - '0';
228 else if (c >= 'a' && c <= 'f')
229 return 10 + c - 'a';
230 else if (c >= 'A' && c <= 'F')
231 return 10 + c - 'A';
232 else
233 return c;
234}
235
236static char debugserver_int2hex(int x)
237{
238 const char *hexchars = "0123456789ABCDEF";
239 return hexchars[x];
240}
241
242#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex(((byte) >> 0x4) & 0xf)
243#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex((byte) & 0xf)
244#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) (((byte) >> 0x4) & 0xf)
245#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) ((byte) & 0xf)
246
247static uint32_t debugserver_get_checksum_for_buffer(const char* buffer, uint32_t size)
248{
249 uint32_t checksum = 0;
250 uint32_t i;
251
252 for (i = 0; i < size; i++) {
253 checksum += buffer[i];
254 }
255
256 return checksum;
257}
258
259static int debugserver_response_is_checksum_valid(const char* response, uint32_t size)
260{
261 uint32_t checksum = 0;
262 if ((size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1) > 0)
263 checksum = debugserver_get_checksum_for_buffer(&response[1], size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1);
264
265 debug_info("checksum: 0x%x", checksum);
266
267 if ((unsigned)debugserver_hex2int(response[size - 2]) != DEBUGSERVER_HEX_DECODE_FIRST_BYTE(checksum))
268 return 0;
269
270 if ((unsigned)debugserver_hex2int(response[size - 1]) != DEBUGSERVER_HEX_DECODE_SECOND_BYTE(checksum))
271 return 0;
272
273 debug_info("valid checksum");
274
275 return 1;
276}
277
278void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length)
279{
280 uint32_t position;
281 uint32_t index;
282 uint32_t length = strlen(buffer);
283 *encoded_length = (2 * length) + DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1;
284
285 *encoded_buffer = malloc(sizeof(char) * (*encoded_length));
286 memset(*encoded_buffer, '\0', *encoded_length);
287 for (position = 0, index = 0; index < length; index++) {
288 position = (index * (2 * sizeof(char)));
289 (*encoded_buffer)[position] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(buffer[index]);
290 (*encoded_buffer)[position + 1] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(buffer[index]);
291 }
292}
293
294void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer)
295{
296 *buffer = malloc(sizeof(char) * ((encoded_length / 2)+1));
297 char* t = *buffer;
298 const char *f = encoded_buffer;
299 const char *fend = f + encoded_length;
300 while (f < fend) {
301 *t++ = debugserver_hex2int(*f) << 4 | debugserver_hex2int(f[1]);
302 f += 2;
303 }
304 *t = '\0';
305}
306
307static void debugserver_format_command(const char* prefix, const char* command, const char* arguments, int calculate_checksum, char** buffer, uint32_t* size)
308{
309 char checksum_hash[DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1] = {'#', '0', '0', '\0'};
310 char* encoded = NULL;
311 uint32_t encoded_length = 0;
312
313 if (arguments) {
314 /* arguments must be hex encoded */
315 debugserver_encode_string(arguments, &encoded, &encoded_length);
316 } else {
317 encoded = NULL;
318 }
319
320 char* encoded_command = string_concat(command, encoded, NULL);
321 encoded_length = strlen(encoded_command);
322
323 if (calculate_checksum) {
324 uint32_t checksum = debugserver_get_checksum_for_buffer(encoded_command, encoded_length);
325 checksum_hash[1] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(checksum);
326 checksum_hash[2] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(checksum);
327 }
328
329 *buffer = string_concat(prefix, encoded_command, checksum_hash, NULL);
330 *size = strlen(prefix) + strlen(encoded_command) + DEBUGSERVER_CHECKSUM_HASH_LENGTH;
331
332 debug_info("formatted command: %s size: %d checksum: 0x%s", *buffer, *size, checksum_hash);
333
334 if (encoded_command)
335 free(encoded_command);
336
337 if (encoded)
338 free(encoded);
339}
340
341static debugserver_error_t debugserver_client_send_ack(debugserver_client_t client)
342{
343 debug_info("sending ACK");
344 return debugserver_client_send(client, "+", sizeof(char), NULL);
345}
346
347static debugserver_error_t debugserver_client_send_noack(debugserver_client_t client)
348{
349 debug_info("sending !ACK");
350 return debugserver_client_send(client, "-", sizeof(char), NULL);
351}
352
353debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled)
354{
355 if (!client)
356 return DEBUGSERVER_E_INVALID_ARG;
357
358 client->noack_mode = (enabled == 0)? 1: 0;
359
360 debug_info("ack mode: %s", client->noack_mode == 0 ? "on": "off");
361
362 return DEBUGSERVER_E_SUCCESS;
363}
364
365debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout)
366{
367 if (!client)
368 return DEBUGSERVER_E_INVALID_ARG;
369
370 client->cancel_receive = cancel_receive;
371 client->receive_loop_timeout = receive_loop_timeout;
372
373 debug_info("receive params: cancel_receive %s, receive_loop_timeout %dms", (client->cancel_receive == NULL ? "unset": "set"), client->receive_loop_timeout);
374
375 return DEBUGSERVER_E_SUCCESS;
376}
377
378static debugserver_error_t debugserver_client_receive_internal_char(debugserver_client_t client, char* received_char)
379{
380 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
381 uint32_t bytes = 0;
382
383 /* we loop here as we expect an answer */
384 res = debugserver_client_receive(client, received_char, sizeof(char), &bytes);
385 if (res != DEBUGSERVER_E_SUCCESS) {
386 return res;
387 }
388 if (bytes != 1) {
389 debug_info("received %d bytes when asking for %d!", bytes, sizeof(char));
390 return DEBUGSERVER_E_UNKNOWN_ERROR;
391 }
392 return res;
393}
394
395debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response, size_t* response_size)
396{
397 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
398
399 char data = '\0';
400 int skip_prefix = 0;
401
402 char* buffer = malloc(1024);
403 uint32_t buffer_size = 0;
404 uint32_t buffer_capacity = 1024;
405
406 if (response)
407 *response = NULL;
408
409 if (!client->noack_mode) {
410 debug_info("attempting to receive ACK (+)");
411 res = debugserver_client_receive_internal_char(client, &data);
412 if (res != DEBUGSERVER_E_SUCCESS) {
413 goto cleanup;
414 }
415 if (data == '+') {
416 debug_info("received ACK (+)");
417 } else if (data == '$') {
418 debug_info("received prefix ($)");
419 buffer[0] = '$';
420 buffer_size = 1;
421 skip_prefix = 1;
422 } else {
423 debug_info("unrecognized response when looking for ACK: %c", data);
424 goto cleanup;
425 }
426 }
427
428 debug_info("skip_prefix: %d", skip_prefix);
429
430 if (!skip_prefix) {
431 debug_info("attempting to receive prefix ($)");
432 res = debugserver_client_receive_internal_char(client, &data);
433 if (res != DEBUGSERVER_E_SUCCESS) {
434 goto cleanup;
435 }
436 if (data == '$') {
437 debug_info("received prefix ($)");
438 buffer[0] = '$';
439 buffer_size = 1;
440 } else {
441 debug_info("unrecognized response when looking for prefix: %c", data);
442 goto cleanup;
443 }
444 }
445
446 uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH;
447 int receiving_checksum_response = 0;
448 debug_info("attempting to read up response until checksum");
449
450 while ((checksum_length > 0)) {
451 res = debugserver_client_receive_internal_char(client, &data);
452 if (res != DEBUGSERVER_E_SUCCESS) {
453 goto cleanup;
454 }
455 if (data == '#') {
456 receiving_checksum_response = 1;
457 }
458 if (receiving_checksum_response) {
459 checksum_length--;
460 }
461 if (buffer_size + 1 >= buffer_capacity) {
462 char* newbuffer = realloc(buffer, buffer_capacity+1024);
463 if (!newbuffer) {
464 return DEBUGSERVER_E_UNKNOWN_ERROR;
465 }
466 buffer = newbuffer;
467 buffer_capacity += 1024;
468 }
469 buffer[buffer_size] = data;
470 buffer_size += sizeof(char);
471 }
472 debug_info("validating response checksum...");
473 if (client->noack_mode || debugserver_response_is_checksum_valid(buffer, buffer_size)) {
474 if (response) {
475 /* assemble response string */
476 uint32_t resp_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1);
477 *response = (char*)malloc(resp_size + 1);
478 memcpy(*response, buffer + 1, resp_size);
479 (*response)[resp_size] = '\0';
480 if (response_size) *response_size = resp_size;
481 }
482 if (!client->noack_mode) {
483 /* confirm valid command */
484 debugserver_client_send_ack(client);
485 }
486 } else {
487 /* response was invalid */
488 res = DEBUGSERVER_E_RESPONSE_ERROR;
489 if (!client->noack_mode) {
490 /* report invalid command */
491 debugserver_client_send_noack(client);
492 }
493 }
494
495cleanup:
496 if (response) {
497 debug_info("response: %s", *response);
498 }
499
500 if (buffer)
501 free(buffer);
502
503 return res;
504}
505
506debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response, size_t* response_size)
507{
508 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
509 int i;
510 uint32_t bytes = 0;
511
512 char* send_buffer = NULL;
513 uint32_t send_buffer_size = 0;
514
515 char* command_arguments = NULL;
516
517 /* concat all arguments */
518 for (i = 0; i < command->argc; i++) {
519 debug_info("argv[%d]: %s", i, command->argv[i]);
520 command_arguments = string_append(command_arguments, command->argv[i], NULL);
521 }
522
523 debug_info("command_arguments(%d): %s", command->argc, command_arguments);
524
525 /* encode command arguments, add checksum if required and assemble entire command */
526 debugserver_format_command("$", command->name, command_arguments, 1, &send_buffer, &send_buffer_size);
527
528 debug_info("sending encoded command: %s", send_buffer);
529
530 res = debugserver_client_send(client, send_buffer, send_buffer_size, &bytes);
531 debug_info("command result: %d", res);
532 if (res != DEBUGSERVER_E_SUCCESS) {
533 goto cleanup;
534 }
535
536 /* receive response */
537 res = debugserver_client_receive_response(client, response, response_size);
538 debug_info("response result: %d", res);
539 if (res != DEBUGSERVER_E_SUCCESS) {
540 goto cleanup;
541 }
542
543 if (response) {
544 debug_info("received response: %s", *response);
545 }
546
547 /* disable sending ack on the client */
548 if (!strncmp(command->name, "QStartNoAckMode", 16)) {
549 debugserver_client_set_ack_mode(client, 0);
550 }
551
552cleanup:
553 if (command_arguments)
554 free(command_arguments);
555
556 if (send_buffer)
557 free(send_buffer);
558
559 return res;
560}
561
562debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response)
563{
564 if (!client || !env)
565 return DEBUGSERVER_E_INVALID_ARG;
566
567 debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR;
568 char* env_tmp = strdup(env);
569 char* env_arg[2] = { env_tmp, NULL };
570
571 debugserver_command_t command = NULL;
572 debugserver_command_new("QEnvironmentHexEncoded:", 1, env_arg, &command);
573 result = debugserver_client_send_command(client, command, response, NULL);
574 debugserver_command_free(command);
575
576 free(env_tmp);
577
578 return result;
579}
580
581debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response)
582{
583 if (!client || !argc)
584 return DEBUGSERVER_E_INVALID_ARG;
585
586 debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR;
587 char *pkt = NULL;
588 size_t pkt_len = 0;
589 int i = 0;
590
591 /* calculate total length */
592 while (i < argc && argv && argv[i]) {
593 char *prefix = NULL;
594 int ret = asprintf(&prefix, ",%zu,%d,", strlen(argv[i]) * 2, i);
595 if (ret < 0 || prefix == NULL) {
596 debug_info("asprintf failed, out of memory?");
597 return DEBUGSERVER_E_UNKNOWN_ERROR;
598 }
599 pkt_len += strlen(prefix) + strlen(argv[i]) * 2;
600 free(prefix);
601 i++;
602 }
603
604 /* allocate packet and initialize it */
605 pkt = (char *) malloc(pkt_len + 1);
606 memset(pkt, 0, pkt_len + 1);
607
608 char *pktp = pkt;
609
610 i = 0;
611 while (i < argc && argv && argv[i]) {
612 debug_info("argv[%d] = \"%s\"", i, argv[i]);
613
614 char *prefix = NULL;
615 char *m = NULL;
616 size_t arg_len = strlen(argv[i]);
617 size_t arg_hexlen = arg_len * 2;
618
619 int ret = asprintf(&prefix, ",%zu,%d,", arg_hexlen, i);
620 if (ret < 0 || prefix == NULL) {
621 debug_info("asprintf failed, out of memory?");
622 return DEBUGSERVER_E_UNKNOWN_ERROR;
623 }
624
625 m = (char *) malloc(arg_hexlen);
626 char *p = m;
627 char *q = (char*)argv[i];
628 while (*q) {
629 *p++ = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(*q);
630 *p++ = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(*q);
631 q++;
632 }
633
634 memcpy(pktp, prefix, strlen(prefix));
635 pktp += strlen(prefix);
636
637 memcpy(pktp, m, arg_hexlen);
638 pktp += arg_hexlen;
639
640 free(prefix);
641 free(m);
642
643 i++;
644 }
645
646 pkt[0] = 'A';
647
648 debugserver_command_t command = NULL;
649 debugserver_command_new(pkt, 0, NULL, &command);
650 result = debugserver_client_send_command(client, command, response, NULL);
651 debugserver_command_free(command);
652
653 if (pkt)
654 free(pkt);
655
656 return result;
657}