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