2012-01-16 32 views
5

Tôi đang cố gắng tạo một ELF tĩnh đơn giản bằng cách sử dụng libelf, nhưng dường như tôi đang gặp sự cố.Tạo ELF bằng cách sử dụng gợi ý libelf

Tôi không muốn tạo một tệp đối tượng và sau đó liên kết nó với LD, thay vào đó tôi muốn tự tạo nó.

Mục đích chính của chương trình này là tạo một ELF tĩnh với một phân đoạn LOAD và để thực thi mã.

Vấn đề chính không nằm trong chính shellcode, nhưng có thể trong một số tiêu đề tôi cố gắng tạo ra một cách sai. Khi tôi cố gắng chạy ELF được tạo ra, nó sẽ bị giết như thể hạt nhân không quản lý tìm phân đoạn mà nó vừa tải, v.v.

Tôi sẽ thích nếu các bạn có thể gợi ý cho tôi.

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

Tôi sẽ thích nếu ai đó có thể gợi ý cho tôi có gì sai ở đây

Cảm ơn

chỉnh sửa

Xin lỗi vì đã không cho biết thêm chi tiết ,

Thế hệ ELF dường như hoạt động tốt, tôi không nhận được bất kỳ lỗi cú pháp nào vv, tuy nhiên bất cứ khi nào tôi cố gắng chạy ELF mà tôi đã tạo, ví dụ: /create_elf.3 foo14 (và foo14 là ELF đã tạo)

bị giết, như thể execve/kernel không muốn tải nó đúng tôi đã cố gắng tải nó w/IDA nhưng IDA cho thấy mã tháo rời phạt đủ

đây là đầu ra từ 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

có lẽ bạn có thể cho chúng tôi biết có chuyện gì vậy? – steabert

+0

Lỗi là gì? Điều gì đang xảy ra? Bạn mong chờ điều gì? 'Objdump' nói gì với bạn về tệp ELF không được tạo ra? 'Mã' có nghĩa vụ phải làm gì (hầu hết chúng ta không thể tháo rời trong đầu chúng)? –

+0

Tại sao 'main' kết thúc bằng' return 1; '? Nó thường là 'return 0;' hoặc 'return EXIT_SUCCESS;' –

Trả lời

4

Thứ nhất, nó sẽ là một ý tưởng tốt trong quá trình thử nghiệm để thay thế các đoạn mã chứa (nghịch ngợm) mã shell với một cái gì đó vô hại, nói:

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

Trên i386 hệ thống GNU/Linux đoạn mã sửa đổi này gây ra quá trình để thoát ngay lập tức với mã thoát 42.

sau Nghệ thuật ASCII minh họa bố cục của ELF thực thi đang được xây dựng:

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

Tên phần chuỗi bảng là không bắt buộc. Nó giúp gọn gàng đầu ra của readelf.

#define LOADADDR 0x08048000 

Tệp thi hành sẽ được tải tại địa chỉ ảo có tên LOADADDR. Giá trị cho LOADADDR phụ thuộc vào hệ thống --- giá trị 0x08048000 dường như hoạt động tốt trên hệ thống của tôi.

Đoạn mã thực thi được đặt ngay sau bảng PHDR. Trường e_entry của Tiêu đề thực thi ELF giữ địa chỉ ảo mà điều khiển sẽ được chuyển đến. Giá trị của lĩnh vực do đó nên được:

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; 

đoạn mã sẽ sử dụng một kiểu dữ liệu của ELF_T_BYTE và một loại phần của SHT_PROGBITS, với sự liên kết của 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; 

Trường sh_addr của mục nhập bảng tiêu đề phần chứa địa chỉ ảo khi bắt đầu dữ liệu của phần.

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; 

Mục duy nhất trong bảng Chương trình ELF header bao gồm các khu vực được nạp, bắt đầu với tiêu đề ELF và bao gồm cả mã thực thi .

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; 

Một phần bảng tên chuỗi là không bắt buộc, và làm cho đầu ra đẹp hơn từ readelf. Một bảng chuỗi cuộn bằng tay cũng đủ:

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

Mã này để thêm bảng chuỗi để thực thi là:

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

Với những thay đổi nhị phân ELF tạo ra bởi chương trình của bạn nên được Runnable.

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

Cấu trúc của thực thi được tạo ra sẽ như sau:

% 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

Nitơ nhỏ: không có lệnh gọi hệ thống '_exit'. Đó là 'exit' hoặc' exit (2) 'nếu bạn muốn nhấn mạnh rằng đó là cuộc gọi hệ thống, và không phải là hàm libc' exit (3) 'mà bạn đang nói đến. Ngoài ra, bạn không giải thích tại sao 'strtab' của bạn bắt đầu bằng 0. –

+0

Cuộc gọi hệ thống 'thô' có vẻ như được viết là' _exit() 'theo tiêu chuẩn POSIX. Các bảng chuỗi ELF thường bắt đầu bằng một byte NUL sao cho một trường 'sh_name' bằng 0 có thể được sử dụng để biểu thị chuỗi độ dài bằng không --- vui lòng xem [libelf by Example] (http://sourceforge.net/projects/elftoolchain)/files/Tài liệu/libelf-by-example) hướng dẫn để biết thêm thông tin. – jkoshy

+0

'_exit' trong tiêu chuẩn POSIX là * không * một cuộc gọi hệ thống. Đó là một hàm: http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html Hệ thống gọi nó sẽ gọi trên Linux là 'exit' hoặc' exit_group'. –

3

lý do hạt nhân từ chối chạy chương trình của bạn khá đơn giản:

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

Điều này là không hợp lệ thực thi vì hạt nhân không thể mmap .text của bạn với bù đắp 0x34 tại địa chỉ ảo 0x08040800. Độ lệch tập tin và VirtAddr phải có cùng căn chỉnh.

Thông thường các phân khúc LOAD đầu tiên chỉ bao gồm các ELF tiêu đề riêng của mình, nghĩa là đã Offset từ 0 (bạn sẽ muốn thiết lập kích thước để 0x55 (== 0x21 + 0x34)). Ngoài ra, bạn có thể sắp xếp Offset để ở số 0x000034 và có VirtAddr trong số 0x08040834.

Các vấn đề liên quan