ELF - an application on an LG phone?
Initial opinion
I always thought that LG phones are for “housewives”. And when the LG KP500 came into my hands, I didn’t particularly change my opinion, although there were a lot of very interesting things in it. I mean this is a task manager (there is even a special button) that can call and close other minimized native and Java applications, work with the file system (JSR - 75) is available in Java, although not completely, this is the functionality of the Java machine ends. This phone (I emphasize this, it is positioned as a “dialer”) even has its own format of executive files - PXE (* .pxo), but the truth is that it is hidden for the user and is launched from a specific drive folder with a read-only attribute. This unit has a good hardware. This is Nand Flash 256 MB / SDRAM 128 MB, TFT resistive touch screen display with a resolution of 400x240 and 262 thousand colors, Infineon SGold-3 3-axis accelerometer and baseband processor (PMB8877), and the standard set: camera, BlueTooth, radio, etc. An operating system such as Android, Windows, iOS - is not there, but there is its own closed "bike" on the nucleus of the Nucleus RTOS of the ancient version. Such "iron", in my opinion, did not come across to underground "elfopisateli" that gives a certain incentive.
PXE - format
PXE is an executable file format on LG phones, starting with the KP500. It is, you guessed it, closed, there is no documentation for it, I generally am silent about the SDK. The phone runs only from the system drive directory with firmware or other native applications of this format:
"/cus/lgapp/Pxo/*.pxo"
Since the processor is in the ARM926EJ-S phone, ARM / Thumb instructions are also found in them.
These files have a “Place Independed Code”, i.e. do not depend on the load address, that is, projection onto memory. This suggests that there is a relocation table. They were written, of course, in ARM C / C ++, but still, developers preferred Thumb to a set of instructions. There are 2 sections: data and code. Of course, PXE applications use some API. It is represented by a 2-level library of functions, well, a group of tables of pointers to firmware procedures. A pointer to this table is reported to the application upon creation. But the program itself is built on the main event handler, i.e. it receives various events: creation, exit, stop, activation, redrawing, timer, etc.
ELF - bootloader
It was possible to create programs in PXE files, but for this you need, at a minimum, a linker who can assemble it from object files, where it is unknown to get it, and to write it yourself is death. The second factor is the limitation of the launch location, not just the limitation, but the whole problem, because just adding it to the system disk directory is a tedious task and the disk is not rubber, although you can cope with it without difficulty and tearing your hair out.
The original way remains - to make your programs in ELF format. Writing a loader for it is a simple task, and there are more than enough compilers that can assemble it.
So I decided to write an elf bootloader for this phone, there were no problems with entering my code into the firmware. Oh yes, about the modification of the firmware, it is very strange that no one has been doing this for 4 years, the community has created / unlocked / retrieved any kind of flash themes, programs for unpacking / collecting firmware, official flash drivers, utilities, even a flasher for merging / flooding the FS (unfortunately, writing / reading areas of the code was incorrect, and therefore useless). Those. there were a lot of achievements, but in terms of patching and researching the firmware code, a little more was done than nothing. I had to study the program for the processor test myself, in it I found the secret protocol (DWD) for working with the phone, wrote a program, simultaneously correcting the glitches of this protocol, and finally merged all the necessary address space dumps. So, back to our elves. Modified firmware can run one elf, and it, in turn, downloads the rest of the elves and the library, as well as patch Java - a machine to expand its capabilities. All this is good, but loaded elves are just bare code projected into memory. And in order for the phone to see a normal program in them, it was decided to use the "parasitization" method of the PXE file. For this, a small file of this format was patched, i.e. he began to redirect all events to the ELF that loaded it, and cleared it from memory when the exit event occurred. The elf was required to download this “donor.pxo” with parameters - pointers to the event processing procedure and the download base, and copy the code that is presented in the PXE executable files. Elves still have the opportunity to use procedure calls through the SWI, i.e. a separate library of functions and a SWI processor, as in ElfPack on Siemens.
This is what the elf code looks like:
#include
#include
#include
#include
#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;
}
}
The first non-test elf that I publicly presented is the Sega console emulator.
Direct link to the video
And everything else on the topic:
Operating time for KP500