echo 'main(){}' | gcc -xc - -o /dev/stdout | ???
有没有办法在类 UNIX 系统上运行输出二进制文件?
编辑:我需要它在我无法编写的沙盒环境中运行 g++ 的输出任何文件(我保证没有恶意)。
答案1
我不相信这是可能的。这执行(2)系统调用始终需要文件名或绝对路径(文件名始终为char*
)。 posix_spawn
对文件名也有类似的要求。
您可以做的最接近的是将输出通过管道传输到命名管道并尝试从管道执行。尽管 shell 可能拒绝执行任何未设置位的文件--x--x--x
,但这可能会起作用。创建管道mkfifo(1)
看看你能否让它发挥作用。
另一种方法是编写一些读取标准输入的内容,将文件写入临时区域,在其上设置 --x 位,forks 和 execs,然后删除该文件。索引节点和内容将保留到程序完成执行,但无法通过文件系统访问。当进程终止时,inode 将被释放,存储空间将返回到空闲列表。
编辑:正如 Mat 指出的那样,第一种方法不起作用,因为加载程序将尝试在可执行文件中请求页面,这将在文件上生成随机查找流量,而这在管道上是不可能的。这就留下了类似于第二种的某种方法。
答案2
你可以尝试TCC,它将一步编译并执行程序,而无需编写任何中间文件。它不是 gcc,这对你来说可能是个问题,但它的速度非常快,所以它甚至可能比 gcc 更适合你的目的。
答案3
使用 memfd 系统调用的解决方案:https://github.com/abbat/elfexec
它在内存中创建一个命名文件描述符,可以在exec
.伪代码:
#include <linux/memfd.h>
...
int memfd = syscall(SYS_memfd_create, "someName", 0);
...
write(memfd,... elf-content...);
...
fexecve(memfd, argv, environ);
答案4
无论如何,gcc
将 C 文件编译为可执行文件时会创建许多临时文件:
$ echo 'int main(){}' | strace -fe /open -o >(grep CREAT) gcc -xc -
96706 openat(AT_FDCWD, "/tmp/ccxiPEgx.s", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96707 openat(AT_FDCWD, "/tmp/ccxiPEgx.s", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 0
96706 openat(AT_FDCWD, "/tmp/ccIBSUD4.o", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96711 openat(AT_FDCWD, "/tmp/ccIBSUD4.o", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
96706 openat(AT_FDCWD, "/tmp/cciAP0FV.res", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96712 openat(AT_FDCWD, "/tmp/ccfZa2PH.cdtor.c", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96712 openat(AT_FDCWD, "/tmp/cc4LkCnx.cdtor.o", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
96713 openat(AT_FDCWD, "a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
因此,您不妨为最终的可执行文件创建另一个。
如果在 Linux 上,它可能是预删除的。例如,对于仍将此处文档或此处字符串实现为已删除临时文件(例如 zsh)的 shell:
$ echo 'int main(){puts("Hello World");}' | { gcc -o /dev/fd/3 -x c - && /dev/fd/3; } 3<<< ''
<stdin>: In function ‘main’:
<stdin>:1:12: warning: implicit declaration of function ‘puts’ [-Wimplicit-function-declaration]
<stdin>:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘puts’
Hello World