我有一个 elf64 可执行文件 foo,我想“手动”加载和启动它,并能够从中调用其他函数。我如何将它加载到内存中,然后设置指令指针来运行它。
foo不是共享对象库,它是一个可执行文件,具有导出的某些功能,就好像它是 SO。
所以,有几个问题:
例如,我有以下内容,但它存在段错误:
#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;
}
Run Code Online (Sandbox Code Playgroud)
由于各种原因,使用常规方法启动 foo() 像 system() 或其他标准 OS 工具不是一种选择。我需要能够调用 _start 来启动它,并在它开始运行后调用“foo_bar()”。我曾尝试使用 dlopen/dlsym 但它不起作用,因为它是可执行文件而不是共享库
printf("loaded ELF onto stack \n");
那是个谎言:您将可执行文件加载到heap 中,而不是stack 中。
我在哪里将二进制文件加载到内存中以便可执行?堆?堆?
堆和栈都不合适;用于mmap
将二进制文件带入内存。mmap
可能需要多次调用(PT_LOAD
目标二进制文件中的每个段一个)。
如何设置指令指针以从我的程序更改为 foo 的入口点?
更改指令指针是您的问题中最少的(jmp
您已经编码的将起作用,或者您可以将地址转换为函数指针,然后简单地调用它——不需要内联汇编)。
您要实现的目标非常重要。查看 UPX 来源以了解所涉及的内容。
如果二进制文件是非 PIE 可执行文件,则您无法将其加载到任意内存位置——它已被链接到要加载到特定地址,如果加载到任何其他地址,则将无法正确运行。
如果二进制文件是 PIE 可执行文件,它可以在任意地址运行(尽管它仍然有对齐要求,这malloc
不太可能满足)。
在旧版本的 GLIBC 中,dlopen()
PIE 可执行文件是可能的,但新版本不允许这样做。
在 PIE 可执行文件可以运行之前,它必须重新定位,这又是一项非常重要的任务。
TL;DR:您可能真的不想这样做(另请参阅http://xyproblem.info),但如果您真的这样做,肯定比仅仅read
将二进制文件放入缓冲区并跳转需要更多的努力到其中的_start
符号。