diff options
author | 2022-01-10 23:47:47 +0100 | |
---|---|---|
committer | 2022-05-05 19:04:56 +0200 | |
commit | be6eca8654f9bc7c686840a85a576b6453ad012d (patch) | |
tree | e54c88dffa86e2b041cf4f5b74a275e97e70fc20 | |
parent | 5b611c495910155e2c30333761adec0793313ab4 (diff) | |
download | libimobiledevice-be6eca8654f9bc7c686840a85a576b6453ad012d.tar.gz libimobiledevice-be6eca8654f9bc7c686840a85a576b6453ad012d.tar.bz2 |
idevicebtlogger: add --format option for 'pcap' and 'packetlogger'
-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; |