我试图了解execve
系统调用的工作方式以及底层程序集。这是我的示例程序:
// p1.c
#include <stdio.h>
void main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
我的系统是x86_64
。我使用gcc -ggdb -static -fno-stack-protector p1.c
进行了编译。 Gcc版本为7.3.0
这里仅是objdump -d ./a.out
的{{1}}
main
我了解每一行,直到0000000000400b4d <main>:
400b4d: 55 push %rbp
400b4e: 48 89 e5 mov %rsp,%rbp
400b51: 48 83 ec 10 sub $0x10,%rsp
400b55: 48 8d 05 08 14 09 00 lea 0x91408(%rip),%rax # 491f64 <_IO_stdin_used+0x4>
400b5c: 48 89 45 f0 mov %rax,-0x10(%rbp)
400b60: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
400b67: 00
400b68: 48 8b 45 f0 mov -0x10(%rbp),%rax
400b6c: 48 8d 4d f0 lea -0x10(%rbp),%rcx
400b70: ba 00 00 00 00 mov $0x0,%edx
400b75: 48 89 ce mov %rcx,%rsi
400b78: 48 89 c7 mov %rax,%rdi
400b7b: e8 50 7f 04 00 callq 448ad0 <__execve>
400b80: 90 nop
400b81: c9 leaveq
400b82: c3 retq
400b83: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
400b8a: 00 00 00
400b8d: 0f 1f 00 nopl (%rax)
通话为止。然后,我从callq 448ad0 <__execve>
输出中检查了__execve
程序集。
objdump
我了解到0000000000448ad0 <__execve>:
448ad0: b8 3b 00 00 00 mov $0x3b,%eax
448ad5: 0f 05 syscall
448ad7: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
448add: 73 01 jae 448ae0 <__execve+0x10>
448adf: c3 retq
448ae0: 48 c7 c1 c0 ff ff ff mov $0xffffffffffffffc0,%rcx
448ae7: f7 d8 neg %eax
448ae9: 64 89 01 mov %eax,%fs:(%rcx)
448aec: 48 83 c8 ff or $0xffffffffffffffff,%rax
448af0: c3 retq
448af1: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
448af8: 00 00 00
448afb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
寄存器用于通过edx,rsi,rdi
函数传递execve
自变量。但是我找不到任何汇编代码来处理main
代码中的这些参数。
我猜测__execve
可能会导致系统调用号和$0x3b
指令隐藏大部分实现。如果我执行交互式gdb前进步骤,则在syscall
的{{1}}之后,shell提示符将启动。
我可以知道(如果是如何吗?),还使用哪些其他寄存器来处理参数并执行syscall instruction
系统调用?或者,位于地址448ad5:
的{{1}}指令是否直接从__execve
寄存器中获取参数值?