Автор: 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 (всегда неизменный)
Аутентификация состоит из следующих шагов:
Настройка auth-уведомлений (для получения ответа) посредством отправки двухбайтового запроса x01x00 в Des.
Отправка 16-байтового ключа шифрования в Char с командой и добавление двух байт x01x00 + KEY.
Запрос случайного ключа с устройства с командой посредством отправки двух байт x02x00 в Char.
Получение случайного ключа от устройства (последние 16 байт).
Шифрование этого случайного номера при помощи 16-байтового ключа, используя алгоритм шифрования AES/ECB/NoPadding (из Crypto.Cipher import AES) и обратная отправка в Char (x03x00 + закодированная информация).
Данные в режиме реального времени
Процесс аутентификации 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. Отключение текущего мониторинга сердцебиений. Отправка запроса x15x02x00 к HMC для одноразовых измерений. Отправка запроса x15x01x00 к HMC для непрерывных измерений.2.
Разрешение сырых данных от гироскопа и измерителя сердцебиений посредством отправки команды x01x03x19 к SENS
Включение уведомлений для HRM посредством отправки запроса x01x00 к DES
Начало непрерывных измерений сердцебиения посредством отправки запроса x15x01x01 к HMC
Отправка команды x02 к SENS (не очень понятно, зачем нужна эта команда)
Затем в процессе получения уведомлений каждый 12 секунд нам нужно отсылать пинг со значением x16 к HCM
Парсинг данных
Далее нужно было разобраться, как распаковать информацию, приходящую с устройства, для последующего парсинга.
Некоторую часть данных можно распарсить из логов, некоторую — нельзя.
Рисунок 4: Ответ от устройства с текущим временем
Поиск правильных пакетов и изучение системы кодирования может отнять некоторое время. В моем случае я попытался найти похожие байты в пакетах рядом с друг с другом. Некоторые байты повторяются внутри пакета.
Raw heart: 02102d8c348c448c458c3d8c428c488c 16Raw heart: 0218468c418c3d8c468c3f8c398c418c 16Realtime heart: 93Raw heart: 0220408c448c3f8c428c498c3c8c3d8c 16Raw heart: 02283d8c398c488c3e8c468c488c328c 16Realtime heart: 99Raw heart: 0230438c408c378c3a8c318c458c388c 16Realtime heart: 102Raw heart: 02404f8c408c458c428c4d8c558c4d8c 16Raw heart: 02483e8c3b8c3f8c348c398c318c428c 16Realtime heart: 98Raw heart: 02504c8c428c5e8c4f8c588c498c558c 16Raw heart: 0258478c458c3c8c4e8c3f8c468c4d8c 16Realtime heart: 100Raw heart: 0260518c4d8c4f8c4b8c4f8c528c458c 16Raw heart: 0268408c3f8c538c4d8c408c548c598c 16Realtime heart: 102Raw heart: 0278418c508c4e8c548c588c468c498c 16Raw heart: 0280368c328c2e8c3c8c338c308c3f8c 16Realtime 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
с примером использования. Ничего сложного нет, и я решил подробно не рассматривать этот момент в статье.
Источник: