Главная » Хабрахабр » [Перевод] Как взломать дорогую камеру, чтобы вас не убила жена

[Перевод] Как взломать дорогую камеру, чтобы вас не убила жена

Дисклеймер: исследование началось в 2013 году, так что если считаете некоторые методы глупыми и опасными — вы правы, так и было. Тем не менее, я многому научился в процессе.

Мы с женой всегда хотели купить крутой фотоаппарат Leica и вдруг поняли, что если не купим его сейчас, то уже долго не сможем это сделать. Вступление
Всё началось за несколько месяцев до рождения моего первого ребенка. Вскоре мне надоело ждать, и я начал изучать их сайт. Поэтому мы заказали камеру M240 и… бум, нас поставили в очередь на полгода. Ну, вы можете догадаться, почему… Прошивки! Моё внимание сразу привлёк раздел с файлами.

Узнаёте? Я увидел незашифрованный и несжатый файл (m8-2_005.upd), который начинается с магии PWAD. Кажется, ребята любят классику. Да, всё правильно, это формат Doom Patch WAD. Формат очень хорошо документирован, так что распарсить его оказалось совсем несложно.

Прошивка Leica M8

На самом деле это очень смешно, потому что когда позже я изучал сжатый файл прошивки Leica T — то первым делом решил проверить методы сжатия, которые в прошлом использовала id Software.

Но распространённые декомпрессоры LZW не подошли, поэтому я начал искать конкретную реализацию программного обеспечения id — и вуаля, нашёл в исходниках Catacomb Armageddon. Википедия говорит, что они использовали формат LHA, который по сути представляет собой LZW. Должен признать, повезло.

Вот структура прошивки: В любом случае, вернёмся к 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) - карта прошивки основного процессора с символами 😀 bf1.map: 0x000402A8 ( 26304:0x000066C0) - карта прошивки подпроцессора с символами 😀
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.

  1. 9D 70 C4
  2. 9D 00
  3. 9D XX YY
  4. 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

в итоге превратился в:

1. 1E1CAF2E — выглядит как "LEICA FILE"
01010002 - 1. 2
005C1AB1 — размер сжатого файла (big endian)
01291A7E — размер несжатого файла (big endian)
AE3873659C3D75B4342F446E13178E6B — хэш MD5
00000001 — количество полезных нагрузок
00000030 — смещение первой полезной нагрузки
0.

По мере понимания структур прошивки я улучшал свой инструмент, и в конечном итоге он выдавал такое:

Running with options:
+ firmware folder: M240_FIRMWARE
+ verbose enabled

FW
File size: 6036193 | 0x005C1AE1 Open firmware file: FW_M240_1_1_0_2.

1. Parse container header:
version: 1. 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 0.

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.

Единственная общая часть — прошивка для чипа управления системой. Это реально большой бинарник, и по коду нетрудно догадаться, для чего он предназначен.

$ 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

IDA из коробки поддерживает этот тип процессора. Таким образом, перед нами процессор Renesas SH7216 (SH-2A), который отвечает за раннюю стадию загрузки, тесты ввода-вывода и обновление прошивки. Оставалось лишь найти правильный адрес базовой загрузки, известный по описанию разделов прошивки: это 0x0.

Section Name: "[A]CTRL_SYS-11"
Section offset: 14680064 | 0x00E00000
Section size: 917277 | 0x000DFF1D
Section base: 0 | 0x00000000

Я загрузил его в IDA и распознал все функции, но не особенно в нём не копался, потому что намного интереснее прошивка основного процессора.

Мы вернёмся к этому позже. Здесь можно ещё отметить, что UART этого чипа открывается на сервисный порт, куда выдаёт журнал загрузки.

Чтобы начать реверс-инжиниринг данной прошивки, необходимо сначала ответить на несколько вопросов:

  1. какой тип процессора
  2. каков адрес базовой загрузки
  3. на какой ОС он основан, если таковая имеется

Благодаря нашему инструменту мы уже знаем адрес базовой загрузки: это 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
...

На самом деле, это намного лучше, чем Blackfin, потому что IDA из коробки поддерживает FR. Таким образом, мы имеем дело с кастомным процессором Fujitsu FR (Leica называет его Maestro) и операционкой Softune REALOS.

Модуль процессора 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

Из вики:

Работает на ядре реального времени REALOS µITRON. Softune — это интегрированная среда разработки Fujitsu для семейств процессоров Fujitsu FR, FR-V и F2MC. Nikon EXPEED) и некоторых камерах Pentax с байонетом K. Например, используется в цифровых зеркальных камерах Nikon (см.

Так что это довольно популярная приличная RTOS с задачами, семафорами и другими вкусностями. Мне было интересно, можно ли распознать в прошивке Leica некоторые стандартные функции библиотеки.

Должен назвать первую часть исследования большой напрасной тратой времени, и вот почему.

Как и ожидалось, IDE включала в себя библиотеки. Softune IDE оказалось очень трудно найти, но в конце концов мне удалось кое-что достать. Там было четыре двоичных файла:

  • lib911.lib
  • lib911e.lib
  • lib911if.lib
  • lib911p.lib

Не знаю почему, может по инерции, как я взламывал всё связанное с Leica, я опять начал реверс-инжиниринг формата. Да, очень хорошо документированного формата Object Module Format. И да, конечно, я написал для этого специальный инструмент:

0
Usage: FRLibTool [-s start] [-i imagebase] [-o output] [-f index] [-dv] FIRMWARE. Fujitsu RISC Library Tool v1. LIB BIN LIBRARY.

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 аналогичная компоновка.

В итоге составил такой список:

Я говорю о прерывании int_uart_in. Многие прерывания вполне ожидаемы, такие как AUDIO/SDIO/VIDEO/JPEG/RAW, но попробуйте определить самое загадочное из них? Похоже, что камера поддерживает какой-то консольный режим UART CLI.

Системные вызовы

Как почти любая ОС, Softline REALOS использует системные вызовы. В ассемблере они выглядят так:

Начнём с поиска обработчика прерывания INT #0x40. Фактический адрес обработчика системных вызовов вычисляется следующим образом. В соответствии с описанием выше это

(0x3FC - 4 × inum) = (0x3FC - 4 × 0x40) = 0x2FC = int_realos_syscall

Конкретная запись в этой таблице вычисляется по формуле syscall_table_bottom + (num * 2): В обработчике легко найти ссылку на нижнюю часть таблицы системных вызовов с 16-битными словами.

[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 задач. Было вопросом времени заглянуть в каждую основную функцию, определить имя и цель задачи (кроме последней). Вот полный список:

  1. SubCPU
    Эта задача, по-видимому, отвечает за операции захвата, такие как экспозиция, визирование по экрану и т. д.
  2. KeyManager
    Скорее всего, эта задача связана с аппаратными кнопками.
  3. GuiManager
    Довольно большая задача, в которой реализована машина состояний UI и отрисовка интерфейса.
  4. DebugManager
    Да, есть кое-что для отладки. Ням-ням.
  5. FileManager
    Эта задача — всё о файловых операциях.
  6. FamManager
    Я бы сказал, что задача отвечает за файлы и память, потому что она зависит от задач файлового менеджера и менеджера памяти.
  7. MemoryManager
    Никаких сюрпризов: операции с памятью, управление пулом и т. д.
  8. ImageManager
    Эта задача управляет процессами кодирования/декодирования и другими процессами обработки изображений.
  9. UsbManager
    Текущая задача — обработка коммуникаций по USB, которая включает в себя MassStorage, PTP и собственный протокол Leica.
  10. IOManager
    Похоже, эта задача управляет устройствами хранения данных, такими как SD и CF-карты (что? какие ещё CF? может, это из модели 213).
  11. Systemmanager
    Разные задачи вроде общих системных операций, управления питанием и т. д.
  12. SettingsManager
    Обрабатывает состояние камеры и настройки.
  13. MonitorManager
    Отслеживает изменения состояния камеры и информирует другие задачи.
  14. PeripheralManager
    Эта задача контролирует GPS, яркость и некоторые другие сенсоры.
  15. Неизвестно
    К сожалению, для неё я не нашел ничего существенного.

Интересно отметить, что после основного массива есть ещё один выдающийся дескриптор.

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.

Но они где-то должны быть и, скорее всего, встроены в IMG-LOKI-212. Что меня удивило, так это полное отсутствие ресурсов GUI.

Не только в коде, но и в разделе данных. Один из моих обычных подходов к обратной разработке прошивки — восстановить все возможные перекрёстные ссылки. Затем я просматриваю их, пытаясь найти некоторые шаблоны или ссылки на известные части кода.

Там много похожих последовательностей данных с адресами на другие последовательности данных, которые обращаются дальше и т. д. Прошивка 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

Логично предположить, что 0x0008 и 0x0014 — ширина и высота в представлении с прямым порядком байтов (big endian). Первые шесть байт выглядят как маленький заголовок, за которым следует какой-то повторяющийся шаблон, где каждый второй байт является либо 0xFF, либо 0x04. Скорее всего, это следующий ресурс (что подтверждается ссылкой на него). В конце этого дампа мы видим начало другой последовательности 00 06 00 14 00 01. Но размер изображения должен быть 0x8 * 0x14 = 0xA0 = 160. Таким образом, размер фактических данных изображения составляет 146 байт. Тогда что? Ясно, что в данных не чисто пиксели и даже не 8-битная LUT, потому что она на 14 байт меньше. Вероятно, какое-то сжатие.

GUI у Leica не очень красочный, так что по моему опыту здесь лучше всего использовать таблицу LUT. Глядя на этот шестнадцатеричный дамп, трудно поверить, что используется какая-то сложная схема. Обычно компрессор пытается избавиться от дублирования данных, заменяя их ссылкой. В этом случае ресурсы UI будут полностью повторять индексы LUT вроде 03 03 03 или А1 А1 А1. Простая команда записать data (значение) number раз. Эти массивы индексов идеальны для сжатия даже простым методом вроде RLE [data][number].

С учётом всего этого я предположил, что перед нами, скорее всего, простое изображение с двумя цветами LUT (0xFF и 0x04), а байт перед цветом — количество пикселей для рисования.

Но нет, я взял ручку и бумагу и начал заполнять клетки. «А потом ты написал ещё один инструмент», — подумаете вы. Забавно, что у меня до сих пор сохранился тот рисунок.

Третье слово 0x0001 указывает, является ли изображение символом ASCII, так что окончательная структура ImageAsset выглядит следующим образом: Где-то по пути я понял, что для этого изображения недостаточно 160 пикселей, так что 0x8 и 0x14 нужно умножить на два.

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.

0
Usage: ./M240UITool [-a address] [-i imagebase] [-s script] [-d dump] [-f folder] [-l LUT] [-rbv] FIRMWARE. Leica M (typ 240) UI Tool v1. BIN

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 This tool will help you to find UI resources in firmware.
Use following arguments:
-a Specify address of the gui_CopyImageDesc function (ex.

Кроме сброса всех ресурсов изображений из файла микропрограммы в BMP/PNG, этот инструмент умеет создавать IDC-скрипты в IDA для определения всех ресурсов UI.

Я подумал, что будет здорово сделать браузер ресурсов UI и определить все функции рендеринга страниц. Мы уже знаем, что из функции, которую создаёт одну страницу UI, несколько раз вызывается gui_CopyImageDesc. Для этого предназначена опция -c — она сооздаёт специальный контейнер для просмотра ресурсов.

И кто сказал, что браузер ресурсов пользовательского интерфейса не может выглядеть необычно?

Будучи интерактивным (полупрозрачные кнопки на скриншоте), этот инструмент позволяет не только прокручивать страницы меню EVF/LCD, но просматривать этапы рендеринга в пределах одной страницы.

К сожалению, исходники этого шедевра где-то потерялись, но заголовочные файлы всё ещё в коде M240UITool, поэтому технически можно воссоздать его с нуля.

Меню отладки

Какую строку мы ищем в первую очередь при реверс-инжиниринге? По-моему, это слово debug и производные от него.

В прошивке было много интересных строк, но эти особенные:

1. $ strings ./IMG_LOKI-212_1. 2.bin | grep "Debug Mode"
GUI: State: %d! 0. 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 — и проблема решена, а также удалось найти страницу меню Reset. Вероятно, она работает только с какой-то конкретной страницы GUI.

Наконец, добро пожаловать в режим отладки.

Я уважаю тяжёлую работу, которую делает 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 делятся на три группы: Legacy, Leica Extended (LE) и Production. В прошивке довольно легко найти обработчики PTP, потому что из этого кода много вызовов.

Отладочные сообщения помогли установить названия почти для всего кода.

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 на работу — и да, она выглядела похожей, просто намного длиннее с большим количеством контактных площадок.

0-L-DV-TR. Осталось спросить номер детали у нашего менеджера по компонентам, а затем найти её в каталоге Samtec: ERM8-013-05.

Мы также спросили у представителей Samtec, можно ли получить образец, и они добросердечно согласились.

Немного работы паяльником, картона и изоленты — и готов мой собственный штекер (образец 2013 года).

Мне хотелось сделать что-то получше. Спустя пять лет, в 2018 году, я решил лично попросить Samtec прислать другой образец.

00-TTR-TTR-1-D
ERCD-013-05.

Опять много работы паяльником, ругани, резки проволоки, ругани, снова работы паяльником, чтобы сделать новый, более симпатичный вариант:

Распиновка

В разъёме 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 и я довольно дёшево куплю желанный аксессуар.

Но сначала было очень любопытно, как внутри гаджета выглядит USB PHY. Наконец, я могу проверить свои предположения о PTP.

Чип работает в дефолтном режиме, потому что нет никакого EEPROM или контактов SCL/SDA. Внутри оказался хаб SMSC 2512b прямо на дороге от разъёма рукоятки к коннектору Mini USB. Первый нисходящий порт маршрутизируется в гнездо корпуса камеры, но второй ни к чему не подключен.

В техническом паспорте написано, что у чипа «полностью интегрированные USB-выводы, а также есть резисторы для повышения и понижения напряжения». Вероятно, я что-то упустил, но для меня такое решение не имеет большого смысла. На самом деле, не могу их винить, потому что раньше я пытался сделать подобное, и это оказалось непростой задачей. Возможно, инженеры Leica решили не внедрять собственный USB PHY, а использовали тот что в хабе, который хорошо протестирован и работает из коробки. Может, это фича для защиты от подделки, кто знает.

В любом случае, если вы разбираетесь в USB PHY и готовы помочь, не стесняйтесь написать мне: должна же быть возможность работать через USB-порт без этого фирменного аксессуара 🙂

Снова PTP

Как я уже сказал, пришло время поиграться с расширением Leica PTP.

Также не заняло много времени написать инструмент на основе этой библиотеки: я уже знал некоторые ограничения в интерфейсе Leica PTP. К счастью, я нашёл довольно классную библиотеку C++ вместо libptp — это libEasyPTP.

И хотя M240PTPTool порядочно глючит, он вполне подходит на роль доказательства концепции (код программы).

Кстати, чтобы модули заполняли журнал отладки, необходимо в меню установить Debug Level как “Debug” или “Debug RAW”. По PTP идёт только два запроса: GetDebugBuffer (0x900C) и DebugCommandString (0x900D).

В интерфейсе 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.

1. $ strings IMG_LOKI-212_1. 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. 0. 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

По ссылке на строку “Dumping files to card” легко найти код, отвечающий за это. Видимо, можно сделать дамп прошивки на SD-карту. Он расположен в гигантском блоке System Task (pid 11, как мы уже знаем) и вызывается сообщением 0x11080006 без аргументов.

Наберите send Message;11;0x11080006 в M240PTPTool, нажмите Enter и смотрите на экран.

Затем извлеките SD-карту и проверьте, что на ней.

Вот он, полный дамп, включая прошивку.

Например, можно сделать крошечное устройство с MCU, поддержкой USB-хоста и кнопками для запуска сложных последовательностей сообщений… Это открывает бесконечные возможности.

🙂 А потом у нас родился второй ребенок.

Если вы не хотите ломать устройство, обычно есть способ его изучить, не открывая корпус или припаивая провода к печатной плате. Ниже мои советы, если вам интересно:

  • найдите всю публичную информацию об устройстве: технические характеристики, данные о комплектующих, фотографии внутренностей, видео с завода 😉
  • если у вас есть прошивка, покопайтесь в ней и поищите подсказки о внешних выходах;
  • всегда гуглите разные магические и странные последовательности байтов, которые найдёте в файлах прошивки;
  • измерьте GND/потенциал/сопротивление для всех неизвестных контактов, выставленных наружу;
  • прозондируйте эти контакты анализатором логики;
  • всегда помните о мерах безопасности, связанных с электроникой;
  • попробуйте исключить контакты, которые вам не нужны (земля, питание);
  • если не можете распознать сигнал по аналоговому виду, попробуйте поискать в Google наиболее популярные (USB/UART/SPI/I2C/1Wire);
  • если возникли идеи о природе сигнала, попробуйте проверить на похожей бытовой электронике;
  • подумайте три пять раз, прежде чем отправлять данные на устройство;
  • и конечно, не стесняйтесь спрашивать совета.

github.com/alexhude

Удачных вам взломов!


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Магнитная лента в 21 веке — как её используют

Магнитная лента не исчезла насовсем и до сих пор находит применение в дата-центрах. Фото — Don DeBold — CC BY Высокая ёмкость Это — одно из главных преимуществ магнитных лент. Когда в середине девяностых на прилавках магазинов появились коммерческие винчестеры ...

Обман автоматизированных камер наблюдения

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