为什么execve系统调用可以在没有任何argv参数的情况下运行"/bin/sh",但不能运行"/bin/ls"?

12

我对__NR_execve系统调用感到困惑。当我学习Linux系统调用时,我知道使用execve的正确方式是这样的:

char *sc[2]; 
sc[0]="/bin/sh"; 
sc[1]= NULL; 
execve(sc[0],sc,NULL); 

然后函数execve将调用syscall()以在将参数放置在寄存器EAXEBXECXEDX上进入系统内核。 但是,即使我使用

execve("/bin/sh",NULL,NULL);

但是,如果我将"/bin/sh"替换为"/bin/ls",它会失败,显示:

但是,如果我将"/bin/sh"替换为"/bin/ls",它会失败,显示:

A NULL argv[0] was passed through an exec system call.

我想知道为什么在不提供足够参数的情况下,"/bin/sh" 可以成功执行,而 "/bin/ls" 却失败了?


我不会依赖于 NULL 在那里起作用;execve 手册说 argv 是一个参数字符串数组;NULL 指针不是指向数组的有效指针。 (使用 NULL 作为 env 也不好看;最好使用 execvexecvp,或者传递一个指向 char *p = NULL 的指针。) - Antti Haapala -- Слава Україні
当数组作为函数参数传递时,它们是指向其第一个元素的指针,因此在这里使用NULL是完全有效的。 - fluter
@AnttiHaapala:Linux的execve(2)接受空指针,并将其视为指向空列表的指针。这是不鼓励的且不可移植的,但在Linux上具有未来性。我看到的主要用例是用于利用shellcode,在系统调用之前将几个寄存器清零(x86 int 0x80syscall),而不是推送一个0并将其指针传递给两个寄存器。也就是说,它可以节省一些字节的利用负载大小,并且如果"/bin/sh"字符串已经在负载中,则不需要写入内存。 - Peter Cordes
@petercordes 很好,Hellcodez 保持兼容性。 - Antti Haapala -- Слава Україні
但是,是的,手册确实提到了这个异常。 - Antti Haapala -- Слава Україні
@AnttiHaapala:我知道,没错。如果唯一的问题是破坏了现有的sys_execve漏洞,返回"-EFAULT",那么这并不是什么缺点。但可能有一些有效的用例,比如传递一个空环境变量,某些代码会使用它。或者在内核实现方面,接受它比拒绝更容易/更清晰。虽然它检查得很早,所以可以轻松地返回错误,但与某些后期错误不同,进程会因为调用方已经部分被破坏而死亡,因此execve无法返回。 - Peter Cordes
1个回答

16

这不是一个内核问题。无论argvenvp是否为NULL,内核都将使用execvefilename参数运行。这只是Unix的约定,即argv[0]指向程序名称。

你看到的只是正常情况,没有任何问题。因为ls是GNU coreutils的一部分,coreutils包中的所有程序都会调用set_program_name来做一些设置工作。你可以在源代码中看到这点:它检查argv [0]是否为NULL,如果是,则会调用abort

另一方面,/bin/sh显然是不属于coreutils的程序,并且不检查argv [0]。这就是为什么它能够正常运行的原因。

参考源代码:


这里是一个没有任何argv的最小示例:https://dev59.com/ZXE85IYBdhLWcg3wZyqt#42290873 - Ciro Santilli OurBigBook.com

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