我想知道在Linux下的x86汇编器中执行系统调用的不同方法是什么。但是,在没有作弊的情况下,只能使用
汇编程序(即,必须使用
gcc完成-nostdlib的编译)。

我知道执行系统调用的四种方法,即:


int sysenterx80

call *%gs:0x10(i586)

syscall(vdso蹦床)

int int gcc -nostdlib -o hello-int80 hello-int80.sx80x80(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

评论

尝试进行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内核上此系统断言失败的原因。

#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