Исследование / 13.09.2026

Запуск, меню и состояния старой прошивки

Исследован C:\dump\fw_128k.bin, восстановленные начальные данные RAM и сохранённый дизассемблерный листинг Untitled-1.txt. Дополнительно 20 проверок выполнили оригинальные ARM-инструкции на программном эмуляторе. Вызовы дисплея, GPIO и энергонезависимой памяти заменены записывающими заглушками; физический прибор не подключался.

Полный протокол: evidence/ui-verification.json; воспроизводимый запуск: tools/verify_ui.py. Протокол содержит SHA-256 исследованного BIN, адреса вызовов, аргументы и полученные переходы. Это проверка управления программой; работоспособность исполнительных узлов и длительности в реальных секундах таким способом не проверяются.

Основная архитектура

Вход прикладной программы — 0x104A4. Она последовательно вызывает настройку периферии, дисплея и данных, затем регистрирует через 0xDAB4 четыре задания и переходит в постоянный диспетчер 0xDC28.

ОбработчикАргумент периодаУстановленная роль
0x1056C10Основная машина состояний, обновление измерительного пути и обработка текущего экрана
0x153410Управление сигнализацией с проверкой флагов каналов и состояния прибора
0xFFF82Преобразование событий кнопок в команды
0xD8985Обработчик очереди/кадров с собственным состоянием; точное назначение здесь не установлено

В обработчике SysTick 0xE590 счётчик делится на 10: только каждый десятый вызов прерывания обслуживает таймеры заданий. Поэтому аргумент «10» не следует автоматически называть 10 мс. Дополнительная проверка даёт для него номинально 100 мс при нормальном начальном тактировании — см. timing.md. Диспетчер исполняет готовые задания по очереди; длительный обработчик способен задержать остальные.

Основное состояние — байт 0x20000094. Структура UI начинается с 0x200014D0, её общий счётчик — слово 0x200014D4. Функция 0xCF00 устанавливает несколько байтов состояния UI и обнуляет счётчики. 0x1ED8 очищает экран: восемь страниц по 128 записей нулевых байтов.

В 0x1056C при состоянии не менее 10 вызываются 0x1A8C, 0x11478, 0xAEC4, 0x1924, 0x1BC4 до обработки отдельного экрана. Это означает, что вход в большинство меню не останавливает этот общий вычислительный путь. Уточнения его расчётов находятся в отчёте об измерениях; один числовой тест state >= 10 нельзя считать полноценной моделью допустимости измерений во всех режимах.

Реальная последовательность запуска

Компилятор поместил таблицу переходов сразу после вызова 0xF1D2 по адресу 0x105FE. Её байты по 0x10602 были перепроверены непосредственно в BIN. В первоначальном экспорте Ghidra часть этих переходов была ошибочно представлена данными/инструкциями. Вход 0x1054C из первой автоматической декомпиляции ложный: там лежит литерал, настоящее начало обработчика — 0x1056C.

СостояниеОбработчикЧто делаетСледующее состояние
00x4604Проверяет вход GPIO; по одному пути входит в режим зарядки, по событию 0x2000 читает показатель питания через 0x1F04 и сравнивает с 0xD481 либо 2 при выполнении условий; единицы показателя питания здесь не установлены
10x3830Экран зарядки: по GPIO выбирает Charging/Charge Over, обновляет индикацию; событие 0x800 начинает запуск2
20x5E70Показывает графический ресурс, выдаёт повторяемые импульсы GPIO3, после 34 вызовов обработчика в контрольном исполнении
30x5670Показывает Welcome, выдаёт короткую последовательность импульсов GPIO6, после 8 вызовов
60x5570Показывает Starting и Please Wait, меняет точки анимации; ждёт счётчик больше 304, после 31 вызова
40x39F4Поочерёдно обрабатывает четыре канала, вызывает загрузку/проверку их данных 0x61D0, затем читает блок калибровок и служебные параметры памяти5, после 40 вызовов при заглушённых зависимостях
50x4848Последовательно показывает Led Check, Shock Check, Audio Check и переключает соответствующие выходы10, после 48 вызовов
100x5390Рабочий экран; перебирает отображаемые каналы, обрабатывает команды пользователяСобытие 0x800 открывает меню 20

Таким образом, реальный маршрут — 2 → 3 → 6 → 4 → 5 → 10. Порядок номеров не отражает порядок этапов. Указанные числа вызовов измерены в отдельных исходных обработчиках с восстановленной начальной RAM; значения периферии и задержки задач при настоящем запуске могут менять итоговое поведение.

Надпись Starting с фиксированным ожиданием не доказывает контроля физической стабилизации газового сенсора. В исследованном 0x5570 нет критерия готовности по разбросу измерений или по отдельному датчику — переход происходит по счётчику. В 0x4848 проверка сигнализации является последовательностью команд включить/выключить: в этой функции не найдено чтения обратной связи, которое подтверждало бы реальный звук, вибрацию или свет.

Управление выходами установлено по веткам с подписанными экранами: Led Check переключает маску 0x40 в блоке 0x40000C00; Shock Check — маску 0x20 в 0x40000C40; Audio Check — маску 0x10 в 0x40000C80. Это программное сопоставление по UI и вызовам 0x91B4/0x91D2, не прозвонка платы и не гарантия активного уровня электрической схемы.

Кнопки и меню

Обработчик 0xFFF8 получает от распознавателя кнопок 0xC9C0 категорию события и номер кнопки. Для категории 0x10 номера 1/2/3 преобразуются в 0x1000/0x800/0x400; для категории 0x45 — в 0x8000/0x2000/0x4000. По использованию это разные виды нажатия; точные времена удержания и физические подписи кнопок требуют отдельной проверки.

Основной обработчик 0x1056C копирует накопленный код из 0x20000098 в 0x2000009C и очищает источник. В меню 0x4684 команды 0x400/0x1000 меняют пункт с циклическим переходом между 1 и 9, 0x800 выбирает его.

Пункт менюНазвание, подтверждённое таблицей UIПереход при выборе
1Zero Adjust0x50: ввод сервисного кода
2Cal Gas0x5A: подменю калибровки при флаге 0x200000CE != 0x5A; иначе 0x50
3Alarm Set0xD2: выбор канала
4Alarm Rec0x78: журнал
5Time Set0xA0: настройка даты/времени
6Language0xAA: язык
7Factory mode0x50: ввод сервисного кода
8DefaultSetting0xB6: экран подтверждения восстановления
9Exit10: рабочий экран

Имена подтверждаются 0x75E8 и восстановленной английской таблицей: 0x76DC использует запись размером 8 байтов по 0x200007FC + ID*8, указатель строки находится в её поле +4. Это связывает текст с используемым пунктом, а не просто отмечает наличие строки в файле.

У большинства исследованных меню общий таймаут counter > 0x6E возвращает в рабочий экран. У Select Gas, обработчик 0x3B74, порог другой — counter > 0x32. Число вызовов до возврата зависит от начального значения счётчика; для заводского меню 0x5D50 эксперимент подтвердил 110 вызовов без ввода. Таймаут — часть поведения UI, которую стоит явно задавать для каждого режима.

Вход в калибровку и заводские действия

0x4330 действительно реализует Input Password: две редактируемые десятичные цифры и сравнения с фиксированными константами, зависящие от выбранного пункта. Для пунктов Zero Adjust/Cal Gas после успешного сравнения открывается выбор канала/подменю калибровки. Это не доказательство общей защищённости сервисного доступа; чужие фиксированные коды переносить в собственную прошивку не следует.

0x3338 показывает Cal Gas, далее 0x6180 рисует три варианта: Zero Cal, Other Cal, Exit. Первые два ведут в 0xD2; там 0x3B74 циклически выбирает канал 0–3 и направляет в обработчик Zero Adjust (state 0x1E), Zero Cal (state 0x28) или Other Cal (state 0x64) в зависимости от выбранной операции. Пункты Stand Value, Real Value, Zero Value, Mark point нужно связывать с конкретными стадиями расчёта по отчёту о калибровке, а не трактовать по одним словам.

Заводские действия разделены:

Эти четыре варианта подтверждения/отмены проверены на исходных инструкциях с подменой функций хранения. На реальном приборе действий не выполнялось.

Важная особенность загрузки: 0x39F4 после чтения EEPROM 0xEC проверяет value != 0x5A || value != 0xA5 и поэтому всегда присваивает RAM-флагу 0x200000E9 значение 0xA5 (0x3B24…0x3B34). Это подтверждается инструкциями сравнения и ветвления. Назначение флага следует устанавливать по потребителям; саму ошибочную проверку двух допустимых значений воспроизводить в новой прошивке не нужно.

Имена газов действительно индексированы, но каналы задаются конфигурацией

0x77E8 в английской ветке берёт имя из 0x20000A8C + type*11. Начальная RAM содержит точное соответствие 0 → EX, 1 → O2, 2 → H2S, 3 → CO, 11 → CH4, 15 → CO2, 23 → VOC. Полный список из первых 25 записей сохранён в протоколе.

В 0x3B74 тип для вывода берётся из конфигурации канала: 0x20000EA3 + channel*0x13 + 2. Поэтому тип 1 означает O2, но канал с индексом 1 не обязан всегда быть кислородным. EX — именно текст прошивки; конкретный сенсор, принцип измерения и физическая распиновка из этой надписи не следуют.

Что заложить в будущую прошивку

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