如何从另一个可执行文件调用二进制文件的 _start?

时间:2021-07-01 23:16:12

标签: c assembly elf

我有一个 elf64 可执行文件 foo,我想“手动”加载和启动它,并能够从中调用其他函数。我如何将它加载到内存中,然后设置指令指针来运行它。

foo 不是一个共享对象库,它是一个可执行文件,它具有像 SO 一样导出的某些函数。

那么,几个问题:

  1. 在哪里将二进制文件加载到内存中以便可执行?堆?堆?
  2. 如何设置指令指针以从我的程序更改为 foo 的入口点?

例如,我有以下内容,但它有段错误:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>

#define ELF_SIZE 10000

int main(int argc, char **argv)
{

    FILE *fp;
    void *   entry_point ;
    Elf64_Ehdr *elfHdr;
    uint8_t *buffer = malloc(ELF_SIZE);

    fp = fopen("./foo", "rb");

    int read_size = fread(buffer, 1, ELF_SIZE, fp);
    if (read_size == ELF_SIZE)
    {
        printf("loaded ELF onto heap
\n");
    } else
    {
        printf("read failed: %d\n", read_size);
        return 0;
    }
    printf("elf loaded at %x\n", buffer);

    elfHdr = (Elf64_Ehdr*) buffer;

    printf("entry point at %x\n", elfHdr->e_entry);

    entry_point = elfHdr->e_entry + buffer;

    printf("trying to jump to: %x\n", entry_point
            );
        int a;
        __asm__ ("jmp %1;"
                : "=r" (a)
                : "r" (entry_point));

    return 0;

} 

由于各种原因,不能使用像 system() 或其他标准操作系统工具这样的常规方法来启动 foo()。我需要能够调用 _start 来启动它,并在它开始运行后调用“foo_bar()”。我曾尝试使用 dlopen/dlsym 但它不起作用,因为它是可执行文件而不是共享库

1 个答案:

答案 0 :(得分:5)

<块引用>

printf("loaded ELF onto stack \n");

那是谎言:您将可执行文件加载到,而不是堆栈

<块引用>

我在哪里将二进制文件加载到内存中以便可执行?堆?堆?

堆和栈都不合适;使用 mmap 将二进制文件带入内存。可能需要多次调用 mmap(目标二进制文件中的每个 PT_LOAD 段一个)。

<块引用>

如何设置指令指针从我的程序更改为 foo 的入口点?

更改指令指针是最少的问题(您已经编码的 jmp 将起作用,或者您可以将地址转换为函数指针,然后简单地调用它-- 不需要内联汇编)。


您要实现的目标非常重要。查看 UPX 来源以了解所涉及的内容。

如果二进制文件是非 PIE 可执行文件,则您无法将其加载到任意内存位置——它已被链接以加载到特定地址,如果出现以下情况,将无法正确运行在任何其他地址加载。

如果二进制文件是 PIE 可执行文件,它可以在任意地址运行(尽管它仍然有对齐要求,malloc 不太可能满足)。

在旧版本的 GLIBC 中,可以dlopen() PIE 可执行文件,但新版本不允许这样做。

在 PIE 可执行文件可以运行之前,它必须重新定位,这又是一项非常重要的任务。

TL;DR:您可能真的不想这样做(另请参阅 http://xyproblem.info),但如果您真的这样做,肯定比仅仅 read二进制进入缓冲区并跳转到其中的 _start 符号。

相关问题