Дисклеймер: исследование началось в 2013 году, так что если считаете некоторые методы глупыми и опасными — вы правы, так и было. Тем не менее, я многому научился в процессе.
Вступление
Всё началось за несколько месяцев до рождения моего первого ребенка. Мы с женой всегда хотели купить крутой фотоаппарат Leica и вдруг поняли, что если не купим его сейчас, то уже долго не сможем это сделать. Поэтому мы заказали камеру M240 и… бум, нас поставили в очередь на полгода. Вскоре мне надоело ждать, и я начал изучать их сайт. Моё внимание сразу привлёк раздел с файлами. Ну, вы можете догадаться, почему… Прошивки!
Я увидел незашифрованный и несжатый файл (m8-2_005.upd
), который начинается с магии PWAD. Узнаёте? Да, всё правильно, это формат Doom Patch WAD. Кажется, ребята любят классику. Формат очень хорошо документирован, так что распарсить его оказалось совсем несложно.
Файлы прошивки Leica
Прошивка Leica M8
На самом деле это очень смешно, потому что когда позже я изучал сжатый файл прошивки Leica T — то первым делом решил проверить методы сжатия, которые в прошлом использовала id Software.
Википедия говорит, что они использовали формат LHA, который по сути представляет собой LZW. Но распространённые декомпрессоры LZW не подошли, поэтому я начал искать конкретную реализацию программного обеспечения id — и вуаля, нашёл в исходниках Catacomb Armageddon. Должен признать, повезло.
В любом случае, вернёмся к M8. Вот структура прошивки:
RULES: 0x0000008C ( 3036:0x00000BDC) - описание XML LUTS: 0x00000C68 ( 183274:0x0002CBEA) GAMMA: 0x0000007C ( 31760:0x00007C10) GAIN: 0x00007C8C ( 50344:0x0000C4A8) LEICA: 0x00014134 ( 7000:0x00001B58) BLEMISH: 0x00015C8C ( 250:0x000000FA) WREF: 0x00015D88 ( 82480:0x00014230) OBJ: 0x00029FB8 ( 11268:0x00002C04) VERSION: 0x0002CBBC ( 46:0x0000002E) PXA: 0x0002D854 ( 858384:0x000D1910) BF: 0x000FF164 ( 134522:0x00020D7A) - семейство процессоров Analog Devices Blackfin GUI: 0x0011FEE0 ( 3574180:0x003689A4) TRANS: 0x0000005C ( 59988:0x0000EA54) - локализация IMAGES: 0x0000EAB0 ( 267433:0x000414A9) 21_1PRT: 0x000000CC ( 18411:0x000047EB) - изображение JFIF 21_2GRP: 0x000048B8 ( 23172:0x00005A84) - изображение JFIF 21_3PAN: 0x0000A33C ( 23034:0x000059FA) - изображение JFIF 24_1PRT: 0x0000FD38 ( 18489:0x00004839) - изображение JFIF 24_2GRP: 0x00014574 ( 23230:0x00005ABE) - изображение JFIF 24_3PAN: 0x0001A034 ( 22998:0x000059D6) - изображение JFIF 28_1PRT: 0x0001FA0C ( 22605:0x0000584D) - изображение JFIF 28_2GRP: 0x0002525C ( 23081:0x00005A29) - изображение JFIF 28_3PAN: 0x0002AC88 ( 23282:0x00005AF2) - изображение JFIF 35_1PRT: 0x0003077C ( 22496:0x000057E0) - изображение JFIF 35_2GRP: 0x00035F5C ( 23532:0x00005BEC) - изображение JFIF 35_3PAN: 0x0003BB48 ( 22881:0x00005961) - изображение JFIF FONT1: 0x0004FF5C ( 1522988:0x00173D2C) FONT2: 0x001C3C88 ( 1723676:0x001A4D1C) VERSION: 0x003689A4 ( 0:0x00000000) M16C: 0x00488884 ( 130406:0x0001FD66) - семейство Renesas M16C (Motorola S-record) FPGA: 0x004A85EC ( 131604:0x00020214) - Xilinx Spartan 3 FSL: 0x004C8800 ( 814:0x0000032E) - загрузчик первой стадии
IDA из коробки не поддерживает процессоры Blackfin, но есть сторонний плагин.
Прошивка Leica M9
Файл прошивки Leica M9 (m9-1_196.upd
) выглядит зашифрованным: гистограмма показывает распределение около 0,45%.
Конец истории? Может и нет. Дело в том, что Leica использовала довольно слабые процессоры в камерах, а в то время в бытовой электронике часто применялось шифрование XOR, поэтому я решил написать простой инструмент для операции XOR, чтобы сравнить прошивку с собой и посчитать некоторую статистику.
Длина ключа определялась путём поиска самого длинного повторяющегося шаблона. Это имеет смысл, так как любая прошивка обычно включает в себя большие блоки повторяющихся данных, таких как набивка 0x00/0xFF или графики с пикселями LUT. Сам ключ вычисляется по частотности байтов в пределах длины ключа, где наиболее часто встречающийся байт идёт в буфер ключа. Результат работы программы явно указывал на шифрование XOR. Затем пришлось немного модифицировать инструмент, чтобы получить потенциальный ключ и расшифровать код. Это опять оказался файл PWAD.
Содержание PWAD выявило следующую структуру:
RULES: 0x0000007C ( 2788:0x00000AE4) - описание XML LUTS: 0x00000B60 ( 4060616:0x003DF5C8) PROCESS: 0x0000004C ( 3900572:0x003B849C) CREATE: 0x0000004C ( 20:0x00000014) - метка времени LUTS: 0x00000060 ( 427744:0x000686E0) GAINMAP: 0x00068740 ( 20008:0x00004E28) LENS: 0x0006D568 ( 3452724:0x0034AF34) CCD: 0x003B84E8 ( 148662:0x000244B6) CREATE: 0x0000004C ( 20:0x00000014) - метка времени BLEMISH: 0x00000060 ( 1092:0x00000444) WREF: 0x000004A4 ( 147452:0x00023FFC) LIN: 0x000244A0 ( 22:0x00000016) ICCPROF: 0x003DC9A0 ( 4304:0x000010D0) ECI-RGB: 0x0000003C ( 540:0x0000021C) sRGB: 0x00000258 ( 3144:0x00000C48) A-RGB: 0x00000EA0 ( 560:0x00000230) WBPARAM: 0x003DDA70 ( 7000:0x00001B58) BF561: 0x003E0128 ( 289128:0x00046968) - Analog Devices Blackfin Processor family bf0: 0x0000004C ( 117846:0x0001CC56) - основной процессор bf1: 0x0001CCA4 ( 117826:0x0001CC42) - прошивка подпроцессора bf0.map: 0x000398E8 ( 27072:0x000069C0) - карта прошивки основного процессора с символами :D bf1.map: 0x000402A8 ( 26304:0x000066C0) - карта прошивки подпроцессора с символами :D BODY: 0x00426A90 ( 143280:0x00022FB0) - семейство Renesas M16C (Motorola S-record) GUI: 0x00449A40 ( 3647624:0x0037A888) TRANS: 0x0000005C ( 131656:0x00020248) - локализация IMAGES: 0x000202A4 ( 267433:0x000414A9) 21_1PRT: 0x000000CC ( 18411:0x000047EB) - изображение JFIF 21_2GRP: 0x000048B8 ( 23172:0x00005A84) - изображение JFIF 21_3PAN: 0x0000A33C ( 23034:0x000059FA) - изображение JFIF 24_1PRT: 0x0000FD38 ( 18489:0x00004839) - изображение JFIF 24_2GRP: 0x00014574 ( 23230:0x00005ABE) - изображение JFIF 24_3PAN: 0x0001A034 ( 22998:0x000059D6) - изображение JFIF 28_1PRT: 0x0001FA0C ( 22605:0x0000584D) - изображение JFIF 28_2GRP: 0x0002525C ( 23081:0x00005A29) - изображение JFIF 28_3PAN: 0x0002AC88 ( 23282:0x00005AF2) - изображение JFIF 35_1PRT: 0x0003077C ( 22496:0x000057E0) - изображение JFIF 35_2GRP: 0x00035F5C ( 23532:0x00005BEC) - изображение JFIF 35_3PAN: 0x0003BB48 ( 22881:0x00005961) - изображение JFIF FONT1: 0x00061750 ( 1522988:0x00173D2C) USBLOGO: 0x001D547C ( 1775:0x000006EF) - изображение JFIF FONT2: 0x001D5B6C ( 1723676:0x001A4D1C) FPGA: 0x007C42C8 ( 150176:0x00024AA0) - Xilinx Spartan 3A BF547: 0x007E8D68 ( 937576:0x000E4E68) - семейство процессоров Analog Devices Blackfin (FSL?)
Прошивка Leica M240
У меня вошло в привычку каждое утро проверять страницу загрузки с прошивками Leica. Скоро там появился новый файл: FW_M240_1_1_0_2.FW.
Он не выглядел зашифрованным, но был сжат…
Сжатие
Гистограмма показывает огромный всплеск на 0x9D.
Возможно, это какая-то магия сжатия. Поиск в интернете [9D+compression] ничего не дал, кроме того, что 0x1F9D используется в качестве подписи сжатия LZW. Если что, я разбираюсь в типах сжатия LZ и решил посмотреть на байты после 0x9D. И увидел четыре варианта:
9D 70 C4
9D 00
9D XX YY
9D XX 8Y YY
Что ещё удалось подметить:
- первый вариант всплывает лишь однажды по адресу 0x30: вероятно, используется в качестве индикатора сжатых данных;
- XX никогда не превышает 0x7F;
- последний байт YY в третьем и четвёртом случаях никогда не превышает 0x7F
Из того, что я знаю о LZ, это очень похоже на LZ77 или LZSS, где YY — шаг отступа, а XX — количество байтов для копирования. И второй вариант — частный случай выдачи 0x9D. Я написал простую функцию на C, которая реализует эту логику. Она подтвердила, что мы движемся в правильном направлении, но четвёртый вариант всё равно не вписывается в схему.
Я всячески пытался его интерпретировать, но ничего не получилось. Поэтому я обратился за советом к товарищам. Один парень заметил, что согласно моим собственным наблюдениям, четвёртый байт YY появляется только тогда, когда установлен самый высокий бит 0x8Y: это просто дополнительное расстояние для шага отступа. Мне было стыдно, всё оказалось настолько очевидно…
Наконец, декомпрессор начал выдавать валидный поток… пока не застрял посередине файла. Это произошло из-за неизвестной длины скользящего окна. Дополнительная отладка и тесты исправили ситуацию.
Так появился инструмент для парсинга прошивки M240.
Структура прошивки
Для работы с неизвестным форматом я не придумал ничего лучше, чем измерить некоторые смещения и размеры участков кода — и попытаться найти самые близкие значения в заголовке файла. Например, этот блок:
0x00: 1E 1C AF 2E 01 01 00 02 07 E1 EA 5E 00 5C 1A B1
0x10: 01 29 1A 7E AE 38 73 65 9C 3D 75 B4 34 2F 44 6E
0x20: 13 17 8E 6B 00 00 00 01 00 00 00 30 E1 E3 50 D1
в итоге превратился в:
1E1CAF2E — выглядит как "LEICA FILE"
01010002 - 1.1.0.2
005C1AB1 — размер сжатого файла (big endian)
01291A7E — размер несжатого файла (big endian)
AE3873659C3D75B4342F446E13178E6B — хэш MD5
00000001 — количество полезных нагрузок
00000030 — смещение первой полезной нагрузки
По мере понимания структур прошивки я улучшал свой инструмент, и в конечном итоге он выдавал такое:
Running with options:
+ firmware folder: M240_FIRMWARE
+ verbose enabled
Open firmware file: FW_M240_1_1_0_2.FW
File size: 6036193 | 0x005C1AE1
Parse container header:
version: 1.1.0.2
packed size: 6036145 | 0x005C1AB1
unpacked size: 19470974 | 0x01291A7E
body blocks: 1 | 0x00000001
body offset: 48 | 0x00000030
MD5: AE387365 9C3D75B4 342F446E 13178E6B
MD5 check: PASSED
Uncompress container body:
6036145 -> 19470974
Uncompression: DONE
Split container:
Number of sections: 9 | 0x00000009
Section table size: 612 | 0x00000264
Section table offset: 36 | 0x00000024
Section 1
Section Name: "[A]IMG_LOKI-212"
Section offset: 0 | 0x00000000
Section size: 7340032 | 0x00700000
Section base: 1048576 | 0x00100000
MD5: A8D55AA2 B0ACDB14 0673AD79 707674F3
MD5 check: PASSED
Create file: M240_FIRMWARE/IMG_LOKI-212.bin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Section 9
Section Name: "[A]IMG-LENSDATA-213"
Section offset: 19214844 | 0x012531FC
Section size: 255478 | 0x0003E5F6
Section base: 16252928 | 0x00F80000
MD5: 39C2BEC0 27ED23F6 2C1C8513 EEE697B9
MD5 check: PASSED
Create file: M240_FIRMWARE/IMG-LENSDATA-213.bin
Splitting container: DONE
Extraction COMPLETE!
Прошивка М240 включает один контейнер с девятью элементами:
IMG_LOKI-212.bin - прошивка прикладного процессора
IMG_LOKI-213.bin - прошивка прикладного процессора
CTRL_SYS-11.bin - прошивка процессора ввода-вывода
IMG-FPGA-212.bin - прошивка для обработки изображений (сенсор)
IMG-FPGA-213.bin - прошивка для обработки изображений (сенсор)
IMG-DSP-212.bin - прошивка DSP
IMG-DSP-213.bin - прошивка DSP
IMG-LENSDATA-212.bin - данные по оптике
IMG-LENSDATA-213.bin - данные по оптике
Как видим, в одной прошивке два набора файлов. Позже я узнал, что 212 — это версия микрсхемы для обработки изображений, а в производство пошло два варианта Leica M240. Данное исследование основано на версии 212.
Управление системой: CTRL_SYS-11.bin
Единственная общая часть — прошивка для чипа управления системой. Это реально большой бинарник, и по коду нетрудно догадаться, для чего он предназначен.
$ strings CTRL_SYS-11.bin | rg SH
-> Test SH7216 data flash driver
-> Test SH7216 SCI driver
-> Test SH7216 I2C driver
-> Test SH7216 MTU2 driver
-> Test SH7216 ADC functions
-> Test SH7216 CMT driver
Таким образом, перед нами процессор Renesas SH7216 (SH-2A), который отвечает за раннюю стадию загрузки, тесты ввода-вывода и обновление прошивки. IDA из коробки поддерживает этот тип процессора. Оставалось лишь найти правильный адрес базовой загрузки, известный по описанию разделов прошивки: это 0x0
.
Section Name: "[A]CTRL_SYS-11"
Section offset: 14680064 | 0x00E00000
Section size: 917277 | 0x000DFF1D
Section base: 0 | 0x00000000
Я загрузил его в IDA и распознал все функции, но не особенно в нём не копался, потому что намного интереснее прошивка основного процессора.
Здесь можно ещё отметить, что UART этого чипа открывается на сервисный порт, куда выдаёт журнал загрузки. Мы вернёмся к этому позже.
Основной чип: IMG_LOKI-212.bin
Чтобы начать реверс-инжиниринг данной прошивки, необходимо сначала ответить на несколько вопросов:
- какой тип процессора
- каков адрес базовой загрузки
- на какой ОС он основан, если таковая имеется
Благодаря нашему инструменту мы уже знаем адрес базовой загрузки: это 0x100000
.
Section Name: "[A]IMG_LOKI-212"
Section offset: 0 | 0x00000000
Section size: 7340032 | 0x00700000
Section base: 1048576 | 0x00100000
Остальные ответы прошивка хранит в удобочитаемом виде. Например, эта строка:
$ strings ./IMG_LOKI-212.bin | rg Softune
6Softune REALOS/FR is Realtime OS for FR Family, based on micro-ITRON COPYRIGHT(C) FUJITSU LIMITED 1994-1999
...
Таким образом, мы имеем дело с кастомным процессором Fujitsu FR (Leica называет его Maestro) и операционкой Softune REALOS. На самом деле, это намного лучше, чем Blackfin, потому что IDA из коробки поддерживает FR.
Модуль процессора FR
Реальность оказалась не такой яркой, потому что после загрузки файла прошивки программа IDA не показала никаких инструкций, внешних ссылок и т. д.
Я решил исправить это, но в итоге пришлось полностью переписать некоторые части прошивки. Вот результат:
Кроме исправлений в ana
, ins
и out
, совершенно новый код emu
умеет:
- распознавать различные типы кода и внешние ссылки на данные;
- распознавать операторы switch;
- выполнять трассировку стека;
- разделять аргументы стека и локальные переменные;
- правильно распознавать функции.
Но самое большое изменение, как вы заметили, — это прописные буквы для инструкций :)
Хотите увидеть полный набор инструкций? Вот он:
ADD OR BTSTH LSR MOV BN LDRES EXTSH ADD2 ORH MUL LSR2 JMP BP STRES EXTUH ADDC ORB MULU ASR CALL BV COPOP SRCH0 ADDN EOR MULH ASR2 RET BNV COPLD SRCH1 ADDN2 EORH MULUH LDI INT BLT COPST SRCHC SUB EORB DIV0S LDI INTE BGE COPSV LDM0 SUBC BANDL DIV0U LDI RETI BLE NOP LDM1 SUBN BANDH DIV1 LD BRA BGT ANDCCR STM0 CMP BORL DIV2 LDUH BNO BLS ORCCR STM1 CMP2 BORH DIV3 LDUB BEQ BHI STILM ENTER AND BEORL DIV4S ST BNE DMOV ADDSP LEAVE ANDH BEORH LSL STH BC DMOVH EXTSB XCHB ANDB BTSTL LSL2 STB BNC DMOVB EXTUB
Вот так, просто и красиво.
Кстати, вы могли заметить, что некоторые инструкции не выровнены:
BRA:D loc_xxx LDI:8 #0x64, R5
Это не ошибка в процессорном модуле, а фактически особенность семейства Fujitsu FR. Она называется слот задержки и довольно типична для RISC-процессоров.
Из руководства по процессору FR80 (примечание: ссылка больше не работает):
Инструкция, которая находится сразу после инструкции ветвления (её расположение называется «слотом задержки»), выполняется перед ветвлением, а инструкция по целевому адресу выполняется после ветвления. Поскольку инструкция в слоте задержки выполняется перед операцией ветвления, видимая скорость выполнения составляет 1 цикл.
Таким образом, это, по сути, оптимизация конвейера, и лучше помнить о ней, потому что она применяется повсюду в прошивке Leica.
Softune REALOS
Softune — это интегрированная среда разработки Fujitsu для семейств процессоров Fujitsu FR, FR-V и F2MC. Работает на ядре реального времени REALOS µITRON. Например, используется в цифровых зеркальных камерах Nikon (см. Nikon EXPEED) и некоторых камерах Pentax с байонетом K.
Так что это довольно популярная приличная RTOS с задачами, семафорами и другими вкусностями. Мне было интересно, можно ли распознать в прошивке Leica некоторые стандартные функции библиотеки.
Должен назвать первую часть исследования большой напрасной тратой времени, и вот почему.
Softune IDE оказалось очень трудно найти, но в конце концов мне удалось кое-что достать. Как и ожидалось, IDE включала в себя библиотеки. Там было четыре двоичных файла:
- lib911.lib
- lib911e.lib
- lib911if.lib
- lib911p.lib
Не знаю почему, может по инерции, как я взламывал всё связанное с Leica, я опять начал реверс-инжиниринг формата. Да, очень хорошо документированного формата Object Module Format. И да, конечно, я написал для этого специальный инструмент:
Fujitsu RISC Library Tool v1.0
Usage: FRLibTool [-s start] [-i imagebase] [-o output] [-f index] [-dv] FIRMWARE.BIN LIBRARY.LIB
This tool will help you to find Softune REALOS library functions in FR (Fujitsu RISC) firmware.
Use following arguments:
-f Specify firmware image file
-s Specify firmware image scan offset
-b Specify firmware imagebase
-o Specify output type (exclusively)
list - list of functions
idc - IDC script
py - IDA python script
pat - FLAIR pattern file
-i xxx Specify index of particular function
-d Dump library
-v Be verbose
С его помощью можно создавать файлы *.pat
и использовать их в качестве входных данных в IDA FLAIR для генерации файлов сигнатур.
$ FRLibTool -o pat lib911.lib
$ FRLibTool -o pat lib911e.lib
$ FRLibTool -o pat lib911if.lib
$ FRLibTool -o pat lib911p.lib
...
$ sigmake -n "SOFTUNE C/C++ Library" lib911.pat lib911e.pat lib911if.pat lib911p.pat softune.sig
После применения этой сигнатуры я наконец-то с радостью увидел соответствия в IMG_LOKI-212.idb.
Макет
Сразу обращает на себя внимание количество строк в прошивке. Многие функции именуются по своей функциональности. Это очень полезно в процессе обратной разработки, чтобы понять общий шаблон.
Также важно отметить, что некоторые части файла прошивки копируются на другой адрес в обработчике сброса (reset handler). Например, встроенный загрузчик во время выполнения перемещается выше по оперативной памяти.
Пришлось вручную создавать дополнительные разделы, в итоге получился такой макет:
Прерывания
Таблицу векторов прерываний можно найти по доступу к TBR (Table Base Register):
LDI:32 #int_table, R0
MOV R0, TBR
Обычно он происходит в обработчике сброса вектора в самом начале прошивки.
Адреса обработчиков в таблице хранятся в обратном порядке по формуле TBR + (0x3FC - 4 × inum)
, так что вектор сброса в конце таблицы со смещением 0x3FC
.
Я нашёл большинство прерываний из руководства по FR и предположил, что у Leica Maestro аналогичная компоновка. Затем взял каждый обработчик и попытался найти строку или любой другой намёк, раскрывающий цель прерывания.
В итоге составил такой список:
Многие прерывания вполне ожидаемы, такие как AUDIO/SDIO/VIDEO/JPEG/RAW, но попробуйте определить самое загадочное из них? Я говорю о прерывании int_uart_in
. Похоже, что камера поддерживает какой-то консольный режим UART CLI.
Системные вызовы
Как почти любая ОС, Softline REALOS использует системные вызовы. В ассемблере они выглядят так:
Фактический адрес обработчика системных вызовов вычисляется следующим образом. Начнём с поиска обработчика прерывания INT #0x40
. В соответствии с описанием выше это
(0x3FC - 4 × inum) = (0x3FC - 4 × 0x40) = 0x2FC = int_realos_syscall
В обработчике легко найти ссылку на нижнюю часть таблицы системных вызовов с 16-битными словами. Конкретная запись в этой таблице вычисляется по формуле syscall_table_bottom + (num * 2)
:
[syscall_table_bottom + (-23 * 2)] = [syscall_table_bottom - 0x2E] = [0x1012EA] = 0xE68
Это не похоже на адрес, потому что фактический адрес обработчика системных вызовов вычисляется как syscall_table_bottom + offset
. Весь процесс показан на диаграмме.
Все системные вызовы и их функциональность указаны в Руководстве по ядру Softline REALOS/FR, поэтому мне удалось восстановить все реализованные обработчики в таблице и ещё немного улучшить IDB.
Конечно, можно сделать код ещё красивее, определив типы системных вызовов в IDA.
Я написал скрипт на Python для автоматического поиска этих системных вызовов и прочего.
Задачи
В системном вызове sta_tsk
я заметил, что в качестве параметра передаётся не основная функция, а pid. Это означает, что пришло время искать большой массив дескрипторов задач. И имеет смысл начать с самого sta_tsk
.
ROM:102180 sys_sta_tsk: ROM:102180 ST RP, @-R15 ROM:102182 LDUB @(R14, 0x4F), R3 ROM:102184 LDI:32 #word_100B80, R14
В самом начале мы видим какие-то ссылки. Пришлось немного повозиться с типами данных, но в итоге части собрались вместе:
ROM:100B80 word_100B80: .word 0xF ; number of tasks ROM:100B82 .word 0x1C ; task descriptor size ROM:100B84 .long 0x82A09F5C ; task 1 descriptor ROM:100B88 .long 0x1000D ROM:100B8C .long 0 ROM:100B90 .long 0x40000000 ROM:100B94 .long sub_1A7DB2 ; task main ROM:100B98 .long 0x8286EEC0 ROM:100B9C .long 0 ROM:100BA0 .long 0x82A09F88 ; task 2 descriptor ROM:100BA4 .long 0x20010 ROM:100BA8 .long 0 ROM:100BAC .long 0x40000000 ROM:100BB0 .long sub_1A6BD2 ; task main ROM:100BB4 .long 0x8287EEC0 ROM:100BB8 .long 0 ...
и так далее. Всего 15 задач. Было вопросом времени заглянуть в каждую основную функцию, определить имя и цель задачи (кроме последней). Вот полный список:
- SubCPU
Эта задача, по-видимому, отвечает за операции захвата, такие как экспозиция, визирование по экрану и т. д. - KeyManager
Скорее всего, эта задача связана с аппаратными кнопками. - GuiManager
Довольно большая задача, в которой реализована машина состояний UI и отрисовка интерфейса. - DebugManager
Да, есть кое-что для отладки. Ням-ням. - FileManager
Эта задача — всё о файловых операциях. - FamManager
Я бы сказал, что задача отвечает за файлы и память, потому что она зависит от задач файлового менеджера и менеджера памяти. - MemoryManager
Никаких сюрпризов: операции с памятью, управление пулом и т. д. - ImageManager
Эта задача управляет процессами кодирования/декодирования и другими процессами обработки изображений. - UsbManager
Текущая задача — обработка коммуникаций по USB, которая включает в себя MassStorage, PTP и собственный протокол Leica. - IOManager
Похоже, эта задача управляет устройствами хранения данных, такими как SD и CF-карты (что? какие ещё CF? может, это из модели 213). - Systemmanager
Разные задачи вроде общих системных операций, управления питанием и т. д. - SettingsManager
Обрабатывает состояние камеры и настройки. - MonitorManager
Отслеживает изменения состояния камеры и информирует другие задачи. - PeripheralManager
Эта задача контролирует GPS, яркость и некоторые другие сенсоры. - Неизвестно
К сожалению, для неё я не нашел ничего существенного.
Интересно отметить, что после основного массива есть ещё один выдающийся дескриптор.
ROM:100D28 dword_100D28: .long 0x82A0A1F0
ROM:100D2C .long 0x21
ROM:100D30 .long 0
ROM:100D34 .long 0x80000000
ROM:100D38 .long tid16_task
ROM:100D3C .long 0x8285EEC0
ROM:100D40 .long 0
А функция задачи — просто ветвление самой себя.
ROM:101494 sub_101494:
ROM:101494 BRA sub_101494 ; CODE XREF: sub_101494
На этот дескриптор стоит ссылка в конце функции start
, которая отвечает за создание других задач и настройку встроенного ПО. Так что это, скорее всего, задача бездействия системы.
Модули и сообщения
Помимо задач, можно определить некоторые логические объекты, такие как IO и периферийные модули. Модули представлены в виде группы обработчиков сообщений в рамках одной из задач.
Группа IO, кажется, включает:
- Менеджер IO
- Подпроцессор
- Менеджер USB
- USB PTP
- Протокол USB Leica
- USB Mass Storage
- Менеджер управления кнопками
- Менеджер отладки
- Менеджер объектива
А в периферийной группе:
- Менеджер периферии
- Датчик освещённости
- Светодиоды
- Динамик
- Датчик угла наклона
- Распознавание закрытия крышки
- Модуль GPS
- Модуль 3DAxis
Сама система обмена сообщениями использует стандартные структуры SOFTUNE:
struct RealOS_MsgPayload
{
uint32_t msgID; // +0x0
uint32_t data[]; // +0x4
}
struct RealOS_Message
{
uint32_t os_reserved1; // +0x0
uint32_t os_reserved2; // +0x4
uint32_t to; // +0x8
uint32_t from; // +0xC
RealOS_MsgPayload* payload; // +0x10
}
Как и ожидалось, в IPC тоже есть несколько групп сообщений. Поскольку много сообщений обрабатывается в задачах и модулях, я смог восстановить только некоторые из этих групп:
0x1101xxxx - глобальные системные сообщения: 0x11010002 = SYS_UPDATE_BOOTLOADER или 0x11010005 = SYS_ERASE_SETTINGS 0x1102xxxx - сообщения, связанные с захватом изображения: 0x11020001 = CMD_CAP_CAPTURE или 0x11020008 = IMAGE_STATUS_CHANGED 0x1104xxxx - сообщения о событиях, связанных с воспроизведением: 0x11040002 = PLY_DISABLE_PLAY_MODE или 0x11040004 = PLY_IMAGE_READY 0x1108xxxx - различные сообщения для отладки PTP и др.: 0x11080002 = DBG_CHANGE_LEVEL или 0x11080012 = DBG_WRITE_ROM_DUMP_SD 0x2201xxxx - сообщения USB PTP 0x22010108 = изменение настроек камеры или 0x22010118 = запрос DebugObject 0x2202xxxx - довольно большая группа сообщений SUBCPU: 0x22020002 = E_SUBCPU_REQUEST_M_EXPOSURE_REQUEST 0x22020015 = E_IO_SUBCPU_COMMAND_CLEANING_SENSOR 0x2203xxxx - некоторые другие сообщения отладки: 0x22030001 = команда отладочной строки 0x2204xxxx - различные сообщения IO: 0x2204000C = включение/отключение Mass Storage или 0x22040012 = перезагрузка устройства 0x330000xx - другая группа сообщений для UI: 0x33000001 = нажатие клавиш 0x33000007 = подключение объектива 0x440000xx - здесь мало информации, но похоже на обработку изображений 0x44000013 = E_IMG_CMD_CHANGE_PINFO 0x55xxxxxx — группы сообщений FAM: 0x558800xx = файл-менеджер FAM или 0x558888xx = первая группа настройки меню FAM 0x6602xxxx — похоже на управляющие сообщения LED, например: 0x66020001 - переключение LED с X Гц 0x66020002 = включение непрерывного LED 0x6604xxxx - управление бипером: 0x66040001 = установка бипера 0x66040007 = писк о заполненной карте 0x6611xxxx - сообщения отладки, связанные с памятью 0x6622xxxx - сообщения обработки сообщений, связанные с памятью 0x6660xxxx - некоторые другие сообщения, связанные с памяьтю: 0x66600006 = HISTOGRAM 0x66600011 = RAWCOMP 0x771100xx and 0x77AA00xx - сообщения, связанные с переключением режимов камеры
К сожалению, много других сообщений остаются неизвестными.
GUI
В файле прошивки посмотрим ещё на следующие разделы: CTRL_SYS-11, IMG-LOKI-212, IMG-DSP-212, IMG-FPGA-212 и IMG-LENSDATA-212.
Что меня удивило, так это полное отсутствие ресурсов GUI. Но они где-то должны быть и, скорее всего, встроены в IMG-LOKI-212.
Один из моих обычных подходов к обратной разработке прошивки — восстановить все возможные перекрёстные ссылки. Не только в коде, но и в разделе данных. Затем я просматриваю их, пытаясь найти некоторые шаблоны или ссылки на известные части кода.
Прошивка Leica не стала исключением. Там много похожих последовательностей данных с адресами на другие последовательности данных, которые обращаются дальше и т. д. Поднимаясь по иерархии ссылок, я в конце концов увидел знакомую функцию.
Например, я нашёл структуру данных без каких-либо ссылок:
g_data = { ... }
К ней обращалась другая структуры:
g_data_struct1 = { ... , &g_data }
К которой в свою очередь обращается ещё одна структура:
g_data_struct2 = { &g_data, ... }
На эту структуру данных есть ссылка из кода, и её передают в качестве параметра другой функции:
func1()
╰ func2(..., &g_data_struct2, ...)
Однако func1()
не вызывается напрямую из другой функции, а хранится в некотором массиве:
g_func_list1[] = { ..., func1(), ... }
Посмотрев выше, я нашел в коде вызов g_func_list1
:
func3() {
g_func_list1[x]
}
И снова эта функция сохранена в массиве:
g_func_list2[] = { ..., func3(), ... }
К самому массиву обращается какой-то другой код:
func4() {
g_func_list2[x]
}
К счастью, на этот раз функция вызвана из другой функции, и так до gui_MADE_ApplicationRun
.
gui_Statemachine_DoStateChange()
╰ gui_MADE_ApplicationRun()
╰ func5()
╰ func4()
В некоторых строках указано, что подсистема GUI называется “MADE”, а переходы страниц обрабатываются с помощью MADE_GetSysTri
, что бы это ни значило. Машина состояний GUI, в основном, реализована в функции gui_Statemachine_DoStateChange
. После сбора информации о GUI сложилась общая картина:
Как видите, основная функция для ресурсов GUI — gui_CopyImageDesc
(хотя это не настоящее имя). У неё следующие аргументы:
gui_CopyImageDesc(
uint32_t dstAddress; // R4 - destination address
UIDescType type; // R5 - description type
UITarget target; // R6 - rendering target
uint32_t descAddress; // R7 - description address
uint8_t always0; // (SP + 0x0) - always 0
uint8_t index1; // (SP + 0x4) - index 1
uint8_t index2; // (SP + 0x8) - index 2
uint16_t x_offset; // (SP + 0xC) - x offset
uint16_t y_offset; // (SP + 0x10) - y offset
uint16_t unknown2; // (SP + 0x14) -
uint32_t language1; // (SP + 0x18) - language id 1
uint32_t language2; // (SP + 0x1C) - language id 2
uint32_t funcAddress; // (SP + 0x20) - function address
)
Есть четыре типа описаний ресурсов:
struct UIDescType0Header struct UIDescType1Header struct UIDescType2 struct UIDescType3
{ { { {
uint32_t address; uint32_t address; uint32_t reg; uint16_t x_offset;
uint16_t entries; uint16_t entries; uint32_t address; uint16_t y_offset;
uint16_t unknown; uint16_t unknown; uint16_t unknown1; uint32_t address;
} } uint16_t unknown2; }
uint16_t unknown3;
struct UIDescType0Entry struct UIDescType1Entry uint16_t tableoff;
{ { }
uint16_t x_offset; uint16_t x_offset;
uint16_t y_offset; uint16_t y_offset;
uint32_t address; uint32_t address;
} uint16_t objects;
uint16_t total_w;
uint16_t total_h;
uint16_t unknown;
}
У первого типа заголовок со ссылкой на массив записей. Каждая запись имеет координаты и адрес пиксельных данных. Текущий тип, похоже, описывает зависимые от состояния элементы, такие как значки, которые могут быть серыми или исчезать из UI.
Второй тип тоже начинается с заголовка и используется для локализации, описания строк или блоков текста.
Третий тип описывает карты символов для разных языков.
Последний тип отвечает за все другие статические ресурсы, такие как изображения, фоны и т. д.
Теперь взглянем на сами данные для изображений.
+0x00: 00 08 00 14 00 01 A2 FF 0A 04 05 FF 0C 04 03 FF
+0x10: 0D 04 03 FF 0E 04 02 FF 0E 04 02 FF 04 04 06 FF
+0x20: 04 04 02 FF 04 04 06 FF 04 04 02 FF 04 04 06 FF
+0x30: 04 04 02 FF 04 04 06 FF 04 04 02 FF 04 04 06 FF
+0x40: 04 04 02 FF 04 04 06 FF 04 04 02 FF 04 04 06 FF
+0x50: 04 04 02 FF 04 04 06 FF 04 04 02 FF 0E 04 02 FF
+0x60: 0E 04 02 FF 0D 04 03 FF 0D 04 03 FF 0C 04 04 FF
+0x70: 04 04 0C FF 04 04 0C FF 04 04 0C FF 04 04 0C FF
+0x80: 04 04 0C FF 04 04 0C FF 04 04 0C FF 04 04 0C FF
+0x90: 04 04 0D FF 02 04 2D FF 00 06 00 14 00 01 79 FF
Первые шесть байт выглядят как маленький заголовок, за которым следует какой-то повторяющийся шаблон, где каждый второй байт является либо 0xFF
, либо 0x04
. Логично предположить, что 0x0008
и 0x0014
— ширина и высота в представлении с прямым порядком байтов (big endian). В конце этого дампа мы видим начало другой последовательности 00 06 00 14 00 01
. Скорее всего, это следующий ресурс (что подтверждается ссылкой на него). Таким образом, размер фактических данных изображения составляет 146 байт. Но размер изображения должен быть 0x8 * 0x14 = 0xA0 = 160. Ясно, что в данных не чисто пиксели и даже не 8-битная LUT, потому что она на 14 байт меньше. Тогда что? Вероятно, какое-то сжатие.
Глядя на этот шестнадцатеричный дамп, трудно поверить, что используется какая-то сложная схема. GUI у Leica не очень красочный, так что по моему опыту здесь лучше всего использовать таблицу LUT. В этом случае ресурсы UI будут полностью повторять индексы LUT вроде 03 03 03
или А1 А1 А1
. Обычно компрессор пытается избавиться от дублирования данных, заменяя их ссылкой. Эти массивы индексов идеальны для сжатия даже простым методом вроде RLE [data][number]
. Простая команда записать data
(значение) number
раз.
С учётом всего этого я предположил, что перед нами, скорее всего, простое изображение с двумя цветами LUT (0xFF
и 0x04
), а байт перед цветом — количество пикселей для рисования.
«А потом ты написал ещё один инструмент», — подумаете вы. Но нет, я взял ручку и бумагу и начал заполнять клетки. Забавно, что у меня до сих пор сохранился тот рисунок.
Где-то по пути я понял, что для этого изображения недостаточно 160 пикселей, так что 0x8 и 0x14 нужно умножить на два. Третье слово 0x0001 указывает, является ли изображение символом ASCII, так что окончательная структура ImageAsset выглядит следующим образом:
struct ImageAsset
{
uint16_t width; // ширина/2 (big endian)
uint16_t height; // высота/2 (big endian)
uint16_t ascii; // 1, если символ ASCII
struct image_data {
uint8_t number; // количество пикселей для рендеринга
uint8_t color; // индекс цвета пикселя в LUT
} data[];
}
Но одна часть всё ещё отсутствует: LUT.
Её было не так сложно найти, потому что многие ссылки и структуры уже восстановлены вручную, так что я медленно прокручивал разделы данных, ища массив на 256 элементов из 16-битных или 32-битных значений, пока не натолкнулся на это:
.long 0x7008080, 0x72D8080, 0x73C8080, 0x75A8080, 0x79B8080, 0x71DFF6B, 0x7BE8080, 0x7FF8080
.long 0x77BBD27, 0x75B60E7, 0x7835F4A, 0x7D3089F, 0x7018080, 0x7028080, 0x7038080, 0x7048080
.long 0x7058080, 0x7068080, 0x7078080, 0x7088080, 0x7098080, 0x70A8080, 0x70B8080, 0x70C8080
.long 0x70D8080, 0x70E8080, 0x70F8080, 0x7108080, 0x7118080, 0x7128080, 0x7952B15, 0x7138080
.long 0x7148080, 0x7158080, 0x7168080, 0x7178080, 0x7188080, 0x7198080, 0x71A8080, 0x71C8080
.long 0x71D8080, 0x71E8080, 0x71F8080, 0x7338080, 0x7208080, 0x7218080, 0x7228080, 0x7238080
.long 0x7248080, 0x7248080, 0x7268080, 0x7278080, 0x7288080, 0x7298080, 0x72A8080, 0x72B8080
.long 0x72C8080, 0x75E8080, 0x7608080, 0x7628080, 0x7648080, 0x7678080, 0x7688080, 0x7698080
.long 0x76B8080, 0x76E8080, 0x7708080, 0x7728080, 0x7758080, 0x7778080, 0x7798080, 0x77C8080
.long 0x77E8080, 0x7818080, 0x7838080, 0x7868080, 0x7888080, 0x78B8080, 0x78D8080, 0x7908080
.long 0x7928080, 0x7958080, 0x7978080, 0x7998080, 0x79C8080, 0x79D8080, 0x7668080, 0x79E8080
.long 0x7A18080, 0x7A28080, 0x7A38080, 0x7A68080, 0x7A78080, 0x7A88080, 0x7AB8080, 0x7AC8080
.long 0x7AD8080, 0x7B08080, 0x7B28080, 0x7B58080, 0x7B88080, 0x7B98080, 0x7BC8080, 0x7CC8080
.long 0x7AB3BBB, 0x7E10094, 0x7E4556E, 0x4008080, 0x2922D17, 0x7B2AB00, 0x7C2A262, 0x71DFF6B
.long 0x768D4A2, 0x769D4EA, 0x7BD88AE, 0x705997B, 0x70BB377, 0x711CC73, 0x717E66F, 0x7238866
.long 0x729A262, 0x72FBB5E, 0x735D55A, 0x7417751, 0x747914D, 0x74DAA48, 0x753C444, 0x75F663B
.long 0x76B9933, 0x7998080, 0x771B32F, 0x77D5526, 0x7836F22, 0x789881E, 0x78FA21A, 0x7159095
.long 0x71AAA91, 0x720C38D, 0x726DD88, 0x7506F6A, 0x7568866, 0x75CA262, 0x762BB5E, 0x76E5E55
.long 0x7747751, 0x77A914D, 0x780AA48, 0x78C4D3F, 0x792663B, 0x7988037, 0x79E9933, 0x7AA3C2A
.long 0x7B05526, 0x7B66F22, 0x7BC881E, 0x72488AE, 0x72AA1AA, 0x72FBBA6, 0x735D4A2, 0x7427799
.long 0x7489095, 0x74DAA91, 0x753C38D, 0x77E556E, 0x7836F6A, 0x7898866, 0x78FA262, 0x79C4459
.long 0x7A15E55, 0x7A77751, 0x7AD914D, 0x7BF4D3F, 0x7CC8080, 0x7C5663B, 0x7CB8037, 0x7337FC8
.long 0x73999C4, 0x73FB2C0, 0x745CCBB, 0x7757799, 0x74C54FF, 0x77B9095, 0x780AA91, 0x7AB3C72
.long 0x7B1556E, 0x7B66F6A, 0x7BC8866, 0x74277E1, 0x74890DD, 0x74EAAD9, 0x754C3D5, 0x76066CC
.long 0x7667FC8, 0x76C99C4, 0x772B2C0, 0x77E55B7, 0x7846EB3, 0x78A88AE, 0x790A1AA, 0x7526EFB
.long 0x75787F7, 0x75DA1F3, 0x763BAEE, 0x76F5DE6, 0x77577E1, 0x77B90DD, 0x781AAD9, 0x78D4CD0
.long 0x79366CC, 0x79F99C4, 0x7E10094, 0x7CF44A1, 0x7DB7799, 0x7E71A90, 0x7ED338C, 0x7FF8080
.long 0x7328080, 0x7DC8080, 0x7C88080, 0x7508080, 0x775CD2C, 0x76944EA, 0x7808080, 0x71A61FF
.long 0x7244D40, 0x7242C15, 0xFFF8080, 0xF338080, 0xF668080, 0xF998080, 0xFCC8080, 0xF008080
.long 0xF4C54FF, 0xFAB3BBB, 0xFE10094, 0xFE4556E, 0xF952B15, 0xFDA7751, 0xFB2AB00, 0xFC2A262
.long 0xF1DFF6B, 0xF68D4A2, 0xF69D4EA, 0xFBD88AE, 0xA922D17, 0xC6E4130, 0xE286963, 0x74C55FF
.long 0x768D536, 0x7FF8080, 0x7FF8080, 0x7FF8080, 0x2922D17, 0x46E4130, 0x6286963, 0x8080
Опять же, благодаря моей работе с Blackmagic Design я сразу распознал пиксели YUV (например, все значения с цифрами 8080).
Я не дурак, чтобы опять рисовать весь пользовательский интерфейс вручную на бумаге, так что да, я написал ещё один инструмент — M240UITool.
Leica M (typ 240) UI Tool v1.0
Usage: ./M240UITool [-a address] [-i imagebase] [-s script] [-d dump] [-f folder] [-l LUT] [-rbv] FIRMWARE.BIN
This tool will help you to find UI resources in firmware.
Use following arguments:
-a Specify address of the gui_CopyImageDesc function (ex. 0x2F95E0)
-i Specify firmware imagebase
-s Specify IDC file name
-c Specify container file name
-d Specify dump image format
png - PNG format
bmp - BMP (ARGB) format
-f Specify folder for dumped images
-l Specify LUT for images (filename of address)
-b Specify number of bytes to display in verbose mode
-r Try to recover string characters
-v Be verbose
Кроме сброса всех ресурсов изображений из файла микропрограммы в BMP/PNG, этот инструмент умеет создавать IDC-скрипты в IDA для определения всех ресурсов UI.
Мы уже знаем, что из функции, которую создаёт одну страницу UI, несколько раз вызывается gui_CopyImageDesc
. Я подумал, что будет здорово сделать браузер ресурсов UI и определить все функции рендеринга страниц. Для этого предназначена опция -c
— она сооздаёт специальный контейнер для просмотра ресурсов.
И кто сказал, что браузер ресурсов пользовательского интерфейса не может выглядеть необычно?
Будучи интерактивным (полупрозрачные кнопки на скриншоте), этот инструмент позволяет не только прокручивать страницы меню EVF/LCD, но просматривать этапы рендеринга в пределах одной страницы.
К сожалению, исходники этого шедевра где-то потерялись, но заголовочные файлы всё ещё в коде M240UITool, поэтому технически можно воссоздать его с нуля.
Меню отладки
Какую строку мы ищем в первую очередь при реверс-инжиниринге? По-моему, это слово debug
и производные от него.
В прошивке было много интересных строк, но эти особенные:
$ strings ./IMG_LOKI-212_1.1.0.2.bin | grep "Debug Mode"
GUI: State: %d! Scanning for Debug Mode successful
GUI: Scanning for Debug Mode: State: %d, Ignore long DEL
GUI: Scanning for Debug Mode: State: %d
GUI: Scanning for Debug Mode: State: %d, Ignore long DEL
GUI: Scanning for Debug Mode: State: %d
GUI: Scanning for Debug Mode: State: %d, Ignore long DEL
GUI: Scanning for Debug Mode: State: %d
GUI: Scanning for Debug Mode: State: %d, Ignore long DEL
GUI: Scanning for Debug Mode: State: %d
GUI: Scanning for Debug Mode: State: %d, Ignore long DEL
GUI: Scanning for Debug Mode: State: %d
...
GUI: ScanningForDebugWithKeyAndJoyStick(): g_GUI_CheckForDebugWithKeyAndJoyStick = %d
Похоже, что в режим отладки можно войти с помощью какой-то комбинации клавиш. Все эти строки вызываются из одной гигантской функции ScanningForDebugWithKeyAndJoyStick
, которая реализует машину состояний сканирования кнопок. Вот как это выглядит в IDA:
Не буду врать, потребовалось некоторое время, чтобы понять, как в прошивке обрабатываются аппаратные кнопки, а затем восстановить перечисляемые типы для кнопок и джойстика. Но когда я получил комбинацию, то с огорчением выяснил, что она ничего не делает. Вероятно, она работает только с какой-то конкретной страницы GUI. Еще пара вечеров ручной трассировки машины состояний GUI — и проблема решена, а также удалось найти страницу меню Reset.
Наконец, добро пожаловать в режим отладки.
Я много думал, огласить ли эту комбинацию, но решил воздержаться. Я уважаю тяжёлую работу, которую делает Leica, выпуская свои уникальные аппараты, и не хочу отвечать за то, что их сервисные центры заполнят сломанные тушки камер в результате некоторого бездумного любопытства.
Но всё-таки предоставлю некоторые перечисляемые типы, чтобы упростить обратную разработку для тех, кто готов идти по этому пути.
enum ControlActionType {
kControlAction_Idle, // 0
kControlAction_Push, // 1
kControlAction_Release, // 2
kControlAction_LongPush // 3
};
enum ControlBtnType {
kControlBtn_LV, // 0
kControlBtn_PLAY, // 1
kControlBtn_DEL, // 2
kControlBtn_ISO, // 3
kControlBtn_MENU, // 4
kControlBtn_SET // 5
};
enum ControlJoystickType {
kControlJoy_INFO, // 0
kControlJoy_Up, // 1
kControlJoy_Down, // 2
kControlJoy_Left, // 3
kControlJoy_Right // 4
};
PTP
Думая над задачей USB, я определил три режима (что также подтверждается в меню отладки):
- PTP
- MSC (Mass Storage Class)
- Leica Custom
PTP наиболее интересен, потому что он хорошо документирован и позволяет управлять камерой.
В прошивке довольно легко найти обработчики PTP, потому что из этого кода много вызовов. Все вызовы PTP делятся на три группы: Legacy, Leica Extended (LE) и Production.
Отладочные сообщения помогли установить названия почти для всего кода.
Legacy: Leica Extented: Production: 0x1001 - GetDeviceInfo 0x9001 - Set Camera Settings 0x9100 - Open Production Session 0x1002 - OpenSession 0x9002 - Get Camera Settings 0x9101 - Close Production Session 0x1003 - CloseSession 0x9003 - Get Lens Parameter 0x9102 - UpdateFirmware 0x1004 - Get Storage ID 0x9004 - Release Stage 0x9103 - Open OSD Session 0x1005 - Get Storage Info 0x9005 - Open LE Session 0x9104 - Close OSD Session 0x1006 - GetNumObjects 0x9006 - Close LE Session 0x9105 - Get OSD Data 0x1007 - GetObjectHandles 0x9007 - RequestObjectTransferReady 0x9106 - GetFirmwareStruct 0x1008 - GetObjectInfo 0x9008 - GetGeoTackingData 0x910B - GetDebugMenu 0x1009 - GetObject 0x900A - Open Debug Session 0x910C - SetDebugMenu 0x100A - Get Thumb 0x900B - Close Debug Session 0x910D - ODIN Message 0x100B - Delete Object 0x900C - Get Debug Buffer 0x910E - GetDebugObjectHandles 0x100E - Initiate Capture 0x900D - Debug Command String 0x910F - GetDebugObject 0x1014 - GetDevicePropDesc 0x900E - Get Debug Route 0x9110 - DeleteDebugObject 0x1015 - GetDevicePropV 0x900F - SetIPTCData 0x9111 - GetDebugObjectInfo 0x101C - Initiate Open Capture 0x9010 - GetIPTCData 0x9112 - WriteDebugObject 0x9020 - Get3DAxisData 0x9113 - CreateDebugObject 0x9030 - OpenLiveViewSession 0x9114 - Calibrate 3Daxis 0x9031 - CloseLiveViewSession 0x9115 - Magnetic calibration 0x9033 - Unknown 0x9116 - Get Viewfinder Data
Сама реализация интерфейса PTP кажется стандартной, однако у некоторых команд есть ограничения, которые я намеренно опускаю здесь.
В любом случае, всё вышесказанное довольно увлекательно. Вы можете подумать: «Давайте просто подключим камеру по USB и начнём зондировать с помощью libptp». Всё верно.
Только блин…
У Leica M240 нет USB-порта.
Порт для рукоятки
Leica предлагает немного аксессуаров для этой камеры, но есть один особенно интересный. Речь идет о многофункциональной рукоятке Leica M (14495). Он заменяет нижнее металлическую часть корпуса, обеспечивает встроенный GPS и несколько разъёмов вроде USB, терминал вспышки SCA, DIN/ISO-X и гнездо для питания.
И вы опять скажете: «Здорово, теперь просто купим её, прикрепим к камере, подключим камеру по USB и начнём зондировать с помощью libptp». Всё верно.
Только блин…
Она стоит почти 900 долларов.
Это почти девятьсот причин, чтобы создать собственный адаптер. Тем не менее, на всякий случай я настроил уведомления eBay для этого аксессуара.
Разъём
Разъём на камере выглядит следующим образом:
Я попытался найти его в интернете, но серьёзно, как бы вы описали его в Google?
Немного отчаявшись, я начал думать о некоторых сумасшедших вещах, вроде приклеить фольгу или иголки к резиновому ластику. Но однажды на работе в Blackmagic Design, глядя на печатную плату камеры, я заметил, что у одного разъёма очень знакомая форма. На следующий день я привёз свою Leica M240 на работу — и да, она выглядела похожей, просто намного длиннее с большим количеством контактных площадок.
Осталось спросить номер детали у нашего менеджера по компонентам, а затем найти её в каталоге Samtec: ERM8-013-05.0-L-DV-TR.
Мы также спросили у представителей Samtec, можно ли получить образец, и они добросердечно согласились.
Немного работы паяльником, картона и изоленты — и готов мой собственный штекер (образец 2013 года).
Спустя пять лет, в 2018 году, я решил лично попросить Samtec прислать другой образец. Мне хотелось сделать что-то получше.
Опять много работы паяльником, ругани, резки проволоки, ругани, снова работы паяльником, чтобы сделать новый, более симпатичный вариант:
Распиновка
В разъёме 26 контактов: по 13 с каждой стороны. Ещё до пайки своей детали я прозондировал разъём камеры мультиметром и логическим анализатором. Кстати, на датчик нижней крышки нужно поставить магнит, чтобы камера считала, что крышка на месте.
Земля (камера выключена, нет батареи)
Я всегда начинаю с земли, потому что это безопасно и её очень легко найти.
Таким образом, у нас восемь линий заземления (тёмно-серый).
Потенциал (камера включена)
Когда камера включена, можно измерить потенциал на каждом контакте и получить представление о логике и уровнях мощности.
alexhude.github.io/assets/2019/2019-01-24-hacking-leica-m240/probe2_potential.png
Показатели на контактах 8-9 и 11-13 слишком высоки для логических контактов, поэтому я определил их как питание (красный).
Сопротивление (камера выключена, нет батареи)
Полезно ещё измерить сопротивление. В некоторых случаях это помогает идентифицировать входы и сгруппировать некоторые линии.
Связанные выходы (камера выключена, нет батареи)
Затем я решил проверить все внешние контактные площадки на корпусе камеры, чтобы проверить, связаны ли они с сервисным портом.
Синхроконтакт вспышки оказался напрямую подключен к линии 10.
Логический анализатор (камера включена)
Данные для каждой строки записывались в следующей последовательности: включаем, камера должна быть в режиме LV, делаем снимок, начинаем видеозапись.
Две линии показывают передачу каких-то данных: 01 и 21.
01 — 115200, передача по 8 бит, 1 стоп-бит, бит чётности, LSB первый.
Каждые 500 мс, он отправляет какой-то счётчик C3 3C 02 81 00 01 00 82, C3 3C 02 81 01 01 00 83, C3 3C 02 81 02 01 00 80
…
21 — 115200, передача по 8 бит, 1 стоп-бит, нет бита проверки чётности, LSB первый.
Он отправляет лог загрузчика SH7216 (“Leica Camera AG” на скриншоте выше).
Давайте пометим их тёмно-синим. Довольно грустно, что лог Maestro не отдаётся наружу даже с максимальными настройками отладки в меню Debug.
На этих контактах сопротивление около 310kOhm.
Не знаю почему, но я предположил, что другие линии данных могут иметь аналогичное сопротивление или будут закрыты. Поэтому я определил линии ~300kOhm, ~200kOhm и ~100кОм тоже как линии данных (оттенки синего цвета на рисунке).
В целом нарисовалась следующая картина.
12 кандидатов на линии данных. Но как их проверить? После небольшой беседы со специалистами по железу об электрической защите интегральных схем я начал тыкать в контакты через резистор 4kOhm, уменьшающий ток до уровня, который не должен сжечь входы.
UART
Я сделал ещё одно предположение, что линия RX должна быть рядом с ТХ. Линии 02, 03 и 20 выглядят как хорошие кандидаты, потому что у обеих напряжение 3,3 В как у TX.
Изначально я пытался изучить эти линии с помощью Bus Pirate. К сожалению, результат оказался довольно грязным. Затем я взял кабели на основе SiLabs как более надёжные и ни с чем не конфликтующие на macOS.
Сначала я подключил кабель TX к контакту 20 и начал набирать help
после загрузчика. Как и ожидалось, после небольшой задержки камера повторила символы.
Контакты 02 и 03 были следующими кандидатами на UART. К сожалению, не оказалось никаких признаков, что эти линии прослушиваются.
На диаграмме известные UART обозначены более тёмным оттенком зеленого.
USB
Всё началось с разреза USB-кабеля пополам с хедером посередине и резисторами на 4kOhm для зондирования. Целостность сигнала дифференциальной пары? Неа, тогда меня это не очень волновало. :)
Затем я дома прозондировал несколько бытовых устройств с USB, чтобы получить представление, как выглядят коммуникации по этому порту.
Камера Canon
Карманная видеокамера Blackmagic
Видеокамера Canon
Видеокамера JVC
Брелок
Камера KidiZoom
Они все немного разные, но начальное состояние D- D+ низкое. Что ж, будем знать, а теперь проверим, что у нас есть:
- 22 — маловероятно, потому что D- D+ являются дифференциальной парой и должны быть довольно близки;
- 04/05 — маловероятно, потому что у них разное сопротивление;
- 14/15 — маловероятно, потому что у них разное сопротивление;
- 15/16 — возможно, потому что они близки и имеют похожее сопротивление.
Поэтому я подсоединил USB D- D+ к пинам 15/16 и подключил к iMac…
На экране USB PTP, но камера не появлялась на хосте. Я пытался настроить разные варианты на макете электронной схемы, но ничего не работало. Beagle показывал много повреждённых пакетов и других ошибок. В конце концов я сдался и вернулся к обратной разработке прошивки.
Это окончательное распиновка, USB обозначен тёмно-зелёным цветом.
Кто бы мог подумать, что несколько лет спустя мне придёт то самое уведомление eBay и я довольно дёшево куплю желанный аксессуар.
Наконец, я могу проверить свои предположения о PTP. Но сначала было очень любопытно, как внутри гаджета выглядит USB PHY.
Внутри оказался хаб SMSC 2512b прямо на дороге от разъёма рукоятки к коннектору Mini USB. Чип работает в дефолтном режиме, потому что нет никакого EEPROM или контактов SCL/SDA. Первый нисходящий порт маршрутизируется в гнездо корпуса камеры, но второй ни к чему не подключен.
Вероятно, я что-то упустил, но для меня такое решение не имеет большого смысла. В техническом паспорте написано, что у чипа «полностью интегрированные USB-выводы, а также есть резисторы для повышения и понижения напряжения». Возможно, инженеры Leica решили не внедрять собственный USB PHY, а использовали тот что в хабе, который хорошо протестирован и работает из коробки. На самом деле, не могу их винить, потому что раньше я пытался сделать подобное, и это оказалось непростой задачей. Может, это фича для защиты от подделки, кто знает.
В любом случае, если вы разбираетесь в USB PHY и готовы помочь, не стесняйтесь написать мне: должна же быть возможность работать через USB-порт без этого фирменного аксессуара :)
Снова PTP
Как я уже сказал, пришло время поиграться с расширением Leica PTP.
К счастью, я нашёл довольно классную библиотеку C++ вместо libptp — это libEasyPTP. Также не заняло много времени написать инструмент на основе этой библиотеки: я уже знал некоторые ограничения в интерфейсе Leica PTP.
И хотя M240PTPTool порядочно глючит, он вполне подходит на роль доказательства концепции (код программы).
По PTP идёт только два запроса: GetDebugBuffer (0x900C) и DebugCommandString (0x900D). Кстати, чтобы модули заполняли журнал отладки, необходимо в меню установить Debug Level как “Debug” или “Debug RAW”.
В интерфейсе M240PTPTool есть несколько опций:
- exit — закрыть инструмент;
- flush — слить отладочный буфер с камеры:
M240> flush
I:[00:11:468]|01| DATE/TIME CORRECTED by 5921 sec
D:[00:12:079]|00| Send message from TID 0 to TID 1 over MBX 3 - length: 4 - MesgID: 0x22020103
D:[00:12:179]|00| Send message from TID 0 to TID 1 over MBX 3 - length: 4 - MesgID: 0x22020103
D:[00:12:282]|11| Message received from TID 0 for TID 1 over MBX 3
D:[00:12:283]|11| Message received from TID 0 for TID 1 over MBX 3
D:[00:12:301]|00| Send message from TID 0 to TID 1 over MBX 3 - length: 4 - MesgID: 0x22020103
D:[00:12:402]|00| Send message from TID 0 to TID 1 over MBX 3 - length: 4 - MesgID: 0x22020103
D:[00:12:502]|00| Send message from TID 0 to TID 1 over MBX 3 - length: 4 - MesgID: 0x22020103
...
Любой другой текст отправляется к камеру как отладочная команда. Например, help
выводит все возможные команды с аргументами:
M240> help
********* debug command description ********
exposure request
Description: requests a release from Sub CPU
Parameter 1: Exposure Time TV
still request
Description: simulates the -still request- command flow of Sub CPU
Parameter: no
...
send Message;[Parameter1];[Parameter2];[Parameter2];...;...
Description: Sending Message to Task
Parameter 1: Receiver Task ID
Parameter 2: Command ID
Parameter 3: Command Data[0] (32 Bit)
Parameter 4: Command Data[1] (32 Bit)
Parameter 5: .
Parameter 6: .
use maximum 10 Parameter
...
Полный список довольно большой, но смотрите, можно отправлять прямые сообщения Softune для любой задачи! Что бы такого интересного туда отправить…
Ещё одна популярная строка, которую часто ищут в прошивке — dump
. Посмотрим, есть ли у нас такая.
$ strings IMG_LOKI-212_1.1.0.2.bin | rg -i dump
GUI: HEX DUMP: Address: %x, Length: %d
HSK: DBG_WRITE_ROM_DUMP_SD: File was properly opened, but it seems to be empty.
ROM_DUMP
HSK: DBG_WRITE_ROM_DUMP_SD: Flushing Dump to ROM. Size %d
SD:ROM_DUMP.bin
HSK: DBG_WRITE_ROM_DUMP_SD Command received!
ROM_DUMP.bin
HSK: DUMP failed, no cards inserted!
HSK: DUMP FlashROM to SD card.
HSK: DUMP FlashROM to CF card.
Dumping files to card
Видимо, можно сделать дамп прошивки на SD-карту. По ссылке на строку “Dumping files to card” легко найти код, отвечающий за это. Он расположен в гигантском блоке System Task (pid 11, как мы уже знаем) и вызывается сообщением 0x11080006
без аргументов.
Наберите send Message;11;0x11080006
в M240PTPTool, нажмите Enter и смотрите на экран.
Затем извлеките SD-карту и проверьте, что на ней.
Вот он, полный дамп, включая прошивку.
Это открывает бесконечные возможности. Например, можно сделать крошечное устройство с MCU, поддержкой USB-хоста и кнопками для запуска сложных последовательностей сообщений…
А потом у нас родился второй ребенок. :)
Эпилог
Если вы не хотите ломать устройство, обычно есть способ его изучить, не открывая корпус или припаивая провода к печатной плате. Ниже мои советы, если вам интересно:
- найдите всю публичную информацию об устройстве: технические характеристики, данные о комплектующих, фотографии внутренностей, видео с завода ;)
- если у вас есть прошивка, покопайтесь в ней и поищите подсказки о внешних выходах;
- всегда гуглите разные магические и странные последовательности байтов, которые найдёте в файлах прошивки;
- измерьте GND/потенциал/сопротивление для всех неизвестных контактов, выставленных наружу;
- прозондируйте эти контакты анализатором логики;
- всегда помните о мерах безопасности, связанных с электроникой;
- попробуйте исключить контакты, которые вам не нужны (земля, питание);
- если не можете распознать сигнал по аналоговому виду, попробуйте поискать в Google наиболее популярные (USB/UART/SPI/I2C/1Wire);
- если возникли идеи о природе сигнала, попробуйте проверить на похожей бытовой электронике;
- подумайте
трипять раз, прежде чем отправлять данные на устройство; - и конечно, не стесняйтесь спрашивать совета.
Удачных вам взломов!
Автор: m1rko