ELF — приложение на телефоне LG?

от автора

Первоначальное мнение

Я всегда думал, что телефоны от компании 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.

Вот так примерно выглядит код эльфа:

main.c

#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/


Комментарии

Добавить комментарий

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