2012-01-16 9 views
5

मैं libelf का उपयोग कर एक साधारण स्थिर ईएलएफ उत्पन्न करने की कोशिश कर रहा हूँ, लेकिन मुझे परेशानी हो रही है।ईएलएफ पीढ़ी libelf संकेत का उपयोग कर

मैं ऑब्जेक्ट फ़ाइल जेनरेट नहीं करना चाहता हूं और फिर इसे w/lD लिंक करना चाहता हूं, इसके बजाय मैं इसे स्वयं उत्पन्न करना चाहता हूं।

इस कार्यक्रम का मुख्य उद्देश्य एक लोड सेगमेंट के साथ एक स्थिर ईएलएफ उत्पन्न करना और कोड निष्पादित करना है।

मुख्य समस्या शेलकोड में नहीं है, लेकिन शायद कुछ शीर्षकों में मैं गलत तरीके से उत्पादन करने की कोशिश करता हूं। जब मैं जेनरेट किए गए ईएलएफ को चलाने की कोशिश करता हूं तो यह मारे जाते हैं जैसे कि कर्नेल इसे लोड करने वाले सेगमेंट को प्रबंधित नहीं करता है, आदि।

यदि आप मुझे संकेत दे सकते हैं तो मुझे शौक होगा।

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

अगर किसी ने मुझे संकेत हो सकता है कि क्या गलत यहाँ

धन्यवाद

संपादित

अधिक जानकारी देने के लिए नहीं खेद है कि मैं शौकीन होगा ,

ईएलएफ पीढ़ी ठीक काम कर रही है, मुझे कोई वाक्यविन्यास त्रुटियां आदि नहीं मिलती हैं, हालांकि जब भी मैं ईएलएफ उत्पन्न करने की कोशिश करता हूं, उदाहरण के लिए ./create_elf.3 foo14 (और foo14 जेनरेट ईएलएफ है)

यह की हत्या कर दी हो जाता है execve/गिरी इसे ठीक से लोड करने के लिए मैं इसे डब्ल्यू लोड हो रहा है की कोशिश की इच्छा नहीं है के रूप में अगर/आईडीए लेकिन आईडीए disassembled कोड ठीक पर्याप्त

यहाँ

से पता चलता 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

हो सकता है आप हमें यह बताएं कि क्या गलत है:

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

उत्पन्न निष्पादन की संरचना इस प्रकार हो सकता है? – steabert

+0

त्रुटि क्या है? क्या हो रहा है? आप क्या उम्मीद करते हैं? 'Objdump' आपको बीमार जेनरेट की गई ईएलएफ फ़ाइल के बारे में क्या बताता है? 'कोड' क्या करना है (हम में से अधिकांश अपने सिर में अलग-अलग नहीं हो पाएंगे)? –

+0

आपका 'मुख्य' 'रिटर्न 1 'के साथ क्यों समाप्त होता है? यह आमतौर पर 'वापसी 0' होना चाहिए, या 'वापसी EXIT_SUCCESS; ' –

उत्तर

4

सबसे पहले, यह (शरारती) कुछ हानिरहित के साथ खोल कोड युक्त कोड टुकड़ा को बदलने के लिए परीक्षण के दौरान एक अच्छा विचार हो सकता है, का कहना है:

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

एक i386 जीएनयू/लिनक्स प्रणाली इस संशोधित कोड टुकड़ा पर प्रक्रिया को का कारण बनता है ताकि 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.      | 
+----------------------------------+ 

अनुभाग नाम स्ट्रिंग तालिका वैकल्पिक है। यह के आउटपुट के आउटपुट को को साफ करने में मदद करता है।

#define LOADADDR 0x08048000 

निष्पादन LOADADDR द्वारा नामित आभासी पते पर लोड किया जाएगा। LOADADDR के लिए मान सिस्टम निर्भर है --- 0x08048000 का मान मेरे सिस्टम पर अच्छी तरह से काम करता प्रतीत होता है।

निष्पादन योग्य कोड खंड पीएचडीआर तालिका के ठीक बाद रखा गया है। e_entry ईएलएफ निष्पादन योग्य शीर्षलेख का क्षेत्र वर्चुअल पता रखता है जिस पर नियंत्रण स्थानांतरित किया जाएगा। क्षेत्र का मूल्य इसलिए किया जाना चाहिए:

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; 

कोड खंडों के 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 क्षेत्र एक संरेखण के साथ ELF_T_BYTE के एक डेटा प्रकार और एक अनुभाग प्रकार SHT_PROGBITS की का प्रयोग करेंगे, सेक्शन हेडर टेबल प्रविष्टि में सेक्शन के डेटा की शुरुआत के आभासी पता है।

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 होना चाहिए के साथ

% 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

माइनर नाइट: कोई '_exit' सिस्टम कॉल नहीं है। यदि आप अंडरस्कोर करना चाहते हैं तो यह 'निकास' या 'निकास (2)' है, यह सिस्टम कॉल है, और libc फ़ंक्शन 'निकास (3)' नहीं है जिसके बारे में आप बात कर रहे हैं। साथ ही, आपने यह नहीं समझाया कि आपका 'स्ट्रैटब' 0 से शुरू होता है। –

+0

'कच्चे' सिस्टम कॉल को POSIX मानक द्वारा '_exit()' के रूप में लिखा गया प्रतीत होता है। ईएलएफ स्ट्रिंग टेबल आमतौर पर एनयूएल बाइट से शुरू होते हैं ताकि शून्य की एक 'sh_name' फ़ील्ड शून्य लंबाई स्ट्रिंग को इंगित करने के लिए उपयोग किया जा सके --- --- कृपया [उदाहरण के द्वारा libelf] देखें (http://sourceforge.net/projects/elftoolchain अधिक जानकारी के लिए/फाइलें/दस्तावेज़ीकरण/libelf-by-example) ट्यूटोरियल। – jkoshy

+0

POSIX मानक में '_exit' * सिस्टम कॉल नहीं है। यह एक फ़ंक्शन है: http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html सिस्टम कॉल जो इसे लिनक्स पर आवेक करेगा, या तो 'निकास' या 'exit_group' है। –

3

कारण से उत्पादन है कर्नेल आपके प्रोग्राम को चलाने से इनकार करता है काफी सरल है:

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

यह अवैध निष्पादन क्योंकि गिरी अपने .text साथ mmap नहीं कर सकते हैं आभासी पता 0x08040800 पर ऑफसेट 0x34 है। फ़ाइल ऑफ़सेट और VirtAddr में एक ही संरेखण होना चाहिए।

आमतौर पर पहले LOAD खंड सिर्फ शामिल ELF हैडर ही है, यानी 0 से Offset (आप 0x55 (== 0x21 + 0x34) को आकार सेट करना चाहते हैं) किया गया है। वैकल्पिक रूप से, आप 0x000034 पर रहने के लिए Offset की व्यवस्था कर सकते हैं और 0x08040834 के VirtAddr पर जा सकते हैं।

संबंधित मुद्दे