六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

編寫自己的緩沖區溢出使用程序

[摘要]內容: 本文主要講解有關Buffer Overflow的原理, 以及結合實戰范例介紹Linux和Solaris下的漏洞利用. 本文并不介紹如何編寫shell code. 要求: 讀者要有一點C和匯編語言基礎. 目標: 希望本文能夠盡量做到通熟易懂,使得稍有計算機基礎知識的朋友看后能夠親自動手寫自己...

內容: 本文主要講解有關Buffer Overflow的原理, 以及結合實戰范例介紹Linux和Solaris下的漏洞利用. 
本文并不介紹如何編寫shell code. 

要求: 讀者要有一點C和匯編語言基礎. 

目標: 希望本文能夠盡量做到通熟易懂,使得稍有計算機基礎知識的朋友看后能夠親自動手寫自己的Exploit 
如果你覺得自己對這些都懂了, 就請不要再往下看了. 


第一部份 概述篇 

1. Buffer overflow是如何產生的? 
所謂Buffer overflow, 中文譯為緩沖區溢出. 顧名思意, 就是說所用的緩沖區太小了, 以至裝不下 
那么多的東西, 多出來的東西跑出來了. 就好象是水缸裝不了那么多的水, 硬倒太多會溢出來一樣;) 
那么, 在編程過程中為什么要用到buffer(緩沖區)呢? 簡單的回答就是做為數據處理的中轉站. 

2. UNIX下C語言函數調用的機制及緩沖區溢出的利用. 
1) 進程在內存中的影像. 
我們假設現在有一個程序, 它的函數調用順序如下. 
main(...) -> func_1(...) -> func_2(...) -> func_3(...) 
即: 主函數main調用函數func_1; 函數func_1調用函數func_2; 函數func_2調用函數func_3 

當程序被操作系統調入內存運行, 其相對應的進程在內存中的影像如下圖所示. 

(內存高址) 
+--------------------------------------+ 
 ......   ... 省略了一些我們不需要關心的區 
+--------------------------------------+ 
 env strings (環境變量字串)   \ 
+--------------------------------------+ \ 
 argv strings (命令行字串)   \ 
+--------------------------------------+ \ 
 env pointers (環境變量指針)   SHELL的環境變量和命令行參數保存區 
+--------------------------------------+ / 
 argv pointers (命令行參數指針)   / 
+--------------------------------------+ / 
 argc (命令行參數個數)   / 
+--------------------------------------+ 
 main 函數的棧幀   \ 
+--------------------------------------+ \ 
 func_1 函數的棧幀   \ 
+--------------------------------------+ \ 
 func_2 函數的棧幀   \ 
+--------------------------------------+ \ 
 func_3 函數的棧幀   Stack (棧) 
+......................................+ / 
   / 
...... / 
   / 
+......................................+ / 
 Heap (堆)   / 
+--------------------------------------+ 
 Uninitialised (BSS) data   非初始化數據(BSS)區 
+--------------------------------------+ 
 Initialised data   初始化數據區 
+--------------------------------------+ 
 Text   文本區 
+--------------------------------------+ 
(內存低址) 

這里需要說明的是: 
i) 隨著函數調用層數的增加, 函數棧幀是一塊塊地向內存低地址方向延伸的. 
隨著進程中函數調用層數的減少, 即各函數調用的返回, 棧幀會一塊塊地 
被遺棄而向內存的高址方向回縮. 
各函數的棧幀大小隨著函數的性質的不同而不等, 由函數的局部變量的數目決定. 
ii) 進程對內存的動態申請是發生在Heap(堆)里的. 也就是說, 隨著系統動態分 
配給進程的內存數量的增加, Heap(堆)有可能向高址或低址延伸, 依賴于不 
同CPU的實現. 但一般來說是向內存的高地址方向增長的. 
iii) 在BSS數據或者Stack(棧)的增長耗盡了系統分配給進程的自由內存的情況下, 
進程將會被阻塞, 重新被操作系統用更大的內存模塊來調度運行. 
(雖然和exploit沒有關系, 但是知道一下還是有好處的) 
iv) 函數的棧幀里包含了函數的參數(至于被調用函數的參數是放在調用函數的棧 
幀還是被調用函數棧幀, 則依賴于不同系統的實現), 
它的局部變量以及恢復調用該函數的函數的棧幀(也就是前一個棧幀)所需要的 
數據, 其中包含了調用函數的下一條執行指令的地址. 
v) 非初始化數據(BSS)區用于存放程序的靜態變量, 這部分內存都是被初始化為零的. 
初始化數據區用于存放可執行文件里的初始化數據. 
這兩個區統稱為數據區. 
vi) Text(文本區)是個只讀區, 任何嘗試對該區的寫操作會導致段違法出錯. 文本區 
是被多個運行該可執行文件的進程所共享的. 文本區存放了程序的代碼. 

2) 函數的棧幀. 
函數調用時所建立的棧幀包含了下面的信息: 
i) 函數的返回地址. 返回地址是存放在調用函數的棧幀還是被調用函數的棧幀里, 
取決于不同系統的實現. 
ii) 調用函數的棧幀信息, 即棧頂和棧底. 
iii) 為函數的局部變量分配的空間 
iv) 為被調用函數的參數分配的空間--取決于不同系統的實現. 

3) 緩沖區溢出的利用. 
從函數的棧幀結構可以看出: 
由于函數的局部變量的內存分配是發生在棧幀里的, 所以如果我們在某一個函數里定義 
了緩沖區變量, 則這個緩沖區變量所占用的內存空間是在該函數被調用時所建立的棧幀里. 

由于對緩沖區的潛在操作(比如字串的復制)都是從內存低址到高址的, 而內存中所保存 
的函數調用返回地址往往就在該緩沖區的上方(高地址)--這是由于棧的特性決定的, 這 
就為復蓋函數的返回地址提供了條件. 當我們有機會用大于目標緩沖區大小的內容來向 
緩沖區進行填充時, 就有可以改寫函數保存在函數棧幀中的返回地址, 從而使程序的執 
行流程隨著我們的意圖而轉移. 換句話來說, 進程接受了我們的控制. 我們可以讓進程 
改變原來的執行流程, 去執行我們準備好的代碼. 

這是馮.諾曼計算機體系結構的缺陷. 

下面是緩沖區溢出利用的示意圖: 
i) 函數對字串緩沖區的操作, 方向一般都是從內存低址向高址的. 
如: strcpy(s, "AAA....."); 

s s+1 s+2 s+3 ... 
+---+---+---+--------+---+...+ 
(內存低址)   A   A   A   ......   A  ...  (內存高址) 
+---+---+---+--------+---+...+ 

ii) 函數返回地址的復蓋 

/   ......   (內存高址) 
/ +--------------------+ 
調用函數棧幀   0x41414141   
\ +--------------------+ 
\   0x41414141   調用函數的返回地址 
\+--------------------+ 
/  ......   
/ +--------------------+ s+8 
/   0x41414141   
/ +--------------------+ s+4 
被調用函數棧幀   0x41414141   
\ +--------------------+ s 
\   0x41414141   
\ +--------------------+ 
\  ......   
+....................+ 
 ......   (內存低址) 

注: 字符A的十六進制ASCII碼值為0x41. 

iii) 從上圖可以看出: 如果我們用的是進程可以訪問的某個地址而不是0x41414141 
來改寫調用函數的返回地址, 而這個地址正好是我們準備好的代碼的入口, 那么 
進程將會執行我們的代碼. 否則, 如果用的是進程無法訪問的段的地址, 將會導 
致進程崩饋--Segment Fault Core dumped (段出錯內核轉儲); 如果該地址處有 
無效的機器指令數據, 將會導致非法指令(Illigal Instruction)錯誤, 等等. 

4) 緩沖區在Heap(堆)區或BBS區的情況 
i) 如果緩沖區的內存空間是在函數里通過動態申請得到的(如: 用malloc()函數申請), 那 
么在函數的棧幀中只是分配了存放指向Heap(堆)中相應申請到的內存空間的指針. 這種 
情況下, 溢出是發生在(Heap)堆中的, 想要復蓋相應的函數返回地址, 看來幾乎是不可 
能的. 這種情況的利用可能性要看具體情形, 但不是不可能的. 
ii) 如果緩沖區在函數中定義為靜態(static), 則緩沖區內存空間的位置在非初始化(BBS)區, 
和在Heap(堆)中的情況差不多, 利用是可能的. 但還有一種特姝情況, 就是可以利用它來 
復蓋函數指針, 讓進程后來調用相應的函數變成調用我們所指定的代碼. 


3. 從緩沖區溢出的利用可以得到什么? 
從上文我們看到, 緩沖區溢出的利用可以使我們能夠改寫相關內存的內容及函數的返回地址, 從而 
改變代碼的執行流程, 讓進程去執行我們準備好的代碼. 

但是, 進程是以我們當前登錄的用戶身份來運行的. 能夠執行我們準備好的代碼又怎樣呢? 我們還 
是無法突破系統對當前用戶的權限設置, 無法干超越權限的事. 

換句話來說, 要想利用緩沖區溢出得到更高的權限, 我們還得利用系統的一些特性. 

對于UNIX來講, 有兩個特性可以利用. 
i) SUID及SGID程序 
UNIX是允許其他用戶可以以某個可執行文件的文件擁有者的用戶ID或用戶組ID的身份來執行該 
文件的,這是通過設置該可執行文件的文件屬性為SUID或SGID來實現的. 
也就是說如果某個可執行文件被設了SUID或SGID, 那么當系統中其他用戶執行該文件時就相當 
于以該文件屬主的用戶或用戶組身份來執行該文件. 
如果某個可執行文件的屬主是root, 而這個文件被設了SUID, 那么如果該可執行文件存在可利 
用的緩沖區溢出漏洞, 我們就可以利用它來以root的身份執行我們準備好的代碼. 沒有比讓它 
為我們產生一個具有超級用戶root身份的SHELL更吸引人了, 是不是? 

ii) 各種端口守護(服務)進程 
UNIX中有不少守護(服務)進程是以root的身份運行的, 如果這些程序存在可利用的緩沖區溢出, 
那么我們就可以讓它們以當前運行的用戶身份--root去執行我們準備被好的代碼. 
由于守護進程已經以root的身份在運行, 我們并不需要相對應的可執行文件為SUID或SGID屬性. 
又由于此類利用通常是從遠程機器上向目標機器上的端口發送有惡意的數據造成的, 所以叫做 
"遠程溢出"利用. 

4. 一個有問題的程序 
以下例程純屬虛構, 如有雷同, 純屬巧合. 

/* 
* 文件名 : p.c 
* 編譯 : gcc -o p p.c 
*/ 

#include <stdio.h> 

void vulFunc(char* s) 

char buf[10]; 
strcpy(buf, s); 
printf("String=%s\n", buf); 


main(int argc, char* argv[]) 

if(argc == 2) 

vulFunc(argv[1]); 

else 

printf("Usage: %s <A string>\n", argv[0]); 




這個例程接受用戶在命令行的字串輸入, 然后在標準輸出(屏幕)上打印出來. 我們可以看出在 
vulFunc()這個函數里, 定義了一個最多可以裝十個字符的緩沖區buf. 如果我們在命令行輸入 
小于等于十個字符的字串, 則一切都很正常. 但是, 如果我們輸入的字串長度大于十呢? 情況 
會怎樣? 緩沖區太小裝不下了, 所以溢出了? 答案有待于具體分析一下才知道. 

對于這個程序在不同操作系統下的分析和模擬攻擊. 請看第二部份基楚篇 


第二部份 基楚篇 
5. Linux x86 平臺 
本文使用了如下Linux平臺: 
Red Hat Linux release 6.2 (Zoot) 
Kernel 2.2.14-12 on an i586 

所使用的編譯器及版本: 
bash$ gcc -v 
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs 
gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) 

注意: 不同版本的編譯器編譯相同代碼所生成的機器指令可能不同. 

1) 例程p.c在Linux x86平臺下的剖析. 
i) 首先我們編譯p.c并用gdb對相關函數進行反匯編 
結果見如下清單: 

bash$ gcc -o p p.c 
bash$ gdb p 
GNU gdb 19991004 
Copyright 1998 Free Software Foundation, Inc. 
GDB is free software, covered by the GNU General Public License, and you are 
welcome to change it and/or 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. 
This GDB was configured as "i386-redhat-linux"... 
(gdb) disas main 
Dump of assembler code for function main: 
0x804842c <main>: push %ebp 
0x804842d <main+1>: mov %esp,%ebp 
0x804842f <main+3>: cmpl $0x2,0x8(%ebp) 
0x8048433 <main+7>: jne 0x8048448 <main+28> 
0x8048435 <main+9>: mov 0xc(%ebp),%eax 
0x8048438 <main+12>: add $0x4,%eax 
0x804843b <main+15>: mov (%eax),%edx 
0x804843d <main+17>: push %edx 
0x804843e <main+18>: call 0x8048400 <vulFunc> 
0x8048443 <main+23>: add $0x4,%esp 
0x8048446 <main+26>: jmp 0x804845b <main+47> 
0x8048448 <main+28>: mov 0xc(%ebp),%eax 
0x804844b <main+31>: mov (%eax),%edx 
0x804844d <main+33>: push %edx 
0x804844e <main+34>: push $0x80484bb 
0x8048453 <main+39>: call 0x8048330 <printf> 
0x8048458 <main+44>: add $0x8,%esp 
0x804845b <main+47>: leave 
0x804845c <main+48>: ret 
0x804845d <main+49>: nop 
0x804845e <main+50>: nop 
0x804845f <main+51>: nop 
End of assembler dump. 
(gdb) disas vulFunc 
Dump of assembler code for function vulFunc: 
0x8048400 <vulFunc>: push %ebp 
0x8048401 <vulFunc+1>: mov %esp,%ebp 
0x8048403 <vulFunc+3>: sub $0xc,%esp 
0x8048406 <vulFunc+6>: mov 0x8(%ebp),%eax 
0x8048409 <vulFunc+9>: push %eax 
0x804840a <vulFunc+10>: lea 0xfffffff4(%ebp),%eax 
0x804840d <vulFunc+13>: push %eax 
0x804840e <vulFunc+14>: call 0x8048340 <strcpy> 
0x8048413 <vulFunc+19>: add $0x8,%esp 
0x8048416 <vulFunc+22>: lea 0xfffffff4(%ebp),%eax 
0x8048419 <vulFunc+25>: push %eax 
0x804841a <vulFunc+26>: push $0x80484b0 
0x804841f <vulFunc+31>: call 0x8048330 <printf> 
0x8048424 <vulFunc+36>: add $0x8,%esp 
0x8048427 <vulFunc+39>: leave 
0x8048428 <vulFunc+40>: ret 
0x8048429 <vulFunc+41>: lea 0x0(%esi),%esi 
End of assembler dump. 


這里我們只對所關心的main和vulFunc兩個函數進行反匯編分析. 

ii) 進程的運行及其在內存中的情況分析 
我們用gdb來跟蹤看看進程是如何在內存中運行的. 

首先把程序調入. 
bash$ gdb p 
GNU gdb 19991004 
Copyright 1998 Free Software Foundation, Inc. 
GDB is free software, covered by the GNU General Public License, and you are 
welcome to change it and/or 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. 
This GDB was configured as "i386-redhat-linux"... 
(gdb) 

把斷點設到main的第一條可執行匯編指令上 
(gdb) b *0x804842c 
Breakpoint 1 at 0x804842c 

運行程序 
(gdb) r AAAAAAAA 
Starting program: /home/vcat/p AAAAAAAA 

Breakpoint 1, 0x804842c in main () 

在斷點處停下來了. 
看一下這時各寄存器的值 
(gdb) i reg 
eax 0x4010b3f8 1074836472 
ecx 0x804842c 134513708 
edx 0x4010d098 1074843800 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6bc -1073744196 
ebp 0xbffff6d8 -1073744168 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x804842c 134513708 
eflags 0x246 582 
cs 0x23 35 
ss 0x2b 43 
ds 0x2b 43 
es 0x2b 43 
fs 0x0 0 
gs 0x0 0 
cwd 0xffff037f -64641 
swd 0xffff0000 -65536 
twd 0xffffffff -1 
fip 0x40034d70 1073958256 
fcs 0x35d0023 56426531 
fopo 0xbfffe400 -1073748992 
fos 0xffff002b -65493 

我們這里關心的是棧底(ebp), 棧頂(esp)及指令寄存器(eip). 
此時, ebp的值為0xbffff6d8, esp的值為0xbffff6bc, 相差28個字節. 
eip的值為0x804842c, 正好是我們所設的斷點. 
(注: 這里的值可能會隨著程序運行在不同的系統環境而不同) 

我們再看看當前棧幀里有什么內容? 
(gdb) x/8x $esp 
0xbffff6bc: 0x400349cb 0x00000002 0xbffff704 0xbffff710 
0xbffff6cc: 0x40013868 0x00000002 0x08048350 0x00000000 

也就是說, main函數剛被調用時進程在內存中的相關部份的影像是這樣的: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- ebp (調用main函數前的ebp) 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  
+--------+ 
00000002  
+--------+ 
400349cb  
0xbffff6bc +--------+ <-- esp (調用main函數前的esp) 
 ......   
(內存低址) 


我們看看接下來的指令做了些什么? 
0x804842c <main>: push %ebp ; esp的值等于esp-4(因為ebp是32位); 
; 把ebp的值放入esp所指的32位內存單 
; 元(注: 這里保存棧底). 
0x804842d <main+1>: mov %esp,%ebp ; ebp的值等于esp的值(注: 這里把原來 
; 的棧頂做為新的棧底). 

運行這兩條指令, 然后看一下寄存器內容和棧的情況. 
(gdb) si 
0x804842d in main () 
(gdb) si 
0x804842f in main () 
(gdb) i reg 
eax 0x4010b3f8 1074836472 
ecx 0x804842c 134513708 
edx 0x4010d098 1074843800 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6b8 -1073744200 
ebp 0xbffff6b8 -1073744200 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x804842f 134513711 
eflags 0x346 838 
cs 0x23 35 
ss 0x2b 43 
ds 0x2b 43 
es 0x2b 43 
fs 0x0 0 
gs 0x0 0 
cwd 0xffff037f -64641 
swd 0xffff0000 -65536 
twd 0xffffffff -1 
fip 0x40034d70 1073958256 
fcs 0x35d0023 56426531 
fopo 0xbfffe400 -1073748992 
fos 0xffff002b -65493 
(gdb) x/9x $esp 
0xbffff6b8: 0xbffff6d8 0x400349cb 0x00000002 0xbffff704 
0xbffff6c8: 0xbffff710 0x40013868 0x00000002 0x08048350 
0xbffff6d8: 0x00000000 

此時進程的相關影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  
+--------+ 
00000002  
+--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  
0xbffff6b8 +--------+ <-- ebp, esp 
 ......   
(內存低址) 

接下來的兩條指令: 
0x804842f <main+3>: cmpl $0x2,0x8(%ebp) ; 2和ebp+8所指向的內存(32位--4 
; 個字節)里面所放的內容比較. 
0x8048433 <main+7>: jne 0x8048448 <main+28> ; 如果不等則跳到0x08048448地址 
; 處繼續執行, 否則執行下條指令. 
這里我們可以看到這是C語言語句 
if(argc == 2) 

... 

else 

... 

的等價匯編語句. 內存地址ebp+8處存放的是argc的值. 
(gdb) x/x $ebp+8 
0xbffff6c0: 0x00000002 

我們來看看在調用vulFunc函數前的指令: 
0x8048435 <main+9>: mov 0xc(%ebp),%eax ; 把內存地址ebp+12處的四個字節的 
; 內容放到eax里. 
0x8048438 <main+12>: add $0x4,%eax ; eax等于eax+4. 
0x804843b <main+15>: mov (%eax),%edx ; 把eax指向的四個內存字節單元里 
; 的內容賦給edx 
0x804843d <main+17>: push %edx ; esp等于esp-4, 把edx的值放到esp 
; 所指的內存地址的四個字節單元里. 

看看ebp+12處放的是什么? 
(gdb) x/x $ebp+12 
0xbffff6c4: 0xbffff704 
懷疑這里放的是指向argv[0]字串的地址的地址, 看看是不是 
(gdb) x/x 0xbffff704 
0xbffff704: 0xbffff83e 
(gdb) x/1s 0xbffff83e 
0xbffff83e: "/home/vcat/p" 
果然是. 那么$ebp+12的所指的四個字節的內容(argv[0]字串的地址)加上四, 應該就是指向 
argv[1]字串的地址了. 
(gdb) x/x 0xbffff704+4 
0xbffff708: 0xbffff856 
(gdb) x/1s 0xbffff856 
0xbffff856: "AAAAAAAA" 

可以看出, 這四條指令是用來計算argv[1](即所輸入的字串"AAAAAAAA"在內存中的起始地址), 
然后把該地址壓入棧中做為參數傳給即將被調用的函數vulFunc的. 

設個斷點在0x804843e, 讓程序繼續執行到調用vulFunc函數之前. 
(gdb) b *0x804843e 
Breakpoint 2 at 0x804843e 
(gdb) c 
Continuing. 

(gdb) i reg 
eax 0xbffff708 -1073744120 
ecx 0x804842c 134513708 
edx 0xbffff856 -1073743786 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6b4 -1073744204 
ebp 0xbffff6b8 -1073744200 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x804843e 134513726 
eflags 0x282 642 
(以下省略) 
... 

(gdb) x/10x $esp 
0xbffff6b4: 0xbffff856 0xbffff6d8 0x400349cb 0x00000002 
0xbffff6c4: 0xbffff704 0xbffff710 0x40013868 0x00000002 
0xbffff6d4: 0x08048350 0x00000000 

此時的進程在內存中的相關影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的開始地址 
0xbffff6b4 +--------+ <-- main函數的esp 
 ......   
(內存低址) 

單步執行 
(gdb) si 
0x8048400 in vulFunc () 

好, 現在進入vulFunc函數了. 
(gdb) i reg 
eax 0xbffff708 -1073744120 
ecx 0x804842c 134513708 
edx 0xbffff856 -1073743786 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6b0 -1073744208 
ebp 0xbffff6b8 -1073744200 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x8048400 134513664 
eflags 0x382 898 
(以下省略) 
... 

這時esp已經變為0xbffff6b0, 和以前的值0xbffff6b4比較相差四個字節. 
我們來看看到底壓了什么東西入棧. 
(gdb) x/11x $esp 
0xbffff6b0: 0x08048443 0xbffff856 0xbffff6d8 0x400349cb 
0xbffff6c0: 0x00000002 0xbffff704 0xbffff710 0x40013868 
0xbffff6d0: 0x00000002 0x08048350 0x00000000 

原來是main函數里調用vulFunc函數的指令的后續指令的地址--即vulFunc函數的返回地址. 
這是我們的第一個焦點. 

... 
0x804843e <main+18>: call 0x8048400 <vulFunc> 
0x8048443 <main+23>: add $0x4,%esp 
... 

我們接著分析vulFunc函數. 
0x8048400 <vulFunc>: push %ebp 
0x8048401 <vulFunc+1>: mov %esp,%ebp 
0x8048403 <vulFunc+3>: sub $0xc,%esp ; esp等于esp-12, 棧幀大小增加12個字節. 

前面兩條指令的功能和main函數的一樣, 用來保存調用函數棧幀的棧底ebp和設置被調用函 
數棧幀棧底. 
即: 保存調用函數的棧幀棧底, 調用函數棧幀的棧頂變為被調用函數的棧底. 可以看出當前 
(被調用函數)的棧幀為空時, ebp和esp的值相等. 


第三條指令在棧幀中分配了0xc(十二)個字節的內存空間, 注意到里面的內容是垃圾. 
(gdb) si 
0x8048401 in vulFunc () 
(gdb) si 
0x8048403 in vulFunc () 
(gdb) si 
0x8048406 in vulFunc () 

(gdb) x/15x $esp 
0xbffff6a0: 0x4000ae60 0xbffff704 0xbffff6b8 0xbffff6b8 
0xbffff6b0: 0x08048443 0xbffff856 0xbffff6d8 0x400349cb 
0xbffff6c0: 0x00000002 0xbffff704 0xbffff710 0x40013868 
0xbffff6d0: 0x00000002 0x08048350 0x00000000 

此時進程在內存中相關的影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  vulFunc函數的返回地址 
0xbffff6b0 +--------+ <-- 調用vulFunc函數前的esp 
bffff6b8  main函數的ebp 
0xbffff6ac +--------+ <-- vulFunc函數的ebp 
bffff6b8  (垃圾) 
0xbffff6a8 +--------+ 
bffff704  (垃圾) 
0xbffff6a4 +--------+ 
4000ae60  (垃圾) 
0xbffff6a0 +--------+ <-- vulFunc的當前esp 
 ......   
(內存低址) 

再看看下面的四條指令. 
0x8048406 <vulFunc+6>: mov 0x8(%ebp),%eax ; 把ebp+8指向的內存單元(4字節)里 
; 的內容賦給eax. 
從上圖看出vulFunc函數棧幀的ebp+8四字節內存單元里放的是指向"AAAAAAAA"字符串的起始地址. 

0x8048409 <vulFunc+9>: push %eax ; eax的值入棧. 
把指向"AAAAAAAA"字符串的起始地址入棧. 

0x804840a <vulFunc+10>: lea 0xfffffff4(%ebp),%eax 
哇! 好嚇人呀! 這條指令是干什么的? 讓我們慢慢來分析一下. 
這條指令是把ebp+0xfffffff4做為地址值賦給eax. 
但是ebp的值加上0xfffffff4指向那里呀, 這是我們要弄清楚的. 
這里如果我們按正數來加, 那是不行的. 
實際上這個十六進制的0xfffffff4所表示的是負數, 要知道它的值, 讓我們來算一下. 

F F F F F F F 4 
+----+----+----+----+----+----+----+----+ 
1111 1111 1111 1111 1111 1111 1111 0100  
+----+----+----+----+----+----+----+----+ 

取反 

0 0 0 0 0 0 0 B 
+----+----+----+----+----+----+----+----+ 
0000 0000 0000 0000 0000 0000 0000 1011  
+----+----+----+----+----+----+----+----+ 

加一 

0 0 0 0 0 0 0 C 
+----+----+----+----+----+----+----+----+ 
0000 0000 0000 0000 0000 0000 0000 1100  
+----+----+----+----+----+----+----+----+ 

也就是負的0xc. ebp+0xfffffff4, 即ebp-0xc. 

所以ebp+0xfffffff4, 就是現在棧頂指向的那十二個字節的起始地址. 

0x804840d <vulFunc+13>: push %eax 
接著把得到的地址入棧. 

讓程序運行到調用strcpy函數之前看看 
(gdb) b *0x804840e 
Breakpoint 3 at 0x804840e 
(gdb) c 
Continuing. 

Breakpoint 3, 0x804840e in vulFunc () 
(gdb) x/17x $esp 
0xbffff698: 0xbffff6a0 0xbffff856 0x4000ae60 0xbffff704 
0xbffff6a8: 0xbffff6b8 0xbffff6b8 0x08048443 0xbffff856 
0xbffff6b8: 0xbffff6d8 0x400349cb 0x00000002 0xbffff704 
0xbffff6c8: 0xbffff710 0x40013868 0x00000002 0x08048350 
0xbffff6d8: 0x00000000 

這時進程在內存的相關影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  vulFunc函數的返回地址 
0xbffff6b0 +--------+ <-- 調用vulFunc函數前的esp 
bffff6b8  main函數的ebp 
0xbffff6ac +--------+ <-- vulFunc函數的ebp 
bffff6b8  (垃圾) 
0xbffff6a8 +--------+ 
bffff704  (垃圾) 
0xbffff6a4 +--------+ 
4000ae60  (垃圾) 
0xbffff6a0 +--------+ 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ <-- vulFunc的當前esp 
 ......   
(內存低址) 

我們這里不關心strcpy函數具體運行, 把斷點設到調用它的后續指令. 
(gdb) b *0x8048413 
Breakpoint 4 at 0x8048413 
(gdb) c 
Continuing. 

Breakpoint 4, 0x8048413 in vulFunc () 
(gdb) x/17x $esp 
0xbffff698: 0xbffff6a0 0xbffff856 0x41414141 0x41414141 
0xbffff6a8: 0xbffff600 0xbffff6b8 0x08048443 0xbffff856 
0xbffff6b8: 0xbffff6d8 0x400349cb 0x00000002 0xbffff704 
0xbffff6c8: 0xbffff710 0x40013868 0x00000002 0x08048350 
0xbffff6d8: 0x00000000 

這時進程在內存中的相關影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  vulFunc函數的返回地址 
0xbffff6b0 +--------+ <-- 調用vulFunc函數前的esp 
bffff6b8  main函數的ebp 
0xbffff6ac +--------+ <-- vulFunc函數的ebp 
bffff600  
0xbffff6a8 +--------+ 
41414141  
0xbffff6a4 +--------+ 
41414141  
0xbffff6a0 +--------+ 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ <-- vulFunc的當前esp 
 ......   
(內存低址) 

我們注意到在vulFunc函數棧幀中所分配的那十二個字節, 從傳遞給strcpy函數的起始 
地址處被我們所輸入的八個'A'(十六進制0x41)填充了. 

這是我們的第二個焦點. 

同時也注意到, 內存地址0xbffff6a8所指向的四個字節的內容由原來的垃圾數據0xbffff6b8 
變成了bffff600. 

低字節的00應該就是字符串"AAAAAAAA"的零結尾字節. 

所以得出結論: vulFunc函數棧幀中分配的那十二個字節是給局部變量buf(緩沖區)的. 
這里會奇怪: 程序中buf緩沖區只定義了十個字節的大小, 為什么為它分配了十二個字 
節? 原因是: 內存的分配是以四字節為單位的.所以十個字節(4+4+2)要用三個內存分 
配單元, 3*4=12. 

如果我們在命令行提供的字串長度為十(多兩個字符, 剛好是程序中定義的緩沖區的大 
小), 那么內存地址0xbffff6a8所指向的四個字節的內容將是bf004141; 如果增加到十 
一個, 內存地址0xbffff6a8所指向的四個字節的內容為00414141, 剛好填滿棧幀中分配 
給buf的內存空間. 可以看出, 在命令行中提供的字串長度小于12, 程序是不會出錯的. 

現在讓我們看看字串長度等于十二的情況, 這時0xbffff6a8所指向的四個字節的內存單 
元已被41414141填滿.0xbffff6ac所指向的四個字節的內存單元的低字節被00所填, 其內 
容變為bffff600, 從上面的影像圖可知: 這個內存單元里保存的是調用函數的ebp. 也就 
是說, 當字串長度大于或等于十二時, 調用函數的ebp被復蓋. 

從進程的影像圖可以看出, 要想全面復蓋vulFunc函數的返回地址, 則字節串的長度至少 
要二十(12+8)個字節. 

我們繼續分析后面的指令: 
0x8048413 <vulFunc+19>: add $0x8,%esp ; 棧幀縮小8個字節--放棄了兩個內存存儲單元. 

可以看到, 在調用strcpy前, 依次壓了s和buf的地址入棧, 現在這條指令是把這兩個地址拋棄. 

所以可以得出, Linux x86系統在調用函數時(其實是編譯器所生成的機器指令), 所傳給 
被調用函數的參數是由調用函數從右到左依次入棧的. 
如現在的strcpy(buf, s), 首先是s先入棧, 然后是buf. 參數的出棧也由調用函數負責. 

0x8048416 <vulFunc+22>: lea 0xfffffff4(%ebp),%eax 
0x8048419 <vulFunc+25>: push %eax 
這兩條指令和前面的一樣, 把argv[1](即"AAAAAAAA"字串)的起始地址入棧. 

0x804841a <vulFunc+26>: push $0x80484b0 
先看一下0x80484b0里面放的是什么, 雖然很明顯是即將調用的printf函數的第一個參數的地址. 
(gdb) x/1s 0x80484b0 
0x80484b0 <_IO_stdin_used+4>: "String=%s\n" 
果然是. 

下面的兩條指令就是調用printf函數和拋棄在棧中的兩個參數了. 
0x804841f <vulFunc+31>: call 0x8048330 <printf> 
0x8048424 <vulFunc+36>: add $0x8,%esp 

我們在0x08048427 leave 指令的前面設個斷點并繼續運行. 
(gdb) b *0x8048427 
Breakpoint 5 at 0x8048427 
(gdb) c 
Continuing. 
String=AAAAAAAA 

Breakpoint 5, 0x8048427 in vulFunc () 
屏幕輸出了"String=AAAAAAAA". 

這時棧幀的內容為: 
(gdb) x/15x $esp 
0xbffff6a0: 0x41414141 0x41414141 0xbffff600 0xbffff6b8 
0xbffff6b0: 0x08048443 0xbffff856 0xbffff6d8 0x400349cb 
0xbffff6c0: 0x00000002 0xbffff704 0xbffff710 0x40013868 
0xbffff6d0: 0x00000002 0x08048350 0x00000000 

進程在內存中的相關影像為: 
(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  vulFunc函數的返回地址 
0xbffff6b0 +--------+ <-- 調用vulFunc函數前的esp 
bffff6b8  main函數的ebp 
0xbffff6ac +--------+ <-- vulFunc函數的ebp 
bffff600  
0xbffff6a8 +--------+ 
41414141  
0xbffff6a4 +--------+ 
41414141  
0xbffff6a0 +--------+ <-- vulFunc的當前esp 
bffff856  (垃圾) 字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  (垃圾) vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ 
 ......   
(內存低址) 

各寄存器的狀況: 
(gdb) i reg 
eax 0x10 16 
ecx 0x400 1024 
edx 0x4010a980 1074833792 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6a0 -1073744224 
ebp 0xbffff6ac -1073744212 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x8048427 134513703 
eflags 0x296 662 
(以下省略) 
... 

請注意: 此時esp的內容為0xbffff6a0, ebp的內容為0xbffff6ac 
單步運行leave指令, 然后看一下寄存器的情況. 
(gdb) si 
0x8048428 in vulFunc () 
(gdb) i reg 
eax 0x10 16 
ecx 0x400 1024 
edx 0x4010a980 1074833792 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6b0 -1073744208 
ebp 0xbffff6b8 -1073744200 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x8048428 134513704 
eflags 0x396 918 
(以下省略) 
... 

此時的esp的內容為0xbffff6b0, 即執行leave指令前的ebp內容0xbffff6ac+4; 
ebp的內容為0xbffff6b8, 這個值從那來的呢? 看一下此時進程在內存中的影像, 正好是 
vulFunc函數的ebp指向的內存的內容, 而隨著這個值的出棧, esp的值正好為0xbffff6b0. 

由此可見, leave指令其實等價于 
mov %ebp,%esp 
pop %ebp 
這兩條指令, 正好和剛進入被調用函數時 
push %ebp 
mov %esp,%ebp 
這兩條指令的功能相反. 
也就是說leave指令拋棄了被調用函數的棧幀, 恢復了調用函數的棧幀. 

此時棧中相關的內容: 
(gdb) x/11x $esp 
0xbffff6b0: 0x08048443 0xbffff856 0xbffff6d8 0x400349cb 
0xbffff6c0: 0x00000002 0xbffff704 0xbffff710 0x40013868 
0xbffff6d0: 0x00000002 0x08048350 0x00000000 

進程在內存中的相關影像: 

(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  vulFunc函數的返回地址 
0xbffff6b0 +--------+ <-- 當前esp 
bffff6b8  (垃圾) main函數的ebp 
0xbffff6ac +--------+ 
bffff600  (垃圾) 
0xbffff6a8 +--------+ 
41414141  (垃圾) 
0xbffff6a4 +--------+ 
41414141  (垃圾) 
0xbffff6a0 +--------+ 
bffff856  (垃圾) 字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  (垃圾) vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ 
 ......   
(內存低址) 

繼續執行下條指令: ret 
(gdb) si 
0x8048443 in main () 
(gdb) i reg 
eax 0x10 16 
ecx 0x400 1024 
edx 0x4010a980 1074833792 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6b4 -1073744204 
ebp 0xbffff6b8 -1073744200 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x8048443 134513731 
eflags 0x396 918 
(以下省略) 
... 

可以看出, 從棧中彈出0x8048443(vulFunc函數調用的返回地址)給了eip. 
至此vulFunc函數調用完畢, 返回到main函數繼續執行. 

值得注意的是: 如果象上面所說的, 我們輸入的字串長度為二十個'A'--剛好復蓋完0xbffff6b0 
所指的單元, 那么此時從棧中彈出給eip的內容將是0x41414141, 而不是0x8048443, 程序 
將跳到0x41414141去執行那里的指令, 由于0x41414141對于當前進程來說是不可訪問的, 
所以導致段出錯(Segmentation fault), 進程停止執行. 

這是我們的第三個焦點. 

如果我們能計算好位移(offset), 用我們準備好的代碼的入口地址來覆蓋0xbffff6b0所 
指的單元, 那么從棧中彈出給eip的內容就是我們的代碼的入口地址, 程序將跳到我們的 
代碼去繼續執行. 

分析到這里, 我們已經清楚了C語言函數調用的機制了. main函數的后續指令對于我們的 
分析已無關緊要. 但是為了保持文章的完整, 我們繼續再往下看看. 

此時棧的情況: 
(gdb) x/10x $esp 
0xbffff6b4: 0xbffff856 0xbffff6d8 0x400349cb 0x00000002 
0xbffff6c4: 0xbffff704 0xbffff710 0x40013868 0x00000002 
0xbffff6d4: 0x08048350 0x00000000 

進程在內存中的相關影像: 

(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  
0xbffff6bc +--------+ <-- 調用main函數前的esp 
bffff6d8  調用main函數前的ebp 
0xbffff6b8 +--------+ <-- main函數的ebp 
bffff856  字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ <-- 當前esp 
08048443  (垃圾) vulFunc函數的返回地址 
0xbffff6b0 +--------+ 
bffff6b8  (垃圾) main函數的ebp 
0xbffff6ac +--------+ 
bffff600  (垃圾) 
0xbffff6a8 +--------+ 
41414141  (垃圾) 
0xbffff6a4 +--------+ 
41414141  (垃圾) 
0xbffff6a0 +--------+ 
bffff856  (垃圾) 字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  (垃圾) vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ 
 ......   
(內存低址) 


再看看后續的指令做了些什么? 
0x8048443 <main+23>: add $0x4,%esp ; 拋棄棧中為被調用函數準備的參數. 
0x8048446 <main+26>: jmp 0x804845b <main+47> ; 跳轉到0x804845b繼續執行 
0x8048448 <main+28>: mov 0xc(%ebp),%eax ; 0x8048433 jne的條件判斷跳轉 
; 入口(即argc!=2的情況) 
; 把ebp+0xc所指向的內存單元的 
; 內容賦給eax, 從上面的分析我 
; 們知道里面放的是argv的地址 
0x804844b <main+31>: mov (%eax),%edx ; 把eax指向的地址的內存單元里 
; 的內容賦給edx, 我們知道argv 
; 是個數組, argv的值就是argv[0] 
0x804844d <main+33>: push %edx ; 把argv[0]入棧. 注意這里的 
; argv[0]其實是個地址值. 
0x804844e <main+34>: push $0x80484bb ; 把常數0x80484bb入棧 
; 以上為調用printf函數準備參數. 
0x8048453 <main+39>: call 0x8048330 <printf> ; 調用printf函數 
0x8048458 <main+44>: add $0x8,%esp ; 拋棄為調用printf函數準備的參數 
0x804845b <main+47>: leave ; 恢復調用main函數的函數的棧幀 
0x804845c <main+48>: ret ; 返回到調用main函數的函數 

估計0x80484bb指向的是printf函數的format字串, 看看是不是? 
(gdb) x/1s 0x80484bb 
0x80484bb <_IO_stdin_used+15>: "Usage: %s <A string>\n" 
果然是. 那從0x8048448到0x8048458這段指令就是C語言 
printf("Usage: %s <A string>\n", argv[0]); 
的等價匯編語句了. 

我們把斷點設到0x804845b, 再繼續執行. 
(gdb) b *0x804845b 
Breakpoint 6 at 0x804845b 
(gdb) c 
Continuing. 

Breakpoint 6, 0x804845b in main () 

下一條指令是leave, 應該是恢復調用函數的函數的棧幀. 
單步執行一下, 看看寄存器及棧的情況. 
(gdb) si 
0x804845c in main () 
(gdb) i reg 
eax 0x10 16 
ecx 0x400 1024 
edx 0x4010a980 1074833792 
ebx 0x4010c1ec 1074840044 
esp 0xbffff6bc -1073744196 
ebp 0xbffff6d8 -1073744168 
esi 0x4000ae60 1073786464 
edi 0xbffff704 -1073744124 
eip 0x804845c 134513756 
eflags 0x386 902 
(以下省略) 
... 

(gdb) x/8x $esp 
0xbffff6bc: 0x400349cb 0x00000002 0xbffff704 0xbffff710 
0xbffff6cc: 0x40013868 0x00000002 0x08048350 0x00000000 

下一條指令是ret, 我們知道棧頂放的是main函數的返回地址(0x400349cb). 

此時進程在內存中的相關影像: 

(內存高址) 
 ......   
+--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  
+--------+ 
00000002  
+--------+ 
40013868  
+--------+ 
bffff710  
+--------+ 
bffff704  argv的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  main函數的返回地址 
0xbffff6bc +--------+ <-- 當前esp 
bffff6d8  (垃圾) 調用main函數前的ebp 
0xbffff6b8 +--------+ 
bffff856  (垃圾) 字符串"AAAAAAAA"在內存中的起始地址 
0xbffff6b4 +--------+ 
08048443  (垃圾) vulFunc函數的返回地址 
0xbffff6b0 +--------+ 
bffff6b8  (垃圾) main函數的ebp 
0xbffff6ac +--------+ 
bffff600  (垃圾) 
0xbffff6a8 +--------+ 
41414141  (垃圾) 
0xbffff6a4 +--------+ 
41414141  (垃圾) 
0xbffff6a0 +--------+ 
bffff856  (垃圾) 字符串"AAAAAAAA"在內存中的起始地址 
0xbffff69c +--------+ 
bffff6a0  (垃圾) vulFunc函數棧幀中分配的十二個字節起始地址 
0xbffff698 +--------+ 
 ......   
(內存低址) 

再單步執行, 返回到調用main函數的函數 
(gdb) si 
0x400349cb in __libc_start_main (main=0x804842c <main>, argc=2, argv=0xbffff704, init=0x80482c0 <_init>, 
fini=0x804848c <_fini>, rtld_fini=0x4000ae60 <_dl_fini>, stack_end=0xbffff6fc) 
at ../sysdeps/generic/libc-start.c:92 
92 ../sysdeps/generic/libc-start.c: No such file or directory. 

原來是 __libc_start_main 函數調用了我們的main函數, 看來和概述里說的有些出入, 
但這對于我們來講不是很重要. 如果想看源碼, 請到../sysdeps/generic/libc-start.c 
文件中找. 
(gdb) x/16x $esp 
0xbffff6c0: 0x00000002 0xbffff704 0xbffff710 0x40013868 
0xbffff6d0: 0x00000002 0x08048350 0x00000000 0x08048371 
0xbffff6e0: 0x0804842c 0x00000002 0xbffff704 0x080482c0 
0xbffff6f0: 0x0804848c 0x4000ae60 0xbffff6fc 0x40013e90 

從上面可以看到, stack_end=0xbffff6fc, 也就是說我們的進程的棧底地址為0xbffff6fc, 
在調用__libc_start_main函數前依次推了如下七個參數入棧: 
0xbffff6fc -> 進程的棧底 
0x4000ae60 -> _dl_fini函數的入口地址. 
0x0804848c -> _fini函數的入口地址 
0x080482c0 -> _init函數的入口地址 
0xbffff704 -> argv命令行參數地址的地址 
0x00000002 -> argc命令行參數個數值 
0x0804842c -> 我們的main函數入口 

從上面的分析可推出, 在內存地址0xbffff6dc的內容0x08048371就是__libc_start_main函數 
的返回地址了. 
我們來看看是什么函數調用了__libc_start_main. 
(gdb) disas 0x08048371 
Dump of assembler code for function _start: 
0x8048350 <_start>: xor %ebp,%ebp 
0x8048352 <_start+2>: pop %esi 
0x8048353 <_start+3>: mov %esp,%ecx 
0x8048355 <_start+5>: and $0xfffffff8,%esp 
0x8048358 <_start+8>: push %eax 
0x8048359 <_start+9>: push %esp 
0x804835a <_start+10>: push %edx 
0x804835b <_start+11>: push $0x804848c 
0x8048360 <_start+16>: push $0x80482c0 
0x8048365 <_start+21>: push %ecx 
0x8048366 <_start+22>: push %esi 
0x8048367 <_start+23>: push $0x804842c 
0x804836c <_start+28>: call 0x8048320 <__libc_start_main> 
0x8048371 <_start+33>: hlt 
0x8048372 <_start+34>: nop 
0x8048373 <_start+35>: nop 
(省略以下的nop) 
End of assembler dump. 

原來是_start函數調用了__libc_start_main函數. 
至于_start函數調用__libc_start_main函數后, 接是如何調用_init函數和_dl_runtime_resove 
函數來調用共享庫函數和我們的main函數然后退出的, 已經遠遠脫離了本文的主題, 這里不再繼 
續介紹. 

(gdb) x/1024x 0xbffff6f0 
0xbffff6f0: 0x0804848c 0x4000ae60 0xbffff6fc 0x40013e90 
0xbffff700: 0x00000002 0xbffff83e 0xbffff856 0x00000000 
0xbffff710: 0xbffff85f 0xbffff881 0xbffff88f 0xbffff89e 
0xbffff720: 0xbffff8c4 0xbffff8d2 0xbffff900 0xbffff91a 
0xbffff730: 0xbffff932 0xbffff94d 0xbffff9a8 0xbffff9df 
0xbffff740: 0xbffffaf3 0xbffffb06 0xbffffb11 0xbffffb31 
0xbffff750: 0xbffffb5a 0xbffffb68 0xbffffc72 0xbffffc7e 
0xbffff760: 0xbffffc8f 0xbffffca4 0xbffffcb4 0xbffffcbf 
0xbffff770: 0xbffffcd7 0xbffffcf5 0xbffffd0e 0xbffffd19 
0xbffff780: 0xbffffd23 0xbffffd6c 0xbffffd79 0xbffffda0 
0xbffff790: 0xbffffdb2 0xbffffdc1 0xbffffde6 0xbffffe08 
0xbffff7a0: 0xbffffe10 0xbfffffd3 0x00000000 0x00000003 
0xbffff7b0: 0x08048034 0x00000004 0x00000020 0x00000005 
0xbffff7c0: 0x00000006 0x00000006 0x00001000 0x00000007 
0xbffff7d0: 0x40000000 0x00000008 0x00000000 0x00000009 
0xbffff7e0: 0x08048350 0x0000000b 0x000001f5 0x0000000c 
0xbffff7f0: 0x000001f5 0x0000000d 0x00000004 0x0000000e 
0xbffff800: 0x00000004 0x00000010 0x008001bf 0x0000000f 
0xbffff810: 0xbffff839 0x00000000 0x00000000 0x00000000 
0xbffff820: 0x00000000 0x00000000 0x00000000 0x00000000 
0xbffff830: 0x00000000 0x00000000 0x38356900 0x682f0036 
0xbffff840: 0x2f656d6f 0x65776f74 0x74742f72 0x2f737775 
0xbffff850: 0x2f6c6469 0x41410070 0x41414141 0x4c004141 
0xbffff860: 0x4f535345 0x3d4e4550 0x73752f7c 0x69622f72 
... 
(省略) 
... 
0xbfffffd0: 0x54003a35 0x75413d5a 0x61727473 0x2f61696c 
0xbfffffe0: 0x0057534e 0x6d6f682f 0x6f742f65 0x2f726577 
0xbffffff0: 0x77757474 0x64692f73 0x00702f6c 0x00000000 
0xc0000000: Cannot access memory at address 0xc0000000 

我們知道內存單元0xbffff704放的是指argv[0]的地址, 那么0xbffff708放的就是argv[1] 
的地址了. 0xbffff700里放的是argc的值. 

那么0xbffff710里放的是什么呢? 看樣子象是指向字符串的地址, 讓我們來看看. 
(gdb) x/1s 0xbffff85f 
0xbffff85f: "LESSOPEN= /usr/bin/lesspipe.sh %s" 
(gdb) 
0xbffff881: "HISTSIZE=1000" 
... 

再看看最后一個. 
(gdb) x/1s 0xbfffffd3 
0xbfffffd3: "TZ=Australia/NSW" 
0xc0000000以后的地址空間已不是進程能合法訪問的了. 

原來都是些SHELL的環境變量字符串. 

這一片東西是從內存地址0xbffff839開始的, 讓我們再看看. 
(gdb) x/1s 0xbffff839 
0xbffff839: "i586" 
(gdb) 
0xbffff83e: "/home/vcat/p" ===> 細心的朋友會發現這里已被俺改掉了, 
讓俺保留一點私隱吧 ;) 
(gdb) 
0xbffff856: "AAAAAAAA" 
(gdb) 
0xbffff85f: "LESSOPEN= /usr/bin/lesspipe.sh %s" 
... 

我們得出結論: 0xbffff700放的是argc的值; 0xbffff704放的是argv[0]的地址, 
0xbffff708放的是argv[1]的地址; 0xbffff710--0xbffffa4放的是指向各個環境變量 
字符串起始地址的指針; 從內存地址0xbffff839開始依次存放的是: 系統平臺信息字 
串; 命令行字串; 環境變量字串. 

至于0xbffff7a8--0xbffff838里放的是什么, 還有待研究. 由于對本文不是至關重要, 
暫時放一下. 

分析到這, 我們來組合一下進程在內存的影像: 

(內存高址) 

 ......   ...省略了一些我們不需要關心的區 
+--------+ 
00000000 \ 
0xbffffffc +--------+ \ 
 ......   \ 

 ......   \ 
0xbffff844 +--------+ 系統平臺信息串(如:"i586")和命令行參數及環境變量字串 
2f656d6f  / 
0xbffff840 +--------+ / 
682f0036  / 
0xbffff83c +--------+ / 
38356900 / --> 從內存地址0xbffff839開始, 0x69353836="i586" 
0xbffff838 +--------+ 
 ......  \ 
里面放的是什么? 還有待研究 
 ......  / 
0xbffff7a8 +--------+ 
bfffffd3 \ 
0xbffff7a4 +--------+ \ 
 ......   \ 
...... 環境變量指針 
 ......   / 
0xbffff714 +--------+ / 
bffff85f / 
0xbffff710 +--------+ 
00000000  
0xbffff70c +--------+ 
bffff856  argv[1]的地址 
0xbffff708 +--------+ 
bffff83e  argv[0]的地址 
0xbffff704 +--------+ 
00000002  argc的值 
0xbffff700 +--------+ 
40013e90  ???? (和_dl_starting_up函數有關) 
0xbffff6fc +--------+ <-- 進程的棧底 
bffff6fc  stack_end(進程的棧底) 
0xbffff6f8 +--------+ 
4000ae60  _dl_fini函數入口地址 
0xbffff6f4 +--------+ 
0804848c  _fini函數入口地址 
0xbffff6f0 +--------+ 
080482c0  _init函數入口地址 
0xbffff6ec +--------+ 
bffff704  argv地址的地址 
0xbffff6e8 +--------+ 
00000002  argc的值 
0xbffff6e4 +--------+ 
0804842c  main函數的入口地址 
0xbffff6e0 +--------+ 
08048371  __libc_start_main的返回地址(指令hlt), 正常情況不會返回到這. 
0xbffff6dc +--------+ 
00000000  
0xbffff6d8 +--------+ <-- 調用main函數前的ebp 
08048350  _start函數的入口地址 
+--------+ 
00000002  argc的值 
+--------+ 
40013868  ???? 
+--------+ 
bffff710  環境變量指針的地址 
+--------+ 
bffff704  argv地址的地址(即argv[0]的地址) 
0xbffff6c4 +--------+ 
00000002  argc的值 
0xbffff6c0 +--------+ 
400349cb  main函數的返回地址 
0xbffff6bc +--------+ <-- 當前esp 



主站蜘蛛池模板: 日本特黄特色免费大片 | 午夜在线社区视频 | 四虎成人免费大片在线 | 四虎永久在线观看视频精品 | 三级在线观看免播放网站 | 欧美性高清suv | 色亚洲天堂| 日韩精品一区二区三区免费观看 | 日本乱子 | 深夜福利国产 | 日韩黄色在线视频 | 三级黄色网络 | 小说区 亚洲 自拍 另类 | 天堂网www在线观看 天堂网www在线 | 亚洲高清视频免费 | 日韩免费高清 | 亚欧aⅴ天堂在线 | 婷婷六月久久综合丁香可观看 | 亚洲欧洲精品视频在线观看 | 天天爱天天做色综合 | 日本成人免费在线观看 | 亚洲男女网站 | 亚洲欧美在线精品 | 亚洲欧美日韩伦中文 | 亚洲欧洲综合在线 | 欧洲性生活视频 | 五月婷婷婷婷 | 亚洲国产精品久久久久666 | 青青草中文字幕 | 最新国产精品精品视频 | 色欧美色 | 亚洲人成在线播放 | 日韩欧美一区黑人vs日本人 | 情侣视频精品免费的国产 | 午夜私人影院粉色视频我要 | 日韩亚洲一区中文字幕在线 | 欧美在线视频不卡 | 最新韩国伦理片大全手机在线播放 | 欧美午夜影院 | 欧美一级成人一区二区三区 | 午夜网站在线观看免费网址免费 |