5

我的问候和问候所有人。我有一个 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

4 回答 4

13
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 是一个段寄存器。TLS最有可能通过此寄存器引用线程本地存储 (AKA )。

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()and put()。然后此代码检查来自 TLS 的值的副本是否未更改。xor %gs:0x14,%eax比较。

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

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

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

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

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

于 2012-09-02T10:09:42.653 回答
8
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可用于线程本地存储。例如,它用于errno,因此多线程程序中的每个线程实际上都有自己的 errno 变量。

上面的函数名是一个很大的线索。这必须是堆栈金丝雀

leave是一些 CISC 指令,可以在实际 ret 之前完成您需要做的所有事情。我不知道细节)。

于 2012-09-02T09:48:55.490 回答
4

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

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

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

xor eax, eax 指令清除寄存器 eax 中的所有位(加载零),但正如您已经发现的那样,这似乎是不必要的,因为寄存器在下一条指令中加载了新值。

但是,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 将不得不进行昂贵的寄存器合并,这需要很多周期。

所以在函数开始时做这些额外的工作可以提高性能。在您的情况下这是不必要的,但是由于编译器无法确定会发出指令。

于 2012-09-02T10:06:08.673 回答
2

stack_check_fail 是 gcc 缓冲区溢出检查的一部分。它使用 libssp (stack-smash-protection),并且您在开始时为堆栈设置了一个保护,xor %gs:0x14... 是一个检查保护是否仍然正常。当它正常时,它跳转到离开(检查汇编文档,它是堆栈处理的帮助指令)并跳过跳转到 stack_chk_fail,这将中止程序并发出错误消息。

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

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

于 2012-09-02T09:54:32.163 回答