2012-01-16 14 views
5

Próbuję wygenerować proste statyczne ELF za pomocą libelf, ale wydaje się, że mam problemy.Generowanie ELF za pomocą wskazówek libelf

Nie chcę generować pliku obiektowego, a następnie połączyć go w/LD, zamiast tego chcę go wygenerować samodzielnie.

Głównym celem tego programu jest wygenerowanie statycznego ELF z jednym segmentem LOAD i wykonanie kodu.

Głównym problemem nie jest sam kod powłoki, ale prawdopodobnie w niektórych nagłówkach próbuję produkować w niewłaściwy sposób. Kiedy próbuję uruchomić wygenerowany ELF, zostaje zabity, tak jakby jądro nie znalazło segmentu, który właśnie został załadowany, itd.

Byłbym bardzo zadowolony, gdybyście mogli mi wskazać.

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; 
} 

będę lubił, jeśli ktoś mógłby schować mi, co się dzieje tutaj

Dzięki

edit

Przepraszamy za nie podając więcej szczegółów ,

Generacja ELF wydaje się działać poprawnie, nie dostaję żadnych błędów składniowych itp., , jednak za każdym razem, gdy próbuję uruchomić ELF wygenerowałem, np. ./create_elf.3 foo14 (i foo14 jest wygenerowanym ELF)

It ginie, jakby execve/kernel nie chce go załadować poprawnie Próbowałem ładowanie go w/IDA ale IDA przedstawia zdemontowany kod porządku wystarczająco

oto wyjście z 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. 
+1

może mógłbyś nam powiedzieć, co się stało? – steabert

+0

Jaki jest błąd? Co się dzieje? Czego oczekujesz? Co "objdump" mówi o źle wygenerowanym pliku ELF? Co ma robić "kod" (większość z nas nie jest w stanie rozmontować w głowie)? –

+0

Dlaczego twój "główny" kończy się na 'return 1;'? Zwykle powinno to być 'return 0;' lub 'return EXIT_SUCCESS;' –

Odpowiedz

4

Po pierwsze, byłby to dobry pomysł, podczas testowania, aby zastąpić kod fragmentu zawierającego (niegrzeczny) Shellcode z czymś nieszkodliwym, powiedzieć:

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

Na i386 systemu GNU/Linux kod zmieniony fragment powoduje, że proces natychmiast kończy się z kodem zakończenia wynoszącym 42.

Poniższy ASCII Art ilustruje układ ELF wykonywalnego buduje:

+----------------------------------+ <- 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.      | 
+----------------------------------+ 

Nazwa sekcji tabeli ciąg jest opcjonalne. Pomaga to zrównoważyć wyjście z readelf.

#define LOADADDR 0x08048000 

wykonywalny zostanie załadowany pod adresem wirtualnym o nazwie LOADADDR. Wartość dla LOADADDR zależy od systemu --- wartość 0x08048000 wydaje się działać dobrze w moim systemie.

Fragment kodu wykonywalnego znajduje się tuż za tabelą PHDR. Pole Wykonywalny nagłówek ELF zawiera adres wirtualny , do którego zostanie przekazana kontrola. Wartość pola powinny być:

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; 

segmentów kodu byłoby użyć typu danych ELF_T_BYTE i typ przekroju z SHT_PROGBITS, z wyrównania 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; 

dziedzinie sh_addr wpisu tablicy nagłówka sekcji zawiera wirtualny adres startowy danych sekcji .

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; 

Jedyna pozycja w tabeli Nagłówek ELF Program obejmuje obszar do załadowany, zaczynając od nagłówka ELF włącznie kod wykonywalny .

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; 

Sekcja tabeli string name jest opcjonalny i sprawia, że ​​ładniejszy wyjściu z readelf. Tabela ciąg ręcznie zwijane wystarczy:

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

Kod dodać tabelę ciąg do pliku wykonywalnego jest:

/* 
* 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; 

z tymi zmianami ELF binarne tworzone przez program powinien być runnable.

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

Struktura wygenerowanego pliku wykonywalnego będzie w następujący sposób:

% 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. 
+0

Drobny nit: nie ma wywołania systemowego "_exit". To 'exit' lub' exit (2) 'jeśli chcesz podkreślić, że jest to wywołanie systemowe, a nie funkcja libc' exit (3) ', o której mówisz. Nie wyjaśniłeś też, dlaczego twój 'strtab' zaczyna się od 0. –

+0

"Surowe" wywołanie systemowe wydaje się być pisane jako '_exit()' według standardu POSIX. Tabele ciągów ELF zwykle zaczynają się od bajtu NUL, więc można użyć pola 'sh_name' do zera, aby wskazać ciąg o zerowej długości --- zobacz [przykład z przykładu] (http://sourceforge.net/projects/elftoolchain/files/Documentation/libelf-by-example) samouczek, aby uzyskać więcej informacji. – jkoshy

+0

'_exit' w standardzie POSIX to * nie * wywołanie systemowe. Jest to funkcja: http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html Wywołanie systemowe wywołane w Linuksie to 'exit' lub' exit_group'. –

3

powód jądro odmawia uruchomienia twojego programu jest dość proste:

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

To nieważne wykonywalny ponieważ jądro nie może mmap swój .text z przesunięciem 0x34 w wirtualnym adresem 0x08040800. Przesunięcie pliku i VirtAddr musi mieć to samo wyrównanie.

Zazwyczaj pierwsze LOAD Segment obejmuje tylko nagłówek sama ELF, to znaczy ma Offset od 0 (co chcesz ustawić rozmiar, aby 0x55 (== 0x21 + 0x34)). Ewentualnie możesz zamówić Offset, aby zatrzymać się pod numerem 0x000034 i mieć numer VirtAddr z 0x08040834.

Powiązane problemy