Генерация ELF с использованием подсказок libelf - PullRequest
6 голосов
/ 16 января 2012

Я пытаюсь сгенерировать простой статический ELF с использованием libelf, но у меня, похоже, возникают проблемы.

Я не хочу создавать объектный файл и затем связывать его с LD, вместо этого я хочусоздать его самостоятельно.

Основная цель этой программы - сгенерировать статический ELF с одним сегментом LOAD и выполнить код.

Основная проблема не в самом шелл-коде., но, вероятно, в некоторых из заголовков я пытаюсь произвести неправильно.Когда я пытаюсь запустить сгенерированный ELF, он убивается, как будто ядру не удается найти только что загруженный сегмент и т. Д.

Мне было бы очень приятно, если бы вы, ребята, могли намекнуть.

create_elf.3.c

#include <err.h>
#include <fcntl.h>
#include <libelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>

unsigned char code[] =
"\x0b\x58\x99\x52\x66\x68\x2d\x70"
"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
"\x51\x53\x89\xe1\xcd\x80";

int main(int argc, char *argv[])
{
  int           fd;
  Elf           *e;
  Elf_Scn       *scn;
  Elf_Data      *data;
  Elf32_Ehdr    *ehdr;
  Elf32_Phdr    *phdr;
  Elf32_Shdr    *shdr;
  if (argc != 2)
    errx(EX_USAGE,"input... ./%s filename\n",argv[0]);
  if (elf_version(EV_CURRENT) == EV_NONE)
    errx(EX_SOFTWARE,"elf_version is ev_none, wtf? %s\n",elf_errmsg(-1));
  if ((fd = open(argv[1], O_WRONLY | O_CREAT, 0777)) < 0)
    errx(EX_OSERR, "open %s\n",elf_errmsg(-1));
  if ((e = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL)
    errx(EX_SOFTWARE,"elf_begin %s\n",elf_errmsg(-1));
  if ((ehdr = elf32_newehdr(e)) == NULL)
    errx(EX_SOFTWARE,"elf32_newehdr %s\n",elf_errmsg(-1));
  /*
     without these definitions objdump/readelf/strace/elf loader
     will fail to load the binary correctly
     be sure to pick them carefully and correctly, preferred exactly like the
     ones like the system you are running on (so if you are running x86,
     pick the same values you seen on a regular readelf -a /bin/ls
     */
  ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
  ehdr->e_ident[EI_CLASS] = ELFCLASS32;
  ehdr->e_machine = EM_386;
  ehdr->e_type = ET_EXEC;
  ehdr->e_entry = 0x8040800;
  if ((phdr = elf32_newphdr(e,1)) == NULL)
    errx(EX_SOFTWARE,"elf32_newphdr %s\n",elf_errmsg(-1));
  if ((scn = elf_newscn(e)) == NULL)
    errx(EX_SOFTWARE,"elf32_newscn %s\n",elf_errmsg(-1));
  if ((data = elf_newdata(scn)) == NULL)
    errx(EX_SOFTWARE,"elf32_newdata %s\n",elf_errmsg(-1));
  data->d_align = 4;
  data->d_off = 0LL;
  data->d_buf = code;
  data->d_type = ELF_T_WORD; // code :x
  data->d_size = sizeof(code);
  data->d_version = EV_CURRENT;
  if ((shdr = elf32_getshdr(scn)) == NULL)
    errx(EX_SOFTWARE,"elf32_getshdr %s\n",elf_errmsg(-1));
  shdr->sh_name = 0;
  shdr->sh_type = SHT_PROGBITS;
  shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
  shdr->sh_entsize = 0; // only used if we hold a table
  if (elf_update(e, ELF_C_NULL) < 0)
    errx(EX_SOFTWARE,"elf_update_1 %s\n",elf_errmsg(-1));
  phdr->p_type = PT_LOAD;
  phdr->p_offset = ehdr->e_phoff;
  phdr->p_filesz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);
  phdr->p_vaddr = 0x8040800;
  phdr->p_paddr = 0x8040800;
  phdr->p_align = 4;
  phdr->p_filesz = sizeof(code);
  phdr->p_memsz = sizeof(code);
  phdr->p_flags = PF_X | PF_R;
  elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY);
  if (elf_update(e, ELF_C_WRITE) < 0 )
    errx(EX_SOFTWARE,"elf32_update_2 %s\n",elf_errmsg(-1));
  elf_end(e);
  close(fd);
  return 1;
}

Я был бы рад, если бы кто-нибудь намекнул мне, что здесь не так

Спасибо

edit

Извините, что не предоставил больше подробностей,

Генерация ELF работает нормально, я не получаю никаких синтаксических ошибок и т. Д., Однако всякий раз, когда я пытаюсь запустить сгенерированный ELF, например, ./create_elf.3 foo14 (а foo14 - сгенерированный ELF)

Он убит, как будто execve / kernel не хочет его загружать должным образом. Я попытался загрузить его с IDA, но IDA показывает достаточно хорошо разобранный код

вот вывод от readelf

readelf -a foo14
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8040800
  Start of program headers:          52 (bytes into file)
  Start of section headers:          116 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         1
  Size of section headers:           40 (bytes)
  Number of section headers:         2
  Section header string table index: 0
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0] <no-name>         NULL            00000000 000000 000000 00      0   0  0
  [ 1] <no-name>         PROGBITS        00000000 000054 000020 00  AX  0   0  4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000034 0x08040800 0x08040800 0x00021 0x00021 R E 0x4
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.

Ответы [ 2 ]

5 голосов
/ 17 января 2012

Во-первых, во время тестирования было бы неплохо заменить фрагмент кода, содержащий (непослушный) код оболочки, чем-то безобидным, скажем:

unsigned char code[] = {
    0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */
    0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */
    0xCD, 0x80            /* int $0x80 */
};

В системе i386 GNU / Linux этот пересмотренный фрагмент кодаприводит к немедленному завершению процесса с кодом выхода 42.

Следующая схема ASCII иллюстрирует компоновку создаваемого исполняемого файла ELF:

+----------------------------------+  <- LOADADDR (0x08048000)
|  The ELF Exec Header.            |  
+----------------------------------+
|  The ELF PHDR Table.             |
+----------------------------------+ <- ehdr->e_entry points here.
|  The ".text" section.            |
+----------------------------------+ <- The end of loadable region
|  The section name string table   |    for this object.
|  (optional).                     |
+----------------------------------+
|  Section headers:                |
|  - Header for section ".text".   |
|  - Section name string table     |
|    header.                       |
+----------------------------------+

Таблица строк имени раздела является необязательной.Это помогает упорядочить вывод readelf .

#define LOADADDR    0x08048000

Исполняемый файл будет загружен по виртуальному адресу, названному LOADADDR.Значение LOADADDR зависит от системы - значение 0x08048000, похоже, хорошо работает в моей системе.

Фрагмент исполняемого кода размещается сразу после таблицы PHDR.Поле e_entry исполняемого заголовка ELF содержит виртуальный адрес, на который будет передан элемент управления.Поэтому значение поля должно быть:

size_t ehdrsz, phdrsz;

ehdrsz = elf32_fsize(ELF_T_EHDR, 1, EV_CURRENT);
phdrsz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);

/* ... */

ehdr->e_entry = LOADADDR + ehdrsz + phdrsz;

Сегменты кода будут использовать тип данных ELF_T_BYTE и тип раздела SHT_PROGBITS с выравниванием 1.

if ((scn = elf_newscn(e)) == NULL)
    errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));

if ((data = elf_newdata(scn)) == NULL)
    errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));

data->d_align = 1;
data->d_off = 0LL;
data->d_buf = code;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(code);
data->d_version = EV_CURRENT;

Поле sh_addr записи таблицы заголовков разделов содержит виртуальный адрес начала данных раздела.

if ((shdr = elf32_getshdr(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));

shdr->sh_name = 1;      /* Offset of ".text", see below. */
shdr->sh_type = SHT_PROGBITS;
shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
shdr->sh_addr = LOADADDR + ehdrsz + phdrsz;

Единственная запись в таблице заголовков программ ELF охватывает загружаемую областьначиная с заголовка ELF и включая исполняемый код.

if ((phdr = elf32_newphdr(e,1)) == NULL)
   errx(EX_SOFTWARE,"elf32_newphdr %s\n", elf_errmsg(-1));

phdr->p_type = PT_LOAD;
phdr->p_offset = 0;
phdr->p_filesz = ehdrsz + phdrsz + sizeof(code);
phdr->p_memsz = phdr->p_filesz;
phdr->p_vaddr = LOADADDR;
phdr->p_paddr = phdr->p_vaddr;
phdr->p_align = 4;
phdr->p_flags = PF_X | PF_R;

Таблица строк с именами разделов является необязательной и обеспечивает лучший вывод из readelf .Достаточно вручную свернуть таблицу строк:

unsigned char strtab[] = {
    0, '.', 't', 'e', 'x', 't', 0,
    '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0
};

Код для добавления таблицы строк в исполняемый файл:

/*
 * Allocate a string table for section names.
 */
if ((scn = elf_newscn(e)) == NULL)
   errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));

if ((data = elf_newdata(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));

data->d_align = 1;
data->d_off = 0LL;
data->d_buf = strtab;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(strtab);
data->d_version = EV_CURRENT;

if ((shdr = elf32_getshdr(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));   

shdr->sh_name = 7;      /* Offset of ".shstrtab". */
shdr->sh_type = SHT_STRTAB;
shdr->sh_flags = SHF_STRINGS;

С этими изменениями двоичный файл ELF, созданный вашей программой, должен бытьrunnable.

% cc a.c -lelf
% ./a.out foo
% ./foo; echo $?
42

Структура сгенерированного исполняемого файла будет следующей:

% readelf -a foo
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048054
  Start of program headers:          52 (bytes into file)
  Start of section headers:          116 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         1
  Size of section headers:           40 (bytes)
  Number of section headers:         3
  Section header string table index: 2
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048054 000054 00000c 00  AX  0   0  1
  [ 2] .shstrtab         STRTAB          00000000 000060 000011 00   S  0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00060 0x00060 R E 0x4
 Section to Segment mapping:
  Segment Sections...
   00     .text 
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
3 голосов
/ 16 января 2012

Причина, по которой ядро ​​отказывается запускать вашу программу, довольно проста:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000034 0x08040800 0x08040800 0x00021 0x00021 R E 0x4

Это недопустимый исполняемый файл, поскольку ядро ​​не может отобразить ваш .text со смещением 0x34 по виртуальному адресу 0x08040800. Смещение файла и VirtAddr должны иметь одинаковое выравнивание.

Обычно первый LOAD сегмент включает в себя сам заголовок ELF, т. Е. Имеет Offset из 0 (вы хотели бы установить размер 0x55 (== 0x21 + 0x34)). Кроме того, вы можете договориться о том, чтобы Offset остановился на 0x000034 и имел VirtAddr из 0x08040834.

...