摘录:更改函数的返回地址

http://www.cnblogs.com/bluesea147/archive/2012/05/19/2508208.html

这是网络安全老师布置的实验,觉得是大学以来做过的最有意思的一个实验。

Task Description:

C语言编写程序,包含一个函数,改变函数的返回地址,使函数返回后跳转到某个指定的指令位置,而不是函数调用后紧跟的位置。

先上代码:

 

#include <stdio.h>

void foo(){
int a, *p;
p = (int*)((int)&a + 8);
*p += 12;
}

int main(){
foo();
printf("First printf call\n");
printf("Second printf call\n");
return 0;
}

编译运行,结果输出

Second printf call.

并没有输出First printf call.达到预期效果。

原理并不复杂:在函数体中修改return地址,即找到return的地址的位置,然后修改它。

下面详细介绍下实验过程:

编译:
gcc main.c -g

反编译:

objdump a.out -d

得到部分汇编代码如下:

 

e4 <foo>:
e4: 55 push %ebp
e5: 89 e5 mov %esp,%ebp
e7: 83 ec 10 sub $0x10,%esp
ea: 8d 45 fc lea -0x4(%ebp),%eax
ed: 83 c0 08 add $0x8,%eax
f0: 89 45 f8 mov %eax,-0x8(%ebp)
f3: 8b 45 f8 mov -0x8(%ebp),%eax
f6: 8b 00 mov (%eax),%eax
f8: 8d 50 0c lea 0xc(%eax),%edx
fb: 8b 45 f8 mov -0x8(%ebp),%eax
fe: 89 10 mov %edx,(%eax)
: c9 leave
: c3 ret
<main>:
: 55 push %ebp
: 89 e5 mov %esp,%ebp
: 83 e4 f0 and $0xfffffff0,%esp
: 83 ec 10 sub $0x10,%esp
b: e8 d4 ff ff ff call 80483e4 <foo>
: c7 04 24 f0 84 04 08 movl $0x80484f0,(%esp)
: e8 fc fe ff ff call 8048318 <puts@plt>
c: c7 04 24 02 85 04 08 movl $0x8048502,(%esp)
: e8 f0 fe ff ff call 8048318 <puts@plt>
: b8 00 00 00 00 mov $0x0,%eax
d: c9 leave
e: c3 ret
f: 90 nop

从上述的汇编代码中,我们可以看到foo后面的指令地址是8048410,而进入调用printf("Second printf call“)的指令是地址804841c, 二者相差12,故我们应该将返回地址的值+12即可。

再说点函数调用的过程:

调用函数的过程是这样的:
调用者从右往左将参数入栈,再调用call 指令,call 指令完成事情:

1) 将返回地址入栈,2)修改eip寄存器,即计算机组成原理里面说的PC,使程序的指令指针指向被调用函数的起始指令地址。

接着就进入了被调用的函数的函数体中,C语言的函数调用习惯是:
pushl %ebp,

movl %esp %ebp

再对栈顶寄存器esp减去一个值,为函数中的局部变量预留空间。(因为栈的增长方向是从高地址往低地址增长的)

好的,介绍完毕。

从上述的汇编代码中:

80483ea: 8d 45 fc lea -0x4(%ebp),%eax

这一句汇编代码:把-4(%ebp)的地址赋值给%eax寄存器。对应的C语言的代码是:
p = &a;

所以我们便从中可以知道:foo()函数中的变量a是存储在-4(%ebp)位置的,即图中的Local Variable1的位置

如此看来,a的地址往上8个单位就是return的地址。
所以p = &a + 8就获得了return返回值跳转地址的地址。

对这个地址的内容进行修改:
*p += 12 // 之所以+12刚才已经分析出来了。

便完成了让函数调用结束后跳转到第二个printf函数调用的位置。

ps:

自己在做得时候,用gdb验证了一些知识,比较有意思:
如,查看%eip里面的值,发现的确是指向当前程序的指令的地址,就是PC的效果

在刚进入foo函数的时候,查看栈顶位置存储的值,发现是8048410,心中一块大石头落下来了,栈顶果然是return的指令地址。

gdb的部分截图:

 

posted @ 2012-10-17 10:07  10,000 hours coder  阅读(280)  评论(0编辑  收藏  举报