Загрузить ELF файл в память

Я пытаюсь поместить в память файл эльфа, а затем выполнить его:

1- файл для размещения в памяти

int main()
{
   printf("Hello world! \n");
   return 0;
}

2- Скомпилируйте его gcc -o hello hello.c -static

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2 complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8120
  Start of program headers:          52 (bytes into file)
  Start of section headers:          119864 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         4
  Size of section headers:           40 (bytes)
  Number of section headers:         18
  Section header string table index: 17

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00008000 0x00008000 0x16828 0x16828 R E 0x1000
  LOAD           0x016840 0x0001f840 0x0001f840 0x00250 0x02660 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0
  EXIDX          0x015f40 0x0001df40 0x0001df40 0x008e8 0x008e8 R   0x4

3- Я пишу загрузчик (скомпилированный для ARM)

mmap2(0x8000, 92200, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x8000
mmap2(0x1f000, 65536, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x1f000

3.1- Затем я скопировал все эльфийские байты в распределения

3.2- jmp к основной функции

ldr r0, =0x008160
blx r0

.text:00008160 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00008160                 EXPORT main
.text:00008160 main                                    ; DATA XREF: _start+8o
.text:00008160                                         ; .text:off_8150o
.text:00008160                 STMFD           SP!, {R11,LR}
.text:00008164                 ADD             R11, SP, #4
.text:00008168                 LDR             R3, =(aHelloWorld - 0x8174)
.text:0000816C                 ADD             R3, PC, R3 ; "Hello world! "
.text:00008170                 MOV             R0, R3
.text:00008174                 BLX             puts
.text:00008178                 MOV             R3, #0
.text:0000817C                 MOV             R0, R3
.text:00008180                 LDMFD           SP!, {R11,PC}
.text:00008180 ; End of function main

Проблема в том, что каждый раз, когда я попадаю на строку 0x8174, а затем перескакиваю, после нескольких инструкций у меня всегда есть SIGSEGV в случайном положении, больше времени команда crash = > 0x9cc0: ldr r0, [r0, #4] с r0=0x70a34

00008000-0002f000 rwxp 00000000 00:00 0 
80000000-80001000 r-xp 00000000 b3:18 129754     /data/local/tmp/main
80001000-8001a000 rwxp 00001000 b3:18 129754     /data/local/tmp/main
becdf000-bed00000 rwxp 00000000 00:00 0 
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

Это больше инструкций после сбоя:

.text:00009CB4 loc_9CB4                                ; CODE XREF: pthread_mutex_lock_impl+18j
.text:00009CB4                 MOV             R3, #0xFFFF0FE0
.text:00009CBC                 BLX             R3
.text:00009CC0                 LDR             R0, [R0,#4]

В этом месте 0x9CB4 значение r0 равно 0x1f96c (это нормально), после blx значение r0 равно 0x70a34

(gdb) x/10x 0xffff0fe0
0xffff0fe0: 0xee1d0f70  0xe12fff1e  0xee1d0f70  0x00000000
0xffff0ff0: 0x00000000  0x00000000  0x00000000  0x00000005
0xffff1000: Cannot access memory at address 0xffff1000

Спасибо!

Ответ 1

Я пытаюсь поместить файл эльфа в память и затем выполнить его,

Для полностью статически связанного исполняемого файла ваши действия будут работать (за исключением того, что вам нужно перейти к _start == точка входа 0x8120, а не main).

Затем я скопировал все эльфийские байты в распределения

Другая возможная проблема - не обращать внимание на .p_offset. Ваши memcpy должны выглядеть примерно так:

unsigned char buf1[0x16828];  // read 0x16828 bytes from start of file
memcpy(0x8000, buf1, 0x16828);

unsigned char buf2[0x250];  // read 0x250 bytes from offset 0x016840 into the file
memcpy(0x0001f840, buf2, 0x250);

Ответ 2

Ваша проблема в том, что вам нужно использовать правильную точку входа, и вам нужно инициализировать стек программы (и, возможно, регистры) так же, как и OS. Вам нужно использовать правильную точку входа, чтобы библиотека времени выполнения C была инициализирована, иначе ваш вызов printf (или puts в зависимости от случая) почти наверняка сбой. Вам нужно правильно настроить стек, потому что там, где код инициализации библиотеки времени выполнения C будет искать аргументы и среду программы (и, возможно, другие вещи).

Вы не сказали, какую операционную систему используете, но если вы используете Linux, вы можете посмотреть как ответ на другой вопрос, заданный CesarB описывающий начальное состояние стек на ARM Linux.