如何直接从内存编译和执行? [英] How to compile and execute from memory directly?

查看:170
本文介绍了如何直接从内存编译和执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可以编译一个C ++(或类似的)程序,而不生成可执行文件,但写入它并直接从内存中执行?



code> GCC 和 clang ,其效果类似于:

  c ++ hello.cpp -o hello.x&&& ./hello.x $ @&&& rm -f hello.x 



但没有将可执行文件写入磁盘以立即加载/再次运行它的负担。



(如果可能,程序可能不使用磁盘空间。)

解决方案

可能?不是你似乎希望的方式。任务有两部分:



1)如何获取二进制内存



当我们指定 / dev / stdout 作为Linux中的输出文件,我们可以导入我们的程序 x0 ,它读取
一个可执行文件stdout并执行它:

  gcc -pipe YourFiles1.cpp YourFile2.cpp -o / dev / stdout -Wall | ./x0 

x0 从stdin读取,直到到达文件的结尾:

  int main(int argc,const char ** argv)
{
const int stdin = 0;
size_t ntotal = 0;
char * buf = 0;
while(true)
{
/ *动态增加缓冲区大小,因为我们不知道要读多少字节* /
buf =(char *)realloc(buf,ntotal + 4096 * sizeof(char));
int nread = read(stdin,buf + ntotal,4096);
if(nread <0)break;
ntotal + = nread;
}
memexec(buf,ntotal,argv);
}

也可以 x0 直接执行编译器并读取输出。此问题已在此处回答:将exec输出重定向到缓冲区或文件



注意:我刚刚发现,由于某些奇怪的原因,当我使用pipe | 但是当我使用 x0< foo



注意:如果你愿意修改你的编译器或者你像JVM LLVM,clang和其他框架可以直接生成可执行代码。



注意:通过临时文件执行



其他程序如UPX通过执行临时文件来实现类似的行为,这比下面概述的方法更容易和更便携。在将 / tmp 映射到RAM磁盘(例如典型的服务器)的系统上,临时文件将以内存为基础。

  int memexec(void * exe,size_t exe_size,const char * argv)
{
/ * / tmp * /
中的随机临时文件名char char [15] =/ tmp / fooXXXXXX;
/ *创建临时文件,返回可写文件描述符* /
int fd_wr = mkostemp(name,O_WRONLY);
/ *使文件可执行和只读* /
chmod(name,S_IRUSR | S_IXUSR);
/ *在删除文件之前创建只读文件描述符* /
int fd_ro = open(name,O_RDONLY);
/ *从文件系统中删除文件,内核缓冲内存中的内容,直到所有fd关闭* /
unlink(name);
/ *可执行文件* /
write(fd_wr,exe,exe_size);
/ * fexecve不会工作,只要有一个开放的可写文件描述符* /
close(fd_wr);
char * const newenviron [] = {NULL};
/ * -fpermissive * /
fexecve(fd_ro,argv,newenviron);
perror(failed);
}

注意:清酒。

通过结合步骤 main()插入单个函数并使用 splice(2)直接在 stdin之间复制



2)直接从内存执行



不能简单地载入并执行 ELF 二进制从内存。一些准备,主要涉及动态链接,必须发生。有很多材料解释了ELF链接过程的各个步骤,并且研究它使我相信理论上可能的。例如,查看与此密切相关的 SO问题,但似乎不存在工作解决方案。



更新 UserModeExec似乎非常接近。



<编写一个工作实现将是非常耗时的,并且肯定会提出一些有趣的问题在自己的权利。我喜欢相信这是设计:对于大多数应用程序,非常不愿意(意外)执行其输入数据,因为它允许



执行ELF时会发生什么?通常,内核接收一个文件名,然后创建一个进程,加载和映射可执行文件的不同部分到内存,执行许多健全检查,并将其标记为可执行,然后传递控制和文件名回到运行时链接器 ld-linux.so (libc的一部分)。它负责重定位函数,处理附加库,设置全局对象和跳转到可执行文件入口点。 AIU这个繁重的工作由 dl_main()(在libc / elf / rtld.c中实现)完成。



<甚至 fexecve 是使用 / proc 中的文件实现的,这就是需要一个文件名引导我们重新实现这个链接过程的一部分。



图书馆





阅读





SO相关问题





因此,您可以决定是否也可行。


Is is possible to compile a C++ (or the like) program without generating the executable file but writting it and executing it directly from memory?

For example with GCC and clang, something that has a similar effect to:

c++ hello.cpp -o hello.x && ./hello.x $@ && rm -f hello.x

in the command line.

But without the burden of writing an executable to disk to immediately load/run it again.

(If possible the procedure may not use disk space.)

解决方案

Possible? Not the way you seem to wish. The task has two parts:

1) How to get the binary into memory

When we specify /dev/stdout as output file in Linux we can then pipe into our program x0 that reads an executable from stdin and executes it:

  gcc -pipe YourFiles1.cpp YourFile2.cpp -o/dev/stdout -Wall | ./x0

In x0 we can just read from stdin until reaching the end of the file:

int main(int argc, const char ** argv)
{
    const int stdin = 0;
    size_t ntotal = 0;
    char * buf = 0;
    while(true)
    {
        /* increasing buffer size dynamically since we do not know how many bytes to read */
        buf = (char*)realloc(buf, ntotal+4096*sizeof(char));
        int nread = read(stdin, buf+ntotal, 4096); 
        if (nread<0) break;
        ntotal += nread;
    }
    memexec(buf, ntotal, argv); 
}

It would also be possible for x0 directly execute the compiler and read the output. This question has been answered here: Redirecting exec output to a buffer or file

Caveat: I just figured out that for some strange reason this does not work when I use pipe | but works when I use the x0 < foo.

Note: If you are willing to modify your compiler or you do JIT like LLVM, clang and other frameworks you could directly generate executable code. However for the rest of this discussion I assume you want to use an existing compiler.

Note: Execution via temporary file

Other programs such as UPX achieve a similar behavior by executing a temporary file, this is easier and more portable than the approach outlined below. On systems where /tmp is mapped to a RAM disk for example typical servers, the temporary file will be memory based anyway.

int memexec(void * exe, size_t exe_size, const char * argv)
{
    /* random temporary file name in /tmp */
    char name[15] = "/tmp/fooXXXXXX"; 
    /* creates temporary file, returns writeable file descriptor */
    int fd_wr = mkostemp(name,  O_WRONLY);
    /* makes file executable and readonly */
    chmod(name, S_IRUSR | S_IXUSR);
    /* creates read-only file descriptor before deleting the file */
    int fd_ro = open(name, O_RDONLY);
    /* removes file from file system, kernel buffers content in memory until all fd closed */
    unlink(name);
    /* writes executable to file */
    write(fd_wr, exe, exe_size);
    /* fexecve will not work as long as there in a open writeable file descriptor */
    close(fd_wr);
    char *const newenviron[] = { NULL };
    /* -fpermissive */
    fexecve(fd_ro, argv, newenviron);
    perror("failed");
}

Caveat: Error handling is left out for clarities sake. Includes for sake of brevity.

Note: By combining step main() and memexec() into a single function and using splice(2) for copying directly between stdin and fd_wr the program could be significantly optimized.

2) Execution directly from memory

One does not simply load and execute an ELF binary from memory. Some preparation, mostly related to dynamic linking, has to happen. There is a lot of material explaining the various steps of the ELF linking process and studying it makes me believe that theoretically possible. See for example this closely related question on SO however there seems not to exist a working solution.

Update UserModeExec seems to come very close.

Writing a working implementation would be very time consuming, and surely raise some interesting questions in its own right. I like to believe this is by design: for most applications it is strongly undesirable to (accidentially) execute its input data because it allows code injection.

What happens exactly when an ELF is executed? Normally the kernel receives a file name and then creates a process, loads and maps the different sections of the executable into memory, performs a lot of sanity checks and marks it as executable before passing control and a file name back to the run-time linker ld-linux.so (part of libc). The takes care of relocating functions, handling additional libraries, setting up global objects and jumping to the executables entry point. AIU this heavy lifting is done by dl_main() (implemented in libc/elf/rtld.c).

Even fexecve is implemented using a file in /proc and it is this need for a file name that leads us to reimplement parts of this linking process.

Libraries

Reading

Related Questions at SO

So it seems possible, you decide whether is also practical.

这篇关于如何直接从内存编译和执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆