轉載請註明出處:http://blog.csdn.net/wangxiaolong_china
關於堆棧空間利用最核心的一點就是:函數調用棧。而要深入理解函數調用棧,最重要的兩點就是:棧的結構變化,ebp寄存器的作用。
首先要認識到這樣兩個事實:
1. 一個函數調用動作可分解爲:零到多個push指令(用於參數入棧),一個call指令。call指令內部其實還暗含了一個將eip返回地址(即call指令下一條指令的地址)壓棧的動作。
2. 幾乎所有本地編譯器都會在每個函數體之前插入類似的指令:push %ebp,mov %esp %ebp。
因此,在程序執行到一個函數的真正函數體的時候,已經有以下數據壓入到堆棧中:零到多個參數,返回地址eip,ebp。
由此得到如下的棧結構(其中參數入棧順序跟調用方式有關,這裏以C語言默認的CDECL爲例):
“push %ebp”“mov %esp %ebp”這兩條指令實在是太有深意了:首先將ebp入棧,然後將棧頂指針esp賦值給ebp。“mov %esp %ebp”這條指令表面上看是用esp把ebp原來的值覆蓋了,其實不然,因爲在給ebp賦值之前,原ebp值已經被壓棧(位於棧頂),esp賦值給ebp後,ebp恰好指向棧頂(即被壓棧的原esp的位置)。
此時,ebp寄存器就處在一個非常重要的地位,該寄存器中存儲着棧中的一個地址(原ebp入棧後的棧頂),以該地址爲基準,向上(棧底方向)能獲取返回地址,函數調用參數值;向下(棧頂方向)能獲取函數局部變量值;而該地址處又存儲着上一層函數調用時的ebp值!!一般而言,ss:[ebp+4]處爲返回地址,ss:[ebp+8]處爲第一個參數值(最後一個入棧的參數值,此處假設其佔用4字節內存),ss:[ebp-4]處爲第一個局部變量,ss:[ebp]處爲上一層ebp值。
由於ebp中的地址總是“上一層函數調用時的ebp值”,而在每一層函數調用中,都能通過當時的ebp值“向上(棧底方向)能獲取返回地址、函數調用參數,向下(棧頂方向)能獲取函數局部變量值”。如此形成遞歸,直至到達棧底。這就是函數調用棧。由此看見,編譯器對於ebp寄存器的使用實在是太精妙了。
此外,從當前ebp出發,逐層向上找到所有的ebp是非常容易的:
unsigned int _ebp;
__asm _ebp, ebp;
while (not stack bottom)
{
//...
_ebp = *(unsigned int*)_ebp;
}
下面通過一個簡單的C程序,簡要的分析一下函數調用棧的變化情況。通過對具體C程序函數調用過程中堆棧空間變化的分析,加深對於函數調用棧的理解。
要分析的C程序源碼如下:
stack.c
1 #include <stdio.h>
2
3 void func1() {
4 printf("in func1.\n");
5 }
6
7 void func2() {
8 printf("in func2.\n");
9 }
10
11 void func3() {
12 int a = 1;
13 *(&a + 2) = (int)func1;
14 }
15
16 int main(void) {
17 int a_main = 1;
18 *(&a_main - 3) = (int)func2;
19 func3();
20
21 return 0;
22 }
程序執行結果:
root@linux:~/pentest# gcc -g -o stack stack.c
root@linux:~/pentest# ./stack
in func1.
in func2.
Segmentation fault
使用GDB反彙編stack程序:
root@linux:~/pentest# gdb stack
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/pentest/stack...done.
(gdb) disass main
Dump of assembler code for function main:
0x080483f8 <+0>: push %ebp
0x080483f9 <+1>: mov %esp,%ebp
0x080483fb <+3>: sub {1}x10,%esp
0x080483fe <+6>: movl {1}x1,-0x4(%ebp)
0x08048405 <+13>: lea -0x4(%ebp),%eax
0x08048408 <+16>: sub {1}xc,%eax
0x0804840b <+19>: mov {1}x80483c8,%edx
0x08048410 <+24>: mov %edx,(%eax)
0x08048412 <+26>: call 0x80483dc <func3>
0x08048417 <+31>: mov {1}x0,%eax
0x0804841c <+36>: leave
0x0804841d <+37>: ret
End of assembler dump.
(gdb) disass func3
Dump of assembler code for function func3:
0x080483dc <+0>: push %ebp
0x080483dd <+1>: mov %esp,%ebp
0x080483df <+3>: sub {1}x10,%esp
0x080483e2 <+6>: movl {1}x1,-0x4(%ebp)
0x080483e9 <+13>: lea -0x4(%ebp),%eax
0x080483ec <+16>: add {1}x8,%eax
0x080483ef <+19>: mov {1}x80483b4,%edx
0x080483f4 <+24>: mov %edx,(%eax)
0x080483f6 <+26>: leave
0x080483f7 <+27>: ret
End of assembler dump.
(gdb) disass func2
Dump of assembler code for function func2:
0x080483c8 <+0>: push %ebp
0x080483c9 <+1>: mov %esp,%ebp
0x080483cb <+3>: sub {1}x18,%esp
0x080483ce <+6>: movl {1}x80484ea,(%esp)
0x080483d5 <+13>: call 0x80482f0 <puts@plt>
0x080483da <+18>: leave
0x080483db <+19>: ret
End of assembler dump.
(gdb) disass func1
Dump of assembler code for function func1:
0x080483b4 <+0>: push %ebp
0x080483b5 <+1>: mov %esp,%ebp
0x080483b7 <+3>: sub {1}x18,%esp
0x080483ba <+6>: movl {1}x80484e0,(%esp)
0x080483c1 <+13>: call 0x80482f0 <puts@plt>
0x080483c6 <+18>: leave
0x080483c7 <+19>: ret
End of assembler dump.
(gdb)
下面將按照程序執行流程,分析函數調用棧的主要變化情況:
程序執行起點是main函數,其調用棧變化如下所示:
Main程序調用func3,故調用func3後,調用棧變化如下所示:
由於func1的地址覆寫了eip_main函數調用返回地址,故func3執行結束後,將返回到func1並繼續執行,程序調用棧變化如下所示:
程序從func1返回時,填入棧中的func2的地址將作爲返回地址使用,即程序返回後將跳轉到func2起始處執行,程序調用棧如下所示:
程序執行func2,在執行ret指令時,由於返回地址可能指向無效的段,從而導致程序執行結果出現Segmentationfault。
要使程序執行可以正常結束,而不會出現Segmentation fault,則需要main函數中18 *(&a_main- 3) = (int)func2;之後添加如下一行代碼即可:
*(&a_main -2) = *(&a_main + 2);
通過該實驗,對於函數調用棧在函數調用過程中的變化的理解進一步加深,有利於更好的理解棧溢出的原理。