如果不传递 0 作为 execve 的环境指针,为什么这个 shellcode 会工作?

Why would this shellcode work if it does NOT pass 0 as the environment pointer for execve?

https://www.exploit-db.com/exploits/46907

我的理解是,由于 x64 调用约定,execve 的第 3 个参数,envp,应该存储在 rdx 中。但是这个 shellcode 不会将这个寄存器置零,它只会将 rsi 寄存器(存储 arv)置零。因此,如果 rdx 的当前值未指向有效位置,则会导致段错误,不是吗?

我是不是漏掉了什么?

asm 确实 写入 RDX(带 0):注意 syscall 之前的 cdqEAX=59的符号位为0,所以EDX=0,将EDX写零扩展到RDX。

给定一个已知的非负 EAX,这是一个 standard code-golf trick,用于使用 1 字节指令而不是 xor edx,edx 将 EDX/RDX 归零。


Linux 特殊情况 NULL argvenvp 指针像空列表一样工作(指向内存中的 NULL 指针)。请参阅手册页:https://man7.org/linux/man-pages/man2/execve.2.html#NOTES

手册页不鼓励 C 程序的做法,因为它不能移植到其他 unix,但 shellcode 已经不能,并且它节省了机器代码大小的字节数。

在 Linux 下静态可执行文件的 _start 中,除 RSP 之外的所有 regs 都将是 0。 (x86-64 SysV ABI 不能保证这一点,它只是内核选择的方便值,以避免在进入用户 space 之前发生信息泄漏。)所以即使它确实有你认为的错误。

但他们也通过将机器代码字节放入 C 程序中 .data 中的数组并从 main() 调用它来进行测试。 这也适用于未修改 RDX 的错误 shellcode:编译器生成的用于通过函数指针调用的代码可能会使 RDX 未修改。
在进入 main 时,EDI=argc,RSI=argv,RDX=envp。所以这个机器代码块将以 RDX 开始,它已经是指向 char **envp 的有效指针!也许比他们预期的测试要少一些。 :P

main 的第 3 个参数 envp 未被 POSIX 指定,但得到广泛支持:Is char *envp[] as a third argument to main() portable
x86-64 Linux 的系统调用约定与其函数调用约定非常相似,有意让系统调用包装函数只需要 mov r10, rcx / mov eax, __NR_... / syscall.


顺便说一句,系统调用的错误参数永远不会导致分段错误(SIGSEGV 信号被传送到您的进程)。相反,当您将指针传递给未映射的内存时,Linux 系统调用 return 一个 -EFAULT 错误代码,以防万一。

(有趣的事实:write(1, buf, way_past_end) 将成功写入从 buf 地址开始实际映射的页面末尾,并且 return 该长度。您只会得到 -EFAULT 如果在遇到无法读取的页面之前向 fd 写入了 0 个字节。)