суббота, 10 июня 2006 г.

Joyemu v3. Пишим сложный патч для телефона на C. Информация должна быть свободной.

Еще одной записью в TODO сегодня стало меньше, после новой версии патча, выпустить которую у меня наконец-то дошли руки. Что изменилось:
1. Теперь при включении режима эмуляции джойстика светодиоды в верхней части телефона горят и горят до выхода из эмуляции. Благодаря этому всегда понятно в каком мы сейчас режиме.
2. Нажатие на софт кнопки ничего не эмулирует. Иногда их надо нажимать, не выходя из эмуляции.
3. Блокировка клавиатуры эмуляцию выключает.

Новая версия патча в формате vkp.

Вопреки сложившейся традиции с форума сайта http://allsiemens.com я выложу и исходники патча, и все функции прошивки Siemens CX65 sw50, которые мне удалось найти (более 400), расскажу как писать патчи на C. Я не придерживаюсь мнения, что каждый новичок должен найти все эти функции сам - этот подход мне кажется бесполезной тратой времени, которое человек мог бы направить на что-то полезное, а не изобретать велосипед. Я также не сторонник подхода, когда создается элитарный клуб, между людьми которого передается информация - мне это несколько противно. Информация должна быть свободной.

Тем не менее, эта информация не освобождает от необходимости знать Assembler ARM.

Пишем патч на C на примере Joyemu.
При написании сложнофункциональных патчей некоторая логика может быть написана на C.
Так, например, при эмуляции джойстика (я рассказывал в первом посте на эту тему) телефона мы имеем множество ветвлений в алгоритме, реализовать которые можно и на ассемблере, но проще и чище это сделать на C.

Первое, что делает joyemu - перехватывает нажатие на клавишу. Для этого мы ищем в прошивке тот участок кода, который обрабатывает это событие. Нашли, допустим, по адресу ORIGINAL_KEY_HANDLER. Теперь надо найти свободное место в прошивке под наш код, который будет обрабатывать это нажатие (ищем в прошивке длинную последовательность FF). Пускай наш обработчик должен быть по адресу OUR_KEY_HANDLER. Передать управление на наш обработчик при нажатии на клавишу можно двумя способами. Найти все места из которых происходит переход в ORIGINAL_KEY_HANDLER. И заменить на переходы на OUR_KEY_HANDLER. Так обычно никто никогда не делает, ибо мест таких может быть много. Мы же вставляем по адресу ORIGINAL_KEY_HANDLER инструкцию для загрузки в регистр PC адрес OUR_KEY_HANDLER. То, что мы затерли этими инструкциями запоминаем, оно нам пригодится, когда мы захотим вызвать оригинальный обработчик. А код, который находится за нашей инструкцией перехода на OUR_KEY_HANDLER, будет иметь адрес ORIGINAL_KEY_HANDLER_CHUNK.
Получается вот что:

ROM:A121D4BC ORIGINAL_KEY_HANDLER:
ROM:A121D4BC 04 F0 1F E5 LDR PC, =OUR_KEY_HANDLER
ROM:A121D4C0 10 F3 65 A1 DCD OUR_KEY_HANDLER
ROM:A121D4C4 ORIGINAL_KEY_HANDLER_CHUNK:


А так как в алгоритме эмуляции нам понадобится вызов оригинального обработчика нажатия, то для этого надо где-то разместить тот самый затертый код, после которого вставить переход к ORIGINAL_KEY_HANDLER_CHUNK. Разместим этот код сразу перед
OUR_KEY_HANDLER по адресу MOVED_ORIGINAL_KEY_HANDLER. Выглядит это вот так (сначала идут 8 оригинальных байт, потом 4 байта инструкции перехода, после 8 байт данных для инструкций):

ROM:A165F2FC MOVED_ORIGINAL_KEY_HANDLER:
ROM:A165F2FC 70 40 2D E9 STMFD SP!, {R4-R6,LR}
ROM:A165F300 04 10 9F E5 LDR R1, =off_A8663120
ROM:A165F304 04 F0 1F E5 LDR PC, =ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F308 C4 D4 21 A1 DCD ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F30C 20 31 66 A8 DCD off_A8663120
ROM:A165F310 OUR_KEY_HANDLER:


Сохраняем изменения как vkp patch. Для этого в интернете валяется IDC скрипт для IDA, а также инструкции.

Из всех адресов, дальше нам понадобятся два:
MOVED_ORIGINAL_KEY_HANDLER - это та точка входа, куда мы должны сделать переход, если хотим вызывать родной обработчик нажатия.
OUR_KEY_HANDLER - здесь будет наш обработчик.

Теперь осталось только наш обработчик написать. Работа с ассемблером на этом заканчивается, на сцену выходит C. Для компиляции я использовал пакет gcc-4.1-arm для Debian. Начну с описания опций необходимых для компиляции под телефон:
-c -- делает объектный файл.
-mcpu=arm926ej-s -- компилировать под указанный процессор. Именно эти процы стоят в Siemens x65.
-mthumb-interwork -- компилировать инструкции дальнего перехода с учетом бита отвечающего за ARM/THUMB режим работы процессора. Короче вместо B и BL будет BX и BLX.
-fomit-frame-pointer -- а нафик он нужен?
-fno-unit-at-a-time -- это чтобы откомпилированные функции в объектном файле были в том же порядке, в котором они были в исходниках.
-mlong-calls -- ну длинные переходы типа.
Прочие опции типа -Wall -Os или -O3 - это на усмотрение.

Допустим мы написали C файл. Скомпилировали его с вышеперечисленными опциями в объектный файл. Теперь его надо слинковать в исполняемый файл elf. Это маленькое извращение нужно для того, чтобы линкер привел все relocation'ы в норму и разместил все по тем адресам, которые мы ему скажем. Нет, конечно можно при компиляции указать -fPIC и получить оверхеадный код, но мы пойдем другим путем. Опции линкера:
-s -- удаляет всю символьную информацию. А нафик она?
-nostdlib -- Не прилинковывать никаких левых библиотек.
-Bstatic -- Эта опция говорит о том, что все релокейшены будут жестко забиты. Никаких динамически подгружаемых библиотек. Статическая компиляция.
-X -- Забыл уже зачем добавил. Но наверное надо. Пусть будет.
-entry 0 -- стартовый адрес. 0 - адрес первой функции первого объектного файла.
-R local.symbols -- Файл, откуда будут подгружаться определения локальных символов.
-R ../CX65sw50dev/firmware.symbols -- определение символов прошивки.
--section-start .text=A165F310 -- адрес, по которому размещать код. Как раз тот самый адрес OUR_KEY_HANDLER.
--section-start .data=A80C8FFC -- адрес, по которому размещать данные (переменные).

Среди опций мелькнули два файла. Первый файл local.symbols:
my_original_press_key = 0xA165F2FC;
-- тут сказано, что C'ная функция с именем 'my_original_press_key' расположена по адресу '0xA165F2FC', а это тот самый адрес MOVED_ORIGINAL_KEY_HANDLER. Тоесть, чтобы вызвать из C оригинальный обработчик, я должен воспользоваться функцией void my_original_press_key (int keycode);

Файл ../CX65sw50dev/firmware.symbols, аналогичен, сюда я внес немного функций и переменных, которые всегда есть в прошивке sw50 у cx65, например кусок:

[...]
switch_phone_off = 0xA1240EF0;
dynamic_light_mode = 0xA863E6E0;

set_dynamic_light_mode = 0xA0B1ECDD;
locked = 0xA8656060;
[...]

Замечу, что если функция прошивки написана THUMB инструкциями, то к адресу нужно прибавить единичку, то есть адрес должен быть нечетным (младший бит включен).
Также напомню, что если мы хотим использовать внешние функции, то их прототипы где-то надо описать. Локальную в рамках патча функцию my_original_press_key я описал прям в .c файле, а функции и переменные прошивки в файле cx65sw50.h:

[...]
void switch_phone_off ();
void set_dynamic_light_mode (unsigned mode);

extern unsigned dynamic_light_mode;
extern unsigned locked;
[...]


Теперь все вспомогательные вещи описаны. Можно приступать к написанию самого патча.
Нужно помнить, что первая функция в объектном файле будет той функцией, что ляжет по адресу OUR_KEY_HANDLER. Еще нужно указывать глобальным переменным секцию .data, чтобы они вдруг не оказались в секции кода .text (как любит делать последний gcc), нужно это из-за того, что в телефоне есть флэш память (под код), а есть RAM (под переменные). Если попытаться изменить с помощью STR что-то, что лежит во флэше, то телефон пискнет и выключится.

К нашим баранам. Первой функцией будет pressKeyChunk, которая и будет эмулировать нажатия на джойстик путем хранения состояния, вызова к оригинальному коду и т.д. Все, что нужно этой функции объявлено перед ней прототипами. Чтобы отслеживать изменение состояния светодиодов и реагировать на блокировку клавиатуры, при первом нажатии на клавишу, будет запущен таймер с частотой вызова обработчика ~0.4с. Как это все сделано можно увидеть скачав исходный код (5kb).
Для компиляции патча необходим мой Набор-Разработчика-Патчей-Для-CX65-SW50 (6.5kb). Этот архив включает в себя:
Makefile.inc в котором прописаны правила сборки,
cx65sw50.h прототипы некоторых функций прошивки,
firmware.symbols адреса некоторых функций прошивки,
fw50.map адреса всех функций, что мне удалось найти в прошивке, пока не надоело,
mk_patch.pl скрипт для создания vkp патча на основе elf файла, использует в свой работе программу elfsh.

Конечный vkp файл создается на основе elf файла скриптом mk_patch.pl и добавлением заголовка - файла, который мы сделали из IDA с помощью IDC скрипта.

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

Немного слов о функциях прошивки которые я нашел (из них более 250 nucleus os).
Как я уже говорил, адреса этих функций находятся в файле fw50.map архива (6.5kb). Если первой цифрой адреса не является 'A', то значит это смещение функции внутри секции и надо перед использованием к адресу прибавить A0000000. Какое-то количество этих функций удалось насобирать по форуму. Немного - из других патчей. Из туториалов. Что-то нашел сам. Небольшое число функций nucleus os удалось получить по сигнатурам, остальные - мой кропотливый труд.

Перед использованием какой-то функции ее желательно проверить. Не гарантирую, что идентифицировал правильно и правильно интерпретировал.

Для использования функций операционной системы Nucleus PLUS достаточно посмотреть на nucleus.h который есть в демоверсии этого продукта, а также можно поискать документацию по этим функциям. Самое полное, что удалось найти - это pdf под названием 'Nucleus Plus Internals'.

P.S. Спасибо авторам программ IDA, ArmDebugger и V_klay, а также другим, кто писал софт для работы с Siemens x65. Без этих программ телефон скорее всего пришлось бы править паяльником или покупать новый.
P.S. Все это является художественным вымыслом. Совпадение с реальностью случайны. Слышал, что обезьяны в состоянии случайно Войну и Мир написать, чем я хуже?

Комментариев нет:

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