如何将ELF映像加载到内存并执行?

4
我已经尝试了数天来弄清楚这个问题。显然,我太缺乏经验而无法理解各种示例中的实际代码,无论我如何努力,都找不到足够简单易懂的解释。这真的不是我的菜。
我的问题是,我能否得到一个链接(或答案),其中包含一些非常易于理解的伪代码或说明,以执行以下操作:
在C程序中,将另一个ELF可执行文件加载到内存中,设置内存、堆栈和所有其他必要的变量,然后执行它。
我理解基本概念,但它对我来说不是很清楚。我已经查看了许多其他来源,包括StackOverflow上的内容,但它们对我这样的白痴大脑来说都太复杂了。
谢谢。

@rici 我正在尝试编写一个程序,它可以在用户空间中运行给定的可执行文件(elf)。据我所知,这意味着复制二进制代码、设置堆栈并将控制权传递给新程序。但我不知道如何实现这一点。 - Sefu
@sefu:操作系统会代为处理所有这些事情。如果您不需要在程序运行时逗留,请使用exec或其变体之一(共有六种); 如果您需要等待程序运行完毕,请使用forkexecwait - rici
@rici 我知道那些,但这个练习的目的是为了学习它在底层是如何工作的。execve本质上就是我想要替换的东西。 - Sefu
1
@sefu:在这种情况下,我不知道哪里越界了。您可以使用dlopen加载对象文件,使用dlsym查找其“main”符号;您可以使用getcontext和makecontext设置堆栈,并使用setcontext切换到它。 (*context函数已弃用;执行此操作的非弃用方式是使用线程,这将更容易。)这是您要寻找的内容吗?还是您应该编写一个加载器? - rici
然后,您将在堆栈地址范围的顶部设置环境指针、ELF辅助向量、参数等,加载正确的寄存器值,并跳转到动态链接器或主程序的入口点(取决于它是否是动态链接)。 - R.. GitHub STOP HELPING ICE
显示剩余5条评论
2个回答

0

在Linux x86上,execve是系统调用号11,可以使用以下方式调用:

long execve(const char *filename, char *const argv[], char *const envp[]){
  long r;
  asm volatile("int $128" : "=a"(r):"a"(11),"b"(filename),"c"(argv),"d"(envp):"memory");
  return r;
}

这就是大多数libc实现它的方式(尽管更间接,但带有错误处理等...)

如果想了解execve系统调用的工作原理,请查看Linux内核源代码。


0
在C程序中,将另一个ELF可执行文件加载到内存中,设置内存、堆栈和所有其他必要的变量,然后执行它。
实际上,在C程序中你无法这样做,因为C程序已经从自己的ELF映像中加载到内存并运行。两个ELF映像会相互冲突,因此当您尝试映射新映像时,您将在运行中的旧映像部分中断,导致事情无法正常工作。
内核在执行映像时的第一件事是清空(清空)用户地址空间,以便可以加载新映像而不会发生冲突。
话虽如此,您可以通过仔细的链接脚本来构建两个没有冲突的ELF映像,以便第一个可以加载第二个,并且两者可以同时存在于内存中。这基本上就是ld.so(动态链接器)的工作方式——它链接到特殊地址,以便与“普通”程序共存。但是,任何两个“普通”的ELF可执行文件都希望位于相同的地址处。

理论上,例如他不能仅通过基本内核工作来加载ELF文件(即可执行程序)到内核的可执行运行地址并运行,或者根本不可能工作? - The Mask
这不是真的。那么UPX是如何做到的呢?去检查它的源代码。 - Zibri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接