Управление лампой через bluetooth

Разбираем bluetooth протокол RGB лампы

Внутри коробки имеется сама лампа, подставка для неё, пульт дистанционного управления и бумажка с QR-кодом для скачивания приложения.

Комплект поставки устройства

Под линзой находится три цветовых круга с световыми элементами:

Лампа

  • Внешний — синий цвет
  • Средний — зелёный цвет
  • Внутренний — красный цвет

Внешний вид официального приложения

Устанавливаем скачанное приложение на телефон — в качестве подопытного используется Samsung A8 2018 года выпуска (SM-A530F). После установки и открытия приложения нас встречает следующий интерфейс:

Интерфейс приложения

  • включить/выключить лампу
  • группировать несколько ламп в группы для одновременного управления
  • Поставить цвет из RGB палитры, отрегулировать яркость
  • Установить один из нескольких предустановленных вариантов свечения («дыхание», мигание и плавное переливание цветов) и скорость работы эффекта
  • Установить таймер работы лампы
  • Функционал свечения в такт музыки — нужно либо выбрать файл с телефона, либо предоставить доступ к микрофону

После подключения лампы к USB разъёму, она становится доступной для соединения с приложением:

Доступное устройство для подключения

Пробуем изменить цвета и установить эффекты — всё работает, значит можно приступать к декомпиляции приложения.

Разбираемся с исходным кодом приложения

Внутри коробки с лампой лежит листок с QR-кодом, который ведёт на страницу скачивания приложения из Google Play или App Store. Чтобы избежать выкачивания приложения из памяти телефона, возьмём APK, который предлагает производитель.

Страница скачивания приложения

Для декомпиляции приложения воспользуемся JADX — декомпилятор DEX файлов в Java. Скачиваем последний актуальный релиз (1.4.6 на момент написания статьи). Из предложенных в релизе вариантов я выбрал версию со встроенным JRE, дабы не устанавливать лишние зависимости в систему. После запуска открываем ранее скачанный .apk файл и. видим, что исходников практически нет, а те, что есть, не несут какой-либо практической пользы:

Обфусцированный код приложения

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

Подготавливаем устройство для сниффинга трафика

Для начала необходимо включить режим разработчика на устройстве — обычно это делается путём 9 нажатий на номер сборки в сведениях об ОС. Далее переходим в настройки режима разработчика, активируем пункты «включить журнал HCI Bluetooth» и «Отладка по USB» и перезапускаем bluetooth.

Заходим в приложение, выбираем из палитры красный, зелёный и синий цвета (чтобы легче было анализировать пакеты), подключаем смартфон через USB к компьютеру и через ADB вытаскиваем дамп:

adb pull /sdcard/btsnoop_hci.log # если не получится с вышеуказанной командой, # то скачиваем полный дамп системы и оттуда вытаскиваем файл по пути # /FS/data/log/bt/btsnoop_hci.log adb bugreport dump

Анализируем протокол общения через bluetooth

Для анализа протокола передачи данных между устройством и лампой воспользуемся Wireshark — программой-анализатором трафика множества различных протоколов. Скачиваем с официального сайта актуальную версию — я выбрал портабельную. Запускаем приложение, открываем bluetoooth dump с устройства, в проставляем фильтр btatt и фильтруем по колонке Info для быстрого поиска отправленных комманд:

Читайте также:  Беспроводная связь bluetooth стерео

Отправленные устройством команды

Соотносим отправленные цвета по времени и получаем следующую картину:

Никакой закономерности между изменением трёх байт цвета и отправленным значением нет — значит, применяется шифрование на клиенте и в таком виде отправляется на лампу, где происходит обратный процесс и применяются отправленные настройки.

Разбираемся с исходным кодом приложения. Опять

Раз с прошлым приложением у нас ничего не получилось, то скачаем с официального источника. Переходим по ссылке скачивания из Google Play и устанавливаем приложение на телефон. Приложение (на удивление) имеет 100к+ скачиваний и обновлено 27 февраля 2023 года:

Информация о приложении из google play

Далее необходимо вытащить apk файл приложения при помощи следующих команд:

# Получаем название пакета adb shell "pm list packages | grep strip" # получаем путь до apk файла (из вывода надо выбрать тот путь, что содержит base.apk): adb shell "pm path com.ben.istrips" # забираем приложение на пк adb pull /data/app/com.ben.istrips-JJlXI2S0nofBY-AqpNwOKA==/base.apk ./iStrip.apk

Открываем полученный apk файл через JADX и видим совсем другую картину:

Декомпилированное приложение

Итак, это успех — у нас теперь есть исходный код приложения, при помощи которого можно узнать, как шифруются данные. Бегло осматриваем исходный код и видим папку ble , в которой содержится файл BleProtocol . Открываем его и видим метод sendColor (комментарии переведены с китайского):

public static void sendColor(DataManager dataManager, int i) < int curColor = dataManager.getCurColor(); byte[] bArr = ; LogUtil.d("send data command:" + ByteUtils.BinaryToHexString(bArr)); boolean writeAll = BleManager.getInstance().writeAll(Agreement.getEncryptData(bArr)); LogUtil.d("send data result :" + writeAll); >

Вуаля — у нас есть массив, который шифруется при помощи AES и отправляется на лампу. Давайте подробно рассмотрим структуру данных:

Значение по умолчанию. Шапка запроса

Значение по умолчанию. Шапка запроса

Значение по умолчанию. Шапка запроса

Значение по умолчанию. Шапка запроса

ID группы (всегда должно быть больше 1, иначе лампа не примет такой запрос)

Неизвестно. В коде именуется как mode

Зелёный спектр цвета — от 0 до 255

Красный спектр цвета — от 0 до 255

Синий спектр цвета — от 0 до 255

Яркость лампы — от 0 до 100

Скорость работы эффекта — от 0 до 100

Используется для команды с типом 4 (настройка таймер) — минута для включения лампы

Используется для команды с типом 4 (настройка таймер) — день недели для выключения лампы

Читайте также:  Усилить сигнал блютуз наушники

Используется для команды с типом 4 (настройка таймер) — час для выключения лампы

Используется для команды с типом 4 (настройка таймер) — минута для выключения лампы

Внимание! Для моего устройства (а может так на всех других) перепутаны местами байты красного и зелёного спектров — поэтому в структуре сначала идёт зелёный, а потом красный, хоть в приложении и наоборот.

Теперь осталось поглядеть getEncryptData и дело сделано! Но тут появляется неожиданное обстоятельство:

public static byte[] getEncryptData(byte[] bArr)

Получается, что приложение использует библиотеку, написанную на C/C++ и ключа шифрования внутри кода нет — метод cipher принимает массив данных и массив, куда необходимо сохранить зашифрованные данные.

Предположим, что ключ шифрования задаётся функцией keyExpansion либо же устанавливается дефолтный ключ функцией keyExpansionDefault — проверим, используются ли эти методы в коде. После поиска по коду было найдено лишь одно использование метода keyExpansionDefault при создании приложения:

public class App extends Application < // . @Override // android.app.Application public void onCreate() < // . aes.keyExpansionDefault(); // . >>

Делаем вывод о том, что ключ всё-таки хранится внутри библиотеки и его необходимо достать оттуда. Для этого в JADX сохраняем проект через меню File -> Save all (или просто жмём CTRL+S ) и выбираем папку для сохранения.

Реверсим нативную библиотеку шифрования

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

Устанавливаем приложение с официального сайта, открываем при помощи него файл libAES.so , расположенный по пути папка проекта из JADX\app\src\main\lib\x86 , оставляем настройки декомпиляции по умолчанию и перед нами появляется список функций, которые есть в библиотеке:

Окно IDA Freeware после декомпиляции

Здесь видим 4 функции, которые начинаются с Java_ — это и есть те самые нативные функции, описанные внутри aes класса приложения. Переходим в keyExpansionDefault путём двойного нажатия на название в списке и видим первый блок функции, внутри которого есть упоминание key_ptr :

Блок метода keyExpansionDefault

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

Указатель key_ptr

Переходим в key и. Бинго! Внутри переменной находится массив из 16 байт, который и является ключом шифрования.

Ключ шифрования

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

Пишем сервис для генерации сообщений протокола

Далее будет использоваться .Net Core 6 и язык программирования C#. Весь исходный код опубликован на гитхабе — ссылка на репозиторий.

Проект не представляет из себя чего-то сложного — шифрование AES’ом массива данных при помощи заранее известного ключа.

Создаём класс PayloadGenerator , внутри которого объявляем ранее полученный ключ, шапку запроса, ID группы по умолчанию и создаём экземпляр криптографического объекта для шифрования данных:

public class PayloadGenerator < /// /// Ключ шифрования данных /// private static readonly byte[] Key = < 0x34, 0x52, 0x2A, 0x5B, 0x7A, 0x6E, 0x49, 0x2C, 0x08, 0x09, 0x0A, 0x9D, 0x8D, 0x2A, 0x23, 0xF8 >; /// /// Шапка для запроса - всегда статичная /// private static readonly byte[] Header = < 0x54, 0x52, 0x0, 0x57 >; private readonly ICryptoTransform _crypt; private const int GroupId = 1; public PayloadGenerator() < var aes = Aes.Create(); aes.Mode = CipherMode.ECB; _crypt = aes.CreateEncryptor(Key, null); >>

Далее опишем метод для генерации payload’a сообщения:

/// /// Получить payload для установки конкретного цвета лампы /// /// Красный спектр /// Зелёный спектр /// Синий спектр /// Яркость лампы (от 0 до 100) /// Скорость смены эффектов (от 0 до 100) /// payload для установки конкретного цвета лампы public string GetRgbPayload(byte red, byte green, byte blue, byte brightness = 100, byte speed = 100) < var payload = new byte[16] < Header[0], Header[1], Header[2], Header[3], (byte)CommandType.Rgb, GroupId, 0, green, red, blue, brightness, speed, 0x0, 0x0, 0x0, 0x0 >; var result = new byte[16]; _crypt.TransformBlock(payload, 0, payload.Length, result, 0); return ConvertToHexString(payload); > private static string ConvertToHexString(IEnumerable payload) < return string.Join("", payload.Select(x =>x.ToString("X2").ToLower())); >

И также создадим перечисление доступных команд из приложения:

public enum CommandType : byte < /// /// Запрос на вступление в группу /// JoinGroupRequest = 1, /// /// Установить конкретный цвет лампы /// Rgb = 2, /// /// Установить режим свечения в такт музыки /// Rhythm = 3, /// /// Установить таймер работы лампы /// Timer = 4, /// /// /// RgbLineSequence = 5, /// /// Установить скорость работы эффекта /// Speed = 6, /// /// Установить яркость лампы /// Light = 7 >

В Program.cs создаем экземпляр класса нашего генератора и выводим в консоль сгенерированное сообщение:

using IStripLight; var lightController = new PayloadGenerator(); var result = lightController.GetRgbPayload(0, 0, 255, 50); Console.WriteLine(result);

Итак, генератор сообщений у нас теперь есть, проверим созданные сообщения на работоспособность.

Читайте также:  Обновление драйвера bluetooth адаптера

Используем gatttool для отправки сообщений лампе

Для отправки сообщений лампе воспользуемся утилитой gatttool — она позволяет считывать и записывать характеристики GATT (Generic Attribute Protocol) для устройств, использующих Bluetooth low energy.

user@pi:~ $ sudo gatttool -I [ ][LE]> connect 43:d0:0c:e6:2b:20 Attempting to connect to 43:d0:0c:e6:2b:20 Connection successful [43:d0:0c:e6:2b:20][LE]> char-write-cmd 0x0009 ae066f229702720ca898a934839235f1

Яркость на лампе убавилась, а цвет поменялся на зелёный!

Вывод

В статье был проанализирован протокола общения приложения и лампы через реверс-инжиниринг android приложения и нативной библиотеки шифрования AES.

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

В дальнейшем планируется написать кастомную интеграцию Home Assistant для управления лампой через UI интерфейс или при помощи автоматизаций.

Источник

Оцените статью
Adblock
detector