c - 缓冲区溢出/溢出解释?

标签 c assembly x86 buffer-overflow

在给定的 url 中给出了这个函数: http://insecure.org/stf/smashstack.html

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

main函数对应的汇编代码为:

Dump of assembler code for function main:
0x8000490 <main>:       pushl  %ebp
0x8000491 <main+1>:     movl   %esp,%ebp
0x8000493 <main+3>:     subl   $0x4,%esp
0x8000496 <main+6>:     movl   $0x0,0xfffffffc(%ebp)
0x800049d <main+13>:    pushl  $0x3
0x800049f <main+15>:    pushl  $0x2
0x80004a1 <main+17>:    pushl  $0x1
0x80004a3 <main+19>:    call   0x8000470 <function>
0x80004a8 <main+24>:    addl   $0xc,%esp
0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax
0x80004b5 <main+37>:    pushl  %eax
0x80004b6 <main+38>:    pushl  $0x80004f8
0x80004bb <main+43>:    call   0x8000378 <printf>
0x80004c0 <main+48>:    addl   $0x8,%esp
0x80004c3 <main+51>:    movl   %ebp,%esp
0x80004c5 <main+53>:    popl   %ebp
0x80004c6 <main+54>:    ret
0x80004c7 <main+55>:    nop

在变量ret 中,它们将ret 指向要运行的下一条指令的地址。我无法理解仅仅通过将下一条指令保存在 ret 变量中,程序将如何跳转到下一个位置? 我知道缓冲区溢出是如何工作的,但是通过更改 ret 变量,这是如何进行缓冲区溢出的? 即使考虑到这是一个虚拟程序,只是为了让我们了解缓冲区溢出的工作原理,更改 ret 变量似乎是错误的。

最佳答案

解释这是一个缓冲区溢出的例子:

function 的局部变量,包括buffer1,和返回地址一起在栈上,返回地址被计算为超出buffer1< 12 个字节。这是一个缓冲区溢出的示例,因为写入超出 buffer1 12 个字节的地址是在 buffer1 的正确边界之外写入。通过用比原来大 8 的数字替换返回地址,当 function 完成时,而不是像往常一样弹出返回到函数调用后的语句 (x = 1;,在这种情况下),返回地址将在 8 个字节之后(在 printf 语句中,在这种情况下)。

跳过 x = 1; 语句不是缓冲区溢出——这是修改返回地址的缓冲区溢出的结果。

关于计算 8 作为跳过 x = 1; 语句的正确偏移量的注意事项:

另见 FrankH's careful reevaluation计算 8 作为添加到返回地址的适当偏移量以实现跳过 x = 1; 的目的。他的发现与基于 GDB 的 insecure.org source article 分析相矛盾。 . 不管这个细节如何,关于如何使用缓冲区溢出来更改返回地址的解释都是一样的——这只是将什么写入溢出的问题。

为了完整起见,这里是对 insecure.org source article基于 GDB 的分析。 :

What we have done is add 12 to buffer1[]'s address. This new address is where the return address is stored. We want to skip pass the assignment to the printf call. How did we know to add 8 to the return address? We used a test value first (for example 1), compiled the program, and then started gdb:

[aleph1]$ gdb example3
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(no debugging symbols found)...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000490 <main>:       pushl  %ebp
0x8000491 <main+1>:     movl   %esp,%ebp
0x8000493 <main+3>:     subl   $0x4,%esp
0x8000496 <main+6>:     movl   $0x0,0xfffffffc(%ebp)
0x800049d <main+13>:    pushl  $0x3
0x800049f <main+15>:    pushl  $0x2
0x80004a1 <main+17>:    pushl  $0x1
0x80004a3 <main+19>:    call   0x8000470 <function>
0x80004a8 <main+24>:    addl   $0xc,%esp
0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax
0x80004b5 <main+37>:    pushl  %eax
0x80004b6 <main+38>:    pushl  $0x80004f8
0x80004bb <main+43>:    call   0x8000378 <printf>
0x80004c0 <main+48>:    addl   $0x8,%esp
0x80004c3 <main+51>:    movl   %ebp,%esp
0x80004c5 <main+53>:    popl   %ebp
0x80004c6 <main+54>:    ret
0x80004c7 <main+55>:    nop

We can see that when calling function() the RET will be 0x8004a8, and we want to jump past the assignment at 0x80004ab. The next instruction we want to execute is the at 0x8004b2. A little math tells us the distance is 8 bytes.

更好一点的数学告诉我们距离是 0x8004a8 - 0x8004b2 = 0xA 或 10 个字节,而不是 8 个字节。

关于c - 缓冲区溢出/溢出解释?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19614743/

相关文章:

c - 从 struct** 初始化 struct - 段错误

词法分析器、理解文档、预处理标记

c - 如何在多引导头文件中正确使用 QEMU

gcc - 如何让 GCC 在使用内部函数时使用两个以上的 SIMD 寄存器?

c++ - 优化位 vector 检查是否是另一个位 vector 的真子集?

c - OpenMP 范围界定 - 段。共享错误()

c - 如何在终端文件中输入我的 C 程序以使其顺序显示?

c - 如何理解汇编级别的原子 test_and_set?

assembly - 软盘读取(AH=0x2,int 0x13)未完成

assembly - 是否可以在实模式下使用 32 位寄存器/指令?