汇编程序(即,必须使用
gcc
完成-nostdlib
的编译)。我知道执行系统调用的四种方法,即:
int sysenter
x80
call *%gs:0x10
(i586)syscall
(vdso蹦床)int int gcc -nostdlib -o hello-int80 hello-int80.s
x80
x80
(amd64 )例如,我擅长使用
sysenter
,这是经典的“ Hello World!”的示例代码。在使用call *%gs:0x10
的汇编程序中(使用gcc -o hello-gs10 hello-gs10.s
进行编译):为什么呢而且,如何正确使用它?这里是
libc
(与main
编译)的示例。请注意,在正确调用_start
之前,我需要对其进行初始化(这就是为什么我使用-nostdlib
而不是syscall
的原因,这也是为什么我从编译行中删除了gcc -m64 -nostdlib -o hello-syscall hello-syscall.s
选项的原因):.data
msg:
.ascii "Hello World!\n"
len = . - msg
.text
.globl _start
_start:
# Write the string to stdout
movl $len, %edx
movl $msg, %ecx
movl , %ebx
movl , %eax
int .data
msg:
.ascii "Hello World!\n"
len = . - msg
.text
.globl main
main:
# Write the string to stdout
movl $len, %edx
movl $msg, %ecx
movl , %ebx
movl , %eax
call *%gs:0x10
# and exit
movl .data
msg:
.ascii "Hello World!\n"
len = . - msg
.text
.globl _start
_start:
# Write the string to stdout
movq $len, %rdx
movq $msg, %rsi
movq , %rdi
movq , %rax
syscall
# and exit
movq .data
msg:
.ascii "Hello World!\n"
len = . - msg
.text
.globl _start
_start:
# Write the string to stdout
movl $len, %edx
movl $msg, %ecx
movl , %ebx
movl , %eax
push final
sub , %esp
mov %esp, %ebp
sysenter
# and exit
final:
movl q4312078q, %ebx
movl , %eax
sub , %esp
mov %esp, %ebp
sysenter
, %rdi
movq , %rax
syscall
, %ebx
movl , %eax
call *%gs:0x10
x80
# and exit
movl q4312078q, %ebx
movl , %eax
int q4312078qx80
另外,如果您知道该体系结构的系统调用代码(感谢lfxgroove)(与
sysenter
编译),则sysenter
也可以正常工作:q4312078q
所以,我要触发系统调用的唯一问题就是这种
gcc -m32 -nostdlib -o hello-sysenter hello-sysenter.s
方式。这是q4312079q以分段错误结尾的示例(与q4312079q编译):q4312078q
#1 楼
通过sysenter
的系统调用sysenter
是i586指令,特别适合32位应用程序。 syscall
已将其包含在64位平台上。sysenter
的一个特殊之处在于,除了常规的寄存器设置外,它确实还需要对堆栈进行一些操作,然后< br调用它。这是因为在离开sysenter
之前,该过程将遍历
__kernel_vsyscall
汇编程序的最后一部分片段(从
0xf7ffd430
开始):Dump of assembler code for function __kernel_vsyscall:
0xf7ffd420 <+0>: push %ecx
0xf7ffd421 <+1>: push %edx
0xf7ffd422 <+2>: push %ebp
0xf7ffd423 <+3>: mov %esp,%ebp
0xf7ffd425 <+5>: sysenter
0xf7ffd427 <+7>: nop
0xf7ffd428 <+8>: nop
0xf7ffd429 <+9>: nop
0xf7ffd42a <+10>: nop
0xf7ffd42b <+11>: nop
0xf7ffd42c <+12>: nop
0xf7ffd42d <+13>: nop
0xf7ffd42e <+14>: int 0x______0c saved_eip (ret)
0x______08 saved_%ecx (pop %ecx)
0x______04 saved_%edx (pop %edx)
0x______00 saved_%ebp (pop %ebp)
x80
=> 0xf7ffd430 <+16>: pop %ebp
0xf7ffd431 <+17>: pop %edx
0xf7ffd432 <+18>: pop %ecx
0xf7ffd433 <+19>: ret
End of assembler dump.
因此,
sysenter
指令期望以这种方式伪造堆栈。必须推送保存的
sysenter
的值,并与%eip
,%ecx
和
%edx
相同。导致:.data
msg:
.ascii "Hello World!\n"
len = . - msg
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
# Write the string to stdout
movl $len, %edx
movl $msg, %ecx
movl , %ebx
movl , %eax
# Setting the stack for the systenter
pushl $sysenter_ret
pushl %ecx
pushl %edx
pushl %ebp
movl %esp,%ebp
sysenter
# and exit
sysenter_ret:
movl q4312078q, %ebx
movl , %eax
# Setting the stack for the systenter
pushl $sysenter_ret # Who cares, this is an exit !
pushl %ecx
pushl %edx
pushl %ebp
movl %esp,%ebp
sysenter
评论
我真的不知道Linux内核如何将指令sysenter的结果重新路由到__kernel_vsyscall + 16。如果有人可以为我澄清这一点,我将很高兴。
–恐怖
13-10-6在16:56
它是通过称为寄存器的MSR(x86.renejeschke.de/html/file_module_x86_id_313.html)在SYSEXIT中他们也使用了这些寄存器。不知道它们是如何设置的或它们在其中声明的源文件是什么。
–桑敦·达米卡(Sandun dhammika)
13-10-7在9:25
@perror实际上,您不必强制将栈上的任何内容退出,只需确保ebp具有可访问的地址,这样内核就可以预先读取可能的第6个syscall参数,而无需#GP(该值不必表示任何东西,因此也无需推动ebp)。即退出呼叫将减少为mov eax,1; xor ebx,ebx; mov ebp,esp; sysenter。
–俄罗斯
2015年8月28日14:55
@Ruslan恐怕您建议的代码不起作用-程序以segfault终止。
– Aleksander Alekseev
16年7月17日在14:22
@AleksanderAlekseev您使用的是哪个Linux版本?尤其是你有什么?
–俄罗斯
16年7月17日在14:31
评论
尝试进行syscall的第一个猜测是您输入了错误的syscall编号,因为对于64位模式(看来是syscall所针对的),数字都是乱码,即:exit是60,而不是您的数字。立即重新使用,请参阅lxr.linux.no/#linux+v2.6.32/arch/x86/include/asm/unistd_64.h了解数字确实,你是对的。我真的很困惑,但是在我的示例中,似乎是第二个调用该写操作的系统调用(而不是我所期望的第一个)。
而不是进行其他质量检查。您介意@perror解释len =。 -msg-edit:我出于明显的原因知道目的,但它的语义。 -谢谢
“。”在gas语法中,请参考当前地址。因此,len =。 -msg是一种以len形式存储字符串msg大小的方法(它计算当前地址和msg标签位置之间的差)。
非常感谢,您帮助我找出了如何进行系统调用的方式以及答案中的代码示例,从而帮助我找出了错误的ChromiumOS内核上此系统断言失败的原因。