在给定的 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/