我在汇编中使用手动系统调用。我之前能够使其正常启动,但在删除空值后,我无法让系统调用执行/bin/date
。这是我用 AT&T 语法编写的代码。
.global main
main:
jmp two
one:
# zero rax and rdx
xor %rax,%rax
mov %rax,%rdx
# save string location
mov (%rsp),%rbx
# push argv array onto the stack
add $16, %rsp
push %rax
push %rbx
# assign argv pointer
mov %rsp,%rcx
# execve call
mov $0xb, %al
int $0x80
# exit on failure
xor %rax,%rax
xor %rbx,%rbx
movb $0x1,%al
int $0x80
two:
# get address of the string
call one
.string "/bin/date"
如果我是对的,%rbx
应该直接指向命名要启动的程序的字符串。%rcx
应该指向一个以空结尾的代表程序的指针数组argv
,并%rdx
指向环境,所以我把它留空了。当然,%rax
还有系统调用号(0x0b
在这种情况下)。
(gdb) info registers
rax 0xb 11
rbx 0x4000a0 4194464
rcx 0x7fffffffe968 140737488349544
rdx 0x0 0
(gdb) x/s $rbx
0x4000a0: "/bin/date"
(gdb) x/s *$rcx
0x4000a0: "/bin/date"
尽管如此,系统调用并没有执行程序,而是返回 -14,它转换为EFAULT
(segfault)。我不确定我忽略了什么,任何帮助将不胜感激。
因此,敏锐的读者可能已经注意到,上面的代码在 64 位系统上使用了32 位系统调用约定(使用%ebx
,和朋友)。int $0x80
这是一个错误,因为仅支持 32 位约定以启用执行 32 位代码。在为 64 位系统编写的代码中,系统调用使用、%rdi
、%rsi
、%rdx
和%r10
以及%r8
指令。这是 64 位系统的更正代码(nullfree):%r9
syscall
.global main
main:
jmp two
one:
# zero rax and rdx
xor %rax,%rax
mov %rax,%rdx
# save string location, note that %rdi is used instead of %rbx
pop %rdi
# push argv array onto the stack
add $16, %rsp
push %rax
push %rdi
# assign argv pointer, using %rsi instead of %rcx
mov %rsp,%rsi
# execve call, note that the syscall number is different than in 32bit
mov $0x3b, %al
syscall
two:
# get address of the string
call one
.string "/bin/date"
但是,64 位系统支持 32 位系统调用约定(因此可以运行 32 位可执行文件),并且我还成功地execve
在该系统上使用 32 位调用约定执行了其他命令。事实上,我为 x86_64 系统检查的大部分“shellcode”都使用 32 位约定。所以,我的问题仍然存在:为什么 32 位调用约定在上述代码中不起作用?