Jump to entry point of ELF from loader

╄→гoц情女王★ 提交于 2019-12-04 04:21:01

问题


Thanks to the help in this question, the loader can now map a statically compiled hello world into memory and jump somewhere in that memory region. The problem I'm facing now is I seem not to jump to the right address or I'm calling the function in the wrong way (or wrong function?).

Below is the code to try; I can't find in glibc where the loader calls the entry point of the program to verify I'm doing the right thing. I tried:

  • Calling _start(void)
  • Calling _start(int, char**, char**)
  • Calling __libc_start_main with the last parameters set to NULL (found here)
  • Calling directly main
  • Calling some addresses in (found with gdb in rdi: 400B4D, r8: 4018E0, rcx: 401840)

The entry point is 0x400a30, some instructions below __libc_start_main. With some of them it SIGSEGV, SIGABRT or prints:

hello, world! haswell xeon_phi ../csu/libc-start.c FATAL: kernel too old
   __ehdr_start.e_phentsize == sizeof *GL(dl_phdr) unexpected reloc type in static binary  FATAL: cannot determine kernel version
 __libc_start_main /dev/full /dev/null   cannot set %fs base address for thread-local storage :  %s%s%s:%u: %s%sAssertion `%s' failed.
%n        Unexpected error.

and some hundred of junk lines.

int main(int argc, char* argv[argc+1]) {
    FILE *fp = fopen(argv[1], "r");
    if (!fp) {
        fprintf(stderr, "cannot open file %s", argv[1]);
        return 1;
    }

    fseek(fp, 0L, SEEK_END);
    size_t sz = ftell(fp) + 1;
    rewind(fp);

    char *region = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
    if (region == MAP_FAILED) {
        fprintf(stderr, "could not mmap");
        return 1;
    }

    Elf64_Ehdr elf64Ehdr;
    memmove(&elf64Ehdr, region, sizeof(elf64Ehdr));

    size_t offset;
    Elf64_Phdr elf64Phdr;
    for (uint16_t i = 0; i != elf64Ehdr.e_phnum; ++i) {
        offset = elf64Ehdr.e_phoff + i * elf64Ehdr.e_phentsize;
        memmove(&elf64Phdr, region + offset, sizeof(elf64Phdr));
        switch (elf64Phdr.p_type) {
            case PT_NULL:
                break;
            case PT_LOAD:
                if (load(&elf64Phdr, region + elf64Phdr.p_offset)) {
                    exit(EXIT_FAILURE);
                }
                break;
            default:
                break;
        }
    }
    printf("jumping to: 0x%x\n", elf64Ehdr.e_entry);

    char *argv1[] = {"", NULL};

    int ret = ((int (*)(int, char **, char **)) elf64Ehdr.e_entry)(1, argv1, argv1);
    return ret;
}

int load(const Elf64_Phdr *phdr, const void *elf_bytes_for_phdr) {
    const size_t pagesize = getpagesize();
    const size_t unaligned_bytes = phdr->p_vaddr % pagesize;

    void *base_addr = phdr->p_vaddr - unaligned_bytes;
    size_t total_bytes = phdr->p_memsz + unaligned_bytes;

    void *region = mmap(base_addr, total_bytes,
            phdr->p_flags | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    if (region != MAP_FAILED) {
        memset(region, 0, unaligned_bytes);
        return memcpy(region + unaligned_bytes, elf_bytes_for_phdr, phdr->p_filesz) != region + unaligned_bytes && 
        ! mprotect(region, total_bytes, phdr->p_flags);
    }
    return 1;
}

回答1:


The problem I'm facing now is I seem not to jump to the right address or I'm calling the function in the wrong way

Your problem is neither of the above (although "calling the wrong way" is not too far).

For a statically-linked executable, Elf64_Ehdr.e_entry is the correct address to call (it points to _start), and _start takes no arguments.

The problem is that it is the job of _start to

  1. initialize libc, and
  2. to find correct values of argc, argv and envp, and finally
  3. to call main(argc, argv, envp).

The question then is: how can _start accomplish step 2?

The answer: there is a protocol that Linux kernel implements and _start uses to accomplish step 2.

In particular, the kernel copies the actual (string) values of argv[0], argv[1], ... envp[0], envp[1], etc. to the stack, then the pointers to these strings. There is also something called auxilliary vector.

The _start expects to find all of this info on the stack, and will misbehave when it doesn't find it. I believe that is the root cause of your current problem.

Here is an article which explains expected setup with references to Linux kernel source code. Another article.



来源:https://stackoverflow.com/questions/55564620/jump-to-entry-point-of-elf-from-loader

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!