diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/iphone.c | 59 | ||||
| -rw-r--r-- | src/lockdown.c | 201 | ||||
| -rw-r--r-- | src/lockdown.h | 1 | ||||
| -rw-r--r-- | src/usbmux.c | 3 | ||||
| -rw-r--r-- | src/utils.c | 32 |
5 files changed, 282 insertions, 14 deletions
diff --git a/src/iphone.c b/src/iphone.c index 32d27f6..1f68180 100644 --- a/src/iphone.c +++ b/src/iphone.c | |||
| @@ -28,6 +28,49 @@ | |||
| 28 | #include <stdlib.h> | 28 | #include <stdlib.h> |
| 29 | #include <string.h> | 29 | #include <string.h> |
| 30 | 30 | ||
| 31 | /** | ||
| 32 | * This function sets the configuration of the given device to 3 | ||
| 33 | * and claims the interface 1. If usb_set_configuration fails, it detaches | ||
| 34 | * the kernel driver that blocks the device, and retries configuration. | ||
| 35 | * | ||
| 36 | * @param phone which device to configure | ||
| 37 | */ | ||
| 38 | static void iphone_config_usb_device(iphone_device_t phone) | ||
| 39 | { | ||
| 40 | int ret; | ||
| 41 | |||
| 42 | log_debug_msg("setting configuration... "); | ||
| 43 | ret = usb_set_configuration(phone->device, 3); | ||
| 44 | if (ret != 0) { | ||
| 45 | log_debug_msg("Hm, usb_set_configuration returned %d: %s, trying to fix:\n", ret, strerror(-ret)); | ||
| 46 | log_debug_msg("-> detaching kernel driver... "); | ||
| 47 | ret = | ||
| 48 | usb_detach_kernel_driver_np(phone->device, | ||
| 49 | phone->__device->config->interface->altsetting->bInterfaceNumber); | ||
| 50 | if (ret != 0) { | ||
| 51 | log_debug_msg("usb_detach_kernel_driver_np returned %d: %s\n", ret, strerror(-ret)); | ||
| 52 | } else { | ||
| 53 | log_debug_msg("done.\n"); | ||
| 54 | log_debug_msg("setting configuration again... "); | ||
| 55 | ret = usb_set_configuration(phone->device, 3); | ||
| 56 | if (ret != 0) { | ||
| 57 | log_debug_msg("Error: usb_set_configuration returned %d: %s\n", ret, strerror(-ret)); | ||
| 58 | } else { | ||
| 59 | log_debug_msg("done.\n"); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } else { | ||
| 63 | log_debug_msg("done.\n"); | ||
| 64 | } | ||
| 65 | |||
| 66 | log_debug_msg("claiming interface... "); | ||
| 67 | ret = usb_claim_interface(phone->device, 1); | ||
| 68 | if (ret != 0) { | ||
| 69 | log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret, strerror(-ret)); | ||
| 70 | } else { | ||
| 71 | log_debug_msg("done.\n"); | ||
| 72 | } | ||
| 73 | } | ||
| 31 | 74 | ||
| 32 | /** | 75 | /** |
| 33 | * Given a USB bus and device number, returns a device handle to the iPhone on | 76 | * Given a USB bus and device number, returns a device handle to the iPhone on |
| @@ -73,8 +116,7 @@ static iphone_error_t iphone_get_specific_device(unsigned int bus_n, int dev_n, | |||
| 73 | if (dev->devnum == dev_n) { | 116 | if (dev->devnum == dev_n) { |
| 74 | phone->__device = dev; | 117 | phone->__device = dev; |
| 75 | phone->device = usb_open(phone->__device); | 118 | phone->device = usb_open(phone->__device); |
| 76 | usb_set_configuration(phone->device, 3); | 119 | iphone_config_usb_device(phone); |
| 77 | usb_claim_interface(phone->device, 1); | ||
| 78 | goto found; | 120 | goto found; |
| 79 | } | 121 | } |
| 80 | 122 | ||
| @@ -115,9 +157,10 @@ static iphone_error_t iphone_get_specific_device(unsigned int bus_n, int dev_n, | |||
| 115 | return IPHONE_E_SUCCESS; | 157 | return IPHONE_E_SUCCESS; |
| 116 | } else { | 158 | } else { |
| 117 | // Bad header | 159 | // Bad header |
| 160 | log_debug_msg("get_iPhone(): Received a bad header/invalid version number.\n"); | ||
| 161 | log_debug_buffer((char *) version, sizeof(*version)); | ||
| 118 | iphone_free_device(phone); | 162 | iphone_free_device(phone); |
| 119 | free(version); | 163 | free(version); |
| 120 | log_debug_msg("get_iPhone(): Received a bad header/invalid version number."); | ||
| 121 | return IPHONE_E_BAD_HEADER; | 164 | return IPHONE_E_BAD_HEADER; |
| 122 | } | 165 | } |
| 123 | 166 | ||
| @@ -173,13 +216,21 @@ iphone_error_t iphone_free_device(iphone_device_t device) | |||
| 173 | if (!device) | 216 | if (!device) |
| 174 | return IPHONE_E_INVALID_ARG; | 217 | return IPHONE_E_INVALID_ARG; |
| 175 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | 218 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; |
| 219 | int bytes; | ||
| 220 | unsigned char buf[512]; | ||
| 221 | |||
| 222 | // read final package | ||
| 223 | bytes = usb_bulk_read(device->device, BULKIN, (void *) &buf, 512, 1000); | ||
| 224 | if (bytes > 0) { | ||
| 225 | log_debug_msg("iphone_free_device: final read returned\n"); | ||
| 226 | log_debug_buffer(buf, bytes); | ||
| 227 | } | ||
| 176 | 228 | ||
| 177 | if (device->buffer) { | 229 | if (device->buffer) { |
| 178 | free(device->buffer); | 230 | free(device->buffer); |
| 179 | } | 231 | } |
| 180 | if (device->device) { | 232 | if (device->device) { |
| 181 | usb_release_interface(device->device, 1); | 233 | usb_release_interface(device->device, 1); |
| 182 | usb_reset(device->device); | ||
| 183 | usb_close(device->device); | 234 | usb_close(device->device); |
| 184 | ret = IPHONE_E_SUCCESS; | 235 | ret = IPHONE_E_SUCCESS; |
| 185 | } | 236 | } |
diff --git a/src/lockdown.c b/src/lockdown.c index c6d5f80..872b7b0 100644 --- a/src/lockdown.c +++ b/src/lockdown.c | |||
| @@ -65,6 +65,104 @@ iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone) | |||
| 65 | return control; | 65 | return control; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | /** | ||
| 69 | * Closes the lockdownd communication session, by sending | ||
| 70 | * the StopSession Request to the device. | ||
| 71 | * | ||
| 72 | * @param control The lockdown client | ||
| 73 | */ | ||
| 74 | static void iphone_lckd_stop_session(iphone_lckd_client_t control) | ||
| 75 | { | ||
| 76 | if (!control) | ||
| 77 | return; //IPHONE_E_INVALID_ARG; | ||
| 78 | |||
| 79 | int bytes = 0, i = 0; | ||
| 80 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 81 | |||
| 82 | plist_t dict = plist_new_dict(); | ||
| 83 | plist_add_sub_element(dict, PLIST_KEY, (void *) "Request", strlen("Request")); | ||
| 84 | plist_add_sub_element(dict, PLIST_STRING, (void *) "StopSession", strlen("StopSession")); | ||
| 85 | plist_add_sub_element(dict, PLIST_KEY, (void *) "SessionID", strlen("SessionID")); | ||
| 86 | plist_add_sub_element(dict, PLIST_STRING, (void *) control->session_id, strlen(control->session_id)); | ||
| 87 | |||
| 88 | log_debug_msg("iphone_lckd_stop_session() called\n"); | ||
| 89 | char *XML_content = NULL; | ||
| 90 | uint32_t length = 0; | ||
| 91 | |||
| 92 | plist_to_xml(dict, &XML_content, &length); | ||
| 93 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 94 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 95 | |||
| 96 | free(XML_content); | ||
| 97 | XML_content = NULL; | ||
| 98 | plist_free(dict); | ||
| 99 | dict = NULL; | ||
| 100 | |||
| 101 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 102 | log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content); | ||
| 103 | plist_from_xml(XML_content, bytes, &dict); | ||
| 104 | |||
| 105 | if (!dict) { | ||
| 106 | log_debug_msg("lockdownd_stop_session(): IPHONE_E_PLIST_ERROR\n"); | ||
| 107 | return; // IPHONE_E_PLIST_ERROR; | ||
| 108 | } | ||
| 109 | |||
| 110 | plist_t query_node = plist_find_node(dict, PLIST_STRING, "StopSession", strlen("StopSession")); | ||
| 111 | plist_t result_node = plist_get_next_sibling(query_node); | ||
| 112 | plist_t value_node = plist_get_next_sibling(result_node); | ||
| 113 | |||
| 114 | plist_type result_type; | ||
| 115 | plist_type value_type; | ||
| 116 | |||
| 117 | char *result_value = NULL; | ||
| 118 | char *value_value = NULL; | ||
| 119 | uint64_t result_length = 0; | ||
| 120 | uint64_t value_length = 0; | ||
| 121 | |||
| 122 | plist_get_type_and_value(result_node, &result_type, (void *) (&result_value), &result_length); | ||
| 123 | plist_get_type_and_value(value_node, &value_type, (void *) (&value_value), &value_length); | ||
| 124 | |||
| 125 | if (result_type == PLIST_KEY && | ||
| 126 | value_type == PLIST_STRING && !strcmp(result_value, "Result") && !strcmp(value_value, "Success")) { | ||
| 127 | log_debug_msg("lockdownd_stop_session(): success\n"); | ||
| 128 | ret = IPHONE_E_SUCCESS; | ||
| 129 | } | ||
| 130 | |||
| 131 | return; // ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | /** | ||
| 136 | * Shuts down the SSL session by first calling iphone_lckd_stop_session | ||
| 137 | * to cleanly close the lockdownd communication session, and then | ||
| 138 | * performing a close notify, which is done by "gnutls_bye". | ||
| 139 | * | ||
| 140 | * @param client The lockdown client | ||
| 141 | */ | ||
| 142 | static void iphone_lckd_stop_SSL_session(iphone_lckd_client_t client) | ||
| 143 | { | ||
| 144 | if (!client) { | ||
| 145 | log_debug_msg("lockdownd_stop_SSL_session(): invalid argument!\n"); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | |||
| 149 | if (client->in_SSL) { | ||
| 150 | log_debug_msg("Stopping SSL Session\n"); | ||
| 151 | iphone_lckd_stop_session(client); | ||
| 152 | log_debug_msg("Sending SSL close notify\n"); | ||
| 153 | gnutls_bye(*client->ssl_session, GNUTLS_SHUT_RDWR); | ||
| 154 | } | ||
| 155 | if (client->ssl_session) { | ||
| 156 | gnutls_deinit(*client->ssl_session); | ||
| 157 | free(client->ssl_session); | ||
| 158 | } | ||
| 159 | client->in_SSL = 0; | ||
| 160 | client->gtls_buffer_hack_len = 0; // dunno if required?! | ||
| 161 | |||
| 162 | return; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 68 | /** Closes the lockdownd client and does the necessary housekeeping. | 166 | /** Closes the lockdownd client and does the necessary housekeeping. |
| 69 | * | 167 | * |
| 70 | * @param control The lockdown client | 168 | * @param control The lockdown client |
| @@ -75,13 +173,17 @@ iphone_error_t iphone_lckd_free_client(iphone_lckd_client_t client) | |||
| 75 | return IPHONE_E_INVALID_ARG; | 173 | return IPHONE_E_INVALID_ARG; |
| 76 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | 174 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; |
| 77 | 175 | ||
| 176 | iphone_lckd_stop_SSL_session(client); | ||
| 177 | |||
| 78 | if (client->connection) { | 178 | if (client->connection) { |
| 179 | lockdownd_close(client); | ||
| 180 | |||
| 181 | // IMO, read of final "sessionUpcall connection closed" packet | ||
| 182 | // should come here instead of in iphone_free_device | ||
| 183 | |||
| 79 | ret = iphone_mux_free_client(client->connection); | 184 | ret = iphone_mux_free_client(client->connection); |
| 80 | } | 185 | } |
| 81 | 186 | ||
| 82 | if (client->ssl_session) | ||
| 83 | gnutls_deinit(*client->ssl_session); | ||
| 84 | free(client->ssl_session); | ||
| 85 | free(client); | 187 | free(client); |
| 86 | return ret; | 188 | return ret; |
| 87 | } | 189 | } |
| @@ -512,6 +614,70 @@ iphone_error_t lockdownd_pair_device(iphone_lckd_client_t control, char *uid, ch | |||
| 512 | return ret; | 614 | return ret; |
| 513 | } | 615 | } |
| 514 | 616 | ||
| 617 | /** | ||
| 618 | * Performs the Goodbye Request to tell the device the communication | ||
| 619 | * session is now closed. | ||
| 620 | * | ||
| 621 | * @param control The lockdown client | ||
| 622 | */ | ||
| 623 | void lockdownd_close(iphone_lckd_client_t control) | ||
| 624 | { | ||
| 625 | if (!control) | ||
| 626 | return; //IPHONE_E_INVALID_ARG; | ||
| 627 | |||
| 628 | int bytes = 0, i = 0; | ||
| 629 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 630 | |||
| 631 | plist_t dict = plist_new_dict(); | ||
| 632 | plist_add_sub_element(dict, PLIST_KEY, (void *) "Request", strlen("Request")); | ||
| 633 | plist_add_sub_element(dict, PLIST_STRING, (void *) "Goodbye", strlen("Goodbye")); | ||
| 634 | |||
| 635 | log_debug_msg("lockdownd_close() called\n"); | ||
| 636 | char *XML_content = NULL; | ||
| 637 | uint32_t length = 0; | ||
| 638 | |||
| 639 | plist_to_xml(dict, &XML_content, &length); | ||
| 640 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 641 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 642 | |||
| 643 | free(XML_content); | ||
| 644 | XML_content = NULL; | ||
| 645 | plist_free(dict); | ||
| 646 | dict = NULL; | ||
| 647 | |||
| 648 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 649 | log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content); | ||
| 650 | plist_from_xml(XML_content, bytes, &dict); | ||
| 651 | |||
| 652 | if (!dict) { | ||
| 653 | log_debug_msg("lockdownd_close(): IPHONE_E_PLIST_ERROR\n"); | ||
| 654 | return; // IPHONE_E_PLIST_ERROR; | ||
| 655 | } | ||
| 656 | |||
| 657 | plist_t query_node = plist_find_node(dict, PLIST_STRING, "Goodbye", strlen("Goodbye")); | ||
| 658 | plist_t result_node = plist_get_next_sibling(query_node); | ||
| 659 | plist_t value_node = plist_get_next_sibling(result_node); | ||
| 660 | |||
| 661 | plist_type result_type; | ||
| 662 | plist_type value_type; | ||
| 663 | |||
| 664 | char *result_value = NULL; | ||
| 665 | char *value_value = NULL; | ||
| 666 | uint64_t result_length = 0; | ||
| 667 | uint64_t value_length = 0; | ||
| 668 | |||
| 669 | plist_get_type_and_value(result_node, &result_type, (void *) (&result_value), &result_length); | ||
| 670 | plist_get_type_and_value(value_node, &value_type, (void *) (&value_value), &value_length); | ||
| 671 | |||
| 672 | if (result_type == PLIST_KEY && | ||
| 673 | value_type == PLIST_STRING && !strcmp(result_value, "Result") && !strcmp(value_value, "Success")) { | ||
| 674 | log_debug_msg("lockdownd_close(): success\n"); | ||
| 675 | ret = IPHONE_E_SUCCESS; | ||
| 676 | } | ||
| 677 | |||
| 678 | return; // ret; | ||
| 679 | } | ||
| 680 | |||
| 515 | /** Generates the device certificate from the public key as well as the host | 681 | /** Generates the device certificate from the public key as well as the host |
| 516 | * and root certificates. | 682 | * and root certificates. |
| 517 | * | 683 | * |
| @@ -654,6 +820,7 @@ iphone_error_t lockdownd_start_SSL_session(iphone_lckd_client_t control, const c | |||
| 654 | uint32_t length = 0, bytes = 0, return_me = 0; | 820 | uint32_t length = 0, bytes = 0, return_me = 0; |
| 655 | 821 | ||
| 656 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | 822 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; |
| 823 | control->session_id[0] = '\0'; | ||
| 657 | 824 | ||
| 658 | /* Setup DevicePublicKey request plist */ | 825 | /* Setup DevicePublicKey request plist */ |
| 659 | dict = plist_new_dict(); | 826 | dict = plist_new_dict(); |
| @@ -699,7 +866,7 @@ iphone_error_t lockdownd_start_SSL_session(iphone_lckd_client_t control, const c | |||
| 699 | XML_content = NULL; | 866 | XML_content = NULL; |
| 700 | plist_free(dict); | 867 | plist_free(dict); |
| 701 | dict = NULL; | 868 | dict = NULL; |
| 702 | 869 | ret = IPHONE_E_SSL_ERROR; | |
| 703 | if (result_key_type == PLIST_KEY && | 870 | if (result_key_type == PLIST_KEY && |
| 704 | result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) { | 871 | result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) { |
| 705 | // Set up GnuTLS... | 872 | // Set up GnuTLS... |
| @@ -749,12 +916,34 @@ iphone_error_t lockdownd_start_SSL_session(iphone_lckd_client_t control, const c | |||
| 749 | return IPHONE_E_SSL_ERROR; | 916 | return IPHONE_E_SSL_ERROR; |
| 750 | } else { | 917 | } else { |
| 751 | control->in_SSL = 1; | 918 | control->in_SSL = 1; |
| 752 | return IPHONE_E_SUCCESS; | 919 | ret = IPHONE_E_SUCCESS; |
| 753 | } | 920 | } |
| 754 | } | 921 | } |
| 922 | //store session id | ||
| 923 | plist_t session_node = plist_find_node(dict, PLIST_KEY, "SessionID", strlen("SessionID")); | ||
| 924 | if (session_node) { | ||
| 925 | |||
| 926 | plist_type session_node_val_type; | ||
| 927 | char *session_id = NULL; | ||
| 928 | uint64_t session_id_length = 0; | ||
| 929 | plist_t session_node_val = plist_get_next_sibling(session_node); | ||
| 930 | |||
| 931 | plist_get_type_and_value(session_node_val, &session_node_val_type, (void *) (&session_id), | ||
| 932 | &session_id_length); | ||
| 933 | if (session_node_val_type == PLIST_STRING && session_id_length > 0) { | ||
| 934 | // we need to store the session ID for StopSession | ||
| 935 | strcpy(control->session_id, session_id); | ||
| 936 | log_debug_msg("SessionID: %s\n", control->session_id); | ||
| 937 | return ret; | ||
| 938 | } | ||
| 939 | } | ||
| 940 | |||
| 941 | if (ret == IPHONE_E_SUCCESS) { | ||
| 942 | log_debug_msg("Failed to get SessionID!\n"); | ||
| 943 | return ret; | ||
| 944 | } | ||
| 755 | 945 | ||
| 756 | log_debug_msg("Apparently failed negotiating with lockdownd.\n"); | 946 | log_debug_msg("Apparently failed negotiating with lockdownd.\n"); |
| 757 | log_debug_msg("Responding dictionary: \n"); | ||
| 758 | return IPHONE_E_SSL_ERROR; | 947 | return IPHONE_E_SSL_ERROR; |
| 759 | } else { | 948 | } else { |
| 760 | log_debug_msg("Didn't get enough bytes.\n"); | 949 | log_debug_msg("Didn't get enough bytes.\n"); |
diff --git a/src/lockdown.h b/src/lockdown.h index 8ca8a7f..cdc46b8 100644 --- a/src/lockdown.h +++ b/src/lockdown.h | |||
| @@ -37,6 +37,7 @@ struct iphone_lckd_client_int { | |||
| 37 | int in_SSL; | 37 | int in_SSL; |
| 38 | char *gtls_buffer_hack; | 38 | char *gtls_buffer_hack; |
| 39 | int gtls_buffer_hack_len; | 39 | int gtls_buffer_hack_len; |
| 40 | char session_id[40]; | ||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone); | 43 | iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone); |
diff --git a/src/usbmux.c b/src/usbmux.c index 427b880..eb7ec97 100644 --- a/src/usbmux.c +++ b/src/usbmux.c | |||
| @@ -182,8 +182,11 @@ iphone_error_t iphone_mux_free_client(iphone_umux_client_t client) | |||
| 182 | return IPHONE_E_INVALID_ARG; | 182 | return IPHONE_E_INVALID_ARG; |
| 183 | 183 | ||
| 184 | client->header->tcp_flags = 0x04; | 184 | client->header->tcp_flags = 0x04; |
| 185 | client->header->length = htonl(0x1C); | ||
| 185 | client->header->scnt = htonl(client->header->scnt); | 186 | client->header->scnt = htonl(client->header->scnt); |
| 186 | client->header->ocnt = htonl(client->header->ocnt); | 187 | client->header->ocnt = htonl(client->header->ocnt); |
| 188 | client->header->window = 0; | ||
| 189 | client->header->length16 = htons(0x1C); | ||
| 187 | int bytes = 0; | 190 | int bytes = 0; |
| 188 | 191 | ||
| 189 | bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800); | 192 | bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800); |
diff --git a/src/utils.c b/src/utils.c index ceb1f5d..fb98471 100644 --- a/src/utils.c +++ b/src/utils.c | |||
| @@ -56,11 +56,35 @@ void log_debug_msg(const char *format, ...) | |||
| 56 | inline void log_debug_buffer(const char *data, const int length) | 56 | inline void log_debug_buffer(const char *data, const int length) |
| 57 | { | 57 | { |
| 58 | #ifndef STRIP_DEBUG_CODE | 58 | #ifndef STRIP_DEBUG_CODE |
| 59 | int i; | ||
| 60 | int j; | ||
| 61 | unsigned char c; | ||
| 59 | 62 | ||
| 60 | /* run the real fprintf */ | 63 | if (toto_debug) { |
| 61 | if (toto_debug) | 64 | for (i = 0; i < length; i += 16) { |
| 62 | fwrite(data, 1, length, stderr); | 65 | fprintf(stderr, "%04x: ", i); |
| 63 | 66 | for (j = 0; j < 16; j++) { | |
| 67 | if (i + j >= length) { | ||
| 68 | fprintf(stderr, " "); | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | fprintf(stderr, "%02hhx ", *(data + i + j)); | ||
| 72 | } | ||
| 73 | fprintf(stderr, " | "); | ||
| 74 | for (j = 0; j < 16; j++) { | ||
| 75 | if (i + j >= length) | ||
| 76 | break; | ||
| 77 | c = *(data + i + j); | ||
| 78 | if ((c < 32) || (c > 127)) { | ||
| 79 | fprintf(stderr, "."); | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | fprintf(stderr, "%c", c); | ||
| 83 | } | ||
| 84 | fprintf(stderr, "\n"); | ||
| 85 | } | ||
| 86 | fprintf(stderr, "\n"); | ||
| 87 | } | ||
| 64 | #endif | 88 | #endif |
| 65 | } | 89 | } |
| 66 | 90 | ||
