2012-12-17 32 views
12

मैं 64 बिट x86 वातावरण में लिनक्स पर "gcc -m32 test.c -o test.exe" के साथ संकलित एक ईएलएफ फ़ाइल लोड करने का प्रयास कर रहा हूं। मैं उस उपयोगकर्ता स्थान ईएलएफ लोडर के अंदर उस 32 बिट फ़ाइल (test.exe) को लोड करने का प्रयास कर रहा हूं जिसमें निम्न कोर तर्क (32 बिट ईएलएफ) है।उपयोगकर्ता स्थान में सी में ईएलएफ फ़ाइल लोड करना

समस्या यह है कि लौटे हुए प्रारंभ पते में कॉलिंग सेगमेंटेशन फॉल्ट कोर डंप में परिणाम देती है। यहां कोड है:

void *image_load (char *elf_start, unsigned int size) 
{ 
    Elf32_Ehdr  *hdr = NULL; 
    Elf32_Phdr  *phdr = NULL; 
    unsigned char *start = NULL; 
    Elf32_Addr  taddr = 0; 
    Elf32_Addr  offset = 0; 
    int i = 0; 
    unsigned char *exec = NULL; 
    Elf32_Addr  estart = 0; 

    hdr = (Elf32_Ehdr *) elf_start; 

    if(!is_image_valid(hdr)) { 
     printk("image_load:: invalid ELF image\n"); 
     return 0; 
    } 

    exec = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, 
         MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 

    if(!exec) { 
     printk("image_load:: error allocating memory\n"); 
     return 0; 
    } 

    // Start with clean memory. 
    memset(exec,0x0,size); 

    phdr = (Elf32_Phdr *)(elf_start + hdr->e_phoff); 

    for(i=0; i < hdr->e_phnum; ++i) { 
      if(phdr[i].p_type != PT_LOAD) { 
        continue; 
      } 
      if(phdr[i].p_filesz > phdr[i].p_memsz) { 
        printk("image_load:: p_filesz > p_memsz\n"); 
        munmap(exec, size); 
        return 0; 
      } 
      if(!phdr[i].p_filesz) { 
        continue; 
      } 

      // p_filesz can be smaller than p_memsz, 
      // the difference is zeroe'd out. 
      start = (unsigned char *) (elf_start + phdr[i].p_offset); 
      // taddr = phdr[i].p_vaddr + (Elf32_Addr)exec; 
      if(!estart) { 
       estart = phdr[i].p_paddr; 
      } 
      taddr = (Elf32_Addr)exec + offset + (phdr[i].p_paddr - estart); 
      memmove((unsigned char *)taddr, 
        (unsigned char *)start,phdr[i].p_filesz); 
      offset += (phdr[i].p_memsz + (phdr[i].p_paddr - estart)); 

      if(!(phdr[i].p_flags & PF_W)) { 
        // Read-only. 
        mprotect((unsigned char *) taddr, 
           phdr[i].p_memsz, 
           PROT_READ); 
      } 

      if(phdr[i].p_flags & PF_X) { 
        // Executable. 
        mprotect((unsigned char *) taddr, 
          phdr[i].p_memsz, 
          PROT_EXEC); 
      } 
    } 

    return (void *)((hdr->e_entry - estart) + (Elf32_Addr)exec); 

}/* image_load */ 

... 
    int (*main)(int, char **)=image_load(...); 
    main(argc,argv); // Crash... 
... 
+1

के बजाय 'printk', एक कर्नेल मॉड्यूल पता चलता है, जबकि' mmap' और 'mprotect' उपयोगकर्ता के अंतरिक्ष हैं। यदि आप उपयोगकर्ता-स्पेस एप्लिकेशन को कोड कर रहे हैं, तो क्या आपने इसे 'gcc -Wall -g' के साथ संकलित करने और इसे' gdb' से डीबग करने पर विचार किया था? और ईएलएफ छवि का प्रारंभ पता * मुख्य * दिनचर्या नहीं है (लेकिन कुछ 'crt0.o' में कुछ '_start') –

उत्तर

23

कृपया ईएलएफ सहित पूर्ण रननेबल कोड प्रदान करें, जिसे आप लोड करने का प्रयास कर रहे हैं। मैंने आपके कोड को जितना संभव हो उतना संशोधित करने के लिए समय निकाला है, और ऐसा लगता है कि कम से कम इस सरल कोड के लिए।

ध्यान दें कि लोडर को 32 बिट कोड के रूप में भी संकलित किया जाना चाहिए, आप 32 बिट फ़ाइल को 64 बिट प्रक्रिया में लोड नहीं कर सकते हैं। इसके अलावा चूंकि आप मूल स्थान पर कोड लोड नहीं कर रहे हैं, इसलिए इसे स्थानांतरित किया जाना चाहिए। अंत में, यह एक स्थिर बाइनरी होना चाहिए क्योंकि आप किसी पुस्तकालय को लोड नहीं कर रहे हैं।

अद्यतन: अपने कोड लोड कोड के प्रवेश बिंदु int (*main)(int, char **) प्रोटोटाइप जो सामान्य रूप में मामले (: main वास्तव में एक तीसरा तर्क, पर्यावरण, भी हो जाता है ओर टिप्पणी) नहीं है के अनुरूप करने की उम्मीद है। startup state of ELF के बारे में पढ़ें। यदि आप मैन्युअल रूप से वर्णित स्टैक लेआउट बनाते हैं, तो आपको एंट्री पॉइंट पर कूदना होगा, और वह कभी वापस नहीं आएगा। सी प्रोग्राम के मामले में, आप main का पता खोद सकते हैं और यह प्रोटोटाइप से मेल खाता है। हालांकि आप सी पुस्तकालय के इनइलाइलाइजेशन को छोड़ रहे हैं (याद रखें, आपका कोड लाइब्रेरी लोडिंग नहीं करता है, इसलिए लोड किया गया प्रोग्राम स्थैतिक रूप से लिंक होना चाहिए) और यह एक समस्या हो सकती है।

मैंने libc संदर्भों को हल करके और main का आह्वान करके एक सरल सी प्रोग्राम को संभालने के लिए आवश्यक बिट्स के साथ कोड बढ़ाया है।

loader.c:

#include <stdio.h> 
#include <string.h> 
#include <libelf.h> 
#include <sys/mman.h> 
#include <dlfcn.h> 

void printk(const char* msg) 
{ 
    fputs(msg, stderr); 
} 

int is_image_valid(Elf32_Ehdr *hdr) 
{ 
    return 1; 
} 

void *resolve(const char* sym) 
{ 
    static void *handle = NULL; 
    if (handle == NULL) { 
     handle = dlopen("libc.so", RTLD_NOW); 
    } 
    return dlsym(handle, sym); 
} 

void relocate(Elf32_Shdr* shdr, const Elf32_Sym* syms, const char* strings, const char* src, char* dst) 
{ 
    Elf32_Rel* rel = (Elf32_Rel*)(src + shdr->sh_offset); 
    int j; 
    for(j = 0; j < shdr->sh_size/sizeof(Elf32_Rel); j += 1) { 
     const char* sym = strings + syms[ELF32_R_SYM(rel[j].r_info)].st_name; 
     switch(ELF32_R_TYPE(rel[j].r_info)) { 
      case R_386_JMP_SLOT: 
      case R_386_GLOB_DAT: 
       *(Elf32_Word*)(dst + rel[j].r_offset) = (Elf32_Word)resolve(sym); 
       break; 
     } 
    } 
} 

void* find_sym(const char* name, Elf32_Shdr* shdr, const char* strings, const char* src, char* dst) 
{ 
    Elf32_Sym* syms = (Elf32_Sym*)(src + shdr->sh_offset); 
    int i; 
    for(i = 0; i < shdr->sh_size/sizeof(Elf32_Sym); i += 1) { 
     if (strcmp(name, strings + syms[i].st_name) == 0) { 
      return dst + syms[i].st_value; 
     } 
    } 
    return NULL; 
} 

void *image_load (char *elf_start, unsigned int size) 
{ 
    Elf32_Ehdr  *hdr  = NULL; 
    Elf32_Phdr  *phdr = NULL; 
    Elf32_Shdr  *shdr = NULL; 
    Elf32_Sym  *syms = NULL; 
    char   *strings = NULL; 
    char   *start = NULL; 
    char   *taddr = NULL; 
    void   *entry = NULL; 
    int i = 0; 
    char *exec = NULL; 

    hdr = (Elf32_Ehdr *) elf_start; 

    if(!is_image_valid(hdr)) { 
     printk("image_load:: invalid ELF image\n"); 
     return 0; 
    } 

    exec = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, 
         MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 

    if(!exec) { 
     printk("image_load:: error allocating memory\n"); 
     return 0; 
    } 

    // Start with clean memory. 
    memset(exec,0x0,size); 

    phdr = (Elf32_Phdr *)(elf_start + hdr->e_phoff); 

    for(i=0; i < hdr->e_phnum; ++i) { 
      if(phdr[i].p_type != PT_LOAD) { 
        continue; 
      } 
      if(phdr[i].p_filesz > phdr[i].p_memsz) { 
        printk("image_load:: p_filesz > p_memsz\n"); 
        munmap(exec, size); 
        return 0; 
      } 
      if(!phdr[i].p_filesz) { 
        continue; 
      } 

      // p_filesz can be smaller than p_memsz, 
      // the difference is zeroe'd out. 
      start = elf_start + phdr[i].p_offset; 
      taddr = phdr[i].p_vaddr + exec; 
      memmove(taddr,start,phdr[i].p_filesz); 

      if(!(phdr[i].p_flags & PF_W)) { 
        // Read-only. 
        mprotect((unsigned char *) taddr, 
           phdr[i].p_memsz, 
           PROT_READ); 
      } 

      if(phdr[i].p_flags & PF_X) { 
        // Executable. 
        mprotect((unsigned char *) taddr, 
          phdr[i].p_memsz, 
          PROT_EXEC); 
      } 
    } 

    shdr = (Elf32_Shdr *)(elf_start + hdr->e_shoff); 

    for(i=0; i < hdr->e_shnum; ++i) { 
     if (shdr[i].sh_type == SHT_DYNSYM) { 
      syms = (Elf32_Sym*)(elf_start + shdr[i].sh_offset); 
      strings = elf_start + shdr[shdr[i].sh_link].sh_offset; 
      entry = find_sym("main", shdr + i, strings, elf_start, exec); 
      break; 
     } 
    } 

    for(i=0; i < hdr->e_shnum; ++i) { 
     if (shdr[i].sh_type == SHT_REL) { 
      relocate(shdr + i, syms, strings, elf_start, exec); 
     } 
    } 

    return entry; 

}/* image_load */ 

int main(int argc, char** argv, char** envp) 
{ 
    int (*ptr)(int, char **, char**); 
    static char buf[1048576]; 
    FILE* elf = fopen(argv[1], "rb"); 
    fread(buf, sizeof buf, 1, elf); 
    ptr=image_load(buf, sizeof buf); 
    return ptr(argc,argv,envp); 
} 

elf.c:

#include <stdio.h> 

int main() 
{ 
    fprintf(stdout, "Hello world! fprintf=%p, stdout=%p\n", fprintf, stdout); 
    return 0; 
} 

परीक्षण चालन:

$ gcc -m32 -g -Wall -ldl -o loader loader.c 
$ gcc -m32 -pie -fPIE -o elf elf.c 
$ ./loader elf 
Hello world! fprintf=0xf7612420, stdout=0xf770e4c0 
+1

आपकी सहायता के लिए धन्यवाद। समस्या तब होती है जब आप एक सी प्रोग्राम लोड करने का प्रयास करते हैं (यहां तक ​​कि एक सरल जो हैलो दुनिया प्रिंट करता है)। मेरा अनुमान है कि या तो सी _स्टार्ट रूटीन को सीधे कॉल करने में कोई समस्या है, या लोडर कोड में एक से अधिक सेक्शन को स्थानांतरित करने वाली बग है (जहां मेममोव को कॉल किया जाता है)। – Smokey

+0

उत्तर अपडेट किया गया। – Jester

+0

इसके लिए खेद है, लेकिन मैं सिर्फ यह जानना चाहता था कि आप कहां से लोगों को पुस्तकालय प्राप्त करते हैं। धन्यवाद – Kimutai

2

Exec = (अहस्ताक्षरित चार *) mmap (शून्य, आकार, ...

यह मनमाना पते पर निष्पादन लोड करने के लिए प्रयास करता है। एक गैर-पीआईई निष्पादन योग्य केवल उस पते पर लोड किया जा सकता है जिस पर यह लिंक किया गया था (आमतौर पर 0x08048000 लिनक्स/ix86 पर)।

समस्या तब होती है जब आप एक सी प्रोग्राम लोड करने का प्रयास करते हैं (यहां तक ​​कि एक सरल जो हैलो दुनिया प्रिंट करता है)।

कि कार्यक्रम गतिशील रूप से जुड़ा हुआ था, तो यह कुछ भी लेकिन आसान है, और अपने लोडर एक बहुत की एक बिल्ली अधिक करने के लिए है: लोड हो रहा है और स्थानांतरित निर्भर साझा पुस्तकालयों, GOT और TLS, आदि ऊपर फिक्सिंग । आदि।

0

उपयोग

exec = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, 
        MAP_PRIVATE | MAP_ANONYMOUS, hdr, 0); 

exec = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, 
        MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);