├── LICENSE ├── README.md └── src ├── main.c ├── util-bluetooth.c └── util-bluetooth.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Robert David Graham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bluetoothid 2 | 3 | This is a work in progress. 4 | 5 | Identifies Bluetooth Low-energy devices 6 | 7 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "bluetooth/bluetooth.h" 9 | #include "bluetooth/hci.h" 10 | #include "bluetooth/hci_lib.h" 11 | 12 | 13 | struct device_version 14 | { 15 | unsigned version; 16 | unsigned revision; 17 | unsigned manufacturer; 18 | char version_string[32]; 19 | const char *manufacturer_string; 20 | }; 21 | 22 | int signal_received = 0; 23 | int global_fd = -1; 24 | static void sigint_handler(int sig) 25 | { 26 | if (global_fd != -1) 27 | close(global_fd); 28 | signal_received = sig; 29 | } 30 | 31 | static struct device_version 32 | get_device_version(int dev_id) 33 | { 34 | struct hci_version ver; 35 | char *hciver; 36 | int dd; 37 | int err; 38 | struct device_version result = {0}; 39 | 40 | result.manufacturer_string = ""; 41 | 42 | /* open this particular device. If the device isn't 'up', then 43 | * this will fail */ 44 | dd = hci_open_dev(dev_id); 45 | if (dd < 0) { 46 | fprintf(stderr, "[-] hci%d: can't open device: %s (%d)\n", 47 | dev_id, strerror(errno), errno); 48 | return result; 49 | } 50 | 51 | /* Get the version informaton for this device */ 52 | err = hci_read_local_version(dd, &ver, 1000); 53 | if (err < 0) { 54 | fprintf(stderr, "hci%d: can't read version: %s (%d)\n", 55 | dev_id, strerror(errno), errno); 56 | exit(1); 57 | } 58 | 59 | /* Convert the version integer into a useful string. Make 60 | * sure it's NUL-terminated in case strncpy() has a problem. */ 61 | hciver = hci_vertostr(ver.hci_ver); 62 | if (hciver) { 63 | strncpy(result.version_string, hciver, sizeof(result.version_string)); 64 | result.version_string[sizeof(result.version_string)-1] = '\0'; 65 | bt_free(hciver); 66 | } else { 67 | memcpy(result.version_string, "n/a", 4); 68 | } 69 | hci_close_dev(dd); 70 | 71 | result.version = ver.hci_ver; 72 | result.revision = ver.hci_rev; 73 | result.manufacturer = ver.manufacturer; 74 | result.manufacturer_string = bt_compidtostr(ver.manufacturer); 75 | 76 | return result; 77 | } 78 | 79 | static unsigned 80 | get_bluetooth_device_count(void) 81 | { 82 | struct hci_dev_list_req *dl; 83 | int err; 84 | int fd; 85 | unsigned result; 86 | 87 | /* Open a control socket to the Bluetooth subsystem */ 88 | fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 89 | if (fd < 0) 90 | return 0; 91 | 92 | /* Allocate memory large enough to hold all the possible 93 | * results. This should only be about 16 devices max */ 94 | dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)); 95 | if (dl == NULL) { 96 | close(fd); 97 | return 0; 98 | } 99 | dl->dev_num = HCI_MAX_DEV; 100 | 101 | /* Call the ioctl to get the initial list of devices */ 102 | err = ioctl(fd, HCIGETDEVLIST, dl); 103 | if (err < 0) { 104 | free(dl); 105 | close(fd); 106 | return 0; 107 | } 108 | 109 | result = dl->dev_num; 110 | free(dl); 111 | close(fd); 112 | 113 | return result; 114 | } 115 | 116 | 117 | static void 118 | print_dev_list(void) 119 | { 120 | struct hci_dev_list_req *dl; 121 | struct hci_dev_req *dr; 122 | int i; 123 | int err; 124 | int fd; 125 | 126 | /* Open a control socket to the Bluetooth subsystem */ 127 | fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 128 | if (fd < 0) { 129 | fprintf(stderr, "[-] HCI: can't open control interface\n"); 130 | fprintf(stderr, "[-] HCI: %s\n", strerror(errno)); 131 | exit(1); 132 | } 133 | 134 | /* Allocate memory large enough to hold all the possible 135 | * results. This should only be about 16 devices max */ 136 | dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)); 137 | if (dl == NULL) 138 | abort(); 139 | dl->dev_num = HCI_MAX_DEV; 140 | dr = dl->dev_req; 141 | 142 | /* Call the ioctl to get the initial list of devices */ 143 | err = ioctl(fd, HCIGETDEVLIST, dl); 144 | if (err < 0) { 145 | perror("Can't get device list"); 146 | free(dl); 147 | exit(1); 148 | } 149 | 150 | /* Query each device for detailed information */ 151 | for (i = 0; i< dl->dev_num; i++) { 152 | struct hci_dev_info di; 153 | di.dev_id = (dr+i)->dev_id; 154 | ioctl(fd, HCIGETDEVINFO, &di); 155 | if (err < 0) 156 | continue; 157 | 158 | printf("%s %s %s ", di.name, 159 | hci_test_bit(HCI_UP, &di.flags)?"UP":"DOWN", 160 | hci_test_bit(HCI_RUNNING, &di.flags)?"RUNNING":"STOPPED" 161 | ); 162 | if (hci_test_bit(HCI_UP, &di.flags)) { 163 | struct device_version v; 164 | v = get_device_version(di.dev_id); 165 | printf("HCIv%s(0x%x) rev(0x%x) manuf(%s %d)", 166 | v.version_string, 167 | v.version, 168 | v.revision, 169 | v.manufacturer_string, 170 | v.manufacturer); 171 | } 172 | printf("\n" ); 173 | } 174 | 175 | free(dl); 176 | close(fd); 177 | } 178 | 179 | /* Unofficial value, might still change */ 180 | #define LE_LINK 0x03 181 | 182 | #define FLAGS_AD_TYPE 0x01 183 | #define FLAGS_LIMITED_MODE_BIT 0x01 184 | #define FLAGS_GENERAL_MODE_BIT 0x02 185 | 186 | #define EIR_FLAGS 0x01 /* flags */ 187 | #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ 188 | #define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ 189 | #define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ 190 | #define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ 191 | #define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ 192 | #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ 193 | #define EIR_NAME_SHORT 0x08 /* shortened local name */ 194 | #define EIR_NAME_COMPLETE 0x09 /* complete local name */ 195 | #define EIR_TX_POWER 0x0A /* transmit power level */ 196 | #define EIR_DEVICE_ID 0x10 /* device ID */ 197 | 198 | static void eir_parse_name(uint8_t *eir, size_t eir_len, 199 | char *buf, size_t buf_len) 200 | { 201 | size_t offset; 202 | 203 | offset = 0; 204 | while (offset < eir_len) { 205 | uint8_t field_len = eir[0]; 206 | size_t name_len; 207 | 208 | /* Check for the end of EIR */ 209 | if (field_len == 0) 210 | break; 211 | 212 | if (offset + field_len > eir_len) 213 | goto failed; 214 | 215 | switch (eir[1]) { 216 | case EIR_NAME_SHORT: 217 | case EIR_NAME_COMPLETE: 218 | name_len = field_len - 1; 219 | if (name_len > buf_len) 220 | goto failed; 221 | 222 | memcpy(buf, &eir[2], name_len); 223 | return; 224 | } 225 | 226 | offset += field_len + 1; 227 | eir += field_len + 1; 228 | } 229 | 230 | failed: 231 | snprintf(buf, buf_len, "(unknown)"); 232 | } 233 | 234 | static int read_flags(uint8_t *flags, const uint8_t *data, size_t size) 235 | { 236 | size_t offset; 237 | 238 | if (!flags || !data) 239 | return -EINVAL; 240 | 241 | offset = 0; 242 | while (offset < size) { 243 | uint8_t len = data[offset]; 244 | uint8_t type; 245 | 246 | /* Check if it is the end of the significant part */ 247 | if (len == 0) 248 | break; 249 | 250 | if (len + offset > size) 251 | break; 252 | 253 | type = data[offset + 1]; 254 | 255 | if (type == FLAGS_AD_TYPE) { 256 | *flags = data[offset + 2]; 257 | return 0; 258 | } 259 | 260 | offset += 1 + len; 261 | } 262 | 263 | return -ENOENT; 264 | } 265 | 266 | static int check_report_filter(uint8_t procedure, le_advertising_info *info) 267 | { 268 | uint8_t flags; 269 | 270 | /* If no discovery procedure is set, all reports are treat as valid */ 271 | if (procedure == 0) 272 | return 1; 273 | 274 | /* Read flags AD type value from the advertising report if it exists */ 275 | if (read_flags(&flags, info->data, info->length)) 276 | return 0; 277 | 278 | switch (procedure) { 279 | case 'l': /* Limited Discovery Procedure */ 280 | if (flags & FLAGS_LIMITED_MODE_BIT) 281 | return 1; 282 | break; 283 | case 'g': /* General Discovery Procedure */ 284 | if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT)) 285 | return 1; 286 | break; 287 | default: 288 | fprintf(stderr, "Unknown discovery procedure\n"); 289 | } 290 | 291 | return 0; 292 | } 293 | 294 | #define NEXT_BYTE(buf, offset, length) ((((offset)+1) < length) ? (buf)[(offset)++] : (-1)) 295 | 296 | static void hexdump(const unsigned char *buf, size_t offset, size_t length) 297 | { 298 | size_t i; 299 | for (i=offset; i offset + len) { 324 | printf("length problem\n"); 325 | length = offset + len; 326 | } 327 | n = NEXT_BYTE(buf, offset, length); 328 | if (n != 0x02) { 329 | hexdump(buf, 0, length); 330 | return; 331 | } 332 | n = NEXT_BYTE(buf, offset, length); 333 | if (n != 0x01) { 334 | hexdump(buf, 0, length); 335 | return; 336 | } 337 | n = NEXT_BYTE(buf, offset, length); 338 | if (n != 0x04 && n != 0x00) { 339 | hexdump(buf, 0, length); 340 | return; 341 | } 342 | printf("-- "); 343 | hexdump(buf, offset, length); 344 | break; 345 | default: 346 | printf("UNKOWN "); 347 | hexdump(buf, 0, length); 348 | } 349 | } 350 | 351 | static int print_advertising_devices(int dd, uint8_t filter_type, const char *filename) 352 | { 353 | unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr; 354 | struct hci_filter nf, of; 355 | struct sigaction sa; 356 | socklen_t olen; 357 | int len; 358 | 359 | olen = sizeof(of); 360 | if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) { 361 | printf("Could not get socket options\n"); 362 | return -1; 363 | } 364 | 365 | hci_filter_clear(&nf); 366 | hci_filter_set_ptype(HCI_EVENT_PKT, &nf); 367 | hci_filter_set_event(EVT_LE_META_EVENT, &nf); 368 | 369 | if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) { 370 | printf("Could not set socket options\n"); 371 | return -1; 372 | } 373 | 374 | memset(&sa, 0, sizeof(sa)); 375 | sa.sa_flags = SA_NOCLDSTOP; 376 | sa.sa_handler = sigint_handler; 377 | sigaction(SIGINT, &sa, NULL); 378 | 379 | unsigned count = 0; 380 | FILE *fp; 381 | fp = fopen(filename, "wb"); 382 | if (fp == NULL) { 383 | perror(filename); 384 | exit(1); 385 | } 386 | global_fd = dd; 387 | while (1) { 388 | evt_le_meta_event *meta; 389 | le_advertising_info *info; 390 | char addr[18]; 391 | 392 | while ((len = read(dd, buf, sizeof(buf))) < 0) { 393 | if (errno == EINTR && signal_received == SIGINT) { 394 | len = 0; 395 | goto done; 396 | } 397 | 398 | if (errno == EAGAIN || errno == EINTR) 399 | continue; 400 | goto done; 401 | } 402 | 403 | if (count % 0xFF == 0) 404 | fprintf(stderr, "%9u\b\b\b\b\b\b\b\b\b", count); 405 | fflush(stderr); 406 | count++; 407 | //decode_packet(buf, len); 408 | fwrite(&len, 1, sizeof(len), fp); 409 | fwrite(buf, 1, len, fp); 410 | continue; 411 | 412 | ptr = buf + (1 + HCI_EVENT_HDR_SIZE); 413 | len -= (1 + HCI_EVENT_HDR_SIZE); 414 | 415 | meta = (void *) ptr; 416 | 417 | if (meta->subevent != 0x02) 418 | goto done; 419 | 420 | /* Ignoring multiple reports */ 421 | info = (le_advertising_info *) (meta->data + 1); 422 | if (check_report_filter(filter_type, info)) { 423 | char name[30]; 424 | 425 | memset(name, 0, sizeof(name)); 426 | 427 | ba2str(&info->bdaddr, addr); 428 | eir_parse_name(info->data, info->length, 429 | name, sizeof(name) - 1); 430 | 431 | //printf("%s %s\n", addr, name); 432 | } 433 | } 434 | 435 | done: 436 | setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)); 437 | 438 | if (len < 0) 439 | return -1; 440 | 441 | return 0; 442 | } 443 | 444 | 445 | int main(int argc, char *argv[]) 446 | { 447 | if (argc == 1) { 448 | printf("usage:\n bleid dev\n bleid mon\n"); 449 | exit(1); 450 | } 451 | 452 | /* Before we do anything else, make sure that Bluetooth devices 453 | * exist in the system. */ 454 | unsigned device_count = get_bluetooth_device_count(); 455 | if (device_count == 0) { 456 | fprintf(stderr, "[-] no Bluetooth hardware devices found\n"); 457 | exit(1); 458 | } 459 | 460 | /* If we are just listing devices and their states, then report 461 | * that. */ 462 | if (strcmp(argv[1], "dev") == 0) { 463 | print_dev_list(); 464 | exit(0); 465 | } 466 | 467 | int dev_id; 468 | int dd; 469 | int err; 470 | 471 | 472 | /* Get the first available bluetooth device */ 473 | dev_id = hci_get_route(NULL); 474 | if (dev_id < 0) { 475 | fprintf(stderr, "[-] hci_get_route(): %s\n", strerror(errno)); 476 | fprintf(stderr, "[-] no bluetooth devices available. Hint: hciconfig hci0 up\n"); 477 | exit(1); 478 | } 479 | fprintf(stderr, "[+] hci%d: active\n", dev_id); 480 | 481 | /* Open the device that we are going to use*/ 482 | dd = hci_open_dev(dev_id); 483 | if (dev_id < 0 || dd < 0) { 484 | fprintf(stderr, "[-] hci_open_dev(): %s\n", strerror(errno)); 485 | exit(1); 486 | } 487 | 488 | uint8_t own_type = 0x00; 489 | uint8_t scan_type = 0x01; 490 | uint8_t filter_type = 0; 491 | uint8_t filter_policy = 0x00; 492 | uint16_t interval = htobs(0x0010); 493 | uint16_t window = htobs(0x0010); 494 | uint8_t filter_dup = 1; /* We want to see duplicates */ 495 | 496 | err = hci_le_set_scan_parameters(dd, scan_type, interval, window, 497 | own_type, filter_policy, 1000); 498 | if (err < 0) { 499 | perror("Set scan parameters failed"); 500 | exit(1); 501 | } 502 | 503 | err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000); 504 | if (err < 0) { 505 | perror("Enable scan failed"); 506 | exit(1); 507 | } 508 | 509 | printf("LE Scan ...\n"); 510 | 511 | err = print_advertising_devices(dd, filter_type, argv[1]); 512 | if (err < 0) { 513 | perror("Could not receive advertising events"); 514 | exit(1); 515 | } 516 | 517 | } 518 | -------------------------------------------------------------------------------- /src/util-bluetooth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef AF_BLUETOOTH 12 | #define AF_BLUETOOTH 31 13 | #define PF_BLUETOOTH AF_BLUETOOTH 14 | #endif 15 | 16 | #define BTPROTO_L2CAP 0 17 | #define BTPROTO_HCI 1 18 | #define BTPROTO_SCO 2 19 | #define BTPROTO_RFCOMM 3 20 | #define BTPROTO_BNEP 4 21 | #define BTPROTO_CMTP 5 22 | #define BTPROTO_HIDP 6 23 | #define BTPROTO_AVDTP 7 24 | 25 | #define SOL_HCI 0 26 | #define SOL_L2CAP 6 27 | #define SOL_SCO 17 28 | #define SOL_RFCOMM 18 29 | 30 | #ifndef SOL_BLUETOOTH 31 | #define SOL_BLUETOOTH 274 32 | #endif 33 | 34 | /* HCI device flags */ 35 | enum { 36 | HCI_UP, 37 | HCI_INIT, 38 | HCI_RUNNING, 39 | 40 | HCI_PSCAN, 41 | HCI_ISCAN, 42 | HCI_AUTH, 43 | HCI_ENCRYPT, 44 | HCI_INQUIRY, 45 | 46 | HCI_RAW, 47 | }; 48 | 49 | typedef struct { 50 | unsigned char b[6]; 51 | } __attribute__((packed)) bdaddr_t; 52 | 53 | /* BD Address type */ 54 | #define BDADDR_BREDR 0x00 55 | #define BDADDR_LE_PUBLIC 0x01 56 | #define BDADDR_LE_RANDOM 0x02 57 | 58 | #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) 59 | #define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) 60 | #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) 61 | 62 | /* Copy, swap, convert BD Address */ 63 | static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) 64 | { 65 | return memcmp(ba1, ba2, sizeof(bdaddr_t)); 66 | } 67 | static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) 68 | { 69 | memcpy(dst, src, sizeof(bdaddr_t)); 70 | } 71 | 72 | 73 | struct sockaddr_hci { 74 | sa_family_t hci_family; 75 | unsigned short hci_dev; 76 | unsigned short hci_channel; 77 | }; 78 | 79 | int bt_device_open(int dev_id) 80 | { 81 | struct sockaddr_hci a; 82 | int dd, err; 83 | 84 | /* Create HCI socket */ 85 | dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 86 | if (dd < 0) 87 | return dd; 88 | 89 | /* Bind socket to the HCI device */ 90 | memset(&a, 0, sizeof(a)); 91 | a.hci_family = AF_BLUETOOTH; 92 | a.hci_dev = dev_id; 93 | if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0) 94 | goto failed; 95 | 96 | return dd; 97 | 98 | failed: 99 | err = errno; 100 | close(dd); 101 | errno = err; 102 | 103 | return -1; 104 | } 105 | 106 | static int __other_bdaddr(int dd, int dev_id, long arg) 107 | { 108 | struct hci_dev_info di = { .dev_id = dev_id }; 109 | 110 | if (ioctl(dd, HCIGETDEVINFO, (void *) &di)) 111 | return 0; 112 | 113 | if (hci_test_bit(HCI_RAW, &di.flags)) 114 | return 0; 115 | 116 | return bacmp((bdaddr_t *) arg, &di.bdaddr); 117 | } 118 | 119 | static int __same_bdaddr(int dd, int dev_id, long arg) 120 | { 121 | struct hci_dev_info di = { .dev_id = dev_id }; 122 | 123 | if (ioctl(dd, HCIGETDEVINFO, (void *) &di)) 124 | return 0; 125 | 126 | return !bacmp((bdaddr_t *) arg, &di.bdaddr); 127 | } 128 | 129 | int hci_get_route(const unsigned char *bdaddr) 130 | { 131 | int dev_id; 132 | 133 | dev_id = hci_for_each_dev(HCI_UP, __other_bdaddr, 134 | (long) (bdaddr ? bdaddr : BDADDR_ANY)); 135 | if (dev_id < 0) 136 | dev_id = hci_for_each_dev(HCI_UP, __same_bdaddr, 137 | (long) (bdaddr ? bdaddr : BDADDR_ANY)); 138 | 139 | return dev_id; 140 | } 141 | 142 | int main(int argc, char *argv[]) 143 | { 144 | int fd = bt_device_open(0); 145 | 146 | if (fd == -1) { 147 | fprintf(stderr, "open(): error (%d) %s\n", errno, strerror(errno)); 148 | exit(1); 149 | } 150 | } -------------------------------------------------------------------------------- /src/util-bluetooth.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertdavidgraham/bluetoothid/84c080d376b7deee3c5235b75d1fb46301887dbe/src/util-bluetooth.h --------------------------------------------------------------------------------