Это пошаговое руководство о том, как я взломал свой фитнес-трекер Bluetooth Low Energy (BLE), чтобы я мог управлять им из Linux.

Эта история началась с публикации в Facebook, в которой я рассказывал о проблеме отсутствия API для фитнес-трекеров и о том, почему это не помогает специалистам по обработке данных создавать для них крутые вещи.

Пост вызвал довольно много дискуссий и привлек внимание моего друга Владимира Шиманского, который попытался мне помочь и нашел на Github код от Leo Soares для моего фитнес-трекера MiBand 2. Он попробовал чтобы запустить его, но возникли проблемы с подключением. Он устранил проблему за несколько часов, зафиксировал код и прислал мне ссылку.

Это дало мне фору. Код, который я позволил мне подключиться к устройству MiBand 2, запустить уведомления и измерить сердце. Но этого было недостаточно для того, чем я хотел заниматься. Мне нужно было получать необработанные данные с датчиков в реальном времени, чтобы использовать их в своих экспериментах по науке о данных. Я хотел сделать предсказатель упражнений в спортзале.

Поэтому я решил взломать свой фитнес-трекер.

Веселье начинается.

У меня не было опыта работы с устройствами BLE, поэтому сначала я попытался понять, как работает эта технология и как разные части сочетаются друг с другом. Как оказалось, это было довольно просто, и вот краткое изложение.

  • Каждое устройство BLE имеет несколько сервисов
  • У каждой услуги есть свои характеристики
  • Некоторые характеристики имеют дескрипторы (если характеристика имеет более одного параметра или имеет тип чтения или уведомления).
  • Некоторые характеристики имеют доступ только для чтения / записи (например, текущее время, состояние батареи или информация о версии)
  • Некоторые характеристики более сложные и работают с использованием цикла запроса / уведомления (например, пульсометр в реальном времени или авторизация)

Это практически все, что вам нужно знать, чтобы начать работать с фитнес-трекером.

Вам также понадобятся два приложения, которые помогут отладить устройство BLE - Анализатор протокола Wireshark и Отладчик BLE. И вам нужно будет получить доступ к параметрам разработчика вашего телефона Android (извините, ребята, iOS, но вам придется найти эквивалентные варианты для платформы iOS).

Для начала вам необходимо отключить устройство MiBand2 от приложения для телефона.

Итак, давайте теперь посмотрим, какие услуги и характеристики есть у нашей группы. Откройте отладчик BLE и запустите сканирование. Вы увидите что-то вроде этого.

Сохраните где-нибудь MAC-адрес вашего устройства, он нам понадобится позже. Теперь давайте подключимся к нему и посмотрим, какими услугами и характеристиками он обладает.

С помощью этих двух простых операций мы уже получили некоторую полезную информацию о нашем устройстве. Другой способ сделать это - использовать инструменты командной строки, такие как hcitool и gatttool.

Чтобы запустить сканирование BLE из командной строки:

sudo hcitool lescan

Подключение к Mac-адресу вашего BLE-устройства и получение сервисов и дескрипторов:

sudo gatttool -b YOUR_MAC -I -t random
> connect
> primary
> char-desc

В некоторых случаях стек BLE может давать сбой, и вы можете включить / выключить устройство Bluetooth или выполнить эту команду:

sudo hciconfig hci0 reset

Подготовка к прослушиванию данных

Чтобы прослушивать данные с нашего телефона, когда он обменивается данными с устройством BLE, нам нужно включить журналы Bluetooth в настройках разработчика. Для этого вам нужно сначала включить настройки разработчика на вашем Android-устройстве. Для этого выполните следующие действия.

На Android 4.1 и ниже по умолчанию доступен экран параметров разработчика. На Android 4.2 и выше вы должны включить этот экран следующим образом:

  1. Откройте приложение Настройки на устройстве Android.
  2. Выберите Система. (Это нужно делать только на устройствах Android 8.0 или выше)
  3. Прокрутите вниз и выберите О телефоне.
  4. Прокрутите вниз и 7 раз нажмите Номер сборки.
  5. Вернитесь к предыдущему экрану и найдите внизу Параметры разработчика.

Теперь откройте настройки разработчика и найдите «Включить журнал отслеживания Bleutooth HCI» и включите его. После этого вся связь Bluetooth устройства с любым внешним устройством Bluetooth будет регистрироваться. Журналы можно просмотреть, открыв файл с именем btsnoop_hci.log (в случае моего устройства Android 7.0 они расположены в /mtklog/btlog/btsnoop_hci.log).

Аутентификация

Итак, теперь нам нужно будет выполнить следующие шаги, чтобы получить некоторую информацию о том, как работает аутентификация (соединение).

  1. Включите Bluetooth и журнал HCI.
  2. Подключите свое устройство к приложению Xiaomi Android.
  3. Выключите Bluetooth.
  4. Загрузите ваш btsnoop_hci.log на свой компьютер.
  5. Откройте его с помощью Wireshark.
  6. Найдите первый запрос протокола ATT, который переходит к дескриптору 0x0055 (который представляет Anhui Huami Information Technology Co, компанию, которая производит носимые устройства и владеет брендом Xiaomi).

Вы увидите что-то вроде этого:

Как видно из рисунка выше, значения UUID дескриптора следующие:

  • Устройство сопряжения
    UUID основной службы
    0000fee1-0000–1000–8000–00805f9b34fb
  • Характеристика аутентификации (символьный) UUID
    00000009–0000–3512–2118–0009af100700
  • Дескриптор уведомления (Des)
    0x2902 (он одинаков для всех вещей)

И вот шаги аутентификации:

  1. Настройка уведомлений об авторизации (получение ответа) путем отправки 2-байтового запроса \ x01 \ x00 на Des.
  2. Отправьте ключ шифрования 16 байт в Char с командой и добавив к нему 2 байта \ x01 \ x00 + KEY.
  3. Запрос случайного ключа от устройства с помощью команды, отправив 2 байта \ x02 \ x00 в Char.
  4. Получение случайного ключа из ответа устройства (последние 16 байт).
  5. Шифрование этого случайного числа с помощью нашего 16-байтового ключа с использованием алгоритма шифрования AES / ECB / NoPadding (из Crypto.Cipher, импорт AES) и отправка его обратно в Char ( \ x03 \ x00 + закодированные данные)

Данные в реальном времени

Процесс аутентификации был немного сложным, и, чтобы добавить к проблемам, монитор сердечного ритма отключался через 15 секунд. Вот значения UUID, полученные от устройства:

  • UUID службы оборудования (HRDW)
    0000fee0–0000–1000–8000–00805f9b34fb
  • UUID службы кардиомониторов (HMS)
    0000180d-0000–1000–8000–00805f9b34fb
  • UUID характеристики измерения частоты пульса (HRM)
    00002a37–0000–1000–8000–00805f9b34fb
  • Характеристика контроля сердечного ритма (HMC) UUID
    00002a39–0000–1000–8000–00805f9b34fb
  • Характеристика датчика (SENS) UUID
    00000001–0000–3512–2118–0009af100700
  • Дескриптор уведомления (DES)
    0x2902 (всегда один и тот же)

А вот как выполняются несколько общих действий:

  1. Чтобы отключить текущее измерение монитора слуха.
    Отправьте запрос в HMC \ x15 \ x02 \ x00 для однократных измерений.
    Отправьте запрос запрос в HMC \ x15 \ x01 \ x00 для непрерывных измерений.
  2. Включение необработанных данных гироскопа и сердца путем отправки команды в SENS \ x01 \ x03 \ x19
  3. Включение уведомления для HRM путем написания запроса в DES \ x01 \ x00
  4. Начните непрерывные измерения пульса, отправив запрос в HMC \ x15 \ x01 \ x01
  5. Отправка команды в SENS \ x02 (не знаю, зачем это нужно)
  6. Затем при получении уведомлений каждые 12 секунд нам нужно отправлять пинг со значением \ x16 в HCM.

Разбор данных

Понимание того, как анализируются данные, было не очень интересно, поскольку вам нужно было узнать, как распаковать данные, которые поступают с устройства.

Какая-то его часть может быть проанализирована из журналов, а какая-то - нет.

Поиск подходящих пакетов и кодирования может занять некоторое время. В моем случае я попытался найти похожие байтовые вхождения в пакетах рядом друг с другом, некоторые из которых повторяются в пакете.

Raw heart: 02102d8c348c448c458c3d8c428c488c 16
Raw heart: 0218468c418c3d8c468c3f8c398c418c 16
Realtime heart: 93
Raw heart: 0220408c448c3f8c428c498c3c8c3d8c 16
Raw heart: 02283d8c398c488c3e8c468c488c328c 16
Realtime heart: 99
Raw heart: 0230438c408c378c3a8c318c458c388c 16
Realtime heart: 102
Raw heart: 02404f8c408c458c428c4d8c558c4d8c 16
Raw heart: 02483e8c3b8c3f8c348c398c318c428c 16
Realtime heart: 98
Raw heart: 02504c8c428c5e8c4f8c588c498c558c 16
Raw heart: 0258478c458c3c8c4e8c3f8c468c4d8c 16
Realtime heart: 100
Raw heart: 0260518c4d8c4f8c4b8c4f8c528c458c 16
Raw heart: 0268408c3f8c538c4d8c408c548c598c 16
Realtime heart: 102
Raw heart: 0278418c508c4e8c548c588c468c498c 16
Raw heart: 0280368c328c2e8c3c8c338c308c3f8c 16
Realtime heart: 101

Мы видим четкую схему с повторяющейся строкой 368c 328c 2e8c 3c8c 338c 308c 3f8c и длиной пакета 16 байт. Если мы распакуем это с 2 байтами без знака, мы получим 7 сырых измерений датчика сердца.

Также мы видим, что второй байт просто повторяется, и я думаю, что это просто разница во времени между измерениями (ответами).

Raw gyro: 01de49ffd9ff3c004cffd8ff3b004dffdcff4400
Raw gyro: 01df4cffd6ff44004dffd8ff40004cffd1ff4700
Raw gyro: 02e1103231323d3274328e329632af32c732cf32
Raw gyro: 01e34fffd7ff56004bffc7ff590049ffccff4c00
Raw gyro: 01e443ffccff43004effcdff40005bffd4ff4c00
Raw gyro: 01e558ffc9ff5f005effbfff66005fffb0ff5900
Raw gyro: 01e64cffacff60005cffa7ff410066ffc9ff4600
Raw gyro: 01e760ffdcff4b0051ffe4ff4f0034ffdeff5300
Raw gyro: 02e903365c36813663361036543688374139fe3a
Raw gyro: 01eb4bffc3ff50004fffc1ff430047ffbbff4100
Raw gyro: 01ec3effb2ff3c0050ffbfff560047ffccff7300
Raw gyro: 01ed4fffe0ff78005cffebff8e0056fff6ff8300
Raw gyro: 01ee7efffbffa1008bff0f00bc00b1ff1900b800
Raw gyro: 01ef9bff0c00d10095fff3ffd600b7ff0800df00
Raw gyro: 02f12445314600479e473348aa481c499749244a
Raw gyro: 01f3c3ff1600fe00beff1800f200a6ff0800e700
Raw gyro: 01f4a9fff8ffd300a7fff3ffd700a9fff1ffdf00
Raw gyro: 01f5b1fff8ffe800b4fff1fff700acfffcffef00
Raw gyro: 01f67ffff7ffc0006bfff4ffb00078ffe9ffb600
Raw gyro: 01f786ffecffc0006ffff0ffbc0060fff1ffc000
Raw gyro: 02f9ca4cbb4c784c964ca84c784c854c444c1b4c
Raw gyro: 01fb7cff0f00bb007eff2700ae0083ff30009800
Raw gyro: 01fc79ff1800b00076ff0f00bc0068ff0900d900
Raw gyro: 01fd78ff07000c01f6fffbff19011c000b00f600
Raw gyro: 01fe4b001100d30054000700c3004300efffeb00
Raw gyro: 01ff1f00d0ff1701fbffe8ff1b01e3ffffff1101
Raw gyro: 0201214b014bec4ad04aba4acb4abe4aba4abd4a
Raw gyro: 0103efffecfffc00e3fff3fff300defff3fffc00
Raw gyro: 0104e3fff0fff400e6ffefff0301dbffe9ff0c01
Raw gyro: 0105e3fff0ff0301e6ffe6fffc00dcffecfffc00
Raw gyro: 0106dffff0fff700dbffeefff600d6fff0fff400
Raw gyro: 0107dfffecffff00e1fff0ff0301defff3fffc00

Что касается гироскопа, то было немного сложнее. Но я думал, что он должен быть упакован аналогично сердечным данным, но в этом случае у нас есть 3 измерения для каждой оси гироскопа, которые должны быть подписаны, а длина пакета составляет 20 байтов. Таким образом, 12 измерений x, y, z не будут охватывать все пакеты, но 3 будут, оставив первые 2 байта (как в предыдущем пакете). Итак, я попробовал это, и это сработало. 😃

Код

Вы можете найти код на Github, как всегда, с приведенным примером использования. Это довольно просто, поэтому мне не нужно вдаваться в подробности в этой статье.

Служба поддержки

Станьте покровителем и поддержите наше сообщество, чтобы оно делало больше интересных статей и руководств

Получайте интересные статьи каждый день - Подпишитесь на Telegram Channel

Прочтите другие мои свежие статьи