緩沖區(qū)溢出解密一
互聯(lián)網(wǎng) 發(fā)布時(shí)間:2008-10-08 19:04:09 作者:佚名
我要評(píng)論
緩沖溢出弱點(diǎn)誕生于70年代。Morris Worm(80年代)可以認(rèn)為是它們的第一次公開(kāi)應(yīng)用。從90年代開(kāi)始,相關(guān)的文檔,如著名的Aleph1的”Smashing the Stack for Fun and Profit”和代碼已經(jīng)在互聯(lián)網(wǎng)上公開(kāi)。
這篇文章是關(guān)于某種需要非常重視的主題的系列文
緩沖溢出弱點(diǎn)誕生于70年代。Morris Worm(80年代)可以認(rèn)為是它們的第一次公開(kāi)應(yīng)用。從90年代開(kāi)始,相關(guān)的文檔,如著名的Aleph1的”Smashing the Stack for Fun and Profit”和代碼已經(jīng)在互聯(lián)網(wǎng)上公開(kāi)。
這篇文章是關(guān)于某種需要非常重視的主題的系列文章的開(kāi)始,并且包括了很多的細(xì)節(jié);它的目的是解釋和闡明非?;镜穆┒搭愋停此^的本地溢出,而且論述了如何編寫(xiě)利用這種漏洞的代碼。
為了理解接下來(lái)的內(nèi)容,需要一些C和匯編的知識(shí)。虛擬內(nèi)存,一些操作系統(tǒng)的基本知識(shí)例如象一個(gè)進(jìn)程在內(nèi)存里是如何布局的。你必須知道什么是setuid二進(jìn)制文件,而且當(dāng)然你需要至少能夠使用UNIX系統(tǒng)。如果你有g(shù)db/cc調(diào)試編譯的經(jīng)驗(yàn),那就非常好了。文檔特定在Linux/ix86環(huán)境下。細(xì)節(jié)的不同之處取決于操作系統(tǒng)或者你使用的架構(gòu)。在接下來(lái)的文檔里面,將介紹相應(yīng)的更高級(jí)的溢出和shellcode技術(shù)。
最新版本的文檔可以在這里找到:
http://www.enderunix.org/documents/eng/bof-eng.txt
什么是溢出?
如果你知道C,你肯定知道什么是字符型數(shù)組。假設(shè)你用C寫(xiě)代碼,你應(yīng)該已經(jīng)知道數(shù)組的基本特性,象:數(shù)組擁有同樣類型的對(duì)象,例如:int,char,float。就像所有的數(shù)據(jù)結(jié)構(gòu)一樣,它們能夠被分成是”靜態(tài)”或者是”動(dòng)態(tài)”。靜態(tài)變量被填入程序的數(shù)據(jù)段,然而,動(dòng)態(tài)變量在內(nèi)存中的可執(zhí)行程序的堆棧區(qū)域分配和重新分配。”基于堆棧”的溢出就在這里發(fā)生了,我們?cè)跀?shù)據(jù)結(jié)構(gòu)中填更多的數(shù)據(jù),也就是說(shuō)多于一個(gè)數(shù)組能夠存儲(chǔ)的數(shù)據(jù),我們忽略許多重要的數(shù)據(jù)超越這個(gè)數(shù)組的界限。簡(jiǎn)單的,拷貝20字節(jié)到一個(gè)只能存儲(chǔ)12字節(jié)的數(shù)組里。
一個(gè)Linux ELF格式二進(jìn)制的存儲(chǔ)結(jié)構(gòu)相當(dāng)?shù)膹?fù)雜。特別是在ELF (詳細(xì)內(nèi)容,在google中搜索”Executable and Linkable Format”)和共享庫(kù)引入后,它已經(jīng)變得更加復(fù)雜。然而,基本上,每一個(gè)進(jìn)程運(yùn)行時(shí)有3段:
1.文本段,是一個(gè)只讀部分包括所有的程序指令。對(duì)于等同于下面C代碼的指令集合將被包括在這段。
for (i = 0; i
2.?dāng)?shù)據(jù)段是初始化了的和未初始化的數(shù)據(jù)(也被認(rèn)為是BBS段)所在的塊。
if you code;
int i;
the variable is an uninitialized variable, and it'll be stored in
the "uninitialized variables" part of the Data Segment. (BSS)
and, if you code;
int j = 5;
the variable is an initialized variable, and the the space for
the j variable will be allocated in the "initialized variables"
part of the Data Segment.
3.一個(gè)被稱為”堆棧”的段,在這里動(dòng)態(tài)變量(或者在C里面叫自動(dòng)變量)被分配和重新分配;并且為函數(shù)返回臨時(shí)存儲(chǔ)地址。例如,在下面代碼片段中,i變量在堆棧中產(chǎn)生,僅僅在函數(shù)返回后,它就消亡了。
int myfunc(void)
{
int i;
for (i = 0; i 如果我們用符號(hào)表示堆棧:
0xBFFFFFFF ---------------------
| |
| . |
| . |
| . |
| . |
| etc |
| env/argv pointer. |
| argc |
|-------------------|
| |
| stack |
| |
| | |
| | |
| V |
/ /
\ \
| |
| ^ |
| | |
| | |
| |
| heap |
|-------------------|
| bss |
|-------------------|
| initialized data |
|-------------------|
| text |
|-------------------|
| shared libraries |
| etc. |
0x8000000 |-------------------|
_* STACK *_
堆棧在基本術(shù)語(yǔ)里面是一個(gè)數(shù)據(jù)結(jié)構(gòu),你們都可以從你們的數(shù)據(jù)結(jié)構(gòu)課程記起來(lái)。它有同樣的基本操作。它是一個(gè)LIFO(后進(jìn),先出)的數(shù)據(jù)結(jié)構(gòu)。它的處理過(guò)程通過(guò)一些特殊的指令象PUSH和POP由CPU直接控制。你PUSH一些數(shù)據(jù)到堆棧里面,又POP一些其它的數(shù)據(jù)。不論誰(shuí)最后到,它將是最先出來(lái)的那個(gè)。因此,用專業(yè)術(shù)語(yǔ)說(shuō),第一個(gè)將被從堆棧中推出來(lái)的是最后一個(gè)被推進(jìn)去的。
在CPU中注冊(cè)的SP(堆棧指針)包括將要從堆棧中推出來(lái)的數(shù)據(jù)地址。不論SP指向最后的數(shù)據(jù)還是堆棧中最后數(shù)據(jù)的后面數(shù)據(jù)是CPU-specific的;然而,我們的目標(biāo)ix86結(jié)構(gòu),SP指向堆棧中最后數(shù)據(jù)的地址。在ix86保護(hù)模式(32位/雙字)下,PUSH和POP指令在4字節(jié)單元中完成。在這里要說(shuō)的另外一個(gè)重要的細(xì)節(jié)是堆棧向下增長(zhǎng),也就是,如果SP是0xFF,執(zhí)行PUSH EAX指令后,SP將變成0xFC并且EAX的值將被放到0xFC地址里。 PUSH指令將從ESP(回顧一下上面的圖)中減去4個(gè)字節(jié),并且將推入一個(gè)雙字到堆棧,放置雙字到ESP寄存器所指的地址中。另一方面,POP指令,讀取ESP寄存器中的地址,POP掉堆棧地址所指的值,并且加4到ESP(加4到ESP寄存器中的地址)。假設(shè)ESP初始化為0x1000,讓我們觀察下面的匯編代碼:
PUSH dword1 ;value at dword1: 1, ESP's value: 0xFFC (0x1000 - 4)
PUSH dword2 ;value at dword2: 2, ESP's value: 0xFF8 (0xFFC - 4)
PUSH dword3 ;value at dword3: 3, ESP's value: 0xFF4 (0xFF8 - 4)
POP EAX ;EAX' value 3, ESP's value: 0xFF8 (0xFF4 4)
POP EBX ;EBX's value 2, ESP's value: 0xFFC (0xFF8 4)
POP ECX ;ECX's value 1, ESP's value: 0x1000 (0xFFC 4)
當(dāng)堆棧被用做動(dòng)態(tài)變量的臨時(shí)存儲(chǔ)的時(shí)候,它被用來(lái)存儲(chǔ)一些調(diào)用存儲(chǔ)臨時(shí)變量函數(shù)的地址和在函數(shù)間傳遞參數(shù)。而且,當(dāng)然,這也是邪惡的來(lái)場(chǎng)。
EIP寄存器,CALL和RET指令
在每一個(gè)機(jī)器周期中,CPU查詢指令指針寄存器存儲(chǔ)中存儲(chǔ)的內(nèi)容(在ix86 32位保護(hù)模式中是EIP-擴(kuò)展指令指針)來(lái)知道下一步要執(zhí)行什么。下一個(gè)將要執(zhí)行的指令地址存儲(chǔ)在EIP寄存器中。通常,地址是連續(xù)的,意味著下個(gè)將要執(zhí)行的下個(gè)指令在內(nèi)存中比當(dāng)前指令靠前幾個(gè)字節(jié)。CPU根據(jù)當(dāng)前指令是幾個(gè)字節(jié)長(zhǎng)度計(jì)算出”靠前的幾個(gè)字節(jié)”,然后把這幾個(gè)字節(jié)值加到當(dāng)前地址。舉個(gè)例子,假設(shè)當(dāng)前指令地址是0x8048438。這是寫(xiě)在EIP中的值。因此,CPU執(zhí)行在內(nèi)存地址:0x8048438中找到的指令。比方說(shuō),是一個(gè)PUSH指令:
push 雙
CPU知道一個(gè)PUSH指令是1字節(jié)長(zhǎng),因此下一個(gè)指令將在0x8048439,可能是
mov %esp,雙
在執(zhí)行PUSH時(shí),CPU將把MOV地址放到EIP中。
好了,我們說(shuō)將被放到EIP中的值是CPU自身計(jì)算出來(lái)的。如果我們JMP到一個(gè)函數(shù)又是什么樣子的呢?函數(shù)中指令的地址將在內(nèi)存中的其它地方。它們執(zhí)行后,CPU是如何知道到哪里繼續(xù)調(diào)用程序執(zhí)行呢?為了這個(gè)目的,在我們JMP到函數(shù)前,我們?cè)谝粋€(gè)臨時(shí)寄存器中保存下一個(gè)指令地址,比如說(shuō)在EDX中;并且在從函數(shù)返回之前,我們又把EDX中的地址寫(xiě)回EIP。如果我們使用JMP來(lái)跳轉(zhuǎn)到函數(shù)地址,那將可能實(shí)際上是一個(gè)非常煩人的工作。
然而,ix86處理器家族給我們提供了兩個(gè)指令:CALL和RET,使我們的生活簡(jiǎn)單!CALL指令把”函數(shù)返回后下個(gè)要被執(zhí)行的指令”寫(xiě)入堆棧。它PUSH地址到堆棧,并且把函數(shù)地址寫(xiě)入EIP。因而,一個(gè)函數(shù)調(diào)用就產(chǎn)生了。另一方面,RET指令從堆棧中POP出”返回地址”,并且把地址寫(xiě)入EIP。因而,我們將從函數(shù)安全地返回,而且繼續(xù)了程序的下一步執(zhí)行。
讓我們看看下面的代碼片段:
x = 0;
function(1, 2, 3);
x = 1;
在幾個(gè)匯編指令運(yùn)行(x=0)之后,我們需要到function()所在的內(nèi)存位置。就如我前面所說(shuō)的,為了實(shí)現(xiàn)這一目標(biāo),首先我們拷貝返回地址的地址(在這個(gè)例子里是x=1的指令地址。)到一些臨時(shí)空間(可能是一個(gè)寄存器)用JMP跳轉(zhuǎn)到函數(shù)的地址空間,而且,在函數(shù)結(jié)束時(shí)我們恢復(fù)我們將要拷貝到EIP的返回地址。
感謝上帝,所有這些麻煩的操作都由CPU自身通過(guò)CALL和RET為我們做了,而你能從上面的篇章中得到相關(guān)細(xì)節(jié)。 一般地,程序堆棧區(qū)域能夠象這樣表示:
|_parameter_I____| ESP 8
|_parameter II___| ESP 4
|_return address_| ESP
Figure X : Stack
ESP,EBP
如我們已經(jīng)說(shuō)過(guò)的,堆棧同樣被用來(lái)存儲(chǔ)動(dòng)態(tài)變量。動(dòng)態(tài)地,程序請(qǐng)求新的空間時(shí),CPU PUSH一些數(shù)據(jù)而我們的程序釋放一些數(shù)據(jù)時(shí)它就POP一些數(shù)據(jù)。為了給內(nèi)存編址,我們使用”相對(duì)尋址”。也就是說(shuō),我們?cè)谖覀兊亩褩V薪o數(shù)據(jù)編址,與一些標(biāo)準(zhǔn)相關(guān)。而這個(gè)標(biāo)準(zhǔn)就是ESP,它是Extended Stack Pointer首字母縮寫(xiě)。這個(gè)寄存器指向堆棧的頂端??紤]這個(gè):
void f()
{
int a;
}
可以看出,在f()函數(shù)中,我們?yōu)橐粋€(gè)名為a的整型變量分配空間。整型變量a的空間將在堆棧中分配。而計(jì)算機(jī)將引用它的地址作為ESP- 的一些字節(jié)。因此堆棧指針對(duì)程序執(zhí)行是非常重要的。若我們調(diào)用一個(gè)函數(shù)呢?這個(gè)調(diào)用的函數(shù)有一個(gè)堆棧,它有一些局部變量,意味著它將要利用堆棧指針寄存器。同樣地,內(nèi)部被調(diào)用的函數(shù)將有局部變量而且它也將需要堆棧指針。
為了克服這個(gè),我們保存老的堆棧指針。就象我們對(duì)返回地址所做的那樣,我們PUSH老的ESP到堆棧,并且利用另外一個(gè)名為EBP的寄存器來(lái)引用被調(diào)用函數(shù)的局部變量。
相關(guān)文章
- “CMOS密碼”就是通常所說(shuō)的“開(kāi)機(jī)密碼”,主要是為了防止別人使用自已的計(jì)算機(jī),設(shè)置的一個(gè)屏障2023-08-01

QQScreenShot之逆向并提取QQ截圖--OCR和其他功能
上一篇文章逆向并提取QQ截圖沒(méi)有提取OCR功能, 再次逆向我發(fā)現(xiàn)是可以本地調(diào)用QQ的OCR的,但翻譯按鈕確實(shí)沒(méi)啥用, 于是Patch了翻譯按鈕事件, 改為了將截圖用百度以圖搜圖搜索.2023-02-04
QQ截圖是我用過(guò)的最好用的截圖工具, 由于基本不在電腦上登QQ了, 于是就想將其提取出獨(dú)立版目前除了屏幕錄制功能其他都逆出來(lái)了, 在此分享一下2023-02-04
非系統(tǒng)分區(qū)使用BitLocker加密導(dǎo)致軟件無(wú)法安裝的解決方法
很多電腦用戶在考慮自己電腦磁盤(pán)分區(qū)安全時(shí)會(huì)采用 Windows 自帶的 BitLocker 加密工具對(duì)電腦磁盤(pán)分區(qū)進(jìn)行加密。但有些人加密后就會(huì)忘記自己設(shè)置的密碼從而導(dǎo)致在安裝其它軟2020-11-25
防止離職員工帶走客戶、防止內(nèi)部員工泄密、避免華為員工泄密事件的發(fā)生
這篇文章為大家詳細(xì)介紹了如何才能防止離職員工帶走客戶、防止內(nèi)部員工泄密、避免華為員工泄密事件的發(fā)生,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-27
徹底防止計(jì)算機(jī)泄密、重要涉密人員離職泄密、涉密人員離崗離職前防范舉
近些年企業(yè)商業(yè)機(jī)密泄漏的事件屢有發(fā)生,這篇文章主要教大家如何徹底防止計(jì)算機(jī)泄密、重要涉密人員離職泄密、告訴大家涉密人員離崗離職前的防范舉措,具有一定的參考價(jià)值,2017-06-27量子計(jì)算機(jī)輕松破解加密算法 如何破解加密算法?
最近有電腦用戶反應(yīng)量子計(jì)算機(jī)可以破解下載的所有的加密算法嗎?其實(shí)也不是不可以,下面虛擬就為大家講解買(mǎi)臺(tái)量子計(jì)算機(jī),如何分分鐘破解加密算法2016-09-26怎么破解Webshell密碼 Burpsuite破解Webshell密碼圖文教程
webshell是以asp、php、jsp或者cgi等網(wǎng)頁(yè)文件形式存在的一種命令執(zhí)行環(huán)境,一種網(wǎng)頁(yè)后門(mén)。黑客通常會(huì)通過(guò)它控制別人網(wǎng)絡(luò)服務(wù)器,那么怎么破解webshell密碼呢?一起來(lái)看看吧2016-09-19針對(duì)Linux系統(tǒng)全盤(pán)加密的啟動(dòng)攻擊
本文討論了針對(duì)Linux系統(tǒng)全盤(pán)加密的冷啟動(dòng)攻擊,大家都認(rèn)為這種攻擊是可行的,但執(zhí)行這么一次攻擊有多難?攻擊的可行性有多少呢?需要的朋友可以參考下2015-12-28防止泄露公司機(jī)密、企業(yè)數(shù)據(jù)防泄密軟件排名、電腦文件加密軟件排行
面對(duì)日漸嚴(yán)重的內(nèi)部泄密事件,我們?nèi)绾问刈o(hù)企業(yè)的核心信息,如何防止內(nèi)部泄密也就成了擺在各個(gè)企業(yè)領(lǐng)導(dǎo)面前的一大問(wèn)題。其實(shí),針對(duì)內(nèi)網(wǎng)安全,防止內(nèi)部信息泄漏早已有了比較2015-12-17






