Общие выкладки по архитектуре прошивок

Так, по свежим следам и в надежде на появление новых энтузиастов-практиков, опишу текущую схему и достигнутые результаты (с пометками по части своего s2is). Речь пойдет, очевидно, только о DII + VxWorks фотоаппаратах.

ОС и процессор
Анализ прошивки s2is, найденной ранее mc.kinley, показывает наличие строк VxWorks 5.5 и Victoria - ARM946ES (ARM)

Процедура штатной загрузки
Процедура штатной загрузки обновления выглядит так:
 * Запускается canon-овский софт UploadFirmware. Он использует TWAIN-драйвер и недокументированную библиотеку iwrap.dll для записи файла на флешку через "модифицированный" Canon-ом протокол PTP. При этом проверяет соответствие типа аппарата и версионность прошивки.
 * Аппарат включаем в режиме просмотра, в меню нижней строкой появлять пункт "прошить".
 * По активации пункта аппарат загружает и распаковывает в ОЗУ файл прошивки, после чего запускает на исполнение WriteInFIR.bin.
 * WriteInFIR.bin выполняет последующие действия по перепрошивке.

Формат прошивки
Для семейства VxWorks 5.5 (в т.ч. иксусы, а610, s2is) подходит распаковщик ixus_fdu.exe Вышеуказанная прошивка s2is содержит некоторый набор флагов и файлы: WriterInFIR.bin PRIMARY.BIN Faexe.bin ImgTbl.bin UIRes.bin UsrSet.BIN AdjTableVersion.bin Param.bin MechaParam.bin

Выводы
(что нужно для изучения и первых модификаций)
 * корректно дизасемблировать бинарные файлы из прошивки
 * корректно вносить изменения в код прошивки (с учетом возможных контрольных сумм)
 * уметь создавать файлы прошивок в корректном для аппарата формате
 * уметь корректно записывать эти файлы на флешку

Распаковка/запаковка прошивки
Как указывалось ранее в качестве распаковщика подходит ixus_fdu.exe (ссылки были в этой теме ранее) В качестве запаковщика можно использовать pak.c от vitalyb (ссылка была выше), предварительно модифицированный для вашего аппарата и с учетом конкретной прошивки. Вот здесь лежит собранная версия для s2is -- http://altsoph.ru/stuff/prjs/canon/pak_s2is_v01.exe Данная версия пакует найденный в текущей директории файл WriterInFIR.bin (и только его!) в прошивку, которую называет EC164HLD.FIR, указывая при этом версию 1.0.1.0 и ModelID+ProductID аппарата s2is ("0x01640000" + "0x30F0")

Запись на флешку
Для заливки самого файла прошифки можно использовать стандартный canon-овский UploadFirmware (после настрокий ini-файла). Взять его можно из любого официального обновления (их список с ссылками есть в первом посте этой темы). Для записи / чтения произвольного файла приходится идти на ухищрения. Если есть card-reader, следует использовать его и ни о чем не беспокоиться. В противном случае потребуется специальный софт:
 * Новые аппараты canon как-то странно поддерживают интерфейс IStillImage, поэтому утилиты типа wins10sh неприменимы.
 * Библиотека usbptp (и содержащаяся в ней тестовая утилита ptpcam) может быть собрана на платформе Linux (в моем случае это Knoppix под VMWare) на базе библиотеки libusb не ниже 0.1.8.
 * Тонкие моменты этого варианта:
 * для получения функции записи следует наложить патч от vitalyb, ссылка проходила выше.
 * портировать под win сходу не удалось по причине отсутствия порта активно используемого mmem-а, а так же по причине очень сильной глючности libusb-win.
 * так или иначе этот вариант является нестабильным, т.к. даже под linux-ом утилита ptpcam жутко глючит.
 * но её использовать за неимением других вариантов можно


 * Другие варианты:
 * поиск подходящей реализации ptp под платформу win результатов не дал
 * утилита UploadFirmware использует iwrap.dll и её недокументированные функции PSPutFile, PSPutFileEx и т.п. (проверено api logger-ом)
 * вывод: нужно либо писать свой драйвер, либо утилиту на основе canon-овского TWAIN-а и недокументированных функций iwrap.dll. Есть умельцы?

Дизассемблер под ARM
Используется IDA Pro, в моем случае 4.9. Некий самоучитель по IDA (за качество не ручаюсь) можно найти тут: http://www.latronik.ru/temp/idahelp.chm Полезный документ "Программная среда ARM" качаем тут: http://chuk.fromru.com/SE/Arm/Programmnaya_sreda_ARM.rar Руководство по ARM-ассемблеру от ныне покойного ЖЖ сообщества ru_ppcre называется 'ru_ppcre's arm manual.pdf', искать гуглом.

Сборка c-кода под ARM
Для сборки используется пакет GNUARM (http://gnuarm.com/), который можно запустить на платформе win под cygwin-ом (http://www.cygwin.com/) или mingw (http://mingw.org/). Примеры скриптов и настроек сборки см в коде vitalyb, ссылки он давал выше.

Выводы

 * таким образом, при наличии card-reader-а полный цикл экспериментов можно проводить под платформой win.
 * на платформе lin потребуется как-то запустить IDA, в остальном там тоже проблем не возникнет.
 * очень хочется иметь вменяемый инструмент для работы с ptp, желательно под win

Изучение содержимого прошивки
Что обычно лежит в прошивке я писал выше в 0.3. На данный момент особый интерес представляют файлы: PRIMARY.BIN -- основной файл прошивки, содержит ОС и все основные процедуры управления камерой, USB PTP, звуки и прочее. WriterInFIR.bin -- этот файл запускается из *.fir (если он сформирован верно) залитого на флешку и осуществляет перепрошивку фотоаппарата, выставляет всякие конфигурационные таблицы и прочее.

Изучение PRIMARY.BIN
Как вычислил vitalyb, файл живет в памяти по адресу 0xff810000 -- туда же его следует загружать в IDA. Дальше надо копать и копать ) Там можно найти весь код управления аппаратом. На первых порах интерес представляют стандартные функции типа open/close/read/CreateTask/и т.п. Кроме того, там же можно поискать адреса портов лампочек, клавиатуры и прочей периферии. При разборе кода следует помнить, что полученный из fir PRIMARY.BIN, даже если он предназначен для вашего фотоаппарата, скорее всего будет отличаться от того что сейчас в памяти фотоаппарата (если вы этот fir еще не установили).

Изучение WriterInFIR.bin
На первых порах WIF представляет наибольший интерес. Как вычислил vitalyb, файл живет в памяти по адресу 0x1900 -- туда же его следует загружать в IDA. Для начала следует найти в конце файла таблицу из ~3000 функций -- она выглядит как массив, где на каждую функцию отведено 3 двойных слова. Первое -- адрес названия функции Второе -- тип ресурса (для функции это 50000, еще попадаются 70000 и 90000) Третье -- адрес тела функции. После их распознования код выглядит значительно понятнее. Например в функции InitializeUniqueLED мне удалось обнаружить адреса портов лампочек для S2 IS:
 * 1) define LED_AF_GREEN 0xc022008C
 * 2) define LED_AF_RED 0xc02200fC
 * 3) define LED_PR 0xc0220088

Изучение дампа
Лично мне получить дамп пока не удалось О том как это делается в теории я напишу в следующем (3.х) разделе. Однако после получения дампа общая логика его изучения такая: Таким образом, видимо, имеет смысл сначала поизучать именно эти области.
 * С адреса 0xff810000 живет реальный WriterInFIR.bin, и если на аппарат не ставились обновления firmware, то изучать внимательно следует именно его, т.к. он содержит реальные адреса функций и т.п.
 * Насколько я понимаю -- с адреса 0х0 примерно до 0х1900 живет часть ОЗУ, активно используемая ОС. Туда драйвера копируют значения портов и хранятся текущие параметры системы.
 * С адреса 0xc0220000 (а может и раньше) живут реальные адреса портов -- лампочки, кнопки, динамик и прочие.

Способы загрузки и исполнения своего кода на аппарате
Пока что найден один стабильный способ заливки кода на аппарат: на флеш-карту заливается файл-пакет, замасикрованный под обновление прошивки и содержащий нужный код. Фотоаппарат обнаружит его и позволит запустить (в режиме REC в меню появляется строка Update Firmware). Далее этот код может выполнять дополнительные действия, в том числе подгружать с карты дополнительные модули и запускать их так, как ему это кажется удобным. О том, как собрать и залить корректный файл-пакет обновления, было написано в 1.1 и 1.2. После запуска с карты файла *.fir, аппарат расшифровывает и распаковывает его, загружает в память файл WriterInFIR.bin по адресу 0х1900 и запускает на исполнение с того же адреса.

Кроме того, имеется информация о том, что существуют и другие способы исполнения кода (видимо, в неразведанном пока Factory Mode) -- есть подозрения, что аппарат может запускать код, получаемый непосредственно через USB. Об этом свидетельствует наличие в Primary.bin таких, например, строк, как "=== Start Faexe From USB ===". Данное направление пока не исследовано, и далее будет рассматриваться метод с загрузкой через патч прошивки.

Запуск "голого" кода
Самым простым способом запуска своего кода является полная подмена WriteInFIR.bin-а своим кодом. Примером такого кода является написанная vitalyb мигалка лампочкой (code_v0). Основные ограничения данного способа следуют из того факта, что на момент выполнения WIF-а периферия не до конца проинициализированна, да и запуск каких либо внутренних функций PRIMARY в таком непроинициализированном окружении грозит проблемами. Однако из этого кода уже можно работать с портами (если знать как с ними работать) в том числе помигать лампочками, попробовать включить клавиатуру и опозновать нажатия клавишей и т.п.

Запуск кода, внедренного в готовый WIF
Если для вашего аппарата есть фирменное обновление, можно модифицировать содержащийся там WIF, поместив свой код в точку, где основная инициализация окружения уже выполнена штатными процедурами WIF-а (использование чужого WIF-а, т.е. WIF-а от другого аппарата, скорее всего ни к чему хорошему не приведет). По такому принципу работает загрузчик elf-ов, реализованный vitalyb в рамках code_v1. Общая логика такова: основная инициализация происходит в процедуре AdditionalFunction, в конце которой делается вызов ExecuteUpgrade. Вот именно на место ExecuteUpgrade заливается свой код. Преимущества этого подхода -- можно более уверено работать с периферией (в т.ч. с ФС на SD) и можно вызывать функции, имеющиеся внутри стандартного WIF-а (среди них и open/write/read/close/loadModuleAt/...). Внедренный таким образом код уже может делать дампы памяти на карточку.

Запуск elf-ов через загрузчик
Развитием предыдущего подхода является написание elf-загрузчика. Суть идеи заключается в следующем: Преимущества подхода очевидны. При этом загрузчик пишется 1 раз, а дальше можно разрабатывать, модифицировать и заливать исключительно elf-ы.
 * пишется специальный код загрузчика, который внедряется в WIF по методу 3.2
 * сам код выполняет открытие elf-файла с карточки и вызов loadModuleAt для этого файла
 * сам elf-код может свободно использовать функции, описанные в WIF-е, причем код является портабельным (явные адреса функций нигде не прописываются, а определяются автоматически при выполнении loadModuleAt), т.е. на S2 IS спокойно работают бинарники собранные vitalyb для a610.

Внедрение процесса
Следующим шагом является запуск своего процесса на аппарате, причем так, чтобы он выполнялся параллельно со штатной работой аппарата. Для этого пишется elf-код, который некоторым образом выделяет область памяти под новый процесс, размещает там код процесса и производит горячую перезагрузку аппарата, после чего процесс начинает выполняться в качестве одной из задач ОС и может влиять на происходящее во время работы аппарата. Данный подход применяется vitalyb для вытаскивания RAW прямо из памяти в момент осуществления снимка.

Некоторая информация об организации портов
Все регионы MMIO расположены, по всей видимости, в области памяти 0хсХХХХХХХ. Регионов там много, они отвечают за различное оборудование. Общий дамп области сделать не получается, видимо там не все доступно на чтение. Анализ прошивки дает такие регионы как 0хс00ХХХХХ, 0хс020ХХХХ, 0хс021ХХХХ, 0хс022ХХХХ, 0хс024ХХХХ, 0хс04ХХХХХ, 0хс0сХХХХХ, и другие.

На данном этапе наибольший интерес представляет регион 0хс022ХХХХ -- там и у a610 и у s2is (а возможно и у других моделей) находятся порты индикаторов и клавиатуры.

Некоторые из портов, назовем их "управляющими портами", чаще всего содержат значения 0х42("выключен") и 0х47("включен"). Работа с управляющими портами происходит обычно так:
 * для включения в выключенный (0х42) порт заносится значение 0х46. В ответ, в качестве подтверждения, порт выставляет первый бит, и его значение становится равным 0х47.
 * для выключения во включенный (0х47) порт заносится значение 0х44. В ответ, в качестве подтверждения, порт снимает второй бит, и его значение становится равным 0х42.

Не забываем, что порт обычно является 4хбайтным словом.

Индикаторы
На аппарате имеется некоторое количество индикаторов (лампочек), каждому из которых соответствует некоторый порт, "включение" которого зажигает индикатор. Количество индикаторов и расположение их портов зависит от модели аппарата.

Согласно vitalyb, в а610 имеются как минимум следующие индикаторы: LED_AF 0xc0220080 LED_PR 0xc0220084

В s2is мне удалось найти такие индикаторы: LED_AF_GREEN 0xc022008C LED_AF_RED 0xc02200fC LED_PR 0xc0220088 (примечание: LED_AF_GREEN и LED_AF_RED нельзя включить одновременно, т.к. это два цвета одного индикатора).

Если необходимо найти порты для какой-то новой модели, проще всего начать их поиск с анализа функции InitializeUniqueLED в родном WIF-е.

Клавиатура
В а610, согласно vitalyb, состояние всех основных клавиш отражается в битах 4хбайтного слова по адресу 0xc0220208, а кнопка выключения (и, видимо, включения) видна по адресу 0xc0220204. Соответствие некоторых кнопок битам можно посмотреть в коде jumping-cube от vitalyb.

В s2is все организовано несколько сложнее. Есть 4 независимых банка кнопок. Им соответствуют порты 0xC0220110, 0xC0220114, 0xC0220118, 0xC022011С. По ним изначально записаны 0х47. Для того чтобы проверить состояние кнопок некоторого банка, он активируется (в соответствующий порт заносится 0х44), происходит чтение и анализ порта 0xc0220208, затем банк деактивируется (в соответствующий порт заносится 0х47).

Полный цикл анализа клавиатуры согласно коду драйвера из PRIMARY выглядит так:
 * включаем первый банк (0xC0220110 заносим 0х44)
 * читаем кнопки из 0xC0220208 (!)
 * выключаем первый банк (0xC0220110 заносим 0х46)
 * включаем второй банк (0xC0220114 заносим 0х44)
 * читаем кнопки из 0xC0220208
 * выключаем второй банк (0xC0220114 заносим 0х46)
 * включаем третий банк (0xC0220118 заносим 0х44)
 * читаем кнопки из 0xC0220208
 * выключаем третий банк (0xC0220118 заносим 0х46)
 * включаем четвертый банк (0xC022011c заносим 0х44)
 * читаем кнопки из 0xC0220208
 * выключаем четвертый банк (0xC022011c заносим 0х46)
 * (!) Судя по обработчику при проверке первого банка также вроде бы читаются 0xC0220200, 0xC0220204, 0xC0223024, 0xC022301с, но зачем -- толком пока не ясно.

Такой метод обработки клавиатуры позволяет читать состояния 36 кнопок и переключателей. Остаются пока не найденными:
 * кнопка отложенной съемки
 * кнопка отсека SD-карты
 * кнопка видео-съемки
 * кнопка POWER OFF
 * кнопка MODE PLAY
 * кнопка MODE REC

Другие порты
Другие порты могут быть найдены анализом кода прошивки или перебором


 * Имеется некий загадочный порт, при включении аппарата выставляемый в 0х46, а при выключении в 0х44. В а610 это 0xc02200a0, а в s2is, видимо, 0xC022002C. Однако, штатная процедура shutdown этим не исчерпывается, поэтому простое выставление этого порта в 0х44 приведет, скорее, к зависанию


 * На своем s2is я при некоторых обстоятельствах добился срабатывания вспышки при занесении 0х46 по адресу 0хc02200a4. Причем вспышка срабатывает даже в закрытом состоянии.

Кроме того в прошивке от s2is попадаются такие адреса:
 * в функции TurnOnSDCardPower 0xC02200B0 <= 46
 * в функции InitializeScanModeADC 0xC022c0B4 = 46
 * в функции SioDrv 0хC0223010 <= ( 8000 | 8088 )
 * неподалеку от строки "Audio" 0xC022c0BC <= 46
 * неподалеку от строки "TgTask" 0xC02200AC <= 46
 * неподалеку от строки "EfDrv" 0xC02200A8 <= 46
 * неподалеку от строки "SDACDrv" 0xC0220018 <=??
 * неподалеку от строки "LATCH_E2" ("ImagePower") 0xC0220030 <= ??

Код драйвера можно взять тут: http://altsoph.ru/stuff/prjs/canon/SDL_nullevents.c

Инструкции по сборке sdl под s2is
 * 1) Берем sdl с офф.сайта или из svn, мне попался что-то вроде 1.2.12.
 * 2) Берем http://vitalyb.mail333.com/a610/sdl-demo.zip и накладываем взятый оттуда sdl.patch ИЛИ (вместо 1-2) берем пропатченные сырцы от vitalyb тут http://vitalyb.mail333.com/a610/sdl/sdl-canon.7z
 * 3) Заменяем sdl/src/video/dummy/SDL_nullevents.c на http://altsoph.ru/stuff/prjs/canon/SDL_nullevents.c
 * 4) Собираем, помахивая бубном...

Собранную версию можно взять тут http://altsoph.ru/stuff/prjs/canon/libsdl.a

(с) altsoph