【问题标题】:Jump to entry point of ELF from loader从 loader 跳转到 ELF 的入口点
【发布时间】:2019-04-07 23:42:24
【问题描述】:

感谢this 问题中的帮助,加载程序现在可以将静态编译的 hello world 映射到内存并跳转到该内存区域的某个位置。我现在面临的问题是我似乎没有跳转到正确的地址或者我以错误的方式调用函数(或错误的函数?)。

下面是要尝试的代码;我在 glibc 中找不到加载程序调用程序入口点的位置来验证我在做正确的事情。我试过了:

  • 调用 _start(void)
  • 调用 _start(int, char**, char**)
  • 在最后一个参数设置为 NULL 的情况下调用 __libc_start_main(找到 here
  • 直接调用main
  • 调用一些地址(使用 gdb 在 rdi: 400B4D, r8: 4018E0, rcx: 401840)

入口点是0x400a30,__libc_start_main下面的一些指令。其中一些是 SIGSEGV、SIGABRT 或打印:

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.

还有几百条垃圾线。

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

【问题讨论】:

  • 我错过了什么吗?看起来您是直接调用 elf64Ehdr.e_entry ,即没有偏移基址(区域)...?

标签: c libc


【解决方案1】:

我现在面临的问题是我似乎没有跳转到正确的地址或者我以错误的方式调用函数

你的问题都不是上述的(虽然“叫错方式”也不算太远)。

对于静态链接的可执行文件,Elf64_Ehdr.e_entry 是要调用的正确地址(它指向 _start),_start 不带任何参数。

问题是_start的工作

  1. 初始化 libc,然后
  2. 找到argcargvenvp的正确值,最后
  3. 致电main(argc, argv, envp)

接下来的问题是:_start 如何完成第 2 步?

答案:Linux 内核实现_start使用的协议来完成第2步。

特别是,内核将argv[0]argv[1]、...envp[0]envp[1] 等的实际(字符串)值复制到堆栈中,然后是指向这些字符串的指针。还有一种叫做辅助向量的东西。

_start 期望在堆栈上找到所有这些信息,如果找不到它就会行为不端。我相信这是您当前问题的根本原因。

这里是article,它通过参考 Linux 内核源代码解释了预期的设置。另一个article

【讨论】:

  • 更简单的说,入口点不是C函数。入口契约要求某些寄存器有特定的初始值,主要参数是指向堆栈指针中的 argc、argv 条目、环境条目、aux 的向量的指针。
  • 我喜欢这两篇文章,多加链接!回到我的问题:我唯一的选择是使用内联 asm(或纯链接 asm)推送到堆栈 argc 以及 argv 和 env 的地址吗?用 (argc, argv, env) 调用 _start 实际上并没有帮助。 Elf64_Ehdr.e_start 是什么?我看不到参考。给它。 + @R..
  • @bicup 对不起,我的意思是.e_entry(你已经在这样做了)。是的,你必须让堆栈看起来完全内核准备它的方式和_start期望。
  • @bicup: “调用”_start(而不是e_entry)一般来说没有 asm 是不可能的,因为它不是一个函数。对于某些拱门,可能会编写一个函数调用,其中 ABI 约束恰好保证 ELF 入口点的正确属性,但这甚至比编写 asm(并且同样特定于拱门)更糟糕。
  • @R.. 我在 x64 中写了一个简单的function,它推送 1 (argc) 和 3 次 (argv, env, aux) 和它在 0x0 崩溃。它可能取决于_start 不是从堆栈中读取而是在x64 中注册吗?
猜你喜欢
  • 1970-01-01
  • 2017-10-13
  • 2017-11-18
  • 2023-03-04
  • 2012-01-29
  • 2019-04-20
  • 1970-01-01
  • 2012-05-09
  • 2015-05-18
相关资源
最近更新 更多