Есть у меня пара электрокарнизов компании Akko — AM82TV. Модель эта выделяется из собратьев наиболее полным набором интерфейсов управления. Шторами можно управлять по радиоканалу, есть “сухие контакты”, пофазное управление (замыканием управляющих проводов с сетевыми). Есть интерфейс RS485 — это, если захочется подключить шторы к “умному дому”. Можно также открыть/закрыть шторы просто дернув их рукой в нужном направлении. “Из коробки” не хватает, пожалуй, только web-интерфейса, ну и MQTT.
Электрокарнизы у меня уже давно, работают надежно, но время от времени стало появляться желание их разобрать — из любопытства посмотреть что внутри и есть ли возможность засунуть туда ESP8266 (или ESP32) с тем, чтобы добавить недостающее. Можно, конечно, подключить всё снаружи, но лучше если внешний вид останется прежним и всё будет спрятано внутри.
Не чини того, что не сломано — это не про меня. Поначалу я пытался гнать от себя дурные идеи, но со временем зуд усиливался и вот, настал момент, когда с ним уже было невозможно бороться. Я снял мотор с электрокарниза и разобрал его. Начальный осмотр должен был дать ответы на два вопроса: есть ли место для ESP8266 и можно ли использовать встроенный источник питания. Разобралось все просто. Достаточно открутить несколько винтов с торцов мотора. Единственно — шлицы винтов сделаны не под отвертку, а под шестигранный ключ torx. После этого можно извлечь содержимое — коллекторный мотор в одном корпусе с редуктором и датчиком вала двигателя, блок питания на 24 вольта и плату управления.
Место под ESP’ху есть, блок питания рассчитан на мощный мотор и не заметит небольшой дополнительной нагрузки. Я стал рассматривать плату управления и мысли про ESP стали уходить на второй план — сердцем платы был микроконтроллер atmega168. Если бы на плате не было контактов для установки разъема ISP (интерфейс внутрисхемного программирования микроконтроллера) это бы уже меня не остановило, но они там были. Чтобы вы поняли меня правильно — я вовсе не фанат avr. У меня нет программатора и я не написал ни строчки кода для микроконтроллеров atmega. Срочно нужен был программатор, чтобы попытаться считать прошивку, но дело было вечером и купить что-то в магазине прямо сейчас уже не было возможности. Чтобы не ждать до утра, я собрал программатор Громова. Затем припаял разъем ISP на плате управления, подключил программатор и попытался считать прошивку — и она считалась.
Пытаться понять что-то в прошивке микроконтроллера не разобравшись в принципиальной схеме, невозможно. Поэтому, по печатной плате я восстановил схему. Мне не часто приходится рисовать схемы. Чтобы не рисовать самому элементы, которые есть на плате, но нет в стандартной библиотеке, я нарисовал схему в easyeda. Да, получается не по ГОСТу (но микроконтроллер нарисован как в документации, что удобно). Для понимания достаточно фрагмента:
Здесь и далее большинство картинок кликабельны.
Если есть желание, можно ознакомиться с полной схемой.
После этого я был готов к исследованию прошивки. Просто перевести машинные коды в мнемоники ассемблера недостаточно для анализа. Нужно видеть состояние регистров и памяти в любой точке программы. К сожалению, отладка на самом микроконтроллере в случае atmega168 невозможна, но можно загрузить прошивку в симулятор. Не слишком удобно, если хочется “почувствовать” реальное железо обвязки микроконтроллера, но обойдемся тем, что есть. Я не стал устанавливать Atmel Studio 7. Решил, что будет достаточно более компактной AVR Studio 4. В принципе, для анализа достаточно одной Studio, но есть возможность переложить часть рутинных операций на другое программное обеспечение. В марте был опубликован инструмент для реверс-инжиниринга — Ghidra. Мне нужен был повод, какая-нибудь практическая задача, чтобы с ним познакомится. Вот как раз удобный случай. Каждый инструмент по-отдельности — AVR Studio 4 и Ghidra у меня немного подглючивали. AVR Studio при нескольких вложенных переходах в подпрограммы могла вдруг перестать отображать правильные адреса переходов в командах (адрес становился нулевым). Ghidra иногда сбивалась в анализе индексной адресации. Но использование сразу обоих инструментов позволяло быстро выявлять причину этих странностей.
Бесцельно копаться в прошивке не слишком интересно. Лучше, если будет какая-то конкретная задача. На вскидку, любопытными могут быть анализ протокола обмена по RS485 и протокол управления по радиоканалу. Я решил покопаться в радиопротоколе.
Когда-то давно, я уже снял сигнал, передаваемый радиопультом и проанализировал его.
Радиоканал односторонний. Мотор способен только принимать сигнал и никак не подтверждает получение команды. Опросить состояние электрокарниза по радиоканалу нельзя. На физическом уровне есть только приемник радиосигнала. Передатчика в моторе нет. Сигнал передаётся радиопультом на частоте 433.92 МГц. Кодируется амплитудной манипуляцией. Команда состоит из стартового бита особой формы, адреса+канала и непосредственно команды. Графически весь сигнал можно представить так:
Канал можно рассматривать как часть адреса. Если передать команду на нулевом канале ее выполнят все устройства с заданным адресом, но разными каналами. Удобно, если нужно передать команду сразу группе электрокарнизов.
Подробнее о радиопротоколе.
Анализируя как передает команды радиопульт, я уже знал коды четырех команд. Не слишком много, если сравнивать с возможностями управления электрокарнизом по RS485. Хотелось убедиться в том, что выявлены все команды и нет “пасхальных яиц” или сервисных команд.
Подход при анализе простой. По принципиальной схеме видно к каким портам микроконтроллера подключены физические устройства. Файл прошивки анализируется в Ghidra. Это удобно — много рутинных операций выполняется в автоматическом режиме. После анализа работы конкретного фрагмента кода или подпрограммы ему присваивается осмысленное название. Дальше фрагменты кода и скриншоты я буду приводить именно из Ghidra. При затупах запускаем код в симуляторе. В особо запутанных ситуациях придется рисовать картинки.
Для того, чтобы названия регистров периферии atmega168 корректно отображались при дизассемблировании, я поправил файл avr8.pspec. По-хорошему нужно было бы создать файл с другим названием, но мне этот файл в исходном виде был не нужен и я исправил тот, что был.
Прошивка начинается с векторов прерываний. Первым идет вектор перехода на начало программы. Выглядит вот так:
Большинство прерываний не используется. Если по каким-то причинам такое прерывание произойдет, сразу же будет выполнена команда возврата. Кроме обработки прерывания по reset’у еще обрабатываются прерывания по переполнению Таймера 0, завершению приема и готовности передачи следующего байта данных в USART. Обмен по RS485 сейчас нас не интересует, а вот переполнение таймера нужно просмотреть.
Разумеется, прежде чем будет разрешено прерывание по таймеру, сам таймер должен быть настроен. Искать в дезассемблированном коде место, где происходит инициализация я не стал. В симуляторе поставил точку останова на адрес 0x1fc — это адрес записанный в обработчике прерывания. Запустил программу, получил выброс и просто посмотрел что записано в регистры. Сlock select для timr0 установлен в clk/8. При кварце на плате 8 МГц на таймер поступают импульсы с частотой 1 МГц.
Начало обработчика прерывания:
Таймер переполняется каждые 107 мкс. Кроме прочего, в обработчике прерывания обрабатывается сигнал с выхода приемника радиосигнала. Если сигнал успешно проходит проверку, полученные данные записываются в буфер по адресу 0x4af. Размер буфера 6 байт. Это на один байт больше, чем необходимо для приёма четырех байт адреса+канала и байта команды. Возможно, шестой байт радиопротокола был предназначен для контрольной суммы, но атрофировался. Иногда такое бывает. Возможно, предназначен для данных, дополняющих какую-то из команд. В протоколе управления электрокарнизом по RS485 есть команда частичного закрытия штор. При этом дополнительно передается байт с данными о величине закрытия в процентах. Подобная команда могла бы существовать и в радиопротоколе. Основной программе сообщается о готовности данных установкой флага — по адресу 0x4bd записывается единица.
На этом анализ прерываний заканчиваем и переходим к основной программе.
Граф функций при переходе по адресу 0x1304 лаконичен и прост:
Нам нужно в “main”. Мы уже проанализировали прерывания и понимаем, что если по каким-то причинам мы вдруг вернемся из main и попадем в “go_to_sleep”, то сон этот окажется летаргическим — электрокарниз перестанет делать что-то полезное. Вероятно, при написании прошивки, использовался готовый шаблон и артефакты остались после него.
В main нам нужно найти место, где происходит обращение к буферу 0x4af. Искать долго не придется. Буквально через несколько команд происходит вызов подпрограммы, начинающейся с адреса 0x11b3, где этот буфер копируется и обрабатывается.
Граф функций процедуры rf_signal_buffer_processing (0x11b3):
Это точно то место, которое нам нужно, но выглядит чуть сложновато. Чтобы разобраться я стал рисовать картинки. Что-то вроде блок-схемы.
Первое, что тут происходит — данные из буфера 0x4af копируются по новому адресу — 0x49f и сбрасывается флаг 0x4bd:
Следующий фрагмент самый интересный:
Фактически это вся проверка полученной по радиоканалу команды. Пока это не очевидно, но дальнейший анализ показал, что переход на следующие ветки — это обработка следующих радиокоманд в случае, если перед этим была получена команда 0xcc — переход в режим настроек. Меня интересовали именно новые коды самих команд. Есть ли что-то, кроме кодов команд 0x11, 0x33, 0x55 и 0xcc.
Ни здесь, ни дальше мне не удалось найти обработку последнего — шестого байта команды. На рисунке он перечеркнут красным. Так что все команды состоят из пяти байт. По крайней мере в прошивке AM82TV.
Первое, что мы видим — это проверка пятого байта буфера — кода самой команды. Вот, кажется, нашлись новые коды команд — 0xad и 0xda. Команда из буфера успешно проходит проверку в случае, если поле адреса совпало с одним из адресов, уже записанных в eeprom, или если код команды 0xad, или 0xda и поле адреса == 0xaaaaaaaa.
Хорошо, будем считать, что очередную проверку мы успешно прошли. Теперь код команды находится по адресу 0x4bf. Нужно найти то, как обрабатывается содержимое этого байта. Сделать это можно несколькими способами. Самый простой — текстовым поиском по дизассемблированному коду в Ghidra. Останется просмотреть где именно происходит чтение содержимого этого байта. Это не гарантированный способ найти все обращения, но в этом, конкретном, случае сработает. Так, вначале, мы увидим чтение байта в процедуре, начинающейся по адресу 0x1262:
Обращение к которой происходит из единственного места — командой, расположенной по адресу 0x5e0.
Граф функций:
Жаль, но все коды команд, кроме уже известных: 0x11, 0x33, 0x55 и 0xcc отбрасываются в этой процедуре. Даже найденные на предыдущем шаге коды 0xad и 0xda. По крайней мере мне не удалось найти в прошивке электрокарниза AM82TV новых кодов радиокоманд.
Тем не менее к прошивке есть доступ, ее можно дизассемблировать, посмотреть как реализованы конкретные функции и изменить что-то под себя. Или даже что-то добавить.
Прошивка и измененный под atmega168 файл avr8.pspec выложен на GitHub.
Электрокарнизы у меня уже давно, работают надежно, но время от времени стало появляться желание их разобрать — из любопытства посмотреть что внутри и есть ли возможность засунуть туда ESP8266 (или ESP32) с тем, чтобы добавить недостающее. Можно, конечно, подключить всё снаружи, но лучше если внешний вид останется прежним и всё будет спрятано внутри.
Не чини того, что не сломано — это не про меня. Поначалу я пытался гнать от себя дурные идеи, но со временем зуд усиливался и вот, настал момент, когда с ним уже было невозможно бороться. Я снял мотор с электрокарниза и разобрал его. Начальный осмотр должен был дать ответы на два вопроса: есть ли место для ESP8266 и можно ли использовать встроенный источник питания. Разобралось все просто. Достаточно открутить несколько винтов с торцов мотора. Единственно — шлицы винтов сделаны не под отвертку, а под шестигранный ключ torx. После этого можно извлечь содержимое — коллекторный мотор в одном корпусе с редуктором и датчиком вала двигателя, блок питания на 24 вольта и плату управления.
Место под ESP’ху есть, блок питания рассчитан на мощный мотор и не заметит небольшой дополнительной нагрузки. Я стал рассматривать плату управления и мысли про ESP стали уходить на второй план — сердцем платы был микроконтроллер atmega168. Если бы на плате не было контактов для установки разъема ISP (интерфейс внутрисхемного программирования микроконтроллера) это бы уже меня не остановило, но они там были. Чтобы вы поняли меня правильно — я вовсе не фанат avr. У меня нет программатора и я не написал ни строчки кода для микроконтроллеров atmega. Срочно нужен был программатор, чтобы попытаться считать прошивку, но дело было вечером и купить что-то в магазине прямо сейчас уже не было возможности. Чтобы не ждать до утра, я собрал программатор Громова. Затем припаял разъем ISP на плате управления, подключил программатор и попытался считать прошивку — и она считалась.
Пытаться понять что-то в прошивке микроконтроллера не разобравшись в принципиальной схеме, невозможно. Поэтому, по печатной плате я восстановил схему. Мне не часто приходится рисовать схемы. Чтобы не рисовать самому элементы, которые есть на плате, но нет в стандартной библиотеке, я нарисовал схему в easyeda. Да, получается не по ГОСТу (но микроконтроллер нарисован как в документации, что удобно). Для понимания достаточно фрагмента:
Здесь и далее большинство картинок кликабельны.
Если есть желание, можно ознакомиться с полной схемой.
После этого я был готов к исследованию прошивки. Просто перевести машинные коды в мнемоники ассемблера недостаточно для анализа. Нужно видеть состояние регистров и памяти в любой точке программы. К сожалению, отладка на самом микроконтроллере в случае atmega168 невозможна, но можно загрузить прошивку в симулятор. Не слишком удобно, если хочется “почувствовать” реальное железо обвязки микроконтроллера, но обойдемся тем, что есть. Я не стал устанавливать Atmel Studio 7. Решил, что будет достаточно более компактной AVR Studio 4. В принципе, для анализа достаточно одной Studio, но есть возможность переложить часть рутинных операций на другое программное обеспечение. В марте был опубликован инструмент для реверс-инжиниринга — Ghidra. Мне нужен был повод, какая-нибудь практическая задача, чтобы с ним познакомится. Вот как раз удобный случай. Каждый инструмент по-отдельности — AVR Studio 4 и Ghidra у меня немного подглючивали. AVR Studio при нескольких вложенных переходах в подпрограммы могла вдруг перестать отображать правильные адреса переходов в командах (адрес становился нулевым). Ghidra иногда сбивалась в анализе индексной адресации. Но использование сразу обоих инструментов позволяло быстро выявлять причину этих странностей.
Бесцельно копаться в прошивке не слишком интересно. Лучше, если будет какая-то конкретная задача. На вскидку, любопытными могут быть анализ протокола обмена по RS485 и протокол управления по радиоканалу. Я решил покопаться в радиопротоколе.
Когда-то давно, я уже снял сигнал, передаваемый радиопультом и проанализировал его.
Радиоканал односторонний. Мотор способен только принимать сигнал и никак не подтверждает получение команды. Опросить состояние электрокарниза по радиоканалу нельзя. На физическом уровне есть только приемник радиосигнала. Передатчика в моторе нет. Сигнал передаётся радиопультом на частоте 433.92 МГц. Кодируется амплитудной манипуляцией. Команда состоит из стартового бита особой формы, адреса+канала и непосредственно команды. Графически весь сигнал можно представить так:
Канал можно рассматривать как часть адреса. Если передать команду на нулевом канале ее выполнят все устройства с заданным адресом, но разными каналами. Удобно, если нужно передать команду сразу группе электрокарнизов.
Подробнее о радиопротоколе.
Анализируя как передает команды радиопульт, я уже знал коды четырех команд. Не слишком много, если сравнивать с возможностями управления электрокарнизом по RS485. Хотелось убедиться в том, что выявлены все команды и нет “пасхальных яиц” или сервисных команд.
Подход при анализе простой. По принципиальной схеме видно к каким портам микроконтроллера подключены физические устройства. Файл прошивки анализируется в Ghidra. Это удобно — много рутинных операций выполняется в автоматическом режиме. После анализа работы конкретного фрагмента кода или подпрограммы ему присваивается осмысленное название. Дальше фрагменты кода и скриншоты я буду приводить именно из Ghidra. При затупах запускаем код в симуляторе. В особо запутанных ситуациях придется рисовать картинки.
Для того, чтобы названия регистров периферии atmega168 корректно отображались при дизассемблировании, я поправил файл avr8.pspec. По-хорошему нужно было бы создать файл с другим названием, но мне этот файл в исходном виде был не нужен и я исправил тот, что был.
Прошивка начинается с векторов прерываний. Первым идет вектор перехода на начало программы. Выглядит вот так:
Большинство прерываний не используется. Если по каким-то причинам такое прерывание произойдет, сразу же будет выполнена команда возврата. Кроме обработки прерывания по reset’у еще обрабатываются прерывания по переполнению Таймера 0, завершению приема и готовности передачи следующего байта данных в USART. Обмен по RS485 сейчас нас не интересует, а вот переполнение таймера нужно просмотреть.
Разумеется, прежде чем будет разрешено прерывание по таймеру, сам таймер должен быть настроен. Искать в дезассемблированном коде место, где происходит инициализация я не стал. В симуляторе поставил точку останова на адрес 0x1fc — это адрес записанный в обработчике прерывания. Запустил программу, получил выброс и просто посмотрел что записано в регистры. Сlock select для timr0 установлен в clk/8. При кварце на плате 8 МГц на таймер поступают импульсы с частотой 1 МГц.
Начало обработчика прерывания:
Граф функций обработчика прерываний “с высоты птичьего полета”
Таймер переполняется каждые 107 мкс. Кроме прочего, в обработчике прерывания обрабатывается сигнал с выхода приемника радиосигнала. Если сигнал успешно проходит проверку, полученные данные записываются в буфер по адресу 0x4af. Размер буфера 6 байт. Это на один байт больше, чем необходимо для приёма четырех байт адреса+канала и байта команды. Возможно, шестой байт радиопротокола был предназначен для контрольной суммы, но атрофировался. Иногда такое бывает. Возможно, предназначен для данных, дополняющих какую-то из команд. В протоколе управления электрокарнизом по RS485 есть команда частичного закрытия штор. При этом дополнительно передается байт с данными о величине закрытия в процентах. Подобная команда могла бы существовать и в радиопротоколе. Основной программе сообщается о готовности данных установкой флага — по адресу 0x4bd записывается единица.
На этом анализ прерываний заканчиваем и переходим к основной программе.
Граф функций при переходе по адресу 0x1304 лаконичен и прост:
Нам нужно в “main”. Мы уже проанализировали прерывания и понимаем, что если по каким-то причинам мы вдруг вернемся из main и попадем в “go_to_sleep”, то сон этот окажется летаргическим — электрокарниз перестанет делать что-то полезное. Вероятно, при написании прошивки, использовался готовый шаблон и артефакты остались после него.
В main нам нужно найти место, где происходит обращение к буферу 0x4af. Искать долго не придется. Буквально через несколько команд происходит вызов подпрограммы, начинающейся с адреса 0x11b3, где этот буфер копируется и обрабатывается.
Граф функций процедуры rf_signal_buffer_processing (0x11b3):
Это точно то место, которое нам нужно, но выглядит чуть сложновато. Чтобы разобраться я стал рисовать картинки. Что-то вроде блок-схемы.
Первое, что тут происходит — данные из буфера 0x4af копируются по новому адресу — 0x49f и сбрасывается флаг 0x4bd:
Следующий фрагмент самый интересный:
Фактически это вся проверка полученной по радиоканалу команды. Пока это не очевидно, но дальнейший анализ показал, что переход на следующие ветки — это обработка следующих радиокоманд в случае, если перед этим была получена команда 0xcc — переход в режим настроек. Меня интересовали именно новые коды самих команд. Есть ли что-то, кроме кодов команд 0x11, 0x33, 0x55 и 0xcc.
Ни здесь, ни дальше мне не удалось найти обработку последнего — шестого байта команды. На рисунке он перечеркнут красным. Так что все команды состоят из пяти байт. По крайней мере в прошивке AM82TV.
Первое, что мы видим — это проверка пятого байта буфера — кода самой команды. Вот, кажется, нашлись новые коды команд — 0xad и 0xda. Команда из буфера успешно проходит проверку в случае, если поле адреса совпало с одним из адресов, уже записанных в eeprom, или если код команды 0xad, или 0xda и поле адреса == 0xaaaaaaaa.
Оставшиеся картинки
Хорошо, будем считать, что очередную проверку мы успешно прошли. Теперь код команды находится по адресу 0x4bf. Нужно найти то, как обрабатывается содержимое этого байта. Сделать это можно несколькими способами. Самый простой — текстовым поиском по дизассемблированному коду в Ghidra. Останется просмотреть где именно происходит чтение содержимого этого байта. Это не гарантированный способ найти все обращения, но в этом, конкретном, случае сработает. Так, вначале, мы увидим чтение байта в процедуре, начинающейся по адресу 0x1262:
Обращение к которой происходит из единственного места — командой, расположенной по адресу 0x5e0.
Граф функций:
Жаль, но все коды команд, кроме уже известных: 0x11, 0x33, 0x55 и 0xcc отбрасываются в этой процедуре. Даже найденные на предыдущем шаге коды 0xad и 0xda. По крайней мере мне не удалось найти в прошивке электрокарниза AM82TV новых кодов радиокоманд.
Тем не менее к прошивке есть доступ, ее можно дизассемблировать, посмотреть как реализованы конкретные функции и изменить что-то под себя. Или даже что-то добавить.
Прошивка и измененный под atmega168 файл avr8.pspec выложен на GitHub.