这条指令的作用是什么?: - mov%gs:0x14,%eax

时间:2012-09-02 09:28:36

标签: c assembly x86

我的问候&尊重所有人。我有一个C程序,基本上是为了测试缓冲区溢出而编写的。

    #include<stdio.h>
    void display()
    {
            char buff[8];
            gets(buff);
            puts(buff);
    }
    main()
    {
        display();
        return(0);
    }

现在我使用GDB反汇编它的显示和主要部分。代码: -

转储函数main的汇编代码:

    0x080484ae <+0>:    push   %ebp        # saving ebp to stack
    0x080484af <+1>:    mov    %esp,%ebp   # saving esp in ebp
    0x080484b1 <+3>:    call   0x8048474 <display>   # calling display function
    0x080484b6 <+8>:    mov    $0x0,%eax   # move 0 into eax , but WHY ????
    0x080484bb <+13>:   pop    %ebp        # remove ebp from stack
    0x080484bc <+14>:   ret                # return

汇编程序转储结束。

转储函数显示的汇编程序代码:

    0x08048474 <+0>:    push   %ebp          #saves ebp to stack        
    0x08048475 <+1>:    mov    %esp,%ebp     # saves esp to ebp
    0x08048477 <+3>:    sub    $0x10,%esp    # making 16 bytes space in stack
    0x0804847a <+6>:    mov    %gs:0x14,%eax  # what does it mean ????
    0x08048480 <+12>:   mov    %eax,-0x4(%ebp) # move eax contents to 4 bytes lower in stack
    0x08048483 <+15>:   xor    %eax,%eax       # xor eax with itself (but WHY??)
    0x08048485 <+17>:   lea    -0xc(%ebp),%eax  #Load effective address of 12 bytes 
                                              lower placed value ( WHY???? )

    0x08048488 <+20>:   mov    %eax,(%esp)      #make esp point to the address inside of eax
    0x0804848b <+23>:   call   0x8048374 <gets@plt>  # calling get, what is "@plt" ????
    0x08048490 <+28>:   lea    -0xc(%ebp),%eax       # LEA of 12 bytes lower to eax
    0x08048493 <+31>:   mov    %eax,(%esp)         # make esp point to eax contained address
    0x08048496 <+34>:   call   0x80483a4 <puts@plt>  # again what is "@plt" ????
    0x0804849b <+39>:   mov    -0x4(%ebp),%eax    # move (ebp - 4) location's contents to eax
    0x0804849e <+42>:   xor    %gs:0x14,%eax         # # again what is this ????
    0x080484a5 <+49>:   je     0x80484ac <display+56> # Not known to me
    0x080484a7 <+51>:   call   0x8048394 <__stack_chk_fail@plt>  # not known to me
    0x080484ac <+56>:   leave                        # a new instruction, not known to me
    0x080484ad <+57>:   ret                          # return to MAIN's next instruction

汇编程序转储结束。

所以大家,你应该考虑我的作业。休息所有代码都是我所知道的,除了几行。我加了一个大“为什么????”每行前面的评论中还有一些问题。对我来说,第一个障碍是“mov%gs:0x14,%eax”指令,我不能在此指令后制作流程图。有人解释一下,这几条指令是什么意思,并在程序中做什么?感谢...

4 个答案:

答案 0 :(得分:10)

0x080484b6 <+8>:    mov    $0x0,%eax   # move 0 into eax , but WHY ????

你有没有这个?:

return(0);

他们可能是相关的。 :)

0x0804847a <+6>:    mov    %gs:0x14,%eax  # what does it mean ????

这意味着从地址gs:0x14的内存中读取4个字节到eax。 gs是一个段寄存器。很可能通过此寄存器引用了线程本地存储(AKA TLS)。

0x08048483 <+15>:   xor    %eax,%eax       # xor eax with itself (but WHY??)

不知道。可能与优化有关。

0x08048485 <+17>:   lea    -0xc(%ebp),%eax  #Load effective address of 12 bytes 
                                          lower placed value ( WHY???? )

它使eax指向一个存在于堆栈中的局部变量。 sub $0x10,%esp为他们分配了一些空间。

0x08048488 <+20>:   mov    %eax,(%esp)      #make esp point to the address inside of eax

错误。它将eax写入堆栈,堆栈顶部。它将作为堆栈参数传递给被调用函数:

0x0804848b <+23>:   call   0x8048374 <gets@plt>  # calling get, what is "@plt" ????

我不知道。可能是一些名字错误。

到现在为止你应该猜到了那个局部变量。 buff,还有什么呢?

0x080484ac <+56>:   leave                        # a new instruction, not known to me

为什么不在CPU手册中查找?

现在,我可以解释一下gs / TLS的事情......

0x08048474 <+0>:    push   %ebp          #saves ebp to stack        
0x08048475 <+1>:    mov    %esp,%ebp     # saves esp to ebp
0x08048477 <+3>:    sub    $0x10,%esp    # making 16 bytes space in stack
0x0804847a <+6>:    mov    %gs:0x14,%eax  # what does it mean ????
0x08048480 <+12>:   mov    %eax,-0x4(%ebp) # move eax contents to 4 bytes lower in stack
...
0x0804849b <+39>:   mov    -0x4(%ebp),%eax    # move (ebp - 4) location's contents to eax
0x0804849e <+42>:   xor    %gs:0x14,%eax         # # again what is this ????
0x080484a5 <+49>:   je     0x80484ac <display+56> # Not known to me
0x080484a7 <+51>:   call   0x8048394 <__stack_chk_fail@plt>  # not known to me
0x080484ac <+56>

因此,此代码从TLS(在gs:0x14处)获取一个值,并将其存储在保存的ebp值(ebp-4)的正下方。然后是get()put()的你的东西。然后,此代码检查来自TLS的值的副本是否未更改。 xor %gs:0x14,%eax进行比较。

如果XORed值相同,则XOR的结果为0,flags.zf为1.否则,结果不为0且flags.zf为0.

如果flags.zf = 1,则

je 0x80484ac <display+56>检查flags.zf并跳过call 0x8048394 <__stack_chk_fail@plt>。如果TLS中的值副本未更改,则跳过此调用。

那是什么一回事?这是一种尝试捕获缓冲区溢出的方法。如果写入超出缓冲区末尾,则会覆盖从TLS复制到堆栈的值。

为什么我们从TLS中获取这个值,为什么不只是一个恒定的,硬编码的值?我们可能希望使用不同的非硬编码值来更频繁地捕获溢出(因此TLS中的值将从程序的运行更改为另一个运行程序,并且在程序的不同线程中它将有所不同)。如果每次程序运行时随机选择值,这也会降低攻击者成功利用缓冲区溢出的几率。

最后,如果发现由于缓冲区溢出而覆盖了值的副本,call 0x8048394 <__stack_chk_fail@plt>将调用专用于执行任何必要操作的特殊功能,例如,报告问题并终止程序。

答案 1 :(得分:6)

0x0804849e <+42>:   xor    %gs:0x14,%eax         # # again what is this ????
0x080484a5 <+49>:   je     0x80484ac <display+56> # Not known to me
0x080484a7 <+51>:   call   0x8048394 <__stack_chk_fail@plt>  # not known to me
0x080484ac <+56>:   leave                        # a new instruction, not known to me
0x080484ad <+57>:   ret                          # return to MAIN's next instruction

gs segment可用于thread local storage。例如。它用于errno,因此多线程程序中的每个线程都有自己的errno变量。

上面的函数名称是一个很大的线索。这必须是stack canary

leave是一些CISC指令,可以在实际返回之前执行您需要执行的所有操作。我不知道详细信息。)

答案 2 :(得分:3)

其他人已经解释了GS的事情(与线程有关)..

0x08048483 <+15>:   xor    %eax,%eax       # xor eax with itself (but WHY??)

解释这需要一些X86架构的历史:

xor eax,eax指令清除寄存器eax中的所有位(加载零),但是你已经发现它似乎是不必要的,因为寄存器在下一条指令中加载了一个新值。 / p>

然而,xor eax,eax也在x86上做了其他事情。您可能知道您可以使用al,ah和ax访问寄存器eax的某些部分。从386开始就是这样,当时eax确实只是一个寄存器就可以了。

然而,现在已不复存在了。您在代码中看到并使用的寄存器只是占位符。 CPU内部使用更多内部寄存器和完全不同的指令集。您编写的指令将转换为此内部指令集。

例如,如果您使用AL,AH和EAX,则从CPU的角度来看,您使用的是三个不同的寄存器。

现在,如果在使用AL或AH后访问EAX,CPU必须合并这些不同的寄存器以构建有效的EAX值。

该行:

0x08048483 <+15>:   xor    %eax,%eax       # xor eax with itself (but WHY??)

不仅清除寄存器eax。它还告诉CPU所有重命名的子寄存器:AL,AH和AX现在可以认为是无效的(设置为零),并且CPU不必进行任何子寄存器合并。

为什么编译器会发出这条指令?

因为编译器不知道将在哪个上下文中调用display()。您可以从使用AL和AH进行大量字节算术的代码中调用它。如果它不能通过XOR清除EAX寄存器,那么CPU将不得不进行昂贵的寄存器合并,这需要花费很多周期。

因此,在函数启动时执行此额外工作可提高性能。在您的情况下,这是不必要的,但由于编译器无法知道发出指令以确定。

答案 3 :(得分:1)

stack_check_fail是gcc缓冲区溢出检查的一部分。它使用libssp(stack-smash-protection),你在开始时的移动为堆栈设置了一个守卫,xor%gs:0x14 ...是一个检查守卫是否还可以。当它没问题时,它跳转到leave(检查汇编程序doc,它是堆栈处理的辅助指令)并跳过跳转到stack_chk_fail,这将中止程序并发出错误消息。

您可以使用gcc选项-fno-stack-protector禁用此溢出检查的发出。

正如评论中已经提到的,xor x,x只是一个清除x的快速命令,最后一个mov 0,%eax是你的main的返回值。