diff options
| -rw-r--r-- | docs/idevicebtlogger.1 | 14 | ||||
| -rw-r--r-- | tools/idevicebtlogger.c | 106 |
2 files changed, 106 insertions, 14 deletions
diff --git a/docs/idevicebtlogger.1 b/docs/idevicebtlogger.1 index 24903b5..39ffff7 100644 --- a/docs/idevicebtlogger.1 +++ b/docs/idevicebtlogger.1 | |||
| @@ -8,7 +8,9 @@ idevicebtlogger \- Capture HCI traffic of a connected device. | |||
| 8 | 8 | ||
| 9 | .SH DESCRIPTION | 9 | .SH DESCRIPTION |
| 10 | 10 | ||
| 11 | Capture HCI traffic of a connected device. Requires Bluetooth logging profile to be installed on device. | 11 | Capture HCI traffic of a connected device. Requires Bluetooth logging profile to be installed on device with iOS 13 or higher. See https://www.bluetooth.com/blog/a-new-way-to-debug-iosbluetooth-applications/ for iOS device configuration. |
| 12 | |||
| 13 | The HCI traffic can be stored in Apple's native PacketLogger format or converted into PCAP format for live feedback in Wireshark. | ||
| 12 | 14 | ||
| 13 | .SH OPTIONS | 15 | .SH OPTIONS |
| 14 | .TP | 16 | .TP |
| @@ -18,6 +20,9 @@ target specific device by UDID | |||
| 18 | .B \-n, \-\-network | 20 | .B \-n, \-\-network |
| 19 | connect to network device | 21 | connect to network device |
| 20 | .TP | 22 | .TP |
| 23 | .B \-f, \-\-format FORMAT | ||
| 24 | set log format: PacketLoggger (default), or pcap | ||
| 25 | .TP | ||
| 21 | .B \-x, \-\-exit | 26 | .B \-x, \-\-exit |
| 22 | exit when device disconnects | 27 | exit when device disconnects |
| 23 | .TP | 28 | .TP |
| @@ -37,9 +42,16 @@ Capture HCI traffic of device with UDID 00008030-0000111ABC000DEF. | |||
| 37 | .TP | 42 | .TP |
| 38 | .B idevicebtlogger \-x | 43 | .B idevicebtlogger \-x |
| 39 | Capture HCI traffic of device and exit when the device is unplugged. | 44 | Capture HCI traffic of device and exit when the device is unplugged. |
| 45 | .TP | ||
| 46 | .B idevicebtlogger \-f pcap | ||
| 47 | Capture HCI traffic of device in PCAP format. | ||
| 48 | .TP | ||
| 49 | .B idevicebtlogger -f pcap - | wireshark -k -i - | ||
| 50 | Capture HCI traffic and pipe it into Wireshark for live feedback. | ||
| 40 | 51 | ||
| 41 | .SH AUTHORS | 52 | .SH AUTHORS |
| 42 | Geoffrey Kruse | 53 | Geoffrey Kruse |
| 54 | Matthias Ringwald | ||
| 43 | 55 | ||
| 44 | .SH ON THE WEB | 56 | .SH ON THE WEB |
| 45 | https://libimobiledevice.org | 57 | https://libimobiledevice.org |
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c index b73d958..c780143 100644 --- a/tools/idevicebtlogger.c +++ b/tools/idevicebtlogger.c | |||
| @@ -32,6 +32,8 @@ | |||
| 32 | #include <stdlib.h> | 32 | #include <stdlib.h> |
| 33 | #include <unistd.h> | 33 | #include <unistd.h> |
| 34 | #include <getopt.h> | 34 | #include <getopt.h> |
| 35 | #include <assert.h> | ||
| 36 | #include <fcntl.h> | ||
| 35 | 37 | ||
| 36 | #ifdef WIN32 | 38 | #ifdef WIN32 |
| 37 | #include <windows.h> | 39 | #include <windows.h> |
| @@ -57,7 +59,14 @@ static idevice_t device = NULL; | |||
| 57 | static bt_packet_logger_client_t bt_packet_logger = NULL; | 59 | static bt_packet_logger_client_t bt_packet_logger = NULL; |
| 58 | static int use_network = 0; | 60 | static int use_network = 0; |
| 59 | static char* out_filename = NULL; | 61 | static char* out_filename = NULL; |
| 60 | static pcap_dumper_t * dump; | 62 | static char* log_format_string = NULL; |
| 63 | static pcap_dumper_t * pcap_dumper = NULL; | ||
| 64 | static int packetlogger_fd = -1; | ||
| 65 | |||
| 66 | static enum { | ||
| 67 | LOG_FORMAT_PACKETLOGGER, | ||
| 68 | LOG_FORMAT_PCAP | ||
| 69 | } log_format = LOG_FORMAT_PACKETLOGGER; | ||
| 61 | 70 | ||
| 62 | typedef enum { | 71 | typedef enum { |
| 63 | HCI_COMMAND = 0x00, | 72 | HCI_COMMAND = 0x00, |
| @@ -67,9 +76,17 @@ typedef enum { | |||
| 67 | } PacketLoggerPacketType; | 76 | } PacketLoggerPacketType; |
| 68 | 77 | ||
| 69 | /** | 78 | /** |
| 79 | * Callback from the packet logger service to handle packets and log to PacketLoggger format | ||
| 80 | */ | ||
| 81 | static void bt_packet_logger_callback_packetlogger(uint8_t * data, uint16_t len, void *user_data) | ||
| 82 | { | ||
| 83 | write(packetlogger_fd, data, len); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 70 | * Callback from the packet logger service to handle packets and log to pcap | 87 | * Callback from the packet logger service to handle packets and log to pcap |
| 71 | */ | 88 | */ |
| 72 | static void bt_packet_logger_callback(uint8_t * data, uint16_t len, void *user_data) | 89 | static void bt_packet_logger_callback_pcap(uint8_t * data, uint16_t len, void *user_data) |
| 73 | { | 90 | { |
| 74 | bt_packet_logger_header_t * header = (bt_packet_logger_header_t *)data; | 91 | bt_packet_logger_header_t * header = (bt_packet_logger_header_t *)data; |
| 75 | uint16_t offset = sizeof(bt_packet_logger_header_t); | 92 | uint16_t offset = sizeof(bt_packet_logger_header_t); |
| @@ -126,8 +143,8 @@ static void bt_packet_logger_callback(uint8_t * data, uint16_t len, void *user_d | |||
| 126 | // having to memcpy things around. | 143 | // having to memcpy things around. |
| 127 | offset -= sizeof(uint32_t); | 144 | offset -= sizeof(uint32_t); |
| 128 | *(uint32_t*)&data[offset] = direction; | 145 | *(uint32_t*)&data[offset] = direction; |
| 129 | pcap_dump((unsigned char*)dump, &pcap_header, &data[offset]); | 146 | pcap_dump((unsigned char*)pcap_dumper, &pcap_header, &data[offset]); |
| 130 | pcap_dump_flush(dump); | 147 | pcap_dump_flush(pcap_dumper); |
| 131 | } | 148 | } |
| 132 | } | 149 | } |
| 133 | 150 | ||
| @@ -173,7 +190,19 @@ static int start_logging(void) | |||
| 173 | bt_packet_logger_client_start_service(device, &bt_packet_logger, TOOL_NAME); | 190 | bt_packet_logger_client_start_service(device, &bt_packet_logger, TOOL_NAME); |
| 174 | 191 | ||
| 175 | /* start capturing bt_packet_logger */ | 192 | /* start capturing bt_packet_logger */ |
| 176 | bt_packet_logger_error_t serr = bt_packet_logger_start_capture(bt_packet_logger, bt_packet_logger_callback, NULL); | 193 | void (*callback)(uint8_t * data, uint16_t len, void *user_data); |
| 194 | switch (log_format){ | ||
| 195 | case LOG_FORMAT_PCAP: | ||
| 196 | callback = bt_packet_logger_callback_pcap; | ||
| 197 | break; | ||
| 198 | case LOG_FORMAT_PACKETLOGGER: | ||
| 199 | callback = bt_packet_logger_callback_packetlogger; | ||
| 200 | break; | ||
| 201 | default: | ||
| 202 | assert(0); | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | bt_packet_logger_error_t serr = bt_packet_logger_start_capture(bt_packet_logger, callback, NULL); | ||
| 177 | if (serr != BT_PACKET_LOGGER_E_SUCCESS) { | 206 | if (serr != BT_PACKET_LOGGER_E_SUCCESS) { |
| 178 | fprintf(stderr, "ERROR: Unable to start capturing bt_packet_logger.\n"); | 207 | fprintf(stderr, "ERROR: Unable to start capturing bt_packet_logger.\n"); |
| 179 | bt_packet_logger_client_free(bt_packet_logger); | 208 | bt_packet_logger_client_free(bt_packet_logger); |
| @@ -243,12 +272,13 @@ static void print_usage(int argc, char **argv, int is_error) | |||
| 243 | "Capture HCI packets from a connected device.\n" \ | 272 | "Capture HCI packets from a connected device.\n" \ |
| 244 | "\n" \ | 273 | "\n" \ |
| 245 | "OPTIONS:\n" \ | 274 | "OPTIONS:\n" \ |
| 246 | " -u, --udid UDID target specific device by UDID\n" \ | 275 | " -u, --udid UDID target specific device by UDID\n" \ |
| 247 | " -n, --network connect to network device\n" \ | 276 | " -n, --network connect to network device\n" \ |
| 248 | " -x, --exit exit when device disconnects\n" \ | 277 | " -f, --format FORMAT logging format: packetlogger (default) or pcap\n" \ |
| 249 | " -h, --help prints usage information\n" \ | 278 | " -x, --exit exit when device disconnects\n" \ |
| 250 | " -d, --debug enable communication debugging\n" \ | 279 | " -h, --help prints usage information\n" \ |
| 251 | " -v, --version prints version information\n" \ | 280 | " -d, --debug enable communication debugging\n" \ |
| 281 | " -v, --version prints version information\n" \ | ||
| 252 | "\n" \ | 282 | "\n" \ |
| 253 | "Homepage: <" PACKAGE_URL ">\n" | 283 | "Homepage: <" PACKAGE_URL ">\n" |
| 254 | "Bug Reports: <" PACKAGE_BUGREPORT ">\n" | 284 | "Bug Reports: <" PACKAGE_BUGREPORT ">\n" |
| @@ -265,6 +295,7 @@ int main(int argc, char *argv[]) | |||
| 265 | { "debug", no_argument, NULL, 'd' }, | 295 | { "debug", no_argument, NULL, 'd' }, |
| 266 | { "help", no_argument, NULL, 'h' }, | 296 | { "help", no_argument, NULL, 'h' }, |
| 267 | { "udid", required_argument, NULL, 'u' }, | 297 | { "udid", required_argument, NULL, 'u' }, |
| 298 | { "format", required_argument, NULL, 'f' }, | ||
| 268 | { "network", no_argument, NULL, 'n' }, | 299 | { "network", no_argument, NULL, 'n' }, |
| 269 | { "exit", no_argument, NULL, 'x' }, | 300 | { "exit", no_argument, NULL, 'x' }, |
| 270 | { "version", no_argument, NULL, 'v' }, | 301 | { "version", no_argument, NULL, 'v' }, |
| @@ -278,7 +309,7 @@ int main(int argc, char *argv[]) | |||
| 278 | signal(SIGPIPE, SIG_IGN); | 309 | signal(SIGPIPE, SIG_IGN); |
| 279 | #endif | 310 | #endif |
| 280 | 311 | ||
| 281 | while ((c = getopt_long(argc, argv, "dhu:nxv", longopts, NULL)) != -1) { | 312 | while ((c = getopt_long(argc, argv, "dhu:f:nxv", longopts, NULL)) != -1) { |
| 282 | switch (c) { | 313 | switch (c) { |
| 283 | case 'd': | 314 | case 'd': |
| 284 | idevice_set_debug_level(1); | 315 | idevice_set_debug_level(1); |
| @@ -292,6 +323,15 @@ int main(int argc, char *argv[]) | |||
| 292 | free(udid); | 323 | free(udid); |
| 293 | udid = strdup(optarg); | 324 | udid = strdup(optarg); |
| 294 | break; | 325 | break; |
| 326 | case 'f': | ||
| 327 | if (!*optarg) { | ||
| 328 | fprintf(stderr, "ERROR: FORMAT must not be empty!\n"); | ||
| 329 | print_usage(argc, argv, 1); | ||
| 330 | return 2; | ||
| 331 | } | ||
| 332 | free(log_format_string); | ||
| 333 | log_format_string = strdup(optarg); | ||
| 334 | break; | ||
| 295 | case 'n': | 335 | case 'n': |
| 296 | use_network = 1; | 336 | use_network = 1; |
| 297 | break; | 337 | break; |
| @@ -319,10 +359,23 @@ int main(int argc, char *argv[]) | |||
| 319 | return 2; | 359 | return 2; |
| 320 | } | 360 | } |
| 321 | 361 | ||
| 362 | if (log_format_string != NULL){ | ||
| 363 | if (strcmp("packetlogger", log_format_string) == 0){ | ||
| 364 | log_format = LOG_FORMAT_PACKETLOGGER; | ||
| 365 | } else if (strcmp("pcap", log_format_string) == 0){ | ||
| 366 | log_format = LOG_FORMAT_PCAP; | ||
| 367 | } else { | ||
| 368 | printf("Unknown logging format: '%s'\n", log_format_string); | ||
| 369 | print_usage(argc, argv, 1); | ||
| 370 | return 2; | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 322 | int num = 0; | 374 | int num = 0; |
| 323 | idevice_info_t *devices = NULL; | 375 | idevice_info_t *devices = NULL; |
| 324 | idevice_get_device_list_extended(&devices, &num); | 376 | idevice_get_device_list_extended(&devices, &num); |
| 325 | idevice_device_list_extended_free(devices); | 377 | idevice_device_list_extended_free(devices); |
| 378 | int oflags; | ||
| 326 | if (num == 0) { | 379 | if (num == 0) { |
| 327 | if (!udid) { | 380 | if (!udid) { |
| 328 | fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); | 381 | fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); |
| @@ -332,7 +385,27 @@ int main(int argc, char *argv[]) | |||
| 332 | } | 385 | } |
| 333 | } | 386 | } |
| 334 | 387 | ||
| 335 | dump = pcap_dump_open(pcap_open_dead(DLT_BLUETOOTH_HCI_H4_WITH_PHDR, BT_MAX_PACKET_SIZE), out_filename); | 388 | switch (log_format){ |
| 389 | case LOG_FORMAT_PCAP: | ||
| 390 | printf("Output Format: PCAP\n"); | ||
| 391 | pcap_dumper = pcap_dump_open(pcap_open_dead(DLT_BLUETOOTH_HCI_H4_WITH_PHDR, BT_MAX_PACKET_SIZE), out_filename); | ||
| 392 | break; | ||
| 393 | case LOG_FORMAT_PACKETLOGGER: | ||
| 394 | printf("Output Format: PacketLogger\n"); | ||
| 395 | oflags = O_WRONLY | O_CREAT | O_TRUNC; | ||
| 396 | #ifdef WIN32 | ||
| 397 | default_oflags |= O_BINARY; | ||
| 398 | #endif | ||
| 399 | packetlogger_fd = open(out_filename, oflags); | ||
| 400 | if (packetlogger_fd < 0){ | ||
| 401 | fprintf(stderr, "Failed to open file %s, errno = %d\n", out_filename, errno); | ||
| 402 | return -2; | ||
| 403 | } | ||
| 404 | break; | ||
| 405 | default: | ||
| 406 | assert(0); | ||
| 407 | return -2; | ||
| 408 | } | ||
| 336 | idevice_event_subscribe(device_event_cb, NULL); | 409 | idevice_event_subscribe(device_event_cb, NULL); |
| 337 | 410 | ||
| 338 | while (!quit_flag) { | 411 | while (!quit_flag) { |
| @@ -342,6 +415,13 @@ int main(int argc, char *argv[]) | |||
| 342 | idevice_event_unsubscribe(); | 415 | idevice_event_unsubscribe(); |
| 343 | stop_logging(); | 416 | stop_logging(); |
| 344 | 417 | ||
| 418 | if (pcap_dumper) { | ||
| 419 | pcap_dump_close(pcap_dumper); | ||
| 420 | } | ||
| 421 | if (packetlogger_fd >= 0){ | ||
| 422 | close(packetlogger_fd); | ||
| 423 | } | ||
| 424 | |||
| 345 | free(udid); | 425 | free(udid); |
| 346 | 426 | ||
| 347 | return 0; | 427 | return 0; |
