История берет свое начало в мае, когда в один из непогожих питерских дней, местная котельная решила отключить отопление. За окном было +10, влажность зашкаливала, ветер дул, а тепленькое солнышко не светило в окна от слова совсем (чертова северная сторона). В квартире стало ощутимо холодно. Кот слезал с теплых колен только для опустошения миски. Мы же кутались в флиски и пледы. Через два дня такой жизни стало понятно - к черту все, нужен обогреватель! Требования были довольно просты - цена, определенная мощность, возможность эту самую мощность регулировать и какой-либо интерфейс (wi-fi\BT\ZigBee\485). Последнее хотелось больше для баловства и неведомого "а вдруг потребуется!". Оставлю за рамками статьи муки выбора, количество обогревателей на полках магазинов в конце весны и прочие приколы наших доставщиков. В итоге, я стал обладателем конвектора под брендом… впрочем, если не показывать шильдик, то можно найти картинки аж трех производителей "клепающих" одинаковые модели. А если погуглить чуть поглубже, то окажется что в РФ франшиза на выпуск конвекторов под этими брендам принадлежит одной единственной конторе. И все встает на свои места - конвектор один, а шильдик лишь влияет на цену. Впрочем, мне то какая разница - грело бы, да управлялось. Что-то я отвлекся. Итак, конвектор модульный:  нагревательный блок блок с инвертором для управления Wi-fi свисток Cобрано, запущено, кот согрет, самое время посмотреть, что там с интерфейсом. Приложение для мобилки подключилось к конвектору, залило настройки wi-fi сети и радостно предложило управлять обогревателем через интернет. И в принципе на этом можно было бы закончить, но в процессе эксплуатации появилось желание - время от времени использовать конвектор для просушки одного из помещений. Датчик влажности есть, к Home Assistant подключен, дело за малым, завести туда же конвектор. Тут меня ждало полнейшее разочарование - никакого API, никаких интеграций, вообще ни_че_го. Лан, инженером же работаю, не в первой городить программно-аппаратные решения. Первое, что делает любой инженер - берет отвертку и смотрит внутрь. А внутри USB-модуля оказалась железка HF-LPT220. Заботливый гугл находит: Статью (часть 1 и часть 2) пользователя @avstepanov Краткое содержание: Кондиционер, модуль такой же, попробовали wi-fi, API нет, решили проблему через управление по IR. Сайт производителя  Support IEEE802.11b/g/n Wireless Standards Based on High-Flying Cost Effective Wi-Fi SOC: MC300 chipset Support UART/GPIO Data Communication Interface Support Work As STA/AP Mode Support Smart LinkFunction(APP program provide) Support Wireless and Remote Firmware Upgrade Function  Support WPS Function(Reserved) Support Internal/ExternalPinAntenna Option Single +3.3V Power Supply Smallest Size: 22mm x 13.5mm x3mm , SMT17 Package FCC/CE Certificated Закатываем рукава достаем чемоданчик с инструментарием:  Железки: Mikrotik для перехвата пакетов Логический анализатор (клон saleae Logic 8) - для анализа интерфейса, потребуется в третьей части Различные кабели (USB-AM - USB-AF) USB-UART преобразователь. В моем случае - CP2102  ПО: Wireshark Java Decompiler Android Studio Putty Раз мобильное приложение общается с железкой через wi-fi, логично запустить сниффер и посмотреть что там бегает. Хорошо что микротик умеет "зеркалить" траффик в Wireshark (wiki mikrotik, инструкция попроще). Запускаем wireshark, включаем обогреватель и наблюдаем. После отрабатывания DHCP-клиента, железка запрашивает IP адрес dongle.***.ru, где *** представительство в РФ (не вендор). Просто примем эту информацию к сведенью и сделаем вывод - в других международных регионах скорее всего будут свои фирмы-представители и, соответственно, адреса.  Происходит установка TCP-соединения и начинается обмен AT-командами Данные команды повторяются несколько раз, как и ответы на них, видимо, происходит не очень быстрая обработка со стороны сервера.  Идем в документацию с сайта hi-flying и действительно находим:AT+NDBGL - Enable\Disable UART informationAT+WSMAC - Set\Query Module MAC address parameters. Setting is valid after reset.AT+VER - Query module software version information. AT+APPVER в документе отсутствует, т.к. модуль и прошивка датированы 2017 г. а вот документация на сайте 2016. Далее, происходит переход на свой, проприетарный, протокол, состоящий из двух секций.  Первая, предположительно, инициализация. Про нее особо сказать нечего, т.к. при каждом запуске прилетают и улетают одинаковые данные.  А вот вторая, заставляет задуматься. Честно говоря, в эпоху пристального внимания к безопасности IoT, не надеялся увидеть что-либо дельное, однако… Серьезно? Как-то это не похоже на зашифрованный TLS\SSL\Ipsec - траффик.  Cходу можно сделать предположение - последний байт, это контрольная, однобайтовая сумма. Открываем калькулятор и складываем AA+C+A+1+1A+1+6+5+1 = E8. Ясно, понятно. Начинаем жать все кнопки в приложении и обнаруживаем некоторые закономерности, попутно пишем на LUA dissector для Wireshark. Не парсить, же байты каждый раз =)  Получаем занятную штуку, на примере: aa 0c 0a 01 1a 01 06 00 00 00 00 05 00 01 e8 Наименование Размер
(байт) Примечание и возможные значения Пример Заголовок 2 Байты неизменны из пакета в пакет, возможно, это внутренний ID устройства. С MAC не совпадают. aa 0c Команда 
или 
событие 1 09 - произошло изменение состояния - от конвектора 0a - установить (запрос на установку) параметры - от сервера 8a - подтверждение установки параметров (ответ на 0а) 88 - отправка данных на сервер. По запросу от сервера. (см. ниже) 0a
(установить) Статус 1 00 - отключен 01 - включен 02 - неизвестно (резерв?) 03 - блокировка клавиш на конвекторе 01
(включить) Температура 1 Поддерживаемая температура. Задается в приложении или на устройстве. 1a 
(установить 26 гр.) Режим 1 Режимы эксплуатации:01 - Comfort - обычный нагрев. 02 - Night - ночной режим, ниже комфортного на 4 градуса 03 - Незамерз. - режим для дач, когда поддерживается 5 гр. 01 (установить Comfort) Мощность 1 01 - 1 ур. - минимальная … 05 - 5 ур. - максимальная06 - Auto - автоматическая 06
(установить режим Auto) Таймер 2 Таймер на отключение, в минутах, минуты в hex, утка в зайце Например, 22:59 22*60+59=1379 минут =0563h 00 00 (таймер выставить в 00:00) Статус таймера 1 00 - оключен01 - включен 00 (отключить) Температура в помещении 1 Температура с датчика (при установке = 00) 00 Неизвестно 2 У меня не используются. Варианты: -в некоторых конвекторах установлен ионизатор возможно один байт отвечает за него.-модуль универсальный, подходит к кондиционерам и водонагревателям, возможно используются там -резерв 05 00 Дисплей 1 00 - отключен 01 - включен Да, все верно, у конвектора можно отключить адскую зеленую подсветку. 01 (дисплей включить) Контрольная сумма 1 Сумма всех предыдущих байт. Формально, остаток от деления на 255. e8 Отдельно хочется выделить запрос текущей конфигурации от сервера: Data Примечание AA 03 08 10 04 C9 Конвектор отвечает пакетом выше, с кодом команды 88 * На этапе инициализации используются дополнительные пакеты и команды, но оставим это за рамками статьи. Фуф, с данными, вроде как понятно. Но будет ли устройство принимать команды от стороннего сервера? Адрес сервера забит в прошивку. Мы конечно можем, подменить ответ DNS-сервера на локальном маршрутизаторе, но сначала стоит попробовать что-нибудь попроще. Посмотрим, какие порты открыты на сервере конвекторе. Linux\WSL > Bash > Netcatnc -vnz <IP конвектора> 1-65000 2>&1 | grep succeeded Через довольно продолжительное время, обнаружится открытый, 8899 порт. ONVIF у конвектора? Whaaat?! Но нам то что? Попробуем отправить пакет на включение и выставление настроек: echo -e "\xAA\x0C\x0A\x01\x17\x01\x01\x00\x00\x00\x00\x00\x00\x00\xDA" | nc <IP конвектора> 8899 И, внезапно, оно работает! Есть одно, НО. При отправке запроса о текущей конфигурации (AA 03 08 10 04 C9), ответ отправляется не тому кто запросил, а на сервер с которым установлено соединение. Как итог, управление возможно только в слепую, никаких данных о включении, отключении, режиме и температуре получить невозможно. Есть два способа изменить это поведение (сборка своей прошивки не в счет). Первый. Так как в прошивке зашито DNS-имя, можно изменить IP-адрес в ответе DNS-сервера, так чтобы dongle.*****.ru ссылался на 192.168.0.* - т.е. прописать статическую DNS запись.Плюс данного способа - все настройки делаются на маршрутизаторе\DNS-сервере в пару кликов.Минус - очевидно, способ нерабочий, если ваш DHCP-сервер выдает клиентам публичные DNS-сервера (8.8.8.8, 1.1.1.1 и прочие "семейные DNS"). Второй способ приведен ниже, в части посвященной UART и касается изменении адреса сервера в модуле wi-fi.Плюсы - гарантированная работа в ЛВС.Минусы - жесткая привязка к конкретной ЛВС - это надо помнить. Итак, запускаем (или пишем на коленке) на локальной машине простенький tcp-сервер. Отправляем железке AA 03 08 10 04 C9 в ответ получаем  aa 0c 88 01 15 01 01 00 00 00 18 00 70 00 00 00 6e Теперь, все работает как надо. Промежуточные результаты: Можно начинать писать свой шлюз\OPC-сервер\плагин для системы управления умным домом. А что если есть более простой способ рулить конвектором? Например, подключить систему управления умным домом напрямую к серверу и перекидываться JSON'ами. Реализовать такое управление, безусловно, проще.Что ж, стоит посмотреть, как мобильное приложение общается с сервером. Запускаем wireshark, настраиваем mikrotik и смотрим. А посмотреть есть на что. Приложение обращается к тому же самому IP - 82.209.**.** (правда я не увидел запроса к DNS, но возможно ОС взяла значение из кэша). А так же смотрим первый TCP Stream. Да у нас тут целый запрос-ответ. Ответ (К сожалению в ответе пришлось бы блюрить огромное количество данных, по этому вставка текстом, некоторые заголовки вырезаны): Что имеем? В запросе используется голый http, передающий в открытом виде:appcode - бренд. Напомню, производитель изготавливает оборудование под разными брендами.login - номер мобильного телефона, который является логином в приложенииpassword - пароль. Не хэш. Именно пароль, указываемый при регистрации, и использующийся для входа в приложение. Интересное в ответе: На той стороне используется би_cms_. Нам передают токен Забегая вперед скажу, что нам передают ключ шифрования - enc_key  *Б - безопасность. Но давайте посмотрим что происходит дальше, а потом проанализируем все вместе. Сморим, что же происходит дальше. А дальше происходит небольшой обмен командами. Cервер запрашивает присвоенный токен, приложение его отправляет и в ответ получает это: Погодите, шифрование? Вот это поворот! Тут наши полномочия, как говорится все. Но погодите, мы встречаем 3 одинаковых последовательности. Причем, одно из сообщений дублируется два раза. Открываем приложение, нажимаем разные кнопочки и обнаруживаем, что некоторые пакеты не меняются. Делаем первый промежуточный вывод - шифрование не привязано ко времени. Конец сообщения очень сильно что-то напоминает. Да и по закону жанра в конце сообщения должен быть хэш\CRC. Проводим некоторое время в калькуляторах перебирая алгоритмы и действительно, конец сообщения содержит свой хэш в MD5 (дайджест?). Ок. Осталось определить алгоритм шифрования. Developer android заботливо подсказывает, что алгоритмов поддерживается дофига, но рекомендуется использовать AES256. Запомним.На вопрос "Как определить алгоритм шифрования зная шифротекст?", гугл лаконично отвечает "Никак, анализируй приложение (де)шифровщик". Забрались далеко, отступать не наши методы. Качаем APK, при помощи dex2jar конвертируем в jdk и открываем в JD. Можно заняться полным реверсом (не ассемблер же), но быстрее будет запустить поиск по ключевому слову. Вопрос, что искать? Что-то отвечающее за шифрование - "encrypt" Внезапно.  Библиотека от espressif  Библиотека от hyflying, с их SmartLink'ом. Три класса реализующих AES. Причем во всех трех классах используются разные режимы шифрования. Библиотеки от espressiа и hyflying можно отложить на потом, WifiUtils, логично предположить, отвечает за шифрование Wi-Fi. Остаются TcpServices, EncryptUtils и AES256Chipher. Заглянем в EncryptUtils и увидим: Краткое содержание:Переменной ENC_PASSWORD два раза присваиваем имя текущего класса ("EncryptUtilsEncryptUtils"). Получаем хэш и на его основе генерим ключ, которым и дешифруем данные. Запускаем Android Studio и пишем простенький код (или пользуемся он-лайн дешифровщиками) и скармливаем наш шифротекст. Fail. Пробуем по другому, снова фейл. И так и сяк - Fail. Окей, посмотрим что лежит во втором классе - AES256Chipher Краткое содержание: берем пароль, считаем от него SHA384 (почему 384?), разбиваем на два байтовых массива [32-48] и [0-32]. Первый - вектор инициализации (IV)Второй - ключ, который и скармливаем SecretKeySpec Вроде, ничего криминального, все так делают. Внимание вопрос - что же у нас может выступать в роли пароля? Может быть, тот самый enc_key из JSONa передающийся открытым http?Пробуем, и… вуаля, сообщения расшифровываются. Внутри нас ждет тот самый REST API с командами getDeviceParams, setDeviceParams, changedDeviceParams. Можно начинать писать плагин для системы управления. Возникают смешанные чувства: - Передача номера телефона и пароля в открытом виде. Я даже не знаю, что тут написать. Хэш? SSL\TLS? Не, не слышали. Зато, на сайте разработчиков гордо красуется "Входим в ТОП-100 разработчиков мобильных приложений".- Ключ шифрования (по сути) передается в открытом виде при установлении каждой сессии. А сессия создается при каждом запуске приложения. Нет, он не сохраняется. Открыл приложение, выставил температуру, закрыл приложение - такой же жизненный цикл имеет ключ. Т.е. нам достаточно перехватить начало одной, любой сессии и все, можем рулить железкой. - Зачем в приложении аж 3 класса реализующих AES? Ладно, тут ответ очевиден - "я его слепила из того что было"  Однако! - Благодаря этому, можно легко написать сторонне приложение\плагин\расширение для системы управления умным домом. Утечки ПД, нет, т.к. нет ФИО (оно вообще нигде не указывается, даже при регистрации) Как выяснилось в первой части, наш wi-fi свисток, это всего лишь "UART to Wi-Fi" преобразователь. Это значит, что мы можем при помощи Esp32\Arduino\RPI создать свою железку для управления конвектором - с MTTQ, bluetooth и прочими соединениями. Надо лишь узнать протокол. Итак, свисток имеет разъем USB-AM, и подписи на плате (см. фото выше) +5v, Tx, Rx, GND. Дело за малым, взять кабель AM - AF, разрезать, зачистить и в параллель подключить логический анализатор. +5v, можно оставить в покое. Таким образом, сразу будем видеть и Тx и Rx - главное, определиться с какой стороны смотреть. Запуск, тыканье в кнопочки на мобильнике и… …что-то это напоминает. UART to Wi-Fi оказался тупым конвертером. Никакой внутренней обработки, никакой логики. Формат сообщения такой же как и в первой части.  В Wireshark мы видели AT-команды. Да и в документации про это что-то было. Берем UART-USB, выставляем +5В и подключаем. Главное Tx и Rx подключить перекрестно (очевидно, но можно забыть). Запускаем Putty или любой другой терминальный клиент. Выбираем com-порт в документации указана скорость 115200, но по факту 9600. Окей, железка что-то шлет в терминал, но на ввод не реагирует. Курим ман и находим - для перехода в режим настройки необходимо отправить "+++" (без enter'а), на что HF-LPT220 ответит "a" и нам надо в ответ послать такую же "a". Сделать это все надо за 3 с. После данной эквилибристики, железка становится отзывчивой. Введем AT+H и увидим список доступных команд: AT+APPVER: Show application version.AT+DCDC=on/off: Enable or disable DCDC Mode .    AT+SMEM: Show memory.    AT+FLSHRD: Show flash data.    AT+UART: Set/Get the UART0/UART1 Parameters.    AT+NDBGL:set/get debug level    AT+MDCH: Put on/off automatic switching WIFI mode.    AT+ENTM: Goto Through MOde.    AT+SMTLKST=mode,protocol: Setup smartlnk mode and protocol.    AT+RELD: Reload the default setting and reboot.    AT+MID: Get The Module ID.    AT+WRMID: Write Module ID.    AT+VER: Get application version.    AT+BVER: Get bootloader version.    AT+CFGRD: Get current system config.    AT+FCLR: Clear Fsetting.    AT+CFGTF: Save Current Config to Default Config.    AT+CFGW=on/off: Enable or disable write config to flash    AT+SRST:Soft Reset the Module.    AT+SLEEP=ms:Cpu sleep ms.    AT+E: Echo ON/Off, to turn on/off command line echo function.    AT+Z: Reset the Module.    AT+H:show help    AT+SOCKB: Set/Get Parameters of socket_b.    AT+TCPDISB: Connect/Dis-connect the TCP_B Client link.    AT+TCPTOB: Set/Get TCP_B time out.    AT+TCPLKB: Get The state of TCP_B link.    AT+RCVB: Recv data from socket_b    AT+SNDB: Send data to socket_b    AT+NETP: Set/Get the Net Protocol Parameters.    AT+TCPLK: Get The state of TCP link.    AT+TCPTO: Set/Get TCP time out.    AT+TCPDIS: Connect/Dis-connect the TCP Client link    AT+MAXSK: Set/Get MAX num of TCP socket (1~5)    AT+DISPS: Disable power saving mode of WIFI    AT+WSLQ: Get Link Quality of the Module (Only for STA Mode).    AT+SMTLK: Start Smartlink.    AT+WSSSID: Set/Get the AP's SSID of WIFI STA Mode.    AT+WAP: Set/Get the AP parameters.    AT+WAPMXSTA: Set/Get the Max Number Of Sta Connected to Ap.    AT+WSKEY: Set/Get the Security Parameters of WIFI STA Mode.    AT+WAKEY: Set/Get the Security Parameters of WIFI AP Mode.    AT+WIFI=UP/DOWN: Power down or up  the wifi chip.    AT+WPSBTNEN:enable/disable wps button.    AT+WALKIND:enable/disable LED indication of AP connection.    AT+WALK:Show sta information of AP connection.    AT+WSCAN: Get The AP site Survey (only for STA Mode).    AT+WMODE: Set/Get the WIFI Operation Mode (AP or STA).    AT+WSLK: Get Link Status of the Module (Only for STA Mode).    AT+WIFI=UP/DOWN: Power down or up  the wifi chip.    AT+WSMAC: Set/Get Module MAC Address.    AT+NTPSER: Set/Get NTP Server address.    AT+UDPLCPT: Set/Get local UDP port.    AT+WANN: Set/Get The WAN setting if in STA mode.    AT+LANN: Set/Get The LAN setting if in ADHOC mode.    AT+WADHCP:enable/disable AP dhcp server and set ip address pool    AT+ASWD: Set/Query WiFi configuration code.    AT+NTPRF: Set/Query NTP.    AT+NTPEN: Enable/Disable NTP Server.    AT+NTPTM: Set/Query Ntp Time.    AT+NTPSER: Set/Query Ntp Server.    AT+PLANG=EN/CN: Set/Get the language of WEB page.    AT+WEBU: Set/Get the Login Parameters of WEB page.    AT+OTA: Auto upgrade firmware.    AT+UPURL: Set/Get the path of remote upgrade.  Можно позапускать всякое, но интерес представляет AT+SOCKB, который возвращает "+ok=TCP,10001,dongle.******.ru". Как внезапно…  Попробуем заменить на что-нибудь свое? "AT+SOCKB=TCP,10001,192.168.0.2". Возвращаем модуль в конвектор и смотрим в wireshark. Теперь железка пытается установить соединение не с сервером в интернете, а с ПК из локальной сети. «Буква S в аббревиатуре IoT обозначает Security» Существует три способа управления конвектором. Через сервер производителя (REST API), напрямую из ЛВС, через UART.  Остался открытый вопрос с модулем для HA - возможно никогда-нибудь, он будет написан =). Инженер