04.06.2019

Пошаговое руководство взлома фитнес-браслета Xiaomi MiBand 2 в Linux

image

В этой статье будет приведено пошаговое руководство взлома фитнес-трекера xiaomi mi band 2


Автор: Andrey Nikishaev


В этой статье будет приведено пошаговое руководство взлома фитнес-трекера с поддержкой Bluetooth и низким энергопотреблением средствами ОС Linux.

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

Тот пост спровоцировал жаркую дискуссию и привлек внимание моего друга Володимира Шиманского, пытавшегося помочь мне с кодом Лео Соареса для моего фитнес-трекера MiBand 2. Володимир пытался запустить этот код, но были проблемы с соединением. Проблема была решена в течение нескольких часов, и код обновлен.

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

В итоге было решено хакнуть фитнес-трекер.

Приступаем.

У меня не было никакого опыта работы с BLE-девайсами (Bluetooth Low Energy), и я решил начать с изучения этой технологии. Выяснилось, что никаких особых сложностей нет:

  • У каждого BLE-устройства есть несколько служб.

  • У каждой службы есть некоторые характеристики.

  • У некоторых характеристик есть дескрипторы (если у характеристики больше чем один параметр или тип соответствует чтению или уведомлению).

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

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

Вышеуказанного списка вполне достаточно для начала работы с фитнес-трекером.

Также вам понадобятся два приложения для отладки BLE-устройства: анализаторпротоколов Wireshark и BLE отладчик. Кроме того, нужно активировать опции разработчика в устройстве на базе Android (для любителей iOS придется поискать эквивалент в этой платформе).

Для начала нужно отключить MiBand2 от приложения телефона.

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

Рисунок 1: Перечень устройств доступных для подключения

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

Рисунок 2: Перечень служб устройства

Выполнив две простые операции, мы уже получили полезную информацию о нашем устройстве. Схожая функция доступна через командную строку при помощи утилит hcitool и gatttool.

Запуск сканирования из командной строки:

sudo hcitool lescan

Подключение к BLE устройство через Mac адрес и получение списка служб и дескрипторов:

sudo gatttool -b YOUR_MAC -I -t random

> connect

> primary

> char-desc

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

sudo hciconfig hci0 reset

Подготовка к сниффингу данных

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

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

1. Откройте раздел Settings на Android-устройстве.

2. Выберите раздел System (только на устройствах с Android 8.0 и выше).

3. Прокрутите вниз и выберите About phone.

4. Прокрутите вниз и тапните 7 раз на Build number.

5. Возвратитесь к предыдущему разделу. Почти в самом низу должен появиться раздел Developer options.

Откройте настройки разработчика и включите опцию «Enable Bleutooth HCI snoop log». Теперь все коммуникации между телефоном и любым внешним Bluetooth-устройством должны отражаться в файле btsnoop_hci.log. (на моем телефоне с Android 7.0 логи находятся в /mtklog/btlog/btsnoop_hci.log).

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

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

1. Включите Bluetooth и HCI логи.

2. Выполните сопряжение устройства с приложением Xiaomi Android App.

3. Отключите Bluetooth.

4. Загрузите btsnoop_hci.log на компьютер.

5. Откройте файл в Wireshark.

6. Найдите первый запрос, имеющий отношение к протоколу ATT, с параметром Handle: 0x0055 (который связан с компанией Anhui Huami Information Technology Co, выпускающей переносные устройства и владеющей брендом Xiaomi).

Должно получиться примерно следующее:

Рисунок 3: Содержимое логов с Android-устройства

Выделенный запрос – первый этап аутентификации.

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

  • Pairing device
    Main service UUID
    0000fee1-0000–1000–8000–00805f9b34fb

  • Auth Characteristic (Char) UUID
    00000009–0000–3512–2118–0009af100700

  • Notification descriptor (Des) handle


0x2902 (всегда неизменный)

Аутентификация состоит из следующих шагов:

  1. Настройка auth-уведомлений (для получения ответа) посредством отправки двухбайтового запроса \x01\x00 в Des.

  2. Отправка 16-байтового ключа шифрования в Char с командой и добавление двух байт \x01\x00 + KEY.

  3. Запрос случайного ключа с устройства с командой посредством отправки двух байт \x02\x00 в Char.

  4. Получение случайного ключа от устройства (последние 16 байт).

  5. Шифрование этого случайного номера при помощи 16-байтового ключа, используя алгоритм шифрования AES/ECB/NoPadding (из Crypto.Cipher import AES) и обратная отправка в Char (\x03\x00 + закодированная информация).

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

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

  • Hardware service (HRDW) UUID
    0000fee0–0000–1000–8000–00805f9b34fb
  • Heart Monitor Service (HMS) UUID
    0000180d-0000–1000–8000–00805f9b34fb
  • Heart Rate Measure Characteristic (HRM) UUID
    00002a37–0000–1000–8000–00805f9b34fb
  • Heart Monitor Control Characteristic (HMC) UUID
    00002a39–0000–1000–8000–00805f9b34fb
  • Sensor Characteristic (SENS) UUID
    00000001–0000–3512–2118–0009af100700
  • Notification descriptor (DES) handle
    0x2902 (всегда неизменный)

Выполняются несколько стандартных операций:

  1. 1. Отключение текущего мониторинга сердцебиений. Отправка запроса \x15\x02\x00 к HMC для одноразовых измерений. Отправка запроса \x15\x01\x00 к HMC для непрерывных измерений.2.

  2. Разрешение сырых данных от гироскопа и измерителя сердцебиений посредством отправки команды \x01\x03\x19 к SENS

  3. Включение уведомлений для HRM посредством отправки запроса \x01\x00 к DES

  4. Начало непрерывных измерений сердцебиения посредством отправки запроса \x15\x01\x01 к HMC

  5. Отправка команды \x02 к SENS (не очень понятно, зачем нужна эта команда)

  6. Затем в процессе получения уведомлений каждый 12 секунд нам нужно отсылать пинг со значением \x16 к HCM

Парсинг данных

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

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

Рисунок 4: Ответ от устройства с текущим временем

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

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, а в 3 измерения по осям x, y, z. Первые 2 байта играют ту же роль, что и в предыдущем случае. В итоге моя гипотеза оказалась верной.

Код

Код, как обычно, можно найти на Github с примером использования. Ничего сложного нет, и я решил подробно не рассматривать этот момент в статье.