我对__NR_execve
系统调用感到困惑。当我学习Linux系统调用时,我知道使用execve
的正确方式是这样的:
char *sc[2];
sc[0]="/bin/sh";
sc[1]= NULL;
execve(sc[0],sc,NULL);
然后函数execve
将调用syscall()
以在将参数放置在寄存器EAX
、EBX
、ECX
和EDX
上进入系统内核。 但是,即使我使用
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
也不好看;最好使用execv
或execvp
,或者传递一个指向char *p = NULL
的指针。) - Antti Haapala -- Слава Україніexecve(2)
接受空指针,并将其视为指向空列表的指针。这是不鼓励的且不可移植的,但在Linux上具有未来性。我看到的主要用例是用于利用shellcode,在系统调用之前将几个寄存器清零(x86int 0x80
或syscall
),而不是推送一个0
并将其指针传递给两个寄存器。也就是说,它可以节省一些字节的利用负载大小,并且如果"/bin/sh"
字符串已经在负载中,则不需要写入内存。 - Peter Cordessys_execve
漏洞,返回"-EFAULT",那么这并不是什么缺点。但可能有一些有效的用例,比如传递一个空环境变量,某些代码会使用它。或者在内核实现方面,接受它比拒绝更容易/更清晰。虽然它检查得很早,所以可以轻松地返回错误,但与某些后期错误不同,进程会因为调用方已经部分被破坏而死亡,因此execve
无法返回。 - Peter Cordes