Первоначальное мнение
Я всегда думал, что телефоны от компании LG для «домохозяек». И когда мне попал в руки аппарат LG KP500 я своё мнение особо не изменил, хотя в нём было много чего весьма интересного. Я имею ввиду это диспетчер задач (даже есть специальная кнопка), который может вызывать и закрывать другие свёрнутые родные и Java — приложения, в Java доступна работа с файловой системой (JSR — 75), хоть и не полностью, на этом функционал Java машины заканчивается. В этом телефоне (это я подчёркиваю, он позиционируется как «звонилка») даже есть свой формат исполнительных файлов — PXE (*.pxo), но правда он скрыт для пользователя и запускается из конкретной папки диска, имеющий атрибут только чтение. Данный аппарат имеет неплохое «железо». Это Nand Flash 256 Мб / SDRAM 128 Мб, TFT сенсорный резистивный дисплей c разрешением 400×240 и 262 тысяч цветов, 3-х осевой акселерометр и бейзбенд-процессор Infineon SGold-3 (PMB8878), ну и стандартный набор: камера, BlueTooth, радио и т.д. Операционной системы такой как Android, Windows, iOS — там нет, зато есть свой закрытый «велосипед» на ядре Nucleus RTOS древней версии. Такое железо, на мой взгляд, к подпольным «эльфописателям» не попадалось, что даёт определённый стимул.
PXE — формат
PXE это формат исполнительных файлов на телефонах LG, начиная с KP500. Он, как вы догадались, закрыт, на него нет документаций, про SDK я вообще молчу. В телефоне запускается только из каталога системного диска прошивкой или другими родными приложениями этого формата:
"/cus/lgapp/Pxo/*.pxo"
Так как процессор в телефоне ARM926EJ-S, то и в них встречается ARM / Thumb инструкции.
Эти файлы имеют «Place Independed Code», т.е. не зависят от адреса загрузки, то бишь проецирования на память. Это говорит о том, что имеется таблица релокаций. Написаны, естественно, на ARM C/C++, но всё-таки разработчики отдали предпочтение Thumb набору инструкций. Имеются 2 секции: данных и кода. Конечно же, PXE — приложения используют некоторое API. Оно представлено 2-уровневой библиотекой функций, ну группой таблиц указателей на процедуры прошивки. Указатель на эту таблицу сообщается приложению при создании. А вот сама программа построена на главном обработчике событий, т.е. в неё поступают различные события: создания, выхода, остановки, активации, перерисовки, таймера и т.д.
ELF — загрузчик
Можно было и создавать программы в файлах PXE, но для этого нужен, как минимум, компоновщик, умеющий собирать его из объектных файлов. где такой достать неизвестно, а самому писать — смерть. Второй фактор это ограничение места запуска, не просто ограничение, а целая проблема, ибо просто так добавить его в каталог системного диска задача утомительная и диск не резиновый, правда с этим можно справится без затруднений и рвания на себе волос.
Остается оригинальный способ — делать свои программы в формате ELF. Написать для него загрузчик задача простая, а компиляторов которые его могут собирать хоть отбавляй.
Так я и решил написать загрузчик эльфов для данного телефона, проблем с внесением своего кода в прошивку особых не было. Ах да, по поводу модификации прошивки, это очень странно, что этим никто не занимался за 4 года сообщество создавало / разблокировало / доставало какие-угодно flash — темы, программы для распаковки / собирания прошивок, официальные прошивальщики, утилиты, даже флешер для слития / залития ФС (к сожалению, запись / чтение областей кода, производилось некорректно, поэтому и бесполезно). Т.е. было очень много достижений, но в плане патчинга и исследования кода прошивки было сделано чуть больше, чем ничего. Пришлось самостоятельно изучать программу для теста процессора, в ней я нашёл секретный протокол (DWD) для работы с телефоном, написал программу, попутно исправляя глюки этого протокол, и наконец-таки слил все нужные дампы адресного пространства. Итак, вернёмся к нашим эльфам. Модифицированная прошивка умеет запускать один эльф, а он в свою очередь загружает остальные эльфы и библиотеку, а так же патчит Java — машину для расширения её возможностей. Всё это хорошо, но загруженные и исполненные эльфы это просто голый код, проецированный на память. И чтобы телефон видел в них нормальную программу принято решение использовать метод «паразитирования» PXE — файла. Для этого был пропатчен маленький файл этого формата, т.е. он стал перенаправлять все события в ELF, который его загрузил, и очищал его из памяти при событии выхода. От эльфа требовалось загрузить этот «donor.pxo» с параметрами — указателями на процедуру обработки событий и базу загрузки ну и копировать код, который представлен в исполнительных файлах PXE. У эльфов есть ещё возможность использовать вызов процедур через SWI, т.е. отдельная библиотека функций и SWI обработчик, как в ElfPack на Siemens.
Вот так примерно выглядит код эльфа:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include "elf/elf.h" extern int thing_w; extern int thing_h; extern int thing_d; extern unsigned char thing_bitmap[]; extern int star_w; extern int star_h; extern int star_d; extern unsigned char star_bitmap[]; /* =================================== GUI ================================== */ #define WINDOW_ID_SCREEN 0x5001 int Screen_EventHandler(int event_id, int item_num, int param); void Screen_OnInit(); void Screen_Close(int action); void Screen_OnExit(); void Screen_OnKeyDown(int key); void Screen_OnKeyUp(int key); void Screen_OnDraw(); void Screen_OnIndicatorDraw(); void Screen_OnTimer(); void Screen_OnPointing(int action, int position); void Screen_OnAwake(); void Screen_OnSleep(); int x1 = 0, y1 = 0, x2 = 0, y2 = 0; int w1 = 0, h1 = 0, w2 = 0, h2 = 0; int z = 0, d = 0; unsigned char *b1 = 0, *b2 = 0; int de1 = 0, de2 = 0; void Draw() { char ascii_text_buffer[256]; unsigned short u16_text_buffer[256]; drw_fillrect(0, GUI_STATUSBAR_HEIGHT, DISPLAY_WITDH, DISPLAY_HEIGHT, drw_color_make_rgb(0, 0, 0)); drw_string_setoutline(1); drw_string_setcolor(drw_color_make_rgb(200, 0, 0)); cp1251_2_utf16(u16_text_buffer, "Тест тачскрина"); drw_string(0, GUI_STATUSBAR_HEIGHT + 16 * 0, u16_text_buffer, 16); sprintf(ascii_text_buffer, "Координаты #1: X: %d / Y: %d", x1 + w1/2, y1 + h1/2); cp1251_2_utf16(u16_text_buffer, ascii_text_buffer); drw_string(0, GUI_STATUSBAR_HEIGHT + 16 * 1, u16_text_buffer, 16); sprintf(ascii_text_buffer, "Координаты #2: X: %d / Y: %d", x2 + w2/2, y2 + h2/2); cp1251_2_utf16(u16_text_buffer, ascii_text_buffer); drw_string(0, GUI_STATUSBAR_HEIGHT + 16 * 2, u16_text_buffer, 16); if (z == 1) { drw_bitmap(x1, y1, w1, h1, de1, b1); drw_bitmap(x2, y2, w2, h2, de2, b2); } else { drw_bitmap(x2, y2, w2, h2, de2, b2); drw_bitmap(x1, y1, w1, h1, de1, b1); } gui_redraw(); } //Действие при создании окна void Screen_OnInit() { printf("Screen_OnInit\r\n"); x1 = 50; x2 = 80; y1 = 200; y2 = 50; w1 = thing_w; h1 = thing_h; w2 = star_w; h2 = star_h; b1 = thing_bitmap; b2 = star_bitmap; de1 = thing_d; de2 = star_d; z = 0; d = 0; Draw(); } //Действие при уничтожении окна void Screen_OnExit() { printf("Screen_OnExit\r\n"); } //Действие при зажатии настоящей кнопки или рабочей области тачскрина void Screen_OnKeyDown(int key) { printf("Screen_OnKeyDown key = %d\r\n", key); switch (key) { case KEY_MULTI: __taskapi_call_taskman(); break; case KEY_END: __taskapi_app_exit(0, 0, 0); break; } Draw(); } //Действие при отпускании настоящей кнопки void Screen_OnKeyUp(int key) { printf("Screen_OnKeyUp key = %d\r\n", key); } //Действие при отрисовке окна void Screen_OnDraw() { //printf("Screen_OnDraw()\r\n"); Draw(); } //Действие при отрисовке статус-бара void Screen_OnIndicatorDraw() { //printf("Screen_OnIndicatorDraw()\r\n"); Draw(); } //Действие при срабатывании таймеров void Screen_OnTimer(int timer_id, int param) { //printf("Screen_OnTimer: %d / %d\r\n", timer_id, param); } //Действие при манипуляциях с тачскрином void Screen_OnPointing(int action, int position) { int x, y; x = PXE_LOWORD(position); y = PXE_HIWORD(position); switch (action) { case TOUCH_ACTION_PRESS: { //Если 1-ый объект наверху if (z == 0) { if (x >= x1 && x < (x1 + w1) && y >= y1 && y < (y1 + h1)) d = 1; else { if (x >= x2 && x < (x2 + w2) && y >= y2 && y < (y2 + h2)) { z = 1; d = 1; } } //Если 2-ой объект наверху } else { if (x >= x2 && x < (x2 + w2) && y >= y2 && y < (y2 + h2)) d = 1; else { if (x >= x1 && x < (x1 + w1) && y >= y1 && y < (y1 + h1)) { z = 0; d = 1; } } } break; } case TOUCH_ACTION_PRESSED: { if (d == 1) { if (z == 0) { x1 = x; y1 = y; } else { x2 = x; y2 = y; } } break; } case TOUCH_ACTION_RELEASE: { d = 0; break; } } Draw(); } //Действие при активации void Screen_OnAwake() { printf("Screen_OnAwake()\r\n"); } //Действие при сворачивании void Screen_OnSleep() { printf("Screen_OnSleep()\r\n"); } //Главный обработчик окна WINDOW_ID_MAINMENU от приложения int Window_EventHandler(int cmd, int subcmd, int status) { switch (cmd) { case Window_OnInit: Screen_OnInit(); break; case Window_OnExit: Screen_OnExit(); break; case Window_OnAwake: Screen_OnAwake(); break; case Window_OnSleep: Screen_OnSleep(); break; case Window_OnKeyDown: Screen_OnKeyDown(subcmd); break; case Window_OnKeyUp: Screen_OnKeyUp(subcmd); break; case Window_OnDraw: Screen_OnDraw(); break; case Window_OnTimer: Screen_OnTimer(subcmd, status); break; case Window_OnPointing: Screen_OnPointing(subcmd, status); break; case Window_OnIndicatorDraw: Screen_OnIndicatorDraw(); break; default: break; } return 1; } /* ---------------------- Обработчик событий приложения --------------------- */ int elf_run(int event_id, int wparam, int lparam) { //printf("elf_run = %d / %d / 0x%08X\r\n", event_id, wparam, lparam); switch (event_id) { //Событие при создании приложения case PXE_RUN_CREATE_EVENT: //Устанавливаем имя приложения в Диспетчере задач __taskapi_app_setname(app_handle, 0, 0, 0); //Создаём окно gui_window_create(WINDOW_ID_SCREEN, Window_EventHandler); //Запускаем инициализацию окна gui_window_init(WINDOW_ID_SCREEN); printf("PXE_RUN_CREATE_EVENT\r\n"); return 1; //Событие при создании приложения case PXE_RUN_DESTROY_EVENT: //Уничтожаем окно gui_window_destroy_all(); printf("PXE_RUN_DESTROY_EVENT\r\n"); return 1; //Событие при активации приложения case PXE_RUN_RESUME_EVENT: printf("PXE_RUN_RESUME_EVENT\r\n"); //Отправим команду на перерисовку gui_window_trans_event(PXE_RUN_PAINT_EVENT, 0, 0); return 1; //Событие при сворачивании приложения case PXE_RUN_SUSPEND_EVENT: printf("PXE_RUN_SUSPEND_EVENT\r\n"); return 1; default: //Конвертируем остальные события приложения для окна gui_window_trans_event(event_id, wparam, lparam); return 1; } }
Первый не тестовый эльф, который я публично представил, это эмулятор приставки Sega.
И всё остальное по теме:
Наработки для KP500
ссылка на оригинал статьи http://habrahabr.ru/post/155289/
Добавить комментарий