Assembly 在64位AMD上使用32位调用约定返回EFAULT的execve syscall
我在汇编中玩手动系统调用。我以前能够使它正确启动,但在删除空值后,我无法让系统调用执行Assembly 在64位AMD上使用32位调用约定返回EFAULT的execve syscall,assembly,x86-64,Assembly,X86 64,我在汇编中玩手动系统调用。我以前能够使它正确启动,但在删除空值后,我无法让系统调用执行/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 ar
/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
应该指向一个以null结尾的指针数组,该数组表示程序的argv
,而%rdx
将指向环境,因此我在这里将其保留为null。当然,%rax
保存系统调用号(0x0b
)
尽管如此,系统调用不执行程序,并返回-14,这将转换为EFAULT
(segfault)。我不确定我忽略了什么,任何帮助都将不胜感激
因此,敏锐的读者可能已经注意到,上述代码在64位系统上使用32位系统调用约定(使用
%ebx
、int$0x80
和friends)。这是一个错误,因为32位约定仅支持执行32位代码。在为64位系统编写的代码中,系统调用使用%rdi
、%rsi
、%rdx
、%r10
、%r8
和%r9
以及syscall
指令。以下是64位系统(无空)的更正代码:
但是,64位系统支持32位系统调用约定(因此可以运行32位可执行文件),我还成功地在该系统上使用32位调用约定执行了execve
d其他命令。事实上,我为x86_64系统检查的大多数“外壳代码”都使用32位约定。因此,我的问题仍然存在:为什么32位调用约定在上述代码中不起作用?execve()调用具有原型
int execve(const char *filename,
char *const argv[],
char *const envp[]);
请注意,通常不会传递NULL
指针,但如果envp[]
数组为空,则应传递指向NULL的指针,该指针用作环境变量的结尾。同样,argv[]
也不能是空指针。它必须至少包含作为程序名的argv[0]
。将filename
作为第一个元素通常就足够了。为了完全符合shell的功能,请剥离路径并仅传递最后一个组件argv[0](date
,在您的示例中)
您的示例的等效C代码为:
char *filename = "/bin/date";
char *argv [2] = {filename, NULL};
char *envp [1] = {NULL};
execve (filename, argv, envp);
也许我没看到,但看起来你做的是(32位和64位)
我不理解你的堆栈操作。推、推、推、系统调用就足够了。我不明白为什么要直接操作堆栈指针
为什么在上面的代码中32位调用约定不起作用
你回答了自己的问题:
32位约定仅支持执行32位代码
您也可以从64位代码调用它,这只是一个意外,内核不会检查。它希望调用源自32位代码,并且zero将带有参数的32位寄存器扩展到64位,因为接口仅使用32位寄存器。这是在(请记住,64位模式下的32位操作会自动清除寄存器的前32位):
因此,不能使用它来传递不适合32位的参数。请注意,rcx
值为0x7fffffe968
,因此将被截断为0xfffffe968
,并最终导致EFAULT
rcx
当然是从rsp
设置的,典型的64位地址空间的堆栈从地址空间正半部分的顶部向下增长,超出了32位范围。为了证明这一点,可以切换到内存不足的堆栈。以下代码可以正常工作:
.lcomm stack, 4096
.global main
main:
movl $stack, %esp
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"
据我所知,
argv
(或32位代码中的rcx
)指向一个char*
数组(注意代码后面的gdb输出)。我操纵堆栈指针以防止碰撞/bin/date
字符串(在jmp
/调用之后由rsp
指向)。但我几乎肯定忽略了一些东西;明天早上我会再看一遍。@BK康拉德:够了。我也很累,9个小时后会更仔细地看。
char *filename = "/bin/date";
char *argv [2] = {filename, NULL};
char *envp [1] = {NULL};
execve (filename, argv, envp);
char *filename = "/bin/date";
execve (filename, NULL); // note missing third parameter, and malformed argv[]
/*
* Emulated IA32 system calls via int 0x80.
*
* Arguments:
* eax system call number
* ebx arg1
* ecx arg2
* edx arg3
* esi arg4
* edi arg5
* ebp arg6 (note: not saved in the stack frame, should not be touched)
*
* Notes:
* Uses the same stack frame as the x86-64 version.
* All registers except eax must be saved (but ptrace may violate that).
* Arguments are zero extended. For system calls that want sign extension and
* take long arguments a wrapper is needed. Most calls can just be called
* directly.
* Assumes it is only called from user space and entered with interrupts off.
*/
[...]
ia32_do_call:
/* 32bit syscall -> 64bit C ABI argument conversion */
movl %edi,%r8d /* arg5 */
movl %ebp,%r9d /* arg6 */
xchg %ecx,%esi /* rsi:arg2, rcx:arg4 */
movl %ebx,%edi /* arg1 */
movl %edx,%edx /* arg3 (zero extension) */
call *ia32_sys_call_table(,%rax,8) # xxx: rip relative
.lcomm stack, 4096
.global main
main:
movl $stack, %esp
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"